Skip to main content

pamoja_actuators/
pca9685.rs

1//! NXP PCA9685 16-channel, 12-bit PWM controller.
2//!
3//! The PCA9685 drives sixteen independent PWM outputs at one shared frequency, which
4//! is what makes it the usual way to run a bank of servos, dimmable LEDs, or the
5//! speed and direction inputs of motor drivers from a single I2C device. This module
6//! builds the values that program it: the prescaler that sets the output frequency,
7//! the address of each channel's registers, and the 12-bit on/off word that sets a
8//! channel's phase and duty, with the full-on and full-off encodings the datasheet
9//! defines.
10//!
11//! It is pure logic: a caller writes the bytes to the device over whatever performs
12//! the I2C transfers.
13
14#[cfg(feature = "embedded-hal")]
15mod driver;
16#[cfg(feature = "embedded-hal")]
17pub mod sim;
18
19#[cfg(feature = "embedded-hal")]
20pub use driver::{Output, Outputs, Pca9685};
21
22/// The I2C address with the six address pins low.
23pub const DEFAULT_I2C_ADDRESS: u8 = 0x40;
24
25/// The general-call address the software reset is sent to.
26pub const SOFTWARE_RESET_ADDRESS: u8 = 0x00;
27
28/// The one data byte the software reset carries; any other byte is not acknowledged.
29pub const SOFTWARE_RESET_BYTE: u8 = 0x06;
30
31/// How long the oscillator takes to run after the SLEEP bit is cleared, in
32/// microseconds; channel timing is not guaranteed inside this window and the
33/// RESTART bit must not be written before it has passed.
34pub const OSCILLATOR_STARTUP_MICROS: u32 = 500;
35
36/// The reset value of MODE2: totem-pole outputs, not inverted, change on STOP.
37pub const MODE2_RESET: u8 = 0x04;
38
39/// The PCA9685 register addresses.
40pub mod register {
41    /// Mode register 1.
42    pub const MODE1: u8 = 0x00;
43    /// Mode register 2.
44    pub const MODE2: u8 = 0x01;
45    /// I2C-bus subaddress 1.
46    pub const SUBADR1: u8 = 0x02;
47    /// I2C-bus subaddress 2.
48    pub const SUBADR2: u8 = 0x03;
49    /// I2C-bus subaddress 3.
50    pub const SUBADR3: u8 = 0x04;
51    /// LED All Call I2C-bus address.
52    pub const ALLCALL_ADDR: u8 = 0x05;
53    /// First register of channel 0 (LED0_ON_L); each channel spans four registers.
54    pub const LED0_ON_L: u8 = 0x06;
55    /// Low byte of the on-count applied to all channels at once.
56    pub const ALL_LED_ON_L: u8 = 0xFA;
57    /// High byte of the on-count applied to all channels at once.
58    pub const ALL_LED_ON_H: u8 = 0xFB;
59    /// Low byte of the off-count applied to all channels at once.
60    pub const ALL_LED_OFF_L: u8 = 0xFC;
61    /// High byte of the off-count applied to all channels at once.
62    pub const ALL_LED_OFF_H: u8 = 0xFD;
63    /// Prescaler that sets the PWM output frequency.
64    pub const PRE_SCALE: u8 = 0xFE;
65}
66
67/// Bit masks for the MODE1 register.
68pub mod mode1 {
69    /// Restart logic.
70    pub const RESTART: u8 = 0x80;
71    /// Use the external clock pin instead of the internal oscillator.
72    pub const EXTCLK: u8 = 0x40;
73    /// Auto-increment the register pointer, needed to write a channel's four bytes in
74    /// one transfer.
75    pub const AUTO_INCREMENT: u8 = 0x20;
76    /// Low-power sleep: the oscillator is off and the prescaler can be written.
77    pub const SLEEP: u8 = 0x10;
78    /// Respond to I2C-bus subaddress 1.
79    pub const SUB1: u8 = 0x08;
80    /// Respond to I2C-bus subaddress 2.
81    pub const SUB2: u8 = 0x04;
82    /// Respond to I2C-bus subaddress 3.
83    pub const SUB3: u8 = 0x02;
84    /// Respond to the LED All Call address.
85    pub const ALLCALL: u8 = 0x01;
86}
87
88/// The MODE2 register bits.
89pub mod mode2 {
90    /// Invert the output logic; for boards with no external driver.
91    pub const INVRT: u8 = 0x10;
92    /// Outputs change on the acknowledge of each register write instead of on STOP.
93    pub const OCH: u8 = 0x08;
94    /// Totem-pole outputs when set, open-drain when clear.
95    pub const OUTDRV: u8 = 0x04;
96    /// With OE high: outputs high when OUTDRV is set, high-impedance otherwise.
97    pub const OUTNE_HIGH: u8 = 0x01;
98    /// With OE high: outputs high-impedance.
99    pub const OUTNE_HIGH_Z: u8 = 0x02;
100}
101
102/// The frequency of the internal oscillator, 25 MHz.
103pub const INTERNAL_OSC_HZ: u32 = 25_000_000;
104/// The number of PWM channels.
105pub const CHANNELS: u8 = 16;
106/// The number of counts in one PWM period (12-bit resolution).
107pub const COUNTS: u16 = 4096;
108/// The power-on value of the PRE_SCALE register (0x1E), about 200 Hz at 25 MHz.
109pub const PRE_SCALE_RESET: u8 = 0x1E;
110/// The power-on value of MODE1 (0x11): sleeping, responding to the All Call address.
111pub const MODE1_RESET: u8 = 0x11;
112/// The smallest value the part loads into PRE_SCALE; a smaller write loads this.
113pub const PRE_SCALE_MIN: u8 = 3;
114/// The power-on value of SUBADR1 (0xE2), the first programmable subaddress.
115pub const SUBADR1_RESET: u8 = 0xE2;
116/// The power-on value of SUBADR2 (0xE4).
117pub const SUBADR2_RESET: u8 = 0xE4;
118/// The power-on value of SUBADR3 (0xE8).
119pub const SUBADR3_RESET: u8 = 0xE8;
120/// The power-on value of ALLCALLADR (0xE0), the LED All Call address.
121pub const ALLCALL_ADDR_RESET: u8 = 0xE0;
122/// The power-on value of every channel's LEDn_OFF_H (0x10): the full-off bit set, so each
123/// output starts off. The channel's other three registers start at zero.
124pub const LED_OFF_H_RESET: u8 = 0x10;
125
126/// Returns the address of a channel's first register (its on-count low byte).
127///
128/// Each channel occupies four consecutive registers (on low, on high, off low, off
129/// high), starting at `0x06` for channel 0, so channel `n` begins at `0x06 + 4 * n`.
130///
131/// # Arguments
132///
133/// * `channel` - the channel number, `0..=15`.
134///
135/// # Returns
136///
137/// The address of `LEDn_ON_L`. Channels past 15 are clamped to 15.
138pub fn channel_register(channel: u8) -> u8 {
139    let channel = if channel >= CHANNELS {
140        CHANNELS - 1
141    } else {
142        channel
143    };
144    register::LED0_ON_L + 4 * channel
145}
146
147/// Computes the PRE_SCALE value for a desired PWM frequency.
148///
149/// This is the datasheet's prescale formula `round(osc_clock / (4096 * update_rate)) - 1`,
150/// clamped to the hardware's `3..=255` range (which bounds the frequency to roughly
151/// 24 Hz to 1.5 kHz at 25 MHz).
152///
153/// # Arguments
154///
155/// * `update_rate_hz` - the desired output frequency in hertz.
156/// * `osc_hz` - the oscillator frequency, [`INTERNAL_OSC_HZ`] unless an external clock
157///   is used.
158///
159/// # Returns
160///
161/// The value to write to the PRE_SCALE register.
162pub fn prescale_for_frequency(update_rate_hz: u32, osc_hz: u32) -> u8 {
163    let divisor = COUNTS as u32 * update_rate_hz.max(1);
164    let rounded = (osc_hz + divisor / 2) / divisor;
165    rounded.saturating_sub(1).clamp(3, 255) as u8
166}
167
168/// Computes the PWM frequency a PRE_SCALE value produces.
169///
170/// # Arguments
171///
172/// * `prescale` - the PRE_SCALE register value.
173/// * `osc_hz` - the oscillator frequency.
174///
175/// # Returns
176///
177/// The output frequency in hertz.
178pub fn frequency_for_prescale(prescale: u8, osc_hz: u32) -> f32 {
179    osc_hz as f32 / (COUNTS as f32 * (prescale as f32 + 1.0))
180}
181
182/// A channel's PWM setting: when in the period it turns on and when it turns off.
183///
184/// The PCA9685 counts from 0 to 4095 each period and lets a channel turn on at one
185/// count and off at another, so duty and phase are both programmable. The special
186/// full-on and full-off states are encoded in a dedicated bit rather than as counts,
187/// and the datasheet rules out loading the same count into both, so 0 % and 100 % are
188/// only ever those flags.
189///
190/// # Examples
191///
192/// ```
193/// use pamoja_actuators::pca9685::Pwm;
194///
195/// // Half brightness with no phase delay: on at count 0, off at the midpoint.
196/// let half = Pwm::duty(2048);
197/// assert_eq!((half.on(), half.off()), (0, 2048));
198///
199/// // What a channel's four registers hold reads back as the same setting.
200/// assert_eq!(Pwm::from_bytes(&half.bytes()), half);
201///
202/// // Fully off is its own encoding, and a zero duty is that encoding.
203/// assert_eq!(Pwm::duty(0), Pwm::full_off());
204/// ```
205#[derive(Clone, Copy, Debug, PartialEq, Eq)]
206pub struct Pwm {
207    on: u16,
208    off: u16,
209}
210
211impl Pwm {
212    /// Builds a setting from explicit on and off counts.
213    ///
214    /// # Arguments
215    ///
216    /// * `on` - the count at which the output goes high, `0..=4095`.
217    /// * `off` - the count at which it goes low, `0..=4095`.
218    ///
219    /// # Returns
220    ///
221    /// The PWM setting; counts are masked to 12 bits.
222    pub fn from_counts(on: u16, off: u16) -> Pwm {
223        Pwm {
224            on: on & 0x0FFF,
225            off: off & 0x0FFF,
226        }
227    }
228
229    /// Builds a setting with no phase delay: on at count 0, off at `off`.
230    ///
231    /// The datasheet says the on and off counts should never hold the same value, which
232    /// a count of 0 would load, so 0 is the full-off setting, and a count of 4096 or more
233    /// the full-on one.
234    ///
235    /// # Arguments
236    ///
237    /// * `off` - the count at which the output goes low, which sets the duty cycle.
238    ///
239    /// # Returns
240    ///
241    /// The PWM setting.
242    pub fn duty(off: u16) -> Pwm {
243        match off {
244            0 => Pwm::full_off(),
245            off if off >= COUNTS => Pwm::full_on(),
246            off => Pwm::from_counts(0, off),
247        }
248    }
249
250    /// Builds the setting that drives a hobby servo to a given pulse width.
251    ///
252    /// A servo reads the high-pulse width each period; the count is that width as a
253    /// fraction of the period, `pulse * 4096 / period`. Typical travel is about 1000
254    /// to 2000 microseconds at a 50 Hz update rate.
255    ///
256    /// # Arguments
257    ///
258    /// * `pulse_micros` - the high-pulse width in microseconds.
259    /// * `update_rate_hz` - the PWM frequency the controller is set to.
260    ///
261    /// # Returns
262    ///
263    /// The PWM setting for that pulse width.
264    pub fn servo(pulse_micros: u32, update_rate_hz: u32) -> Pwm {
265        let counts = (pulse_micros as u64 * COUNTS as u64 * update_rate_hz as u64) / 1_000_000;
266        Pwm::duty(counts.min(COUNTS as u64 - 1) as u16)
267    }
268
269    /// The setting that holds the output continuously high.
270    ///
271    /// # Returns
272    ///
273    /// The full-on setting.
274    pub fn full_on() -> Pwm {
275        Pwm { on: 0x1000, off: 0 }
276    }
277
278    /// The setting that holds the output continuously low.
279    ///
280    /// # Returns
281    ///
282    /// The full-off setting, the power-on state of every channel.
283    pub fn full_off() -> Pwm {
284        Pwm { on: 0, off: 0x1000 }
285    }
286
287    /// Returns the four register bytes for this setting.
288    ///
289    /// The order is on-low, on-high, off-low, off-high, matching the channel's four
290    /// consecutive registers; the full-on and full-off flags ride in bit 4 of the
291    /// high bytes.
292    ///
293    /// # Returns
294    ///
295    /// `[on_low, on_high, off_low, off_high]`.
296    pub fn bytes(self) -> [u8; 4] {
297        [
298            self.on as u8,
299            (self.on >> 8) as u8,
300            self.off as u8,
301            (self.off >> 8) as u8,
302        ]
303    }
304
305    /// Reads a setting back from the four register bytes a channel holds.
306    ///
307    /// The inverse of [`bytes`](Pwm::bytes), so a caller can read a channel back off the
308    /// bus and see what it is set to rather than decoding the registers by hand.
309    ///
310    /// # Arguments
311    ///
312    /// * `bytes` - the four channel registers, `[on_low, on_high, off_low, off_high]`.
313    ///
314    /// # Returns
315    ///
316    /// The PWM setting those registers hold, full-on and full-off flags included.
317    pub fn from_bytes(bytes: &[u8; 4]) -> Pwm {
318        Pwm {
319            on: u16::from_le_bytes([bytes[0], bytes[1]]),
320            off: u16::from_le_bytes([bytes[2], bytes[3]]),
321        }
322    }
323
324    /// Returns the count at which the output goes high.
325    ///
326    /// # Returns
327    ///
328    /// The on count, including the full-on flag in bit 12 when the output is held high.
329    pub fn on(self) -> u16 {
330        self.on
331    }
332
333    /// Returns the count at which the output goes low.
334    ///
335    /// # Returns
336    ///
337    /// The off count, including the full-off flag in bit 12 when the output is held low.
338    pub fn off(self) -> u16 {
339        self.off
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[test]
348    fn prescale_matches_the_datasheet_example() {
349        // The datasheet's worked example: 200 Hz at 25 MHz gives prescale 0x1E.
350        assert_eq!(prescale_for_frequency(200, INTERNAL_OSC_HZ), 0x1E);
351        assert_eq!(
352            prescale_for_frequency(200, INTERNAL_OSC_HZ),
353            PRE_SCALE_RESET
354        );
355        // The documented bounds: 1526 Hz is the fastest (prescale 3), and the value
356        // is clamped to the hardware minimum.
357        assert_eq!(prescale_for_frequency(1526, INTERNAL_OSC_HZ), 3);
358        assert_eq!(prescale_for_frequency(100_000, INTERNAL_OSC_HZ), 3);
359        // A common 50 Hz servo rate.
360        assert_eq!(prescale_for_frequency(50, INTERNAL_OSC_HZ), 0x79);
361    }
362
363    #[test]
364    fn frequency_and_prescale_round_trip() {
365        for prescale in [3u8, 30, 0x79, 255] {
366            let freq = frequency_for_prescale(prescale, INTERNAL_OSC_HZ);
367            assert_eq!(
368                prescale_for_frequency(freq as u32, INTERNAL_OSC_HZ),
369                prescale
370            );
371        }
372    }
373
374    #[test]
375    fn channel_registers_are_four_apart() {
376        assert_eq!(channel_register(0), 0x06);
377        assert_eq!(channel_register(1), 0x0A);
378        assert_eq!(channel_register(15), 0x42);
379        // Past the last channel clamps rather than running into the prescaler.
380        assert_eq!(channel_register(20), 0x42);
381    }
382
383    #[test]
384    fn pwm_bytes_pack_counts_little_endian() {
385        // On at 0x199 (409), off at 0xCCC (3276): the 10 %/90 % example shape.
386        let pwm = Pwm::from_counts(0x199, 0xCCC);
387        assert_eq!(pwm.bytes(), [0x99, 0x01, 0xCC, 0x0C]);
388    }
389
390    #[test]
391    fn full_on_and_full_off_set_the_flag_bit() {
392        assert_eq!(Pwm::full_on().bytes(), [0x00, 0x10, 0x00, 0x00]);
393        assert_eq!(Pwm::full_off().bytes(), [0x00, 0x00, 0x00, 0x10]);
394    }
395
396    #[test]
397    fn a_duty_never_loads_the_same_count_into_on_and_off() {
398        // Section 7.3.4: "The LEDn_ON and LEDn_OFF count registers should never be
399        // programmed with the same values." A duty starts on at count 0, so its ends are
400        // the flags.
401        assert_eq!(Pwm::duty(0), Pwm::full_off());
402        assert_eq!(Pwm::duty(COUNTS), Pwm::full_on());
403        assert_eq!(Pwm::duty(u16::MAX), Pwm::full_on());
404        assert_eq!(Pwm::duty(1).bytes(), [0x00, 0x00, 0x01, 0x00]);
405        assert_eq!(Pwm::duty(COUNTS - 1).bytes(), [0x00, 0x00, 0xFF, 0x0F]);
406        assert_eq!(Pwm::servo(0, 50), Pwm::full_off(), "no pulse at all");
407    }
408
409    #[test]
410    fn servo_midpoint_is_a_centered_pulse() {
411        // 1500 µs at 50 Hz is 7.5 % of the 20 ms period: 0.075 * 4096 = 307 counts.
412        assert_eq!(Pwm::servo(1500, 50), Pwm::duty(307));
413        // The travel extremes.
414        assert_eq!(Pwm::servo(1000, 50), Pwm::duty(204));
415        assert_eq!(Pwm::servo(2000, 50), Pwm::duty(409));
416    }
417    #[test]
418    fn a_setting_reads_back_from_the_registers_it_writes() {
419        for setting in [
420            Pwm::servo(1500, 50),
421            Pwm::duty(0),
422            Pwm::duty(2048),
423            Pwm::full_on(),
424            Pwm::full_off(),
425            Pwm::from_counts(410, 1229),
426        ] {
427            assert_eq!(Pwm::from_bytes(&setting.bytes()), setting);
428        }
429
430        // A centered servo holds its output high for 1500 us of a 20 ms period, which is
431        // 307 of the part's 4096 counts.
432        assert_eq!(Pwm::servo(1500, 50).off(), 307);
433        assert_eq!(Pwm::servo(1500, 50).on(), 0);
434    }
435}