1#[cfg(test)]
2mod test;
3
4use core::{marker::PhantomData, ops::Deref};
5use device_driver::AsyncBufferInterface;
6use embedded_hal_async::i2c::I2c;
7
8use crate::ll::{self, DeviceError};
9
10pub use crate::ll::MaxCurrentOption;
11
12#[derive(Debug, Default, Clone, Copy)]
14pub enum Address {
15 #[default]
17 Address0,
18 Address1,
20 Address2,
22 Address3,
24 Broadcast,
29}
30
31#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
33pub enum Error<T> {
34 Interface(T),
36 Index,
41}
42
43impl<T> From<DeviceError<T>> for Error<T> {
44 fn from(value: DeviceError<T>) -> Self {
45 match value {
46 DeviceError::Interface(e) => Error::Interface(e),
47 DeviceError::BufferTooSmall => unreachable!(), }
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq)]
55#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
56pub struct Rgb(pub [u8; 3]);
57
58impl core::ops::Deref for Rgb {
59 type Target = [u8; 3];
60
61 fn deref(&self) -> &Self::Target {
62 &self.0
63 }
64}
65
66impl From<[u8; 3]> for Rgb {
67 fn from(value: [u8; 3]) -> Self {
68 Rgb(value)
69 }
70}
71
72impl From<(u8, u8, u8)> for Rgb {
73 fn from(value: (u8, u8, u8)) -> Self {
74 Rgb(value.into())
75 }
76}
77
78pub mod marker {
80 pub struct Standby;
81 pub struct Normal;
82
83 trait Sealed {}
84
85 #[allow(private_bounds)]
86 pub trait Marker: Sealed {}
87
88 macro_rules! impl_marker {
89 ($struct:ty) => {
90 impl Sealed for $struct {}
91 impl Marker for $struct {}
92 };
93 }
94
95 impl_marker!(Standby);
96 impl_marker!(Normal);
97}
98
99pub struct Driver<VARIANT: LP50xx, T: I2c, STATE: marker::Marker> {
106 device: ll::Device<ll::i2c::DeviceInterface<T>>,
107 marker: PhantomData<VARIANT>,
108 state: PhantomData<STATE>,
109}
110
111pub struct Config {
113 pub log_scale: bool,
115 pub power_save: bool,
117 pub pwm_dithering: bool,
119 pub max_current: ll::MaxCurrentOption,
123}
124
125impl Default for Config {
126 fn default() -> Self {
127 Self {
128 log_scale: true,
129 power_save: true,
130 pwm_dithering: true,
131 max_current: ll::MaxCurrentOption::Current25MA5,
132 }
133 }
134}
135
136impl<VARIANT: LP50xx, T: I2c> Driver<VARIANT, T, marker::Standby> {
137 fn new(interface: T, address: Address) -> Self {
138 let address = match address {
139 Address::Address0 => VARIANT::I2C_ADDRESS_BASE,
140 Address::Address1 => VARIANT::I2C_ADDRESS_BASE | 0b01,
141 Address::Address2 => VARIANT::I2C_ADDRESS_BASE | 0b10,
142 Address::Address3 => VARIANT::I2C_ADDRESS_BASE | 0b11,
143 Address::Broadcast => VARIANT::I2C_ADDRESS_BROADCAST,
144 };
145
146 Self {
147 device: ll::Device::new(ll::i2c::DeviceInterface::new(interface, address)),
148 marker: PhantomData,
149 state: PhantomData,
150 }
151 }
152
153 pub async fn enable(
158 mut self,
159 ) -> Result<Driver<VARIANT, T, marker::Normal>, DeviceError<T::Error>> {
160 self.device
161 .device_config_0()
162 .write_async(|w| w.set_chip_en(true))
163 .await?;
164
165 Ok(Driver {
166 device: self.device,
167 marker: PhantomData,
168 state: PhantomData,
169 })
170 }
171}
172
173impl<VARIANT: LP50xx, T: I2c> Driver<VARIANT, T, marker::Normal> {
174 pub async fn disable(
181 mut self,
182 ) -> Result<Driver<VARIANT, T, marker::Standby>, DeviceError<T::Error>> {
183 self.device
184 .device_config_0()
185 .write_async(|w| w.set_chip_en(false))
186 .await?;
187
188 Ok(Driver {
189 device: self.device,
190 marker: PhantomData,
191 state: PhantomData,
192 })
193 }
194}
195
196impl<VARIANT: LP50xx, T: I2c, MARKER: marker::Marker> Driver<VARIANT, T, MARKER> {
197 pub async fn configure(&mut self, config: &Config) -> Result<(), DeviceError<T::Error>> {
199 self.device
200 .device_config_1()
201 .modify_async(|w| {
202 w.set_log_scale_en(config.log_scale);
203 w.set_max_current_option(config.max_current);
204 w.set_power_save_en(config.power_save);
205 w.set_pwm_dithering_en(config.pwm_dithering);
206 })
207 .await?;
208 Ok(())
209 }
210
211 pub async fn set_channel(&mut self, channel_i: u8, value: u8) -> Result<(), Error<T::Error>> {
215 if channel_i > VARIANT::LED_COUNT {
216 return Err(Error::Index);
217 }
218
219 self.device
220 .interface()
221 .write(VARIANT::OUT_START_ADDRESS + channel_i, &[value])
222 .await?;
223 Ok(())
224 }
225
226 pub async fn set_rgb(
230 &mut self,
231 rgb_i: u8,
232 value: impl Into<Rgb>,
233 ) -> Result<(), Error<T::Error>> {
234 if rgb_i > VARIANT::RGB_COUNT {
235 return Err(Error::Index);
236 }
237
238 self.device
240 .interface()
241 .write(VARIANT::OUT_START_ADDRESS + rgb_i * 3, value.into().deref())
242 .await?;
243 Ok(())
244 }
245
246 pub async fn set_rgb_brightness(
250 &mut self,
251 rgb_i: u8,
252 value: u8,
253 ) -> Result<(), Error<T::Error>> {
254 if rgb_i > VARIANT::RGB_COUNT {
255 return Err(Error::Index);
256 }
257
258 self.device
259 .interface()
260 .write(VARIANT::LED_START_ADDRESS + rgb_i, &[value])
261 .await?;
262 Ok(())
263 }
264
265 pub async fn set_all_brightness(&mut self, value: u8) -> Result<(), Error<T::Error>> {
267 let mut buf: heapless::Vec<u8, 36> = heapless::Vec::new();
268 buf.extend(core::iter::repeat_n(value, VARIANT::RGB_COUNT as usize));
269
270 self.device
271 .interface()
272 .write(VARIANT::LED_START_ADDRESS, &buf)
273 .await?;
274 Ok(())
275 }
276}
277
278pub trait LP50xx: Sized {
280 const LED_COUNT: u8;
281 const RGB_COUNT: u8 = Self::LED_COUNT / 3;
282
283 const I2C_ADDRESS_BASE: u8;
284 const I2C_ADDRESS_BROADCAST: u8;
285
286 const LED_START_ADDRESS: u8;
288 const OUT_START_ADDRESS: u8 = Self::LED_START_ADDRESS + Self::RGB_COUNT;
290
291 fn new<T: I2c>(interface: T, address: Address) -> Driver<Self, T, marker::Standby> {
293 Driver::new(interface, address)
294 }
295}
296
297pub struct LP5009;
298pub struct LP5012;
299pub struct LP5018;
300pub struct LP5024;
301pub struct LP5030;
302pub struct LP5036;
303
304impl LP50xx for LP5009 {
305 const LED_COUNT: u8 = 9;
306 const I2C_ADDRESS_BASE: u8 = 0b0110000;
307 const I2C_ADDRESS_BROADCAST: u8 = 0b0011100;
308 const LED_START_ADDRESS: u8 = 0x07;
309 const OUT_START_ADDRESS: u8 = 0x0b;
310}
311
312impl LP50xx for LP5012 {
313 const LED_COUNT: u8 = 12;
314 const I2C_ADDRESS_BASE: u8 = 0b0110000;
315 const I2C_ADDRESS_BROADCAST: u8 = 0b0011100;
316 const LED_START_ADDRESS: u8 = 0x07;
317}
318
319impl LP50xx for LP5018 {
320 const LED_COUNT: u8 = 18;
321 const I2C_ADDRESS_BASE: u8 = 0b0101000;
322 const I2C_ADDRESS_BROADCAST: u8 = 0b0111100;
323 const LED_START_ADDRESS: u8 = 0x07;
324 const OUT_START_ADDRESS: u8 = 0x0f;
325}
326
327impl LP50xx for LP5024 {
328 const LED_COUNT: u8 = 24;
329 const I2C_ADDRESS_BASE: u8 = 0b0101000;
330 const I2C_ADDRESS_BROADCAST: u8 = 0b0111100;
331 const LED_START_ADDRESS: u8 = 0x07;
332}
333
334impl LP50xx for LP5030 {
335 const LED_COUNT: u8 = 30;
336 const I2C_ADDRESS_BASE: u8 = 0b0110000;
337 const I2C_ADDRESS_BROADCAST: u8 = 0b0011100;
338 const LED_START_ADDRESS: u8 = 0x08;
339 const OUT_START_ADDRESS: u8 = 0x14;
340}
341
342impl LP50xx for LP5036 {
343 const LED_COUNT: u8 = 36;
344 const I2C_ADDRESS_BASE: u8 = 0b0110000;
345 const I2C_ADDRESS_BROADCAST: u8 = 0b0011100;
346 const LED_START_ADDRESS: u8 = 0x08;
347}