use std::time::Duration;
use rppal::i2c::I2c;
pub const PCA9685_ADDRESS: u16 = 0x40;
pub const MODE1: u8 = 0x00;
pub const MODE2: u8 = 0x01;
pub const SUBADR1: u8 = 0x02;
pub const SUBADR2: u8 = 0x03;
pub const SUBADR3: u8 = 0x04;
pub const PRESCALE: u8 = 0xFE;
pub const LED0_ON_L: u8 = 0x06;
pub const LED0_ON_H: u8 = 0x07;
pub const LED0_OFF_L: u8 = 0x08;
pub const LED0_OFF_H: u8 = 0x09;
pub const ALL_LED_ON_L: u8 = 0xFA;
pub const ALL_LED_ON_H: u8 = 0xFB;
pub const ALL_LED_OFF_L: u8 = 0xFC;
pub const ALL_LED_OFF_H: u8 = 0xFD;
pub const RESTART: u8 = 0x80;
pub const SLEEP: u8 = 0x10;
pub const ALLCALL: u8 = 0x01;
pub const INVRT: u8 = 0x10;
pub const OUTDRV: u8 = 0x04;
pub const SWRST: u8 = 0x06;
pub struct Pca9685 {
i2c: I2c,
}
impl Pca9685 {
pub fn new() -> rppal::i2c::Result<Self> {
let mut i2c = I2c::new()?;
i2c.set_slave_address(PCA9685_ADDRESS)?;
Ok(Self { i2c })
}
pub fn init(&mut self) -> rppal::i2c::Result<()> {
self.set_all_pwm(0, 0)?;
self.i2c.smbus_write_byte(MODE2, OUTDRV)?;
self.i2c.smbus_write_byte(MODE1, ALLCALL)?;
std::thread::sleep(Duration::from_millis(5));
let mode1 = self.i2c.smbus_read_byte(MODE1)?;
let mode1 = mode1 & !SLEEP;
self.i2c.smbus_write_byte(MODE1, mode1)?;
std::thread::sleep(Duration::from_millis(5));
Ok(())
}
pub fn set_pwm_freq(&mut self, freq_hz: f32) -> rppal::i2c::Result<()> {
let prescaleval = 25e6 / 4096.0 / freq_hz - 1.0;
let prescale = (prescaleval + 0.5).floor() as u8;
let old_mode = self.i2c.smbus_read_byte(MODE1)?;
let new_mode = old_mode & 0x7F | SLEEP;
self.i2c.smbus_write_byte(MODE1, new_mode)?;
self.i2c.smbus_write_byte(PRESCALE, prescale)?;
self.i2c.smbus_write_byte(MODE1, old_mode)?;
std::thread::sleep(Duration::from_millis(5));
self.i2c.smbus_write_byte(MODE1, old_mode | 0xA1)
}
pub fn set_pwm(&mut self, channel: u8, on: u16, off: u16) -> rppal::i2c::Result<()> {
self.i2c
.smbus_write_byte(LED0_ON_L + 4 * channel, (on & 0xFF) as u8)?;
self.i2c
.smbus_write_byte(LED0_ON_H + 4 * channel, (on >> 8) as u8)?;
self.i2c
.smbus_write_byte(LED0_OFF_L + 4 * channel, (off & 0xFF) as u8)?;
self.i2c
.smbus_write_byte(LED0_OFF_H + 4 * channel, (off >> 8) as u8)
}
pub fn set_all_pwm(&mut self, on: u16, off: u16) -> rppal::i2c::Result<()> {
self.i2c.smbus_write_byte(ALL_LED_ON_L, (on & 0xFF) as u8)?;
self.i2c.smbus_write_byte(ALL_LED_ON_H, (on >> 8) as u8)?;
self.i2c
.smbus_write_byte(ALL_LED_OFF_L, (off & 0xFF) as u8)?;
self.i2c.smbus_write_byte(ALL_LED_OFF_H, (off >> 8) as u8)
}
}