Adafruit CircuitPython Module Install
To use the PCA9685 with your Adafruit CircuitPython board you'll need to install the Adafruit_CircuitPython_PCA9685 module on your board. Remember this module is for Adafruit CircuitPython firmware and not MicroPython.org firmware!
First make sure you are running the latest version of Adafruit CircuitPython for your board. Next you'll need to install the necessary libraries to use the hardware--read below and carefully follow the referenced steps to find and install these libraries from Adafruit's CircuitPython library bundle (version 20180110 or later).
Bundle Install
For express boards that have extra flash storage, like the Feather/Metro M0 express and Circuit Playground express, you can easily install the necessary libraries with Adafruit's CircuitPython bundle. This is an all-in-one package that includes the necessary libraries to use the PCA9685 and motors with CircuitPython. To install the bundle follow the steps in your board's guide, like these steps for the Feather M0 express board.
Remember for non-express boards like the Trinket M0, Gemma M0, and Feather/Metro M0 basic you'll need to manually install the necessary libraries from the bundle:
- adafruit_pca9685
- adafruit_bus_device
- adafruit_register
- adafruit_motor
If your board supports USB mass storage, like the M0-based boards, then simply drag the files to the board's file system. Note on boards without external SPI flash, like a Feather M0 or Trinket/Gemma M0, you might run into issues on Mac OSX with hidden files taking up too much space when drag and drop copying, see this page for a workaround.
If your board doesn't support USB mass storage, like the ESP8266, then use a tool like ampy to copy the file to the board. You can use the latest version of ampy and its new directory copy command to easily move module directories to the board.
Before continuing make sure your board's lib folder or root filesystem has the adafruit_pca9685, adafruit_bus_device, adafruit_motor, and adafruit_register folders/modules copied over.
Usage
The following section will show how to control the PCA9685 from the board's Python prompt / REPL. You'll learn how to interactively control DC Motors and Steppers by typing in the code below.
First connect to the board's serial REPL so you are at the CircuitPython >>> prompt.
I2C Initialization
First you'll need to initialize the I2C bus for your board. First import the necessary modules:
import board import busio
Now run this command to create the I2C instance using the default SCL and SDA pins (which will be marked on the boards pins if using a Feather or similar Adafruit board):
i2c = busio.I2C(board.SCL, board.SDA)
DC Motors
After initializing the I2C interface you need to import and initialize the PCA9685 class to use it in your own code. We'll use it as our PWM source.
We provide the I2C address here explicitly because the Adafruit Motor FeatherWing adjusts its default address to 0x60 so it doesn't conflict with the Adafruit Servo FeatherWing.
Setting the frequency to 1600 helps smooth the motor control.
import adafruit_pca9685 pca = adafruit_pca9685.PCA9685(i2c, address=0x60) pca.frequency = 1600
Next, we'll import and initialize the DCMotor class from adafruit_motor. This class uses the pwm channels to control the throttle of the motor. Think of it as the gas pedal from a car.
To control the motor, DCMotor needs two PWM channels, one for each wire from the motor. These should never be connected directly to PCA9685 or microcontroller because DCMotors use lots of current. Instead, use a motor driver chip, such as the TB6612, to connect to the motor. The Motor FeatherWing has these built in. This chip is a little weird because it provides a third PWM pin marked PWMX in addition to XIN1 and XIN2. They make this available in case the controller can only PWM on a single pin. Since we can PWM two pins, we hold PWMX high.
Keeping track of which three channels of the PCA9685 to use can be tricky. If you are using the FeatherWing use the MotorFeatherWing class in the Adafruit CircuitPython FeatherWing library to make your life easier. If not, here is the convention for many motor related shields:
- Motor 1 is channels 10 and 9 with 8 held high.
- Motor 2 is channels 11 and 12 with 13 held high.
- Motor 3 is channels 4 and 3 with 2 held high.
- Motor 4 is channels 5 and 6 with 7 held high.
We'll use motor 1 in our example:
from adafruit_motor import motor pwm_channel = pca.channels[8] channel1 = pca.channels[10] channel2 = pca.channels[9] pwm_channel.duty_cycle = 0xffff # hold high motor1 = motor.DCMotor(channel1, channel2)
Note: For small DC motors like sold in the shop you might run into problems with electrical noise they generate and erratic behavior on your board. The SAMD21 Feather M0 boards in particular have been susceptible to this issue. If you see erratic behavior like the motor not spinning or the board resetting at high motor speeds this is likely the problem. See this motor guide FAQ page for information on capacitors you can solder to the motor to reduce noise.
Now to move a motor you can set the throttle attribute. We don't call it speed because it doesn't correlate to a particular number of revolutions per minute (RPM). RPM depends on the motor and the voltage which is unknown.
For example to drive motor M1 forward at a full speed you set it to 1.0:
motor1.throttle = 1.0
To run the motor at half throttle forward use a decimal:
motor1.throttle = 0.5
Or to reverse the direction use a negative throttle:
motor1.throttle = -0.5
You can stop the motor with a throttle of zero:
motor1.throttle = 0
To let the motor coast and then spin freely set throttle to None.
motor1.throttle = None
That's all there is to controlling DC motors with CircuitPython! With DC motors you can build fun moving projects like robots or remote controlled cars that glide around with ease.
Stepper Motors
To control stepper motors you'll need to import the stepper module from adafruit_motor and create an instance of the Stepper class inside of it. Unipolar and Bipolar stepper motors function by PWMing four wires in a particular sequence.
Like DCMotors, stepper motors use a lot of current to function and therefore also need a driver chip. Again, we'll need to hold the pwm pins high for each pair of control wires.
Keeping track of which six channels of the PCA9685 to use can be tricky. If you are using the FeatherWing use the MotorFeatherWing class in the Adafruit CircuitPython FeatherWing library to make your life easier. If not, the convention for many stepper related shields is:
- Stepper 1 is a combination of Motor 1 and Motor 2 as above.
- Stepper 2 is a combination of Motor 3 and Motor 4 as above.
from adafruit_motor import stepper # First part of Stepper 1 is Motor 1 above. pwma = pca.channels[8] ain1 = pca.channels[10] ain2 = pca.channels[9] # Second part of Stepper 1 is Motor 2 above. pwmb = pca.channels[13] bin1 = pca.channels[11] bin2 = pca.channels[12] stepper1 = stepper.Stepper(ain1, ain2, bin1, bin2) # Hold PWM pins high for TB6612 driver pwma.duty_cycle = 0xffff pwmb.duty_cycle = 0xffff
Now you can call the onestep function to move the stepper one 'step'. This function takes two keyword arguments:
- Direction, this should be the constant value stepper.FORWARD or stepper.BACKWARD.
-
Style, this should be one of the values:
- stepper.SINGLE for a full step rotation to a position where one single coil is powered
- stepper.DOUBLE for a full step rotation to position where two coils are powered providing more torque
- stepper.INTERLEAVED for a half step rotation interleaving single and double coil positions and torque
- stepper.MICROSTEP for a microstep rotation to a position where two coils are partially active.
A single coil forward step is default:
stepper1.onestep()
The function returns the current step 'position' in microsteps which can be handy to understand how far the stepper has moved, or you can ignore the result.
To take a double-coil step backward call:
stepper1.onestep(direction=stepper.BACKWARD, style=stepper.DOUBLE)
You can even use a loop to continuously call onestep and move the stepper, for example a loop of 200 microsteps forward for smooth movement:
for i in range(200): stepper1.onestep(style=stepper.MICROSTEP)
That's all there is to controlling a stepper motor from CircuitPython! Steppers are handy motors for when you need smooth or precise control of something--for example 3D printers and CNC machines use steppers to precisely move tools around surfaces.
Page last edited March 08, 2024
Text editor powered by tinymce.