You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
5 years ago
|
# Stepper Motor Shield/Wing Driver
|
||
|
# Based on Adafruit Motorshield library:
|
||
|
# https://github.com/adafruit/Adafruit_Motor_Shield_V2_Library
|
||
|
# Author: Tony DiCola
|
||
|
import pca9685
|
||
|
|
||
|
|
||
|
# Constants that specify the direction and style of steps.
|
||
|
FORWARD = const(1)
|
||
|
BACKWARD = const(2)
|
||
|
SINGLE = const(1)
|
||
|
DOUBLE = const(2)
|
||
|
INTERLEAVE = const(3)
|
||
|
MICROSTEP = const(4)
|
||
|
|
||
|
# Not a const so users can change this global to 8 or 16 to change step size
|
||
|
MICROSTEPS = 16
|
||
|
|
||
|
# Microstepping curves (these are constants but need to be tuples/indexable):
|
||
|
_MICROSTEPCURVE8 = (0, 50, 98, 142, 180, 212, 236, 250, 255)
|
||
|
_MICROSTEPCURVE16 = (0, 25, 50, 74, 98, 120, 141, 162, 180, 197, 212, 225, 236, 244, 250, 253, 255)
|
||
|
|
||
|
# Define PWM outputs for each of two available steppers.
|
||
|
# Each tuple defines for a stepper: pwma, ain2, ain1, pwmb, bin2, bin1
|
||
|
_STEPPERS = ((8, 9, 10, 13, 12, 11), (2, 3, 4, 7, 6, 5))
|
||
|
|
||
|
|
||
|
class StepperMotor:
|
||
|
def __init__(self, pca, pwma, ain2, ain1, pwmb, bin2, bin1):
|
||
|
self.pca9685 = pca
|
||
|
self.pwma = pwma
|
||
|
self.ain2 = ain2
|
||
|
self.ain1 = ain1
|
||
|
self.pwmb = pwmb
|
||
|
self.bin2 = bin2
|
||
|
self.bin1 = bin1
|
||
|
self.currentstep = 0
|
||
|
|
||
|
def _pwm(self, pin, value):
|
||
|
if value > 4095:
|
||
|
self.pca9685.pwm(pin, 4096, 0)
|
||
|
else:
|
||
|
self.pca9685.pwm(pin, 0, value)
|
||
|
|
||
|
def _pin(self, pin, value):
|
||
|
if value:
|
||
|
self.pca9685.pwm(pin, 4096, 0)
|
||
|
else:
|
||
|
self.pca9685.pwm(pin, 0, 0)
|
||
|
|
||
|
def onestep(self, direction, style):
|
||
|
ocra = 255
|
||
|
ocrb = 255
|
||
|
# Adjust current steps based on the direction and type of step.
|
||
|
if style == SINGLE:
|
||
|
if (self.currentstep//(MICROSTEPS//2)) % 2:
|
||
|
if direction == FORWARD:
|
||
|
self.currentstep += MICROSTEPS//2
|
||
|
else:
|
||
|
self.currentstep -= MICROSTEPS//2
|
||
|
else:
|
||
|
if direction == FORWARD:
|
||
|
self.currentstep += MICROSTEPS
|
||
|
else:
|
||
|
self.currentstep -= MICROSTEPS
|
||
|
elif style == DOUBLE:
|
||
|
if not (self.currentstep//(MICROSTEPS//2)) % 2:
|
||
|
if direction == FORWARD:
|
||
|
self.currentstep += MICROSTEPS//2
|
||
|
else:
|
||
|
self.currentstep -= MICROSTEPS//2
|
||
|
else:
|
||
|
if direction == FORWARD:
|
||
|
self.currentstep += MICROSTEPS
|
||
|
else:
|
||
|
self.currentstep -= MICROSTEPS
|
||
|
elif style == INTERLEAVE:
|
||
|
if direction == FORWARD:
|
||
|
self.currentstep += MICROSTEPS//2
|
||
|
else:
|
||
|
self.currentstep -= MICROSTEPS//2
|
||
|
elif style == MICROSTEP:
|
||
|
if direction == FORWARD:
|
||
|
self.currentstep += 1
|
||
|
else:
|
||
|
self.currentstep -= 1
|
||
|
self.currentstep += MICROSTEPS*4
|
||
|
self.currentstep %= MICROSTEPS*4
|
||
|
ocra = 0
|
||
|
ocrb = 0
|
||
|
if MICROSTEPS == 8:
|
||
|
curve = _MICROSTEPCURVE8
|
||
|
elif MICROSTEPS == 16:
|
||
|
curve = _MICROSTEPCURVE16
|
||
|
else:
|
||
|
raise RuntimeError('MICROSTEPS must be 8 or 16!')
|
||
|
if 0 <= self.currentstep < MICROSTEPS:
|
||
|
ocra = curve[MICROSTEPS - self.currentstep]
|
||
|
ocrb = curve[self.currentstep]
|
||
|
elif MICROSTEPS <= self.currentstep < MICROSTEPS*2:
|
||
|
ocra = curve[self.currentstep - MICROSTEPS]
|
||
|
ocrb = curve[MICROSTEPS*2 - self.currentstep]
|
||
|
elif MICROSTEPS*2 <= self.currentstep < MICROSTEPS*3:
|
||
|
ocra = curve[MICROSTEPS*3 - self.currentstep]
|
||
|
ocrb = curve[self.currentstep - MICROSTEPS*2]
|
||
|
elif MICROSTEPS*3 <= self.currentstep < MICROSTEPS*4:
|
||
|
ocra = curve[self.currentstep - MICROSTEPS*3]
|
||
|
ocrb = curve[MICROSTEPS*4 - self.currentstep]
|
||
|
self.currentstep += MICROSTEPS*4
|
||
|
self.currentstep %= MICROSTEPS*4
|
||
|
# Set PWM outputs.
|
||
|
self._pwm(self.pwma, ocra*16)
|
||
|
self._pwm(self.pwmb, ocrb*16)
|
||
|
latch_state = 0
|
||
|
# Determine which coils to energize:
|
||
|
if style == MICROSTEP:
|
||
|
if 0 <= self.currentstep < MICROSTEPS:
|
||
|
latch_state |= 0x3
|
||
|
elif MICROSTEPS <= self.currentstep < MICROSTEPS*2:
|
||
|
latch_state |= 0x6
|
||
|
elif MICROSTEPS*2 <= self.currentstep < MICROSTEPS*3:
|
||
|
latch_state |= 0xC
|
||
|
elif MICROSTEPS*3 <= self.currentstep < MICROSTEPS*4:
|
||
|
latch_state |= 0x9
|
||
|
else:
|
||
|
latch_step = self.currentstep//(MICROSTEPS//2)
|
||
|
if latch_step == 0:
|
||
|
latch_state |= 0x1 # energize coil 1 only
|
||
|
elif latch_step == 1:
|
||
|
latch_state |= 0x3 # energize coil 1+2
|
||
|
elif latch_step == 2:
|
||
|
latch_state |= 0x2 # energize coil 2 only
|
||
|
elif latch_step == 3:
|
||
|
latch_state |= 0x6 # energize coil 2+3
|
||
|
elif latch_step == 4:
|
||
|
latch_state |= 0x4 # energize coil 3 only
|
||
|
elif latch_step == 5:
|
||
|
latch_state |= 0xC # energize coil 3+4
|
||
|
elif latch_step == 6:
|
||
|
latch_state |= 0x8 # energize coil 4 only
|
||
|
elif latch_step == 7:
|
||
|
latch_state |= 0x9 # energize coil 1+4
|
||
|
# Energize coils as appropriate:
|
||
|
if latch_state & 0x1:
|
||
|
self._pin(self.ain2, True)
|
||
|
else:
|
||
|
self._pin(self.ain2, False)
|
||
|
if latch_state & 0x2:
|
||
|
self._pin(self.bin1, True)
|
||
|
else:
|
||
|
self._pin(self.bin1, False)
|
||
|
if latch_state & 0x4:
|
||
|
self._pin(self.ain1, True)
|
||
|
else:
|
||
|
self._pin(self.ain1, False)
|
||
|
if latch_state & 0x8:
|
||
|
self._pin(self.bin2, True)
|
||
|
else:
|
||
|
self._pin(self.bin2, False)
|
||
|
return self.currentstep
|
||
|
|
||
|
|
||
|
class Steppers:
|
||
|
def __init__(self, i2c, address=0x60, freq=1600):
|
||
|
self.pca9685 = pca9685.PCA9685(i2c, address)
|
||
|
self.pca9685.freq(freq)
|
||
|
|
||
|
def get_stepper(self, num):
|
||
|
pwma, ain2, ain1, pwmb, bin2, bin1 = _STEPPERS[num]
|
||
|
return StepperMotor(self.pca9685, pwma, ain2, ain1, pwmb, bin2, bin1)
|