1use embedded_hal::delay::DelayNs;
4use embedded_hal::i2c::I2c;
5use pamoja_core::Actuator;
6
7use super::{
8 channel_register, frequency_for_prescale, mode1, mode2, prescale_for_frequency, register, Pwm,
9 CHANNELS, INTERNAL_OSC_HZ, OSCILLATOR_STARTUP_MICROS, SOFTWARE_RESET_ADDRESS,
10 SOFTWARE_RESET_BYTE,
11};
12use crate::error::DriverError;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub struct Output {
17 pub channel: u8,
19 pub pwm: Pwm,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub struct Outputs {
26 pub totem_pole: bool,
30 pub inverted: bool,
32 pub change_on_ack: bool,
35}
36
37impl Default for Outputs {
38 fn default() -> Self {
39 Outputs {
40 totem_pole: true,
41 inverted: false,
42 change_on_ack: false,
43 }
44 }
45}
46
47impl Outputs {
48 pub fn bits(self) -> u8 {
54 let mut bits = 0;
55 if self.totem_pole {
56 bits |= mode2::OUTDRV;
57 }
58 if self.inverted {
59 bits |= mode2::INVRT;
60 }
61 if self.change_on_ack {
62 bits |= mode2::OCH;
63 }
64 bits
65 }
66}
67
68#[derive(Debug)]
113pub struct Pca9685<I2C, D> {
114 bus: I2C,
115 delay: D,
116 address: u8,
117 frequency_hz: u32,
118 oscillator_hz: u32,
119 outputs: Outputs,
120 initialized: bool,
121}
122
123impl<I2C, D> Pca9685<I2C, D> {
124 pub fn new(bus: I2C, address: u8, delay: D) -> Self {
138 Pca9685 {
139 bus,
140 delay,
141 address,
142 frequency_hz: 200,
143 oscillator_hz: INTERNAL_OSC_HZ,
144 outputs: Outputs::default(),
145 initialized: false,
146 }
147 }
148
149 pub fn with_frequency(mut self, hz: u32) -> Self {
162 self.frequency_hz = hz;
163 self.initialized = false;
164 self
165 }
166
167 pub fn with_oscillator(mut self, hz: u32) -> Self {
178 self.oscillator_hz = hz;
179 self.initialized = false;
180 self
181 }
182
183 pub fn with_outputs(mut self, outputs: Outputs) -> Self {
193 self.outputs = outputs;
194 self.initialized = false;
195 self
196 }
197
198 pub fn prescale(&self) -> u8 {
200 prescale_for_frequency(self.frequency_hz, self.oscillator_hz)
201 }
202
203 pub fn frequency(&self) -> f32 {
205 frequency_for_prescale(self.prescale(), self.oscillator_hz)
206 }
207
208 pub fn release(self) -> (I2C, D) {
214 (self.bus, self.delay)
215 }
216}
217
218impl<I2C: I2c, D: DelayNs> Pca9685<I2C, D> {
219 pub fn init(&mut self) -> Result<(), DriverError<I2C::Error>> {
225 let awake = mode1::AUTO_INCREMENT;
226 self.write(register::MODE1, awake | mode1::SLEEP)?;
227 self.write(register::PRE_SCALE, self.prescale())?;
228 self.write(register::MODE2, self.outputs.bits())?;
229 self.write(register::MODE1, awake)?;
230 self.delay.delay_us(OSCILLATOR_STARTUP_MICROS);
231 self.write(register::MODE1, awake | mode1::RESTART)?;
232 self.initialized = true;
233 Ok(())
234 }
235
236 pub fn set_channel(&mut self, channel: u8, pwm: Pwm) -> Result<(), DriverError<I2C::Error>> {
250 if channel >= CHANNELS {
251 return Err(DriverError::Command(
252 "the PCA9685 has sixteen channels, 0 to 15",
253 ));
254 }
255 if !self.initialized {
256 self.init()?;
257 }
258 let [on_l, on_h, off_l, off_h] = pwm.bytes();
259 self.bus
260 .write(
261 self.address,
262 &[channel_register(channel), on_l, on_h, off_l, off_h],
263 )
264 .map_err(DriverError::Bus)
265 }
266
267 pub fn channel(&mut self, channel: u8) -> Result<Pwm, DriverError<I2C::Error>> {
286 if channel >= CHANNELS {
287 return Err(DriverError::Command(
288 "the PCA9685 has sixteen channels, 0 to 15",
289 ));
290 }
291 let first = channel_register(channel);
292 let mut bytes = [0; 4];
293 for (register, byte) in (first..).zip(bytes.iter_mut()) {
294 *byte = self.read(register)?;
295 }
296 Ok(Pwm::from_bytes(&bytes))
297 }
298
299 pub fn set_all(&mut self, pwm: Pwm) -> Result<(), DriverError<I2C::Error>> {
309 if !self.initialized {
310 self.init()?;
311 }
312 let [on_l, on_h, off_l, off_h] = pwm.bytes();
313 self.bus
314 .write(
315 self.address,
316 &[register::ALL_LED_ON_L, on_l, on_h, off_l, off_h],
317 )
318 .map_err(DriverError::Bus)
319 }
320
321 pub fn sleep(&mut self) -> Result<(), DriverError<I2C::Error>> {
327 self.write(register::MODE1, mode1::AUTO_INCREMENT | mode1::SLEEP)
328 }
329
330 pub fn wake(&mut self) -> Result<(), DriverError<I2C::Error>> {
336 self.write(register::MODE1, mode1::AUTO_INCREMENT)?;
337 self.delay.delay_us(OSCILLATOR_STARTUP_MICROS);
338 self.write(register::MODE1, mode1::AUTO_INCREMENT | mode1::RESTART)
339 }
340
341 pub fn software_reset(&mut self) -> Result<(), DriverError<I2C::Error>> {
350 self.initialized = false;
351 self.bus
352 .write(SOFTWARE_RESET_ADDRESS, &[SOFTWARE_RESET_BYTE])
353 .map_err(DriverError::Bus)
354 }
355
356 fn write(&mut self, register: u8, value: u8) -> Result<(), DriverError<I2C::Error>> {
357 self.bus
358 .write(self.address, &[register, value])
359 .map_err(DriverError::Bus)
360 }
361
362 fn read(&mut self, register: u8) -> Result<u8, DriverError<I2C::Error>> {
363 let mut value = [0];
364 self.bus
365 .write_read(self.address, &[register], &mut value)
366 .map_err(DriverError::Bus)?;
367 Ok(value[0])
368 }
369}
370
371impl<I2C, D> Actuator for Pca9685<I2C, D>
372where
373 I2C: I2c + Send,
374 I2C::Error: core::fmt::Debug,
375 D: DelayNs + Send,
376{
377 type Command = Output;
378
379 async fn apply(&mut self, command: Output) -> pamoja_core::Result<()> {
380 self.set_channel(command.channel, command.pwm)
381 .map_err(pamoja_core::Error::from)
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::pca9685::DEFAULT_I2C_ADDRESS;
389 use alloc::vec;
390 use alloc::vec::Vec;
391 use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
392 use pamoja_hal::script::{block_on, DelayLog, I2cScript, I2cStep};
393
394 fn init_steps(address: u8, prescale: u8, mode2: u8) -> Vec<I2cStep> {
395 vec![
396 I2cStep::write(address, [register::MODE1, 0x30]),
397 I2cStep::write(address, [register::PRE_SCALE, prescale]),
398 I2cStep::write(address, [register::MODE2, mode2]),
399 I2cStep::write(address, [register::MODE1, 0x20]),
400 I2cStep::write(address, [register::MODE1, 0xA0]),
401 ]
402 }
403
404 #[test]
405 fn init_sleeps_to_set_the_prescale_then_wakes_and_restarts() {
406 let mut board = Pca9685::new(
407 I2cScript::new(init_steps(DEFAULT_I2C_ADDRESS, 0x79, 0x04)),
408 DEFAULT_I2C_ADDRESS,
409 DelayLog::new(),
410 )
411 .with_frequency(50);
412 assert_eq!(board.prescale(), 0x79, "the datasheet formula at 50 Hz");
413 assert!((board.frequency() - 50.03).abs() < 0.01);
414 board.init().unwrap();
415 let (bus, delay) = board.release();
416 assert!(bus.done());
417 assert_eq!(delay.waits_ns(), [OSCILLATOR_STARTUP_MICROS * 1_000]);
418 }
419
420 #[test]
421 fn the_default_frequency_is_the_datasheet_default_prescale() {
422 let board = Pca9685::new(I2cScript::new([]), 0x41, DelayLog::new());
423 assert_eq!(board.prescale(), super::super::PRE_SCALE_RESET);
424 }
425
426 #[test]
427 fn channels_load_four_bytes_through_auto_increment() {
428 let pulse = Pwm::servo(1_500, 50);
429 let [on_l, on_h, off_l, off_h] = pulse.bytes();
430 let mut steps = init_steps(0x41, 0x79, 0x14);
431 steps.push(I2cStep::write(
432 0x41,
433 [0x06 + 4 * 15, on_l, on_h, off_l, off_h],
434 ));
435 steps.push(I2cStep::write(
436 0x41,
437 [register::ALL_LED_ON_L, 0x00, 0x10, 0x00, 0x00],
438 ));
439 let mut board = Pca9685::new(I2cScript::new(steps), 0x41, DelayLog::new())
440 .with_frequency(50)
441 .with_outputs(Outputs {
442 inverted: true,
443 ..Outputs::default()
444 });
445 board.set_channel(15, pulse).unwrap();
446 board.set_all(Pwm::full_on()).unwrap();
447 assert_eq!(
448 board.set_channel(16, pulse),
449 Err(DriverError::Command(
450 "the PCA9685 has sixteen channels, 0 to 15"
451 ))
452 );
453 assert!(board.release().0.done());
454 }
455
456 #[test]
457 fn a_channel_reads_back_one_register_a_transfer_without_initializing() {
458 let pulse = Pwm::servo(1_500, 50);
459 let [on_l, on_h, off_l, off_h] = pulse.bytes();
460 let first = channel_register(2);
461 let steps = [
462 I2cStep::write_read(0x40, [first], [on_l]),
463 I2cStep::write_read(0x40, [first + 1], [on_h]),
464 I2cStep::write_read(0x40, [first + 2], [off_l]),
465 I2cStep::write_read(0x40, [first + 3], [off_h]),
466 ];
467 let mut board = Pca9685::new(I2cScript::new(steps), 0x40, DelayLog::new());
468 assert_eq!(board.channel(2).unwrap(), pulse);
469 assert_eq!(
470 board.channel(16),
471 Err(DriverError::Command(
472 "the PCA9685 has sixteen channels, 0 to 15"
473 ))
474 );
475 assert!(board.release().0.done());
476 }
477
478 #[test]
479 fn sleep_wake_and_software_reset_write_what_the_datasheet_names() {
480 let steps = [
481 I2cStep::write(0x40, [register::MODE1, 0x30]),
482 I2cStep::write(0x40, [register::MODE1, 0x20]),
483 I2cStep::write(0x40, [register::MODE1, 0xA0]),
484 I2cStep::write(SOFTWARE_RESET_ADDRESS, [SOFTWARE_RESET_BYTE]),
485 ];
486 let mut board = Pca9685::new(I2cScript::new(steps), 0x40, DelayLog::new());
487 board.sleep().unwrap();
488 board.wake().unwrap();
489 board.software_reset().unwrap();
490 let (bus, delay) = board.release();
491 assert!(bus.done());
492 assert_eq!(delay.total_micros(), 500);
493 }
494
495 #[test]
496 fn a_bus_fault_surfaces_as_a_bus_error_and_maps_to_the_core() {
497 let kind = ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address);
498 let mut board = Pca9685::new(
499 I2cScript::new([I2cStep::fault(0x40, kind)]),
500 0x40,
501 DelayLog::new(),
502 );
503 let error = board.init().unwrap_err();
504 assert!(matches!(error, DriverError::Bus(_)));
505 let core: pamoja_core::Error = error.into();
506 assert!(matches!(core, pamoja_core::Error::Io(_)));
507 let refused: pamoja_core::Error = DriverError::<ErrorKind>::Command("channel").into();
508 assert!(matches!(refused, pamoja_core::Error::Codec(_)));
509 }
510
511 #[test]
512 fn as_an_actuator_a_mapped_command_drives_a_servo_from_a_bool() {
513 let open = Pwm::servo(2_000, 50);
514 let closed = Pwm::servo(1_000, 50);
515 let mut steps = init_steps(0x40, 0x79, 0x04);
516 let [a, b, c, d] = open.bytes();
517 steps.push(I2cStep::write(0x40, [0x06, a, b, c, d]));
518 let [a, b, c, d] = closed.bytes();
519 steps.push(I2cStep::write(0x40, [0x06, a, b, c, d]));
520 let board = Pca9685::new(I2cScript::new(steps), 0x40, DelayLog::new()).with_frequency(50);
521 let mut valve = board.map_command(|open_now: bool| Output {
522 channel: 0,
523 pwm: if open_now { open } else { closed },
524 });
525 block_on(valve.apply(true)).unwrap();
526 block_on(valve.apply(false)).unwrap();
527 assert!(valve.into_inner().release().0.done());
528 }
529
530 #[test]
531 fn init_at_fifty_hertz_and_one_servo_make_the_transfers_the_datasheet_gives() {
532 const PART: u8 = DEFAULT_I2C_ADDRESS;
533 let center = Pwm::servo(1_500, 50);
534 let [on_l, on_h, off_l, off_h] = center.bytes();
535 let bus = I2cScript::new([
536 I2cStep::write(PART, [0x00, 0x30]),
537 I2cStep::write(PART, [0xFE, 0x79]),
538 I2cStep::write(PART, [0x01, 0x04]),
539 I2cStep::write(PART, [0x00, 0x20]),
540 I2cStep::write(PART, [0x00, 0xA0]),
541 I2cStep::write(PART, [0x06, on_l, on_h, off_l, off_h]),
542 ]);
543
544 let mut board = Pca9685::new(bus, PART, DelayLog::new()).with_frequency(50);
545 block_on(board.apply(Output {
546 channel: 0,
547 pwm: center,
548 }))
549 .expect("the scripted part answers");
550 let (bus, delay) = board.release();
551 assert!(bus.done());
552 assert_eq!(delay.total_micros(), 500);
553 }
554}