Adding motors and updating scripts
parent
5ffdc6e63c
commit
3a26cf4b16
@ -0,0 +1,13 @@
|
|||||||
|
# Motors (servos, steppers, DC)
|
||||||
|
|
||||||
|
The modules for this came from this legacy Adafruit repo https://github.com/adafruit/micropython-adafruit-pca9685. Look at it's
|
||||||
|
[Docs](https://micropython-pca9685.readthedocs.io/en/latest/)
|
||||||
|
|
||||||
|
## I2C Servo driver (PCA9685)
|
||||||
|
|
||||||
|
Using the [PCA9685](https://www.adafruit.com/product/815) from adafruit, we can drive multiple
|
||||||
|
servos using PWM. A large amount of the content for this read my comes from the adafruit article
|
||||||
|
on [PCA9685 with Micropython](https://learn.adafruit.com/micropython-hardware-pca9685-pwm-and-servo-driver/micropython)
|
||||||
|
|
||||||
|
The git repo https://github.com/adafruit/micropython-adafruit-pca9685 is still up. Look at it's
|
||||||
|
[Docs](https://micropython-pca9685.readthedocs.io/en/latest/)
|
@ -0,0 +1,45 @@
|
|||||||
|
import pca9685
|
||||||
|
|
||||||
|
|
||||||
|
_DC_MOTORS = ((8, 9, 10), (13, 12, 11), (2, 3, 4), (7, 6, 5))
|
||||||
|
|
||||||
|
|
||||||
|
class DCMotors:
|
||||||
|
def __init__(self, i2c, address=0x60, freq=1600):
|
||||||
|
self.pca9685 = pca9685.PCA9685(i2c, address)
|
||||||
|
self.pca9685.freq(freq)
|
||||||
|
|
||||||
|
def _pin(self, pin, value=None):
|
||||||
|
if value is None:
|
||||||
|
return bool(self.pca9685.pwm(pin)[0])
|
||||||
|
if value:
|
||||||
|
self.pca9685.pwm(pin, 4096, 0)
|
||||||
|
else:
|
||||||
|
self.pca9685.pwm(pin, 0, 0)
|
||||||
|
|
||||||
|
def speed(self, index, value=None):
|
||||||
|
pwm, in2, in1 = _DC_MOTORS[index]
|
||||||
|
if value is None:
|
||||||
|
value = self.pca9685.duty(pwm)
|
||||||
|
if self._pin(in2) and not self._pin(in1):
|
||||||
|
value = -value
|
||||||
|
return value
|
||||||
|
if value > 0:
|
||||||
|
# Forward
|
||||||
|
self._pin(in2, False)
|
||||||
|
self._pin(in1, True)
|
||||||
|
elif value < 0:
|
||||||
|
# Backward
|
||||||
|
self._pin(in1, False)
|
||||||
|
self._pin(in2, True)
|
||||||
|
else:
|
||||||
|
# Release
|
||||||
|
self._pin(in1, False)
|
||||||
|
self._pin(in2, False)
|
||||||
|
self.pca9685.duty(pwm, abs(value))
|
||||||
|
|
||||||
|
def brake(self, index):
|
||||||
|
pwm, in2, in1 = _DC_MOTORS[index]
|
||||||
|
self._pin(in1, True)
|
||||||
|
self._pin(in2, True)
|
||||||
|
self.pca9685.duty(pwm, 0)
|
@ -0,0 +1,59 @@
|
|||||||
|
import ustruct
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class PCA9685:
|
||||||
|
def __init__(self, i2c, address=0x40):
|
||||||
|
self.i2c = i2c
|
||||||
|
self.address = address
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def _write(self, address, value):
|
||||||
|
self.i2c.writeto_mem(self.address, address, bytearray([value]))
|
||||||
|
|
||||||
|
def _read(self, address):
|
||||||
|
return self.i2c.readfrom_mem(self.address, address, 1)[0]
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._write(0x00, 0x00) # Mode1
|
||||||
|
|
||||||
|
def freq(self, freq=None):
|
||||||
|
if freq is None:
|
||||||
|
return int(25000000.0 / 4096 / (self._read(0xfe) - 0.5))
|
||||||
|
prescale = int(25000000.0 / 4096.0 / freq + 0.5)
|
||||||
|
old_mode = self._read(0x00) # Mode 1
|
||||||
|
self._write(0x00, (old_mode & 0x7F) | 0x10) # Mode 1, sleep
|
||||||
|
self._write(0xfe, prescale) # Prescale
|
||||||
|
self._write(0x00, old_mode) # Mode 1
|
||||||
|
time.sleep_us(5)
|
||||||
|
self._write(0x00, old_mode | 0xa1) # Mode 1, autoincrement on
|
||||||
|
|
||||||
|
def pwm(self, index, on=None, off=None):
|
||||||
|
if on is None or off is None:
|
||||||
|
data = self.i2c.readfrom_mem(self.address, 0x06 + 4 * index, 4)
|
||||||
|
return ustruct.unpack('<HH', data)
|
||||||
|
data = ustruct.pack('<HH', on, off)
|
||||||
|
self.i2c.writeto_mem(self.address, 0x06 + 4 * index, data)
|
||||||
|
|
||||||
|
def duty(self, index, value=None, invert=False):
|
||||||
|
if value is None:
|
||||||
|
pwm = self.pwm(index)
|
||||||
|
if pwm == (0, 4096):
|
||||||
|
value = 0
|
||||||
|
elif pwm == (4096, 0):
|
||||||
|
value = 4095
|
||||||
|
value = pwm[1]
|
||||||
|
if invert:
|
||||||
|
value = 4095 - value
|
||||||
|
return value
|
||||||
|
if not 0 <= value <= 4095:
|
||||||
|
raise ValueError("Out of range")
|
||||||
|
if invert:
|
||||||
|
value = 4095 - value
|
||||||
|
if value == 0:
|
||||||
|
self.pwm(index, 0, 4096)
|
||||||
|
elif value == 4095:
|
||||||
|
self.pwm(index, 4096, 0)
|
||||||
|
else:
|
||||||
|
self.pwm(index, 0, value)
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
import pca9685
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
class Servos:
|
||||||
|
def __init__(self, i2c, address=0x40, freq=50, min_us=600, max_us=2400,
|
||||||
|
degrees=180):
|
||||||
|
self.period = 1000000 / freq
|
||||||
|
self.min_duty = self._us2duty(min_us)
|
||||||
|
self.max_duty = self._us2duty(max_us)
|
||||||
|
self.degrees = degrees
|
||||||
|
self.freq = freq
|
||||||
|
self.pca9685 = pca9685.PCA9685(i2c, address)
|
||||||
|
self.pca9685.freq(freq)
|
||||||
|
|
||||||
|
def _us2duty(self, value):
|
||||||
|
return int(4095 * value / self.period)
|
||||||
|
|
||||||
|
def position(self, index, degrees=None, radians=None, us=None, duty=None):
|
||||||
|
span = self.max_duty - self.min_duty
|
||||||
|
if degrees is not None:
|
||||||
|
duty = self.min_duty + span * degrees / self.degrees
|
||||||
|
elif radians is not None:
|
||||||
|
duty = self.min_duty + span * radians / math.radians(self.degrees)
|
||||||
|
elif us is not None:
|
||||||
|
duty = self._us2duty(us)
|
||||||
|
elif duty is not None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return self.pca9685.duty(index)
|
||||||
|
duty = min(self.max_duty, max(self.min_duty, int(duty)))
|
||||||
|
self.pca9685.duty(index, duty)
|
||||||
|
|
||||||
|
def release(self, index):
|
||||||
|
self.pca9685.duty(index, 0)
|
@ -0,0 +1,170 @@
|
|||||||
|
# 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)
|
@ -0,0 +1,14 @@
|
|||||||
|
export AMPY_PORT=/dev/cu.SLAB_USBtoUART
|
||||||
|
export BAUD_RATE=115200
|
||||||
|
|
||||||
|
function repl_connect (){
|
||||||
|
echo "Connecting to $AMPY_PORT"
|
||||||
|
screen $AMPY_PORT $BAUD_RATE
|
||||||
|
}
|
||||||
|
|
||||||
|
function upy (){
|
||||||
|
_command=${1}
|
||||||
|
shift
|
||||||
|
# echo "command $_command params $@"
|
||||||
|
ampy -p $AMPY_PORT -b $BAUD_RATE ${_command} $@
|
||||||
|
}
|
Loading…
Reference in New Issue