adafruit_seesaw/modules/
neopixel.rs

1use super::{Modules, Reg};
2use crate::{devices::SeesawDevice, driver::Driver, DriverExt, SeesawError};
3use rgb::ComponentSlice;
4
5/// WO - 8 bits
6/// This register sets the pin number (PORTA) that is used for the NeoPixel
7/// output.
8const SET_PIN: &Reg = &[Modules::Neopixel.into_u8(), 0x01];
9/// WO - 8 bits
10/// The protocol speed. (see `NeopixelSpeed`) Default is 800khz.
11const SET_SPEED: &Reg = &[Modules::Neopixel.into_u8(), 0x02];
12/// WO - 16 bits
13/// The number of bytes currently used for the pixel array. This is
14/// dependent on when the pixels you are using are RGB or RGBW.
15const SET_LEN: &Reg = &[Modules::Neopixel.into_u8(), 0x03];
16/// WO - 256 bits (32 bytes)
17/// The data buffer. The first 2 bytes are the start address, and the data
18/// to write follows. Data should be written in blocks of maximum size 30
19/// bytes at a time.
20const SET_BUF: &Reg = &[Modules::Neopixel.into_u8(), 0x04];
21/// W0 - Zero bits
22/// Sending the SHOW command will cause the output to update. There's no
23/// arguments/data after the command.
24const SHOW: &Reg = &[Modules::Neopixel.into_u8(), 0x05];
25
26pub trait NeopixelModule<D: Driver>: SeesawDevice<Driver = D> {
27    /// The size of the color type in bytes
28    const C_SIZE: usize = {
29        match core::mem::size_of::<Self::Color>() {
30            3 => 3,
31            4 => 4,
32            _ => panic!("Invalid color size"),
33        }
34    };
35    /// The number of neopixels on or connected to the device
36    const N_LEDS: usize = 1;
37    /// The output pin of the neopixel signal
38    const PIN: u8;
39
40    type Color: ComponentSlice<u8>;
41
42    /// Set which pin the device sends the neopixel signal through and
43    /// set the length of its internal pixel buffer
44    fn enable_neopixel(&mut self) -> Result<(), SeesawError<D::Error>> {
45        let addr = self.addr();
46
47        self.driver()
48            .write_u8(addr, SET_PIN, Self::PIN)
49            .map(|_| self.driver().delay_us(10_000))
50            .and_then(|_| {
51                self.driver()
52                    .write_u16(addr, SET_LEN, (Self::C_SIZE * Self::N_LEDS) as u16)
53            })
54            .map(|_| self.driver().delay_us(10_000))
55            .map_err(SeesawError::I2c)
56    }
57
58    fn set_neopixel_speed(&mut self, speed: NeopixelSpeed) -> Result<(), SeesawError<D::Error>> {
59        let addr = self.addr();
60
61        self.driver()
62            .write_u8(
63                addr,
64                SET_SPEED,
65                match speed {
66                    NeopixelSpeed::Khz400 => 0,
67                    NeopixelSpeed::Khz800 => 1,
68                },
69            )
70            .map(|_| self.driver().delay_us(10_000))
71            .map_err(SeesawError::I2c)
72    }
73
74    /// Set the color of the first (and, in the case of some devices, only)
75    /// neopixel
76    fn set_neopixel_color(&mut self, color: Self::Color) -> Result<(), SeesawError<D::Error>>
77    where
78        [(); 2 + Self::C_SIZE]: Sized,
79    {
80        self.set_nth_neopixel_color(0, color)
81    }
82
83    /// Set the color of the nth neopixel
84    fn set_nth_neopixel_color(
85        &mut self,
86        n: usize,
87        color: Self::Color,
88    ) -> Result<(), SeesawError<D::Error>>
89    where
90        [(); 2 + Self::C_SIZE]: Sized,
91    {
92        assert!(n < Self::N_LEDS);
93        let addr = self.addr();
94        let mut buf = [0; 2 + Self::C_SIZE];
95        buf[..2].copy_from_slice(&u16::to_be_bytes((Self::C_SIZE * n) as u16));
96        buf[2..].copy_from_slice(color.as_slice());
97        self.driver()
98            .register_write(addr, SET_BUF, &buf)
99            .map_err(SeesawError::I2c)
100    }
101
102    /// Set the color of all neopixels
103    ///
104    /// Minimizes the number of transactions performed by chunking the `colors`
105    /// array into the largest (max 30 byte) buffers possible
106    ///
107    /// Note that if `C_SIZE` is _not_ 3 or 4 bytes, the chunking optimization
108    /// is effectively skipped
109    fn set_neopixel_colors(
110        &mut self,
111        colors: &[Self::Color; Self::N_LEDS],
112    ) -> Result<(), SeesawError<D::Error>>
113    where
114        [(); 2 + color_bytes_per_write(Self::C_SIZE)]: Sized,
115    {
116        let mut buf = [0; 2 + color_bytes_per_write(Self::C_SIZE)];
117        let addr = self.addr();
118
119        colors
120            .chunks(max_colors_per_write(Self::C_SIZE))
121            .enumerate()
122            .try_for_each(|(i, chunk)| {
123                let offset = u16::to_be_bytes(
124                    (Self::C_SIZE * i * max_colors_per_write(Self::C_SIZE)) as u16,
125                );
126                buf[..2].copy_from_slice(&offset);
127                chunk.iter().enumerate().for_each(|(j, c)| {
128                    let start = 2 + (j * Self::C_SIZE);
129                    buf[start..start + Self::C_SIZE].copy_from_slice(c.as_slice());
130                });
131
132                self.driver().register_write(
133                    addr,
134                    SET_BUF,
135                    &buf[0..2 + (Self::C_SIZE * chunk.len())],
136                )
137            })
138            .map_err(SeesawError::I2c)
139    }
140
141    fn sync_neopixel(&mut self) -> Result<(), SeesawError<D::Error>> {
142        let addr = self.addr();
143
144        self.driver()
145            .register_write(addr, SHOW, &[])
146            .map(|_| self.driver().delay_us(125))
147            .map_err(SeesawError::I2c)
148    }
149}
150
151/// Get the maximum number of colors that can be written in a single write
152/// operation as a function of the number of bytes per color
153pub const fn max_colors_per_write(c_size: usize) -> usize {
154    match c_size {
155        3 => 9, // 27
156        4 => 7, // 28
157        _ => 1, // effectively skips the optimization
158    }
159}
160
161/// Get the number of bytes dedicated to writing colors in a single write
162/// operation as a function of the number of bytes per color
163pub const fn color_bytes_per_write(c_size: usize) -> usize {
164    c_size * max_colors_per_write(c_size)
165}
166
167/// NeopixelModule: The Neopixel protocol speed
168#[derive(Debug, Default)]
169#[cfg_attr(feature = "defmt", derive(defmt::Format))]
170pub enum NeopixelSpeed {
171    Khz400 = 0,
172    #[default]
173    Khz800 = 1,
174}