1#![cfg_attr(not(test), no_std)]
8
9use embedded_hal::blocking::i2c;
28
29const MAX_WIDTH: usize = 16;
33const MAX_HEIGHT: usize = 9;
34
35pub struct Delay<const TAU_US: u32, const A_MAX: u8, const EXPONENTIAL: bool> {
53 value: u8,
54 is_exact: bool,
55}
56
57fn log2(mut value: u128) -> u8 {
61 let mut exponent = 0;
62 loop {
63 if value <= 1 {
64 return exponent;
65 }
66 value /= 2;
67 exponent += 1;
68 }
69}
70
71impl<const TAU_US: u32, const A_MAX: u8, const EXPONENTIAL: bool>
72 Delay<TAU_US, A_MAX, EXPONENTIAL>
73{
74 pub fn new(delay: core::time::Duration) -> Self {
75 let desired_micros = delay.as_micros();
76
77 let desired_value = if !EXPONENTIAL {
78 desired_micros / TAU_US as u128
79 } else {
80 log2(desired_micros / TAU_US as u128) as u128
81 };
82
83 if desired_value < 1 && !EXPONENTIAL {
84 Self {
85 value: 1,
86 is_exact: false,
87 }
88 } else if desired_value > A_MAX as u128 {
89 Self {
90 value: A_MAX,
91 is_exact: false,
92 }
93 } else {
94 let is_exact = if !EXPONENTIAL {
95 TAU_US as u128 * desired_value == desired_micros
96 } else {
97 TAU_US as u128 * (1 << desired_value) == desired_micros
98 };
99 Self {
100 value: desired_value as u8,
101 is_exact,
102 }
103 }
104 }
105
106 pub fn micros(&self) -> u32 {
108 if !EXPONENTIAL {
109 TAU_US * self.value as u32
110 } else {
111 TAU_US * (1 << self.value as u32)
112 }
113 }
114
115 pub fn value(&self) -> u8 {
117 self.value
118 }
119
120 pub fn is_exact(&self) -> bool {
123 self.is_exact
124 }
125}
126
127const CMD_REG: u8 = 0xFD;
132
133const FRAME_CONTROL_REG: u8 = 0x00;
135
136const FRAME_BLINK_REG: u8 = 0x12;
138
139const FRAME_PWM_REG: u8 = 0x24;
141
142pub struct Frame<const WIDTH: usize, const HEIGHT: usize> {
146 pub pixels: [[u8; WIDTH]; HEIGHT],
147}
148
149#[derive(PartialEq, Eq)]
153pub enum PowerActive<PwrPin: embedded_hal::digital::v2::OutputPin> {
154 High(PwrPin),
155 Low(PwrPin),
156 AlwaysOn,
157}
158
159#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
161pub enum Addr {
162 GND = 0b1110100, VCC = 0b1110111, SCL = 0b1110101, SDA = 0b1110110, }
167
168#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
170pub enum FrameId {
171 One = 0x00,
172 Two = 0x01,
173 Three = 0x02,
174 Four = 0x03,
175 Five = 0x04,
176 Six = 0x05,
177 Seven = 0x06,
178 Eight = 0x07,
179}
180
181pub enum AnimationLoops {
183 Endless = 0x00,
184 One = 0x01,
185 Two = 0x02,
186 Three = 0x03,
187 Four = 0x04,
188 Five = 0x05,
189 Six = 0x06,
190 Seven = 0x07,
191}
192
193#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
195pub enum Functions {
196 Config = 0x00,
197 PictureDisplay = 0x01,
198 AutoPlayCtrl1 = 0x02,
199 AutoPlayCtrl2 = 0x03,
200 DisplayOption = 0x05,
201 AutoSynch = 0x06,
202 FrameState = 0x07,
203 BreathCtrl1 = 0x08,
204 BreathCtrl2 = 0x09,
205 Shutdown = 0x0A,
206 AGCCtrl = 0x0B,
207 AudioADCRate = 0x0C,
208}
209
210#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
212pub enum Mode {
213 Picture = 0x00,
214 AutoFramePlay = 0x01,
215 AudioFramePlay = 0x02,
216}
217
218#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
219pub enum Page {
220 Frame(FrameId),
221 Function,
222}
223
224impl Page {
225 fn id(&self) -> u8 {
226 match self {
227 Page::Frame(id) => *id as u8,
228 Page::Function => 0x0B, }
230 }
231}
232
233#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
235enum ShutdownControl {
236 ShutdownMode = 0x00,
237 NormalOperation = 0x01,
238}
239
240#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
242pub enum Blink {
243 Enable = 0x01,
244 Disable = 0x00,
245}
246
247#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
252pub enum IntensityControl {
253 LikeFirstFrame = 0x01,
254 Individual = 0x00,
255}
256
257#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
259pub enum Breath {
260 Enable = 0x01,
261 Disable = 0x00,
262}
263
264#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
266pub enum Orientation {
267 SensorUp,
268 SensorDown,
269}
270
271pub struct LedMatrix<PwrPin, ShutdownPin, I2C, const WIDTH: usize, const HEIGHT: usize>
275where
276 PwrPin: embedded_hal::digital::v2::OutputPin,
277 ShutdownPin: embedded_hal::digital::v2::OutputPin,
278 I2C: i2c::WriteRead + i2c::Write,
279{
280 addr: Addr,
281 power_active: PowerActive<PwrPin>,
282 shutdown_pin: ShutdownPin,
283 i2c: I2C,
284}
285
286#[derive(Debug)]
287pub enum Error<PwrPinErr, ShutdownPinErr, I2cErr> {
288 PwrPin(PwrPinErr),
289 ShutdownPin(ShutdownPinErr),
290 I2c(I2cErr),
291}
292
293macro_rules! GenericError {
295 () => {
296 Error<PwrPin::Error, ShutdownPin::Error, I2cErr>
297 };
298}
299
300impl<PwrPin, ShutdownPin, I2C, I2cErr, const WIDTH: usize, const HEIGHT: usize>
301 LedMatrix<PwrPin, ShutdownPin, I2C, WIDTH, HEIGHT>
302where
303 PwrPin: embedded_hal::digital::v2::OutputPin,
304 ShutdownPin: embedded_hal::digital::v2::OutputPin,
305 I2C: i2c::WriteRead<Error = I2cErr> + i2c::Write<Error = I2cErr>,
306{
307 pub fn new(
309 addr: Addr,
310 power_active: PowerActive<PwrPin>,
311 shutdown_pin: ShutdownPin,
312 i2c: I2C,
313 ) -> Self {
314 Self {
315 addr,
316 power_active,
317 shutdown_pin,
318 i2c,
319 }
320 }
321
322 fn write_function_register(
324 &mut self,
325 reg: Functions,
326 value: u8,
327 ) -> Result<(), GenericError!()> {
328 self.select_page(Page::Function)?;
329 self.i2c
330 .write(self.addr as u8, &[reg as u8, value])
331 .map_err(Error::I2c)
332 }
333
334 fn select_page(&mut self, page: Page) -> Result<(), GenericError!()> {
336 self.i2c
337 .write(self.addr as u8, &[CMD_REG, page.id()])
338 .map_err(Error::I2c)
339 }
340
341 fn set_shutdown(&mut self, shutdown: ShutdownControl) -> Result<(), GenericError!()> {
343 self.write_function_register(Functions::Shutdown, shutdown as u8)
344 }
345
346 fn set_configuration(
349 &mut self,
350 mode: Mode,
351 start_frame_autoplay: FrameId,
352 ) -> Result<(), GenericError!()> {
353 let value = (mode as u8) << 3 | start_frame_autoplay as u8;
354 self.write_function_register(Functions::Config, value)
355 }
356
357 fn set_picture_display(&mut self, picture: FrameId) -> Result<(), GenericError!()> {
359 self.write_function_register(Functions::PictureDisplay, picture as u8)
360 }
361
362 fn write_read_register(&mut self, reg: Functions) -> Result<u8, GenericError!()> {
364 let mut buf = [0_u8; 1];
365 self.i2c
370 .write_read(self.addr as u8, &[reg as u8], &mut buf)
371 .map_err(Error::I2c)?;
372
373 Ok(buf[0])
374 }
375
376 pub fn power_on(&mut self, on: bool) -> Result<(), GenericError!()> {
383 if !on {
384 self.set_shutdown(ShutdownControl::ShutdownMode)?;
385 self.shutdown_pin.set_low().map_err(Error::ShutdownPin)?;
386 }
387 match (&mut self.power_active, on) {
388 (PowerActive::High(pp), true) => pp.set_high(),
389 (PowerActive::Low(pp), true) => pp.set_low(),
390 (PowerActive::High(pp), false) => pp.set_low(),
391 (PowerActive::Low(pp), false) => pp.set_high(),
392 (_, _) => Ok(()),
393 }
394 .map_err(Error::PwrPin)?;
395 if on {
396 self.shutdown_pin.set_high().map_err(Error::ShutdownPin)?;
397 self.set_shutdown(ShutdownControl::ShutdownMode)?;
398 }
399 Ok(())
400 }
401
402 pub fn write_frame(
408 &mut self,
409 frame_id: FrameId,
410 frame: Frame<WIDTH, HEIGHT>,
411 ) -> Result<(), GenericError!()> {
412 let mut led_control = [0_u8; 1 + (MAX_WIDTH * MAX_HEIGHT) / 8];
413 let mut led_blink = [0_u8; 1 + (MAX_WIDTH * MAX_HEIGHT) / 8];
414 let mut led_pwm = [0_u8; 1 + (MAX_WIDTH * MAX_HEIGHT)];
415
416 led_control[0] = FRAME_CONTROL_REG;
418 led_blink[0] = FRAME_BLINK_REG;
419 led_pwm[0] = FRAME_PWM_REG;
420
421 let mut counter = 0;
422 let mut control = 0;
423
424 for y in 0..MAX_HEIGHT {
425 for x in 0..MAX_WIDTH {
426 if x < WIDTH && y < HEIGHT {
427 led_pwm[1 + y * MAX_WIDTH + x] = frame.pixels[y][x];
428 control <<= 1;
429 control |= 1;
430 }
431
432 counter += 1;
433
434 if counter % 8 == 0 {
435 led_control[counter / 8] = control;
436 control = 0;
437 }
438 }
439 }
440
441 self.select_page(Page::Frame(frame_id))?;
442 self.i2c
443 .write(self.addr as u8, &led_control)
444 .map_err(Error::I2c)?;
445 self.i2c
446 .write(self.addr as u8, &led_blink)
447 .map_err(Error::I2c)?;
448 self.i2c
449 .write(self.addr as u8, &led_pwm)
450 .map_err(Error::I2c)?;
451 Ok(())
452 }
453
454 pub fn show_frame(&mut self, frame_id: FrameId) -> Result<(), GenericError!()> {
456 self.set_configuration(Mode::Picture, FrameId::One)?; self.set_picture_display(frame_id)?;
458 self.set_shutdown(ShutdownControl::NormalOperation)
459 }
460
461 pub fn clear_frame(&mut self, frame_id: FrameId) -> Result<(), GenericError!()> {
463 let frame_zero = [[0_u8; WIDTH]; HEIGHT];
464 self.write_frame(frame_id, Frame { pixels: frame_zero })?;
465 self.show_frame(frame_id)
466 }
467
468 pub fn read_frame_state(&mut self) -> Result<u8, GenericError!()> {
472 self.write_read_register(Functions::FrameState)
473 }
474
475 pub fn play_animation(
483 &mut self,
484 first_frame: FrameId,
485 last_frame: FrameId,
486 repeats: AnimationLoops,
487 inter_frame_delay: Delay<11000, 63, false>,
488 ) -> Result<(), GenericError!()> {
489 self.set_configuration(Mode::AutoFramePlay, first_frame)?;
490
491 let mut num_of_frames_playing = last_frame as u8 - first_frame as u8 + 1;
492 if num_of_frames_playing > 7 {
494 num_of_frames_playing = 0;
495 }
496
497 let auto_play_ctrl1 = (repeats as u8) << 4 | num_of_frames_playing;
498 self.write_function_register(Functions::AutoPlayCtrl1, auto_play_ctrl1)?;
499
500 let auto_play_ctrl2 = inter_frame_delay.value();
501 self.write_function_register(Functions::AutoPlayCtrl2, auto_play_ctrl2)?;
502 self.set_shutdown(ShutdownControl::NormalOperation)
503 }
504
505 pub fn blink_frame(
513 &mut self,
514 frame_id: FrameId,
515 blink_enable: Blink,
516 intensity_control: IntensityControl,
517 blink_period_time: Delay<270000, 7, false>,
518 ) -> Result<(), GenericError!()> {
519 let display_option =
520 (blink_enable as u8) << 3 | (intensity_control as u8) << 5 | blink_period_time.value();
521 self.select_page(Page::Frame(frame_id))?;
522 self.write_function_register(Functions::DisplayOption, display_option)?;
523
524 if blink_enable == Blink::Enable {
525 self.show_frame(frame_id)?;
526 }
527 Ok(())
528 }
529
530 pub fn breath_control(
538 &mut self,
539 breath_enable: Breath,
540 fade_in_time: Delay<26000, 7, true>,
541 fade_out_time: Delay<26000, 7, true>,
542 extinguish_time: Delay<3500, 7, true>,
543 ) -> Result<(), GenericError!()> {
544 let breath_ctrl1 = fade_out_time.value() << 4 | fade_in_time.value();
545 let breath_ctrl2 = (breath_enable as u8) << 4 | extinguish_time.value();
546 self.write_function_register(Functions::BreathCtrl1, breath_ctrl1)?;
547 self.write_function_register(Functions::BreathCtrl2, breath_ctrl2)
548 }
549}
550
551#[cfg(test)]
552mod test {
553 use super::*;
554 use core::time::Duration;
555
556 #[test]
557 fn test_log2() {
558 assert_eq!(log2(0), 0);
559 assert_eq!(log2(1), 0);
560 assert_eq!(log2(2), 1);
561 assert_eq!(log2(3), 1);
562 assert_eq!(log2(4), 2);
563 assert_eq!(log2(5), 2);
564 assert_eq!(log2(7), 2);
565 assert_eq!(log2(8), 3);
566 assert_eq!(log2(128), 7);
567 assert_eq!(log2(255), 7);
568 assert_eq!(log2(256), 8);
569 assert_eq!(log2(0xffffffff_ffffffff_ffffffff_ffffffff), 127);
570 }
571
572 #[test]
573 fn test_quantized_delay() {
574 let d = Delay::<1000, 10, false>::new(Duration::from_millis(1));
575 assert_eq!(d.value(), 1);
576 assert_eq!(d.is_exact(), true);
577 let d = Delay::<400, 10, false>::new(Duration::from_millis(1));
578 assert_eq!(d.value(), 2);
579 assert_eq!(d.is_exact(), false); let d = Delay::<1000, 10, false>::new(Duration::from_millis(0));
581 assert_eq!(d.value(), 1);
582 assert_eq!(d.is_exact(), false); let d = Delay::<1000, 10, false>::new(Duration::from_millis(100));
584 assert_eq!(d.value(), 10);
585 assert_eq!(d.is_exact(), false); let d = Delay::<1000, 10, true>::new(Duration::from_millis(1));
587 assert_eq!(d.value(), 0);
588 assert_eq!(d.is_exact(), true);
589 let d = Delay::<1000, 10, true>::new(Duration::from_millis(16));
590 assert_eq!(d.value(), 4);
591 assert_eq!(d.is_exact(), true);
592 let d = Delay::<1000, 20, true>::new(Duration::from_millis(17));
593 assert_eq!(d.value(), 4);
594 assert_eq!(d.is_exact(), false); let d = Delay::<1000, 20, false>::new(Duration::from_millis(17));
596 assert_eq!(d.value(), 17);
597 assert_eq!(d.is_exact(), true); let d = Delay::<1000, 10, true>::new(Duration::from_micros(500));
599 assert_eq!(d.value(), 0);
600 assert_eq!(d.is_exact(), false); let d = Delay::<1000, 10, true>::new(Duration::from_millis(1025));
602 assert_eq!(d.value(), 10);
603 assert_eq!(d.is_exact(), false); }
605}