PCA9685 16 Channel 12 Bit PWM Servo Driver

From Wiki
Jump to: navigation, search


MG 1687.JPG
The PCA9685 is a 16-channel I2C-bus controlled LED controller optimized for Red/Green/Blue/Amber (RGBA) color backlighting applications. Each LED output has individual 12-bit resolution (4096 steps) PWM controller with a fixed frequency. The controller operates at a programmable frequency from a typical 24 Hz to 1526 Hz with a duty cycle that is adjustable from 0% to 100% so the LED can be set to output a specific brightness. All outputs are set to the same PWM frequency.
With the PCA9685 as the master chip, the 16-channel 12-bit PWM Servo Driver only needs 2 pins to control 16 servos, thus greatly reducing the occupant I/Os. Moreover, it can be connected to 62 driver boards at most in a cascade way, which means it will be able to control 992 servos in total.


1. Contains an I2C-controlled PWM driver with a built-in clock. It means, unlike the TLC5940 family, you do not need to continuously send it signals tying up your microcontroller; it's completely free running!
2. 5V compliant, which means you can control it from a 3.3V microcontroller and still safely drive up to 6V outputs, which is good when you want to control white or blue LEDs with a 3.4V+ forward voltage
3. Supports using only two pins to control 16 free-running PWM outputs – you can even chain up 62 breakouts to control up to 992 PWM outputs.
4. 3 pin connectors in 4 groups, so you can plug in 16 servos at one time (Servo plugs are slightly wider than 0.1" so you can only stack 4 adjacent ones on 0.1"-hole female headers.
5. 12-bit resolution for each output - for servos, that means about 4us resolution at an update rate of 60Hz.
6. Size: 62 x 26 mm


Based on the above introduction, we can see the module applies the PCA9685 chip as the controller. It can control the output of the 16-channel PWM values. We can manage the PWM frequency and duty cycle to control the servo precisely by programming the controller.
The turn-on time of each LED driver output and the duty cycle of PWM can be controlled independently using the LEDn_ON and LEDn_OFF registers.
If we set the LED_ON time to 409 and LED_OFF time to 1228, the duty cycle of PWM should be:
(1228-409/4096) x 100%= 20%
The servo works like this: The PWM signal captured by the receiving channel is transmitted to the signal demodulation circuit, and a DC offset voltage is generated. Next, this voltage will be compared with the potentiometer’s voltage, and then the voltage drop between them will be input into the motor driving integrated circuit to make the motor rotate clockwise or counter-clockwise. When the rotating speed reaches a certain value, it will drive the potentiometer R0 to spin by the cascaded gear reducer. The motor would not stop rotating until the voltage drop decreases to 0. The servo is controlled by PWM signal, i.e., the changed duty cycle decides where the servo rotates to.

Control a Servo

Controlling a servo rotate from 0°to 180°.
-1 x PCA968 Servo Driver
-1 x Arduino Uno
-1 x Servo
-1 x 18650 Battery Holder
-2 x 18650 Li-on Batteries
-Several jump wires

Step 1. Wiring
Since the servo will use large amount of power, you need to provide this module with an independent power supply so as to ensure the servo will have adequate supply.
Connect the independent power supply to the module as shown below:

Independent Power Supply 16-Channel PWM Servo Driver

Connect the Arduino Uno to the module:

Arduino Uno 16-Channel PWM Servo Driver

Connect the servo to the module:

Servo 16-Channel PWM Servo Driver
Orange wire PWM
Red wire V+
Brown wire GND

Step 2. Install the library
Open the Arduino software, and select Sketch -> Include Library -> Manage Libraries.
Search out the library file Adafruit-PWM-Servo-Driver-Library and click Install.

Step 3. Copy the code to your IDE (the "//" is used to comment out contents behind it)


#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define MIN_PULSE_WIDTH       650
#define MAX_PULSE_WIDTH       2350
#define DEFAULT_PULSE_WIDTH   1500
#define FREQUENCY             50
// our servo # counter
uint8_t servonum = 0;

void setup() {
  Serial.println("16 channel Servo test!");
  pwm.setPWMFreq(FREQUENCY);  // Analog servos run at ~60 Hz updates

void loop() {
  pwm.setPWM(0, 0, pulseWidth(0));
  pwm.setPWM(0, 0, pulseWidth(45));
  pwm.setPWM(0, 0, pulseWidth(90));
  pwm.setPWM(0, 0, pulseWidth(135));
  pwm.setPWM(0, 0, pulseWidth(180));
  pwm.setPWM(0, 0, pulseWidth(135));
  pwm.setPWM(0, 0, pulseWidth(90));
  pwm.setPWM(0, 0, pulseWidth(45));

int pulseWidth(int angle)
  int pulse_wide, analog_value;
  pulse_wide   = map(angle, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
  analog_value = int(float(pulse_wide) / 1000000 * FREQUENCY * 4096);
  return analog_value;

Save the code and click Upload. When upload is done successfully, you will see the servo rotate from 0° to 180°, then from 180° to 0°, back and forth.
If you want to adjust the servo to rotate to a certain position, just try to modify the angle value in the code.
Note: If you want to control 16 servos at the same time, you can open Servo.ino, which is used to rotate the 16 servos from 0° to 180°, then return to 0°. PCA9685-5.png

Connect Multiple Control Boards in Serial

You can connect more than one control board (62 at most) in serial to control more servos. Here we'll see how to connect two. The case is similar for connecting more than two. There are pins at the two ends of the board and you can connect those at one end of a board and pins at the other end of another board, with 6 jumper wires, as shown below.

Address the Boards

Each board in the chain must be assigned a unique address. This is done with the address soldering pads on the upper right edge of the board. The I2C base address for each board is 0x40. The binary address that you program with the address soldering pads is added to the base I2C address. To program the address offset, use a drop of solder to bridge the corresponding address jumper for each binary '1' in the address.

Board 0: Address = 0x40 Offset = binary 00000 (no jumpers required)
Board 1: Address = 0x41 Offset = binary 00001 (bridge A0 as in the photo above)
Board 2: Address = 0x42 Offset = binary 00010 (bridge A1)
Board 3: Address = 0x43 Offset = binary 00011 (bridge A0 & A1)
Board 4: Address = 0x44 Offset = binary 00100 (bridge A2)

State the objects

After assigning the address, you need to state an independent object for each board in the code. See the code below:

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);
void setup() {
  Serial.println("16 channel PWM test!");
  pwm1.setPWMFreq(1600);  // This is the maximum PWM frequency
  pwm2.setPWMFreq(1600);  // This is the maximum PWM frequency

After stating the objects, you can now control the servos via programming as you like!