1#![no_std]
78#![allow(dead_code, clippy::unusual_byte_groupings)]
79use embedded_hal::{delay::DelayNs, i2c};
80
81const REGISTER_SHUTDOWN: u8 = 0x00;
83const REGISTER_BREATHING_CONTROL: u8 = 0x01;
84const REGISTER_LED_MODE: u8 = 0x02;
85const REGISTER_CURRENT_SETTING: u8 = 0x03;
86const REGISTER_LED1_PWM: u8 = 0x04;
87const REGISTER_LED2_PWM: u8 = 0x05;
88const REGISTER_LED3_PWM: u8 = 0x06;
89const REGISTER_DATA_UPDATE: u8 = 0x07;
90const REGISTER_LED1_T0: u8 = 0x0A;
91const REGISTER_LED2_T0: u8 = 0x0B;
92const REGISTER_LED3_T0: u8 = 0x0C;
93const REGISTER_LED1_T1T2: u8 = 0x10;
94const REGISTER_LED2_T1T2: u8 = 0x11;
95const REGISTER_LED3_T1T2: u8 = 0x12;
96const REGISTER_LED1_T3T4: u8 = 0x16;
97const REGISTER_LED2_T3T4: u8 = 0x17;
98const REGISTER_LED3_T3T4: u8 = 0x18;
99const REGISTER_TIME_UPDATE: u8 = 0x1C;
100const REGISTER_LED_CONTROL: u8 = 0x1D;
101const REGISTER_RESET: u8 = 0x2F;
102
103const SHUTDOWN_CHANNEL_ENABLE: u8 = 0b00_1_0000_0;
105const SHUTDOWN_CHANNEL_DISABLE: u8 = 0b00_0_0000_0;
106const SOFTWARE_SHUTDOWN_MODE: u8 = 0b00_0_0000_0;
107const SOFTWARE_SHUTDOWN_NORMAL: u8 = 0b00_0_0000_1;
108
109#[derive(Debug, PartialEq)]
110pub enum CurrentSettings {
111 Current42mA = 0b000_000_00,
112 Current10mA = 0b000_001_00,
113 Current5mA = 0b000_010_00,
114 Current30mA = 0b000_011_00,
115 Current17p5mA = 0b000_100_00,
116}
117
118#[derive(Debug, PartialEq)]
120pub enum LEDModeSettings {
121 PWM = 0b00_0_00000,
123 Breathing = 0b00_1_00000,
125}
126
127#[derive(Debug, PartialEq)]
130pub enum BreathingIntroTime {
131 Time0s = 0b0000_0000,
132 Time0p13s = 0b0001_0000,
133 Time0p26s = 0b0010_0000,
134 Time0p52s = 0b0011_0000,
135 Time1p04s = 0b0100_0000,
136 Time2p08s = 0b0101_0000,
137 Time4p16s = 0b0110_0000,
138 Time8p32s = 0b0111_0000,
139 Time16p64s = 0b1000_0000,
140 Time33p28s = 0b1001_0000,
141 Time66p56s = 0b1010_0000,
142}
143
144#[derive(Debug, PartialEq)]
147pub enum BreathingRampUpTime {
148 Time0p13s = 0b000_0000_0,
149 Time0p26s = 0b001_0000_0,
150 Time0p52s = 0b010_0000_0,
151 Time1p04s = 0b011_0000_0,
152 Time2p08s = 0b100_0000_0,
153 Time4p16s = 0b101_0000_0,
154 Time8p32s = 0b110_0000_0,
155 Time16p64s = 0b111_0000_0,
156}
157
158#[derive(Debug, PartialEq)]
161pub enum BreathingHoldHighTime {
162 Time0s = 0b000_0000_0,
163 Time0p13s = 0b000_0001_0,
164 Time0p26s = 0b000_0010_0,
165 Time0p52s = 0b000_0011_0,
166 Time1p04s = 0b000_0100_0,
167 Time2p08s = 0b000_0101_0,
168 Time4p16s = 0b000_0110_0,
169 Time8p32s = 0b000_0111_0,
170 Time16p64s = 0b000_1000_0,
171}
172
173#[derive(Debug, PartialEq)]
176pub enum BreathingRampDownTime {
177 Time0p13s = 0b000_0000_0,
178 Time0p26s = 0b001_0000_0,
179 Time0p52s = 0b010_0000_0,
180 Time1p04s = 0b011_0000_0,
181 Time2p08s = 0b100_0000_0,
182 Time4p16s = 0b101_0000_0,
183 Time8p32s = 0b110_0000_0,
184 Time16p64s = 0b111_0000_0,
185}
186
187#[derive(Debug, PartialEq)]
190pub enum BreathingHoldLowTime {
191 Time0s = 0b000_0000_0,
192 Time0p13s = 0b000_0001_0,
193 Time0p26s = 0b000_0010_0,
194 Time0p52s = 0b000_0011_0,
195 Time1p04s = 0b000_0100_0,
196 Time2p08s = 0b000_0101_0,
197 Time4p16s = 0b000_0110_0,
198 Time8p32s = 0b000_0111_0,
199 Time16p64s = 0b000_1000_0,
200 Time33p28s = 0b000_1001_0,
201 Time66p56s = 0b000_1010_0,
202}
203
204#[derive(Debug, PartialEq)]
205pub enum LEDId {
206 LED1 = 0b001,
207 LED2 = 0b010,
208 LED3 = 0b100,
209 ALL = 0b111,
210}
211#[derive(Debug)]
213pub enum SN3193Error<I2C>
214where
215 I2C: i2c::I2c,
216{
217 I2CError(I2C::Error),
219}
220
221#[cfg(feature = "defmt")]
222impl<I2C> defmt::Format for SN3193Error<I2C>
223where
224 I2C: i2c::I2c,
225{
226 fn format(&self, fmt: defmt::Formatter) {
227 let msg = match self {
228 SN3193Error::I2CError(_) => "I2C error",
229 };
230 defmt::write!(fmt, "{}", msg);
231 }
232}
233
234#[cfg(feature = "ufmt")]
235impl<I2C> ufmt::uDisplay for SN3193Error<I2C>
236where
237 I2C: i2c::I2c,
238{
239 fn fmt<W>(&self, w: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
240 where
241 W: ufmt::uWrite + ?Sized,
242 {
243 let msg = match self {
244 SN3193Error::I2CError(_) => "I2C error",
245 };
246 ufmt::uwrite!(w, "{}", msg)
247 }
248}
249
250pub struct SN3193Driver<I2C, DELAY>
251where
252 I2C: i2c::I2c,
253 DELAY: DelayNs,
254{
255 i2c: I2C,
256 address: u8,
257 delay: DELAY,
258}
259
260impl<I2C, DELAY> SN3193Driver<I2C, DELAY>
261where
262 I2C: i2c::I2c,
263 DELAY: DelayNs,
264{
265 pub fn default_address() -> u8 {
267 0x68
268 }
269
270 pub fn new(i2c: I2C, delay: DELAY) -> Self {
272 Self::new_with_address(i2c, delay, Self::default_address())
273 }
274
275 pub fn new_with_address(i2c: I2C, delay: DELAY, address: u8) -> Self {
283 Self {
284 i2c,
285 address,
286 delay,
287 }
288 }
289
290 pub fn i2c(&mut self) -> &mut I2C {
292 &mut self.i2c
293 }
294
295 pub fn init(&mut self) -> Result<&mut Self, SN3193Error<I2C>> {
297 self.delay.delay_ms(10);
300 self.i2c
302 .write(self.address, &[REGISTER_RESET])
303 .map_err(SN3193Error::I2CError)?;
304
305 self.delay.delay_ms(10);
306 self.i2c
307 .write(
308 self.address,
309 &[
310 REGISTER_SHUTDOWN,
311 SHUTDOWN_CHANNEL_ENABLE | SOFTWARE_SHUTDOWN_MODE,
312 ],
313 )
314 .map_err(SN3193Error::I2CError)?;
315 self.delay.delay_ms(50);
316 self.set_led_mode(LEDModeSettings::PWM)?;
318
319 self.set_current(CurrentSettings::Current17p5mA)?
321 .enable_leds(true, true, true)?;
322
323 Ok(self)
324 }
325
326 pub fn set_led_mode(&mut self, mode: LEDModeSettings) -> Result<&mut Self, SN3193Error<I2C>> {
328 self.delay.delay_ms(10);
330
331 self.i2c
332 .write(self.address, &[REGISTER_LED_MODE, mode as u8])
333 .map_err(SN3193Error::I2CError)?;
334 self.delay.delay_ms(10);
335 Ok(self)
336 }
337
338 pub fn set_current(&mut self, current: CurrentSettings) -> Result<&mut Self, SN3193Error<I2C>> {
340 self.delay.delay_ms(1);
342
343 self.i2c
344 .write(self.address, &[REGISTER_CURRENT_SETTING, current as u8])
345 .map_err(SN3193Error::I2CError)?;
346 self.delay.delay_ms(5);
347 Ok(self)
348 }
349
350 pub fn enable_leds(
352 &mut self,
353 led1: bool,
354 led2: bool,
355 led3: bool,
356 ) -> Result<&mut Self, SN3193Error<I2C>> {
357 self.delay.delay_ms(1);
359
360 let mut led_enable = 0;
361 if led1 {
362 led_enable |= 0b001;
363 }
364 if led2 {
365 led_enable |= 0b010;
366 }
367 if led3 {
368 led_enable |= 0b100;
369 }
370 self.i2c
371 .write(self.address, &[REGISTER_LED_CONTROL, led_enable])
372 .map_err(SN3193Error::I2CError)?;
373 self.load_register_data()
374 }
375
376 pub fn set_pwm_levels(
378 &mut self,
379 led1: u8,
380 led2: u8,
381 led3: u8,
382 ) -> Result<&mut Self, SN3193Error<I2C>> {
383 self.delay.delay_ms(2);
385 self.i2c
386 .write(self.address, &[REGISTER_LED1_PWM, led1])
387 .map_err(SN3193Error::I2CError)?;
388 self.delay.delay_ms(2);
390 self.i2c
391 .write(self.address, &[REGISTER_LED2_PWM, led2])
392 .map_err(SN3193Error::I2CError)?;
393 self.delay.delay_ms(2);
395 self.i2c
396 .write(self.address, &[REGISTER_LED3_PWM, led3])
397 .map_err(SN3193Error::I2CError)?;
398 self.load_register_data()
399 }
400
401 pub fn set_breathing_times_for_led(
404 &mut self,
405 led: LEDId,
406 intro: BreathingIntroTime,
407 ramp_up: BreathingRampUpTime,
408 hold_high: BreathingHoldHighTime,
409 ramp_down: BreathingRampDownTime,
410 hold_low: BreathingHoldLowTime,
411 ) -> Result<&mut Self, SN3193Error<I2C>> {
412 let t0_value = intro as u8;
413 let t1t2_value = (ramp_up as u8) | (hold_high as u8);
414 let t3t4_value = (ramp_down as u8) | (hold_low as u8);
415
416 if led == LEDId::LED1 || led == LEDId::ALL {
417 self.set_breathing_register(REGISTER_LED1_T0, t0_value)?;
418 self.set_breathing_register(REGISTER_LED1_T1T2, t1t2_value)?;
419 self.set_breathing_register(REGISTER_LED1_T3T4, t3t4_value)?;
420 }
421 if led == LEDId::LED2 || led == LEDId::ALL {
422 self.set_breathing_register(REGISTER_LED2_T0, t0_value)?;
423 self.set_breathing_register(REGISTER_LED2_T1T2, t1t2_value)?;
424 self.set_breathing_register(REGISTER_LED2_T3T4, t3t4_value)?;
425 }
426 if led == LEDId::LED3 || led == LEDId::ALL {
427 self.set_breathing_register(REGISTER_LED3_T0, t0_value)?;
428 self.set_breathing_register(REGISTER_LED3_T1T2, t1t2_value)?;
429 self.set_breathing_register(REGISTER_LED3_T3T4, t3t4_value)?;
430 }
431 self.load_register_time_data()?;
432 Ok(self)
433 }
434
435 fn load_register_data(&mut self) -> Result<&mut Self, SN3193Error<I2C>> {
437 self.delay.delay_ms(10);
439 self.i2c
440 .write(self.address, &[REGISTER_DATA_UPDATE, 0xFF])
441 .map_err(SN3193Error::I2CError)?;
442 self.delay.delay_ms(30);
443 Ok(self)
444 }
445
446 fn load_register_time_data(&mut self) -> Result<&mut Self, SN3193Error<I2C>> {
448 self.delay.delay_ms(10);
450 self.i2c
451 .write(self.address, &[REGISTER_TIME_UPDATE, 0xFF])
452 .map_err(SN3193Error::I2CError)?;
453 self.delay.delay_ms(30);
454 Ok(self)
455 }
456
457 fn set_breathing_register(
459 &mut self,
460 register: u8,
461 value: u8,
462 ) -> Result<&mut Self, SN3193Error<I2C>> {
463 self.delay.delay_ms(10);
465 self.i2c
466 .write(self.address, &[register, value])
467 .map_err(SN3193Error::I2CError)?;
468 self.delay.delay_ms(30);
469 Ok(self)
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 extern crate std;
476 use super::*;
477 use embedded_hal_mock::eh1::{
478 delay::NoopDelay,
479 i2c::{Mock as I2cMock, Transaction as I2cTransaction},
480 };
481
482 #[test]
483 fn test_current_settings_into() {
484 assert_eq!(CurrentSettings::Current42mA as u8, 0b000_000_00);
485 assert_eq!(CurrentSettings::Current10mA as u8, 0b000_001_00);
486 assert_eq!(CurrentSettings::Current5mA as u8, 0b000_010_00);
487 assert_eq!(CurrentSettings::Current30mA as u8, 0b000_011_00);
488 assert_eq!(CurrentSettings::Current17p5mA as u8, 0b000_100_00);
489 }
490
491 #[test]
492 fn test_led_mode_settings_into() {
493 assert_eq!(LEDModeSettings::PWM as u8, 0b00_0_00000);
494 assert_eq!(LEDModeSettings::Breathing as u8, 0b00_1_00000);
495 }
496
497 #[test]
498 fn test_set_led_mode() {
499 let expectations = [I2cTransaction::write(
500 0x68,
501 std::vec![REGISTER_LED_MODE, LEDModeSettings::PWM as u8],
502 )];
503 let i2c = I2cMock::new(&expectations);
504 let mut driver = SN3193Driver::new(i2c, NoopDelay);
505 driver.set_led_mode(LEDModeSettings::PWM).unwrap();
506 driver.i2c().done();
507 }
508
509 #[test]
510 fn test_set_current() {
511 let expectations = [
512 I2cTransaction::write(
513 0x68,
514 std::vec![
515 REGISTER_CURRENT_SETTING,
516 CurrentSettings::Current17p5mA as u8
517 ],
518 ),
519 I2cTransaction::write(
520 0x68,
521 std::vec![REGISTER_CURRENT_SETTING, CurrentSettings::Current42mA as u8],
522 ),
523 ];
524 let i2c = I2cMock::new(&expectations);
525 let mut driver = SN3193Driver::new(i2c, NoopDelay);
526 assert!(driver.set_current(CurrentSettings::Current17p5mA).is_ok());
527 assert!(driver.set_current(CurrentSettings::Current42mA).is_ok());
528 driver.i2c().done();
529 }
530
531 #[test]
532 fn test_enable_leds() {
533 let expectations = [
534 I2cTransaction::write(0x68, std::vec![REGISTER_LED_CONTROL, 0b111]),
535 I2cTransaction::write(0x68, std::vec![REGISTER_DATA_UPDATE, 0xFF]),
536 I2cTransaction::write(0x68, std::vec![REGISTER_LED_CONTROL, 0b101]),
537 I2cTransaction::write(0x68, std::vec![REGISTER_DATA_UPDATE, 0xFF]),
538 I2cTransaction::write(0x68, std::vec![REGISTER_LED_CONTROL, 0b011]),
539 I2cTransaction::write(0x68, std::vec![REGISTER_DATA_UPDATE, 0xFF]),
540 ];
541 let i2c = I2cMock::new(&expectations);
542 let mut driver = SN3193Driver::new(i2c, NoopDelay);
543 assert!(driver.enable_leds(true, true, true).is_ok());
544 assert!(driver.enable_leds(true, false, true).is_ok());
545 assert!(driver.enable_leds(true, true, false).is_ok());
546 driver.i2c().done();
547 }
548
549 #[test]
550 fn test_set_pwm_levels() {
551 let expectations = [
552 I2cTransaction::write(0x6B, std::vec![REGISTER_LED1_PWM, 255]),
553 I2cTransaction::write(0x6B, std::vec![REGISTER_LED2_PWM, 128]),
554 I2cTransaction::write(0x6B, std::vec![REGISTER_LED3_PWM, 0]),
555 I2cTransaction::write(0x6B, std::vec![REGISTER_DATA_UPDATE, 0xFF]),
556 ];
557 let i2c = I2cMock::new(&expectations);
558 let mut driver = SN3193Driver::new_with_address(i2c, NoopDelay, 0x6B);
559 assert!(driver.set_pwm_levels(255, 128, 0).is_ok());
560 driver.i2c().done();
561 }
562}