PCA9685 PWM LED/Servo Controller Driver in Rust

PCA9685 displaying a rainbow with 2 RGB LEDs and controlling 5 servo motors. (source code)


Today I present you a Rust driver for the PCA9685 PWM LED / Servo motor controller.

The device

This device is an I2C-bus controlled 16-channel, 12-bit PWM controller. Its outputs can be used to control servo motors or LEDs, for example.

Each channel output has its own 12-bit resolution (4096 steps) fixed frequency individual PWM controller that operates at a programmable frequency from a typical of 24 Hz to 1526 Hz with a duty cycle that is adjustable from 0% to 100%. All outputs are set to the same PWM frequency.

Each channel output can be off or on (no PWM control), or set at its individual PWM controller value. The output driver is programmed to be either open-drain with a 25 mA current sink capability at 5 V or totem pole with a 25 mA sink, 10 mA source capability at 5 V. The PCA9685 operates with a supply voltage range of 2.3 V to 5.5 V and the inputs and outputs are 5.5 V tolerant. LEDs can be directly connected to the outputs (up to 25 mA, 5.5 V) or controlled with external drivers and a minimum amount of discrete components for larger current, higher voltage LEDs, etc.
It is optimized to be used as an LED controller for Red/Green/Blue/Amber (RGBA) color backlighting applications.

Datasheet: PCA9685

Using the driver

To use the device from Rust, you have to add the pwm-pca9685 crate to your project as well as a concrete implementation of the embedded-hal traits. For example if you are using the Raspberry Pi running Linux (see driver-examples for bare-metal hardware):

# Cargo.toml
pwm-pca9685 = "0.2"
linux-embedded-hal = "0.3"

In this example we set a PWM frequency of 60 Hz and a duty cycle of 50% on channel 0 (source):

extern crate linux_embedded_hal as hal;
extern crate pwm_pca9685 as pca9685;
use pca9685::{Channel, Pca9685, SlaveAddr};

fn main() {
let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let mut pwm = Pca9685::new(dev, address);
// This corresponds to a frequency of 60 Hz.

// Turn on channel 0 at 0.
pwm.set_channel_on(Channel::C0, 0).unwrap();

// Turn off channel 0 at 2047, which is 50% in
// the range `[0..4095]`.
pwm.set_channel_off(Channel::C0, 2047).unwrap();

For the title video in this post I crated an example program that runs on the STM32F1 “BluePill” board and displays the colors of the rainbow in two RGB LEDs and moves five servo motors. You can find the application source code here.

In the driver-examples repository you can find further examples which you can adapt to do other things with this device.

Where to go from here?

There is much more information and example programs in the crate documentation.
Please give this driver a try and report any issues you may encounter in the issue tracker.
Feedback, suggestions and improvements are gladly welcome.

What’s next?

I have been writing many other platform-agnostic Rust drivers although I am slow to announce them here. If you want to know what I am currently working on you can follow me on github.

Thanks for reading and stay tuned!

Links: Source code - Crate - Documentation