I am trying to send a set of pulses equally spaced to 4 stepper motors drivers (on different GPIOs) at the same time.
These pulses I am wanting to be around 10 microseconds in width so more precise timing than time.sleep can offer.
I have tried to use the pigpio library to accomplish this.
I have not used the built in PWM mode as I am wanting to send a certain number of pulses. (I realise that you could turn the PWM on and off in a specified time to get a specified number of pulses but am wanting to avoid this)
I thought the wave_create function would work well but I have multiple issues:
1st: The pulses are twice as long as I set them (I have fudged the code to mask this, but this may be me not understanding the commands)
2nd: Previously setup waveforms are being cancelled when I start another one
Here is a zoom-in on a single pulse (the delay in the pigpio.pulse is set to 50 microseconds yet on the scope it is 100 microseconds)
Here is a simplified section of my code that replicates the error I was having with the waveforms being cancelled
import pigpio
motorStepPins = (18, 23, 24, 25) # These are the GPIO addresses for the motor wires
motorDirectionPins = (4, 17, 27, 22)
motorEnablePins = (5, 6, 13, 26)
numberOfSteps = (100, 50, 20, 10) # This is the number of steps that each motor will advance
motorNumbers = (0, 1, 2, 3)
pulseWidth = 100 # The pulsewidth in microseconds
cycleTime = 20 # time that the number of steps needs to be completed in
motors = pigpio.pi() # This initiates pigpio
for motorPin in motorStepPins: # defines the mode of all the motor pins
motors.set_mode(motorPin, pigpio.OUTPUT)
for motorPin in motorDirectionPins:
motors.set_mode(motorPin, pigpio.OUTPUT)
for motorPin in motorEnablePins:
motors.set_mode(motorPin, pigpio.OUTPUT)
motors.wave_clear()
packages = [0, 0, 0, 0]
for motor in motorNumbers: # iterates through each motor
pulses = []
for i in range(numberOfSteps[motor]): # This generates a certain number of steps equally spaced in the cycletime
pulses.append(pigpio.pulse(1<<motorStepPins[motor], 0, int(pulseWidth / 2)))
pulses.append(pigpio.pulse(0, 1<<motorStepPins[motor], int(((cycleTime*1000/numberOfSteps[motor])-pulseWidth)/2)))
motors.wave_add_generic(pulses)
packages[motor] = motors.wave_create()
for motor in motorNumbers:
motors.write(motorDirectionPins[motor], 1)
motors.wave_send_once(packages[motor])
Here we can see that after each successive motor.wave_send_once it cancelled the squarewave that had been started on the other pin.
import pigpio
motorStepPins = (18, 23, 24, 25) # These are the GPIO addresses for the motor wires
motorDirectionPins = (4, 17, 27, 22)
motorEnablePins = (5, 6, 13, 26)
numberOfSteps = (100, 50, 20, 10) # This is the number of steps that each motor will advance
motorNumbers = (0, 1, 2, 3)
pulseWidth = 100 # The pulsewidth in microseconds
cycleTime = 20 # time that the number of steps needs to be completed in
motor1 = pigpio.pi() # This initiates pigpio
motor2 = pigpio.pi()
motor3 = pigpio.pi()
motor4 = pigpio.pi()
motors = [motor1, motor2, motor3, motor4]
for motorPin in enumerate(motorStepPins): # defines the mode of all the motor pins
motors[motorPin[0]].set_mode(motorPin[1], pigpio.OUTPUT)
for motorPin in enumerate(motorDirectionPins): # defines the mode of all the motor pins
motors[motorPin[0]].set_mode(motorPin[1], pigpio.OUTPUT)
for motorPin in enumerate(motorEnablePins): # defines the mode of all the motor pins
motors[motorPin[0]].set_mode(motorPin[1], pigpio.OUTPUT)
for motor in motors:
motor.wave_clear()
for motor in enumerate(motors): # iterates through each motor
pulses = []
for i in range(numberOfSteps[motor[0]]): # This generates a certain number of steps equally spaced in the cycletime
pulses.append(pigpio.pulse(1<<motorStepPins[motor[0]], 0, int(pulseWidth / 2)))
pulses.append(pigpio.pulse(0, 1<<motorStepPins[motor[0]], int(((cycleTime*1000/numberOfSteps[motor[0]])-pulseWidth)/2)))
motor[1].wave_add_generic(pulses)
package = motor[1].wave_create()
motor[1].wave_send_once(package)
Here we can see that despite each motor (GPIO Channel) being on a different instance of pigpio.pi() the squarewaves were stopped each time a wave_send_once was called.
I believe the increase in number of pulses being displayed is due to it taking more time between each wave_send_once being called.
I am running this on a raspberry pi 3 and python 3.7.3 32-bit.
Any help would be great as I have been stuck on this for a while.
EDIT with the answer:
Joan's answer was extremely helpful and with their input I produced this (probably incredibly inefficient) working program:
import pigpio
import numpy as np
motorStepPins = (18, 23, 24, 25) # These are the GPIO addresses for the motor wires
motorDirectionPins = (4, 17, 27, 22)
motorEnablePins = (5, 6, 13, 26)
numberOfSteps = (3, 5, 7, 11) # This is the number of steps that each motor will advance
motorNumbers = (0, 1, 2, 3)
pulseWidth = 100 # The pulsewidth in microseconds
cycleTime = 20 # time that the number of steps needs to be completed in
motors = pigpio.pi() # This initiates pigpio
for motorPin in motorStepPins: # defines the mode of all the motor pins
motors.set_mode(motorPin, pigpio.OUTPUT)
for motorPin in motorDirectionPins:
motors.set_mode(motorPin, pigpio.OUTPUT)
for motorPin in motorEnablePins:
motors.set_mode(motorPin, pigpio.OUTPUT)
motors.wave_clear()
toggleDataType = [('name', int), ('state', int), ('time', float)] # This is the data frmat for each toggle event
toggles = []
for motor in motorNumbers: # iterates through each motor
for toggle in range(numberOfSteps[motor]): # for each pulse it creates a toggle high and a toggle low event
toggles.append((motor, 1, (toggle * 1000 * cycleTime / numberOfSteps[motor])))
toggles.append((motor, 0, (toggle * 1000 * cycleTime / numberOfSteps[motor] + pulseWidth)))
toggles = np.array(toggles, dtype = toggleDataType)
toggles = np.sort(toggles, order = 'time') # Here we have converted to a numpy array in oder to sort the list by time
toggles = toggles.tolist()
pulses = []
for event in enumerate(toggles): # This generates a certain number of steps equally spaced in the cycletime
if (event[0]==len(toggles)-1): # This handles the last event not having a period (I don't think entirely necesary but looks nice at least)
if event[1][1]:
pulses.append(pigpio.pulse(1<<motorStepPins[event[1][0]], 0, int((cycleTime * 1000 - event[1][2]) / 2)))
else:
pulses.append(pigpio.pulse(0, 1<<motorStepPins[event[1][0]], int((cycleTime * 1000 - event[1][2]) / 2)))
else:
if event[1][1]:
pulses.append(pigpio.pulse(1<<motorStepPins[event[1][0]], 0, int((toggles[event[0]+1][2] - event[1][2]) / 2)))
else:
pulses.append(pigpio.pulse(0, 1<<motorStepPins[event[1][0]], int((toggles[event[0]+1][2] - event[1][2]) / 2)))
motors.wave_add_generic(pulses)
package = motors.wave_create()
motors.wave_send_once(package)
and that is the image I wanted all along