Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
tdicola committed Jan 19, 2017
0 parents commit ff3b400
Show file tree
Hide file tree
Showing 16 changed files with 1,166 additions and 0 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2016 Radomir Dopieralski, written for Adafruit Industries

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Micropython driver for 16-channel, 12-bit PWM chip the pca9685

Full documentation at http://micropython-pca9685.readthedocs.io/
Empty file added adafruit_pca9685/__init__.py
Empty file.
45 changes: 45 additions & 0 deletions adafruit_pca9685/motor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from adafruit_pca9685 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)
72 changes: 72 additions & 0 deletions adafruit_pca9685/pca9685.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import ustruct
import time

from adafruit_register import i2c_struct
from adafruit_bus_device import i2c_device


class PCA9685:
# Registers:
mode1_reg = i2c_struct.Struct(0x00, '<B')
prescale_reg = i2c_struct.Struct(0xFE, '<B')
pwm_regs = (i2c_struct.Struct(0x06, '<HH'), # PWM 0
i2c_struct.Struct(0x0A, '<HH'), # PWM 1
i2c_struct.Struct(0x0E, '<HH'), # PWM 2
i2c_struct.Struct(0x12, '<HH'), # PWM 3
i2c_struct.Struct(0x16, '<HH'), # PWM 4
i2c_struct.Struct(0x1A, '<HH'), # PWM 5
i2c_struct.Struct(0x1E, '<HH'), # PWM 6
i2c_struct.Struct(0x22, '<HH'), # PWM 7
i2c_struct.Struct(0x26, '<HH'), # PWM 8
i2c_struct.Struct(0x2A, '<HH'), # PWM 9
i2c_struct.Struct(0x2E, '<HH'), # PWM 10
i2c_struct.Struct(0x32, '<HH'), # PWM 11
i2c_struct.Struct(0x36, '<HH'), # PWM 12
i2c_struct.Struct(0x3A, '<HH'), # PWM 13
i2c_struct.Struct(0x3E, '<HH'), # PWM 14
i2c_struct.Struct(0x42, '<HH')) # PWM 15

def __init__(self, i2c, address=0x40):
self.i2c_device = i2c_device.I2CDevice(i2c, address)
self.reset()

def reset(self):
self.mode1_reg = (0x00,) # Mode1

def freq(self, freq=None):
if freq is None:
return int(25000000.0 / 4096 / (self.prescale_reg[0] - 0.5))
prescale = int(25000000.0 / 4096.0 / freq + 0.5)
old_mode = self.mode1_reg[0] # Mode 1
self.mode1_reg = ((old_mode & 0x7F) | 0x10,) # Mode 1, sleep
self.prescale_reg = (prescale,) # Prescale
self.mode1_reg = (old_mode,) # Mode 1
time.sleep(0.005)
self.mode1_reg = (old_mode | 0xa1,) # Mode 1, autoincrement on

def pwm(self, index, on=None, off=None):
if on is None or off is None:
return self.pwm_regs[index].__get__(self)
self.pwm_regs[index].__set__(self, (on, off))

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)
36 changes: 36 additions & 0 deletions adafruit_pca9685/servo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import math

from adafruit_pca9685 import pca9685


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)
170 changes: 170 additions & 0 deletions adafruit_pca9685/stepper.py
Original file line number Diff line number Diff line change
@@ -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
from adafruit_pca9685 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)
Loading

0 comments on commit ff3b400

Please sign in to comment.