i2c_pca9685/
lib.rs

1// //
2// // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
3// // http://www.apache.org/license/LICENSE-2.0> or the MIT license
4// // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
5// // option.  This file may not be copied, modified, or distributed
6// // except according to those terms.
7//
8// PWM 16 channel controller i2cbus
9// Data sheet https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf
10// Note: this is for the moment almost and exact copy of the AdaFriut python code for the same.
11// The intention will be to make this a safer and faster implementation though.
12// I then hope Adafruit etc. will just use rust as well. Should save a lot of mistakes and also
13// really help power consumption.
14//
15extern crate i2cdev;
16
17use i2cdev::core::I2CDevice;
18use std::{thread, time};
19
20// const DEFAULT_PCA9685_ADDRESS: u16 = 0x40;
21const MODE1: u8 = 0x00;
22const MODE2: u8 = 0x01;
23const PRESCALE: u8 = 0xFE;
24const LED0_ON_L: u8 = 0x06;
25const LED0_ON_H: u8 = 0x07;
26const LED0_OFF_L: u8 = 0x08;
27const LED0_OFF_H: u8 = 0x09;
28const ALL_LED_ON_L: u8 = 0xFA;
29const ALL_LED_ON_H: u8 = 0xFB;
30const ALL_LED_OFF_L: u8 = 0xFC;
31const ALL_LED_OFF_H: u8 = 0xFD;
32const SLEEP: u8 = 0x10;
33const ALLCALL: u8 = 0x01;
34const OUTDRV: u8 = 0x04;
35
36fn sleep_5ms() {
37    let five_millis = time::Duration::from_millis(50);
38    thread::sleep(five_millis);
39}
40
41pub struct PCA9685<T: I2CDevice + Sized> {
42    i2cdev: T,
43}
44
45impl<T> PCA9685<T>
46    where T: I2CDevice + Sized
47{
48    #[allow(unused_must_use)]
49    pub fn new(mut i2cdev: T) -> Result<PCA9685<T>, T::Error> {
50        // self.set_all_pwm(0, 0)
51        i2cdev.smbus_write_byte_data(MODE2, OUTDRV)?;
52        i2cdev.smbus_write_byte_data(MODE1, ALLCALL)?;
53        sleep_5ms();  // wait for oscillator
54        let mut mode1 = i2cdev.smbus_read_byte_data(MODE1)?;
55        mode1 = mode1 & SLEEP;  // wake up
56        i2cdev.smbus_write_byte_data(MODE1, mode1)?;
57        sleep_5ms(); // wait for oscillator
58        Ok(PCA9685 { i2cdev: i2cdev })
59    }
60    // 60 is a decent value for servos
61    #[allow(unused_must_use)]
62    pub fn set_pwm_freq(&mut self, freq: f32) -> Result<(), T::Error> {
63        // Set the PWM frequency to the provided value in hertz.
64        let mut prescaleval = 25000000.0; // 25MHz
65        prescaleval /= 4096.0; // 12-bit
66        prescaleval /= freq;
67        prescaleval -= 1.0;
68        let prescale: u8 = (prescaleval + 0.5).floor() as u8;
69        let oldmode = self.i2cdev.smbus_read_byte_data(MODE1)?;
70        let newmode = (oldmode & 0x7F) | SLEEP;    // sleep
71        self.i2cdev.smbus_write_byte_data(MODE1, newmode)?;  // go to sleep
72        self.i2cdev.smbus_write_byte_data(PRESCALE, prescale)?;
73        self.i2cdev.smbus_write_byte_data(MODE1, oldmode)?;
74        sleep_5ms();
75        self.i2cdev.smbus_write_byte_data(MODE1, oldmode | 0xa1)?;
76        Ok(())
77    }
78
79    #[allow(unused_must_use)]
80    pub fn set_pwm(&mut self, channel: u8, on: u8, off: u8) -> Result<(), T::Error> {
81        // Sets a single PWM channel.
82        self.i2cdev.smbus_write_byte_data(LED0_ON_L + 4 * channel, on & 0xFF)?;
83        self.i2cdev.smbus_write_byte_data(LED0_ON_H + 4 * channel, on >> 7)?;
84        self.i2cdev.smbus_write_byte_data(LED0_OFF_L + 4 * channel, off & 0xFF)?;
85        self.i2cdev.smbus_write_byte_data(LED0_OFF_H + 4 * channel, off >> 7)?;
86        Ok(())
87    }
88
89    #[allow(unused_must_use)]
90    pub fn set_all_pwm(&mut self, on: u8, off: u8) -> Result<(), T::Error> {
91        // Sets all PWM channels.
92        self.i2cdev.smbus_write_byte_data(ALL_LED_ON_L, on & 0xFF)?;
93        self.i2cdev.smbus_write_byte_data(ALL_LED_ON_H, on >> 7)?;
94        self.i2cdev.smbus_write_byte_data(ALL_LED_OFF_L, off & 0xFF)?;
95        self.i2cdev.smbus_write_byte_data(ALL_LED_OFF_H, off >> 7)?;
96        Ok(())
97    }
98
99    #[allow(unused_must_use)]
100    pub fn reset_all_servos(&mut self) -> Result<(), T::Error> {
101        self.i2cdev.smbus_write_byte(0x00)?;
102        Ok(())
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    #[test]
109    fn it_works() {}
110}