lm36011/lib.rs
1#![no_std]
2
3use embedded_hal::blocking::i2c;
4use bitflags::bitflags;
5use core::fmt;
6
7/// Library for the Texas instruments LM36011 inductorless LED driver
8///
9/// https://www.ti.com/lit/ds/symlink/lm36011.pdf?ts=1694461699965&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FLM36011
10///
11/// This crate enables register only read / write, or complete register one-shot read/write based
12/// on the last known register values and the bitflags crate for updating specific features.
13
14/// Custom errors for the LM36011.
15#[derive(Debug)]
16pub enum LM36011Error<E> {
17 I2CError(E),
18 InvalidInput,
19 CurrentOutOfRange,
20 DeviceIDError,
21}
22
23/// Represents the configuration registers of the LM36011.
24pub enum Register {
25 /// Enable Register
26 EnableRegister = 0x01,
27 /// Configuration Register
28 ConfigurationRegister = 0x02,
29 /// LED Flash Brightness Register
30 LEDFlashBrightnessRegister = 0x03,
31 /// LED Torch Brightness Register
32 LEDTorchBrightnessRegister = 0x04,
33 /// Flags Register
34 FlagsRegister = 0x05,
35 /// Device ID Register
36 DeviceIdRegister = 0x06,
37}
38
39/// implement display trait for Register Enum to be used in printing out to serial if needed
40impl fmt::Display for Register {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match *self {
43 Register::EnableRegister => write!(f, "Enable Register"),
44 Register::ConfigurationRegister => write!(f, "Configuration Register"),
45 Register::LEDFlashBrightnessRegister => write!(f, "LED Flash Brightness Register"),
46 Register::LEDTorchBrightnessRegister => write!(f, "LED Torch Brightness Register"),
47 Register::FlagsRegister => write!(f, "Flags Register"),
48 Register::DeviceIdRegister => write!(f, "Device ID Register"),
49 }
50 }
51}
52
53// Bitflags for the Enable Register (0x01)
54bitflags! {
55 pub struct EnableRegisterFlags: u8 {
56 // Reserved for future use
57 const ENABLE_REGISTER_RFU = 0b1110_0000;
58 // enables
59 const IVFM_ENABLE = 0b0001_0000;
60 const STROBE_TYPE_EDGE_TRIGGERED = 0b0000_1000;
61 const STROBE_ENABLE = 0b0000_0100;
62 // mode settings
63 const MODE_IR_DRIVE = 0b0000_0001;
64 const MODE_TORCH = 0b0000_0010;
65 const MODE_FLASH = 0b0000_0011;
66 const MODE_MASK = 0b0000_0011;
67 }
68}
69
70// Bitflags for the Configuration Register (0x02)
71bitflags! {
72 pub struct ConfigurationRegisterFlags: u8 {
73 /// IVFM Levels (IVFM-D) [Bit 7-5]
74 const IVFM_2_9V = 0b0000_0000;
75 const IVFM_3_0V = 0b0010_0000;
76 const IVFM_3_1V = 0b0100_0000;
77 const IVFM_3_2V = 0b0110_0000;
78 const IVFM_3_3V = 0b1000_0000;
79 const IVFM_3_4V = 0b1010_0000;
80 const IVFM_3_5V = 0b1100_0000;
81 const IVFM_3_6V = 0b1110_0000;
82
83 /// Flash Time-out Duration [Bit 4-1]
84 const TIMEOUT_40MS = 0b0000_0000;
85 const TIMEOUT_80MS = 0b0000_0010;
86 const TIMEOUT_120MS = 0b0000_0100;
87 const TIMEOUT_160MS = 0b0000_0110;
88 const TIMEOUT_200MS = 0b0000_1000;
89 const TIMEOUT_240MS = 0b0000_1010;
90 const TIMEOUT_280MS = 0b0000_1100;
91 const TIMEOUT_320MS = 0b0000_1110;
92 const TIMEOUT_360MS = 0b0001_0000;
93 const TIMEOUT_400MS = 0b0001_0010;
94 const TIMEOUT_600MS = 0b0001_0100;
95 const TIMEOUT_800MS = 0b0001_0110;
96 const TIMEOUT_1000MS = 0b0001_1000;
97 const TIMEOUT_1200MS = 0b0001_1010;
98 const TIMEOUT_1400MS = 0b0001_1100;
99 const TIMEOUT_1600MS = 0b0001_1110;
100
101 /// Torch Ramp [Bit 0]
102 const TORCH_RAMP_OFF = 0b0000_0000;
103 const TORCH_RAMP_1MS = 0b0000_0001;
104 }
105}
106
107// Bitflags for the LED Flash Brightness Register (0x03)
108bitflags! {
109 pub struct LedFlashBrightnessFlags: u8 {
110 /// LED Flash Brightness Level [Bit 6:0]
111 const FLASH_11MA = 0x00;
112 const FLASH_257MA = 0x15;
113 const FLASH_750MA = 0x3F;
114 const FLASH_1030MA = 0x5F;
115 const FLASH_1200MA = 0x66;
116 const FLASH_1500MA = 0x7F;
117
118 /// Thermal Current Scale-Back [Bit 7]
119 const THERMAL_SCALEBACK_ENABLED = 0b1000_0000;
120 }
121}
122
123// Bitflags for the LED Torch Brightness Register (0x04)
124bitflags! {
125 pub struct LedTorchBrightnessFlags: u8 {
126 // Reserved for future use
127 const TORCH_BRIGHTNESS_RFU = 0b1000_0000;
128 // Torch currents
129 const TORCH_2_4MA = 0x00;
130 const TORCH_64MA = 0x15;
131 const TORCH_188MA = 0x3F;
132 const TORCH_258MA = 0x5F;
133 const TORCH_302MA = 0x66;
134 const TORCH_376MA = 0x7F;
135 }
136}
137
138// Bitflags for the Flags Register (0x05)
139bitflags! {
140 pub struct FlagRegisterFlags: u8 {
141 // Reserved for future use
142 const FLAGS_REGISTER_RFU = 0b1000_0000;
143 // Flags
144 const IVFM_TRIP = 0b0100_0000;
145 const VLED_SHORT_FAULT = 0b0010_0000;
146 const THERMAL_CURRENT_SCALE_BACK = 0b0000_1000;
147 const THERMAL_SHUTDOWN_FAULT = 0b0000_0100;
148 const UVLO_FAULT = 0b0000_0010;
149 const FLASH_TIMEOUT_FLAG = 0b0000_0001;
150 }
151}
152
153// Bitflags for the Device ID Register (0x06)
154bitflags! {
155 /// Represents the Device ID and RESET Register of the LM36011.
156 pub struct DeviceIdFlags: u8 {
157 // Software RESET
158 // 0 = Normal (default)
159 // 1 = Force device RESET
160 const SOFTWARE_RESET = 0b1000_0000;
161
162 // Reserved for Future Use
163 const DEVICE_ID_RFU = 0b0100_0000;
164
165 // Device ID
166 const DEVICE_ID_MASK = 0b0011_1000;
167
168 // Silicon Revision Bits
169 const SILICON_REVISION_MASK = 0b0000_0111;
170 }
171}
172
173/// I2C address for the LM36011 device.
174const LM36011_I2C_ADDRESS: u8 = 0x64;
175
176/// Represents the LM36011 device with an associated I2C interface.
177pub struct LM36011<I2C> {
178 /// The I2C interface used to communicate with the device.
179 i2c: I2C,
180 pub enable_flags: EnableRegisterFlags,
181 pub config_flags: ConfigurationRegisterFlags,
182 pub flash_brightness_flags: LedFlashBrightnessFlags,
183 pub torch_brightness_flags: LedTorchBrightnessFlags,
184 pub flag_register_flags: FlagRegisterFlags,
185 pub device_id: DeviceIdFlags,
186}
187
188impl<I2C> fmt::Display for LM36011<I2C> {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 write!(
191 f,
192 "Enable Register: {:?}, \
193 Configuration Register: {:?}, \
194 LED Flash Brightness Register: {:?}, \
195 LED Torch Brightness Register: {:?}, \
196 Flags Register: {:?}, \
197 Device ID Register: {:?}",
198 self.enable_flags,
199 self.config_flags,
200 self.flash_brightness_flags,
201 self.torch_brightness_flags,
202 self.flag_register_flags,
203 self.device_id
204 )
205 }
206}
207
208impl<I2C, E> LM36011<I2C>
209 where
210 I2C: i2c::Write<Error=E> + i2c::WriteRead<Error=E>,
211{
212 /// Creates a new instance of the LM36011 with the provided I2C interface.
213 pub fn new(i2c: I2C) -> Self {
214 Self {
215 i2c,
216 enable_flags: EnableRegisterFlags::IVFM_ENABLE,
217 config_flags: ConfigurationRegisterFlags::IVFM_2_9V |
218 ConfigurationRegisterFlags::TIMEOUT_600MS |
219 ConfigurationRegisterFlags::TORCH_RAMP_1MS,
220 flash_brightness_flags: LedFlashBrightnessFlags::FLASH_11MA |
221 LedFlashBrightnessFlags::THERMAL_SCALEBACK_ENABLED,
222 torch_brightness_flags: LedTorchBrightnessFlags::TORCH_2_4MA,
223 flag_register_flags: FlagRegisterFlags::empty(),
224 device_id: DeviceIdFlags::empty(),
225 }
226 }
227
228 /// Sets the flash current of the LM36011 device.
229 ///
230 /// This function configures the flash current of the LM36011 by writing to the
231 /// `LEDFlashBrightnessRegister`. The desired current value is passed as an argument.
232 ///
233 /// # Arguments
234 ///
235 /// * `current` - The desired flash current value to be set. The exact range and interpretation
236 /// of this value should be based on the LM36011 documentation.
237 ///
238 /// # Returns
239 ///
240 /// * `Ok(())` if the operation was successful.
241 /// * `Err(E)` if there was an error during the operation. The error type `E` is determined by the I2C interface.
242 ///
243 /// # Example
244 ///
245 /// ```
246 /// // Some initialization to get the device instance
247 /// //(I2C needs to be inititilized first)
248 /// let mut driver = lm36011::LM36011::new(i2c);; // Some initialization to get the device instance
249 /// match driver.set_flash_current(0x55) {
250 /// Ok(_) => println!("Flash current set successfully"),
251 /// Err(e) => eprintln!("Error setting flash current: {:?}", e),
252 /// }
253 /// ```
254 pub fn set_flash_current_hex(&mut self, current: u8) -> Result<(), LM36011Error<E>> {
255 if current > 0b1000_0000 {
256 return Err(LM36011Error::CurrentOutOfRange);
257 }
258
259 // Use the set_register function to set the flash current
260 self.set_register(Register::LEDFlashBrightnessRegister, current)
261 }
262
263 /// Sets the flash current of the LM36011 device.
264 ///
265 /// This function configures the flash current of the LM36011 by writing to the
266 /// `LEDFlashBrightnessRegister`. The desired current value is passed as an argument.
267 ///
268 /// # Arguments
269 ///
270 /// * `current` - The desired flash current value to be set. The input current in mA will be
271 /// divided by 11.7 and converted to a u8 byte. Note: since the resolution of the driver is
272 /// 11.7mA, setting fractions of the current is likly overkill, but could be more accurate in a
273 /// very small subset of results.
274 ///
275 /// # Returns
276 ///
277 /// * `Ok(())` if the operation was successful.
278 /// * `Err(E)` if there was an error during the operation. The error type `E` is determined by the I2C interface.
279 ///
280 /// # Example
281 ///
282 /// ```
283 /// // Some initialization to get the device instance
284 /// //(I2C needs to be inititilized first)
285 /// let mut driver = lm36011::LM36011::new(i2c);; // Some initialization to get the device instance
286 /// match driver.set_flash_current(150.0) {
287 /// Ok(_) => println!("Flash current set successfully"),
288 /// Err(e) => eprintln!("Error setting flash current: {:?}", e),
289 /// }
290 /// ```
291 pub fn set_flash_current(&mut self, current: f32) -> Result<(), LM36011Error<E>> {
292 if current < 0.0 || current > 1500.0 {
293 return Err(LM36011Error::CurrentOutOfRange);
294 }
295 // take in the current in mA (f32) and convert it to a hex value
296 let brightness_flags: u8 = (current / 11.7) as u8;
297
298 // convert the u8 value to a LedFlashBrightnessFlags
299 let mut brightness_bitflags =
300 LedFlashBrightnessFlags::from_bits_truncate(brightness_flags);
301
302 // Ensure the thermal current scale-back bit remains set/not set
303 brightness_bitflags.set(
304 LedFlashBrightnessFlags::THERMAL_SCALEBACK_ENABLED,
305 self.flash_brightness_flags.contains(
306 LedFlashBrightnessFlags::THERMAL_SCALEBACK_ENABLED),
307 );
308
309 // Use the set_register function to set the flash current
310 self.set_register(Register::LEDFlashBrightnessRegister, brightness_flags)?;
311
312 // update internal struct state
313 self.flash_brightness_flags = brightness_bitflags;
314
315 Ok(())
316 }
317
318 /// Retrieves the device ID from the LM36011.
319 ///
320 /// This function reads the `DeviceIdRegister` of the LM36011 device to obtain its ID.
321 /// It uses the I2C `write_read` method to request and retrieve the device ID.
322 ///
323 /// # Returns
324 ///
325 /// * `Ok(u8)` containing the device ID if the read operation was successful.
326 /// * `Err(E)` if there was an error during the read operation. The error type `E` is determined by the I2C interface.
327 ///
328 /// # Example
329 ///
330 /// ```
331 /// // Some initialization to get the device instance
332 /// //(I2C needs to be inititilized first)
333 /// let mut driver = lm36011::LM36011::new(i2c);; // Some initialization to get the device instance
334 /// match driver.get_device_id() {
335 /// Ok(id) => println!("LM36011 device ID: {}", id),
336 /// Err(e) => eprintln!("Error reading device ID: {:?}", e),
337 /// }
338 /// ```
339 pub fn get_device_id(&mut self) -> Result<u8, E> {
340 let mut buffer = [0u8; 1];
341 self.i2c.write_read(LM36011_I2C_ADDRESS, &[Register::DeviceIdRegister as u8],
342 &mut buffer)?;
343 Ok(buffer[0])
344 }
345
346 /// Retrieves the value of a specified register from the device.
347 ///
348 /// This function reads a byte of data from a specified register on the LM36011 device.
349 /// It uses the I2C `write_read` method to request and retrieve the data.
350 ///
351 /// # Arguments
352 ///
353 /// * `reg` - The register from which the data should be read. This is specified using the `Register` enum.
354 ///
355 /// # Returns
356 ///
357 /// * `Ok(u8)` containing the byte value read from the specified register if the read operation was successful.
358 /// * `Err(E)` if there was an error during the read operation. The error type `E` is determined by the I2C interface.
359 ///
360 /// # Example
361 ///
362 /// ```
363 /// // Some initialization to get the device instance
364 /// //(I2C needs to be inititilized first)
365 /// let mut driver = lm36011::LM36011::new(i2c);
366 /// match driver.get_register(Register::DeviceIdRegister) {
367 /// Ok(value) => println!("Register value: {}", value),
368 /// Err(e) => eprintln!("Error reading register: {:?}", e),
369 /// }
370 /// ```
371 pub fn get_register(&mut self, reg: Register) -> Result<u8, E> {
372 let mut buffer = [0u8; 1];
373 self.i2c.write_read(LM36011_I2C_ADDRESS, &[reg as u8], &mut buffer)?;
374 Ok(buffer[0])
375 }
376
377 /// Sets the value of a specified register on the device.
378 ///
379 /// This function writes a given data byte to a specified register on the LM36011 device.
380 /// It uses the I2C `write` method to send the data.
381 ///
382 /// # Arguments
383 ///
384 /// * `reg` - The register to which the data should be written. This is specified using the `Register` enum.
385 /// * `data` - The data byte to be written to the specified register.
386 ///
387 /// # Returns
388 ///
389 /// * `Ok(())` if the write operation was successful.
390 /// * `Err(E)` if there was an error during the write operation. The error type `E` is determined by the I2C interface.
391 ///
392 /// # Example
393 ///
394 /// ```
395 /// // Some initialization to get the device instance
396 /// //(I2C needs to be inititilized first)
397 /// let mut driver = lm36011::LM36011::new(i2c);
398 /// let result = driver.set_register(Register::DeviceIdRegister, 0x01);
399 /// if result.is_err() {
400 /// // Handle the error
401 /// }
402 /// ```
403 pub fn set_register(&mut self, reg: Register, data: u8) -> Result<(), LM36011Error<E>> {
404 let buffer: [u8; 2] = [reg as u8, data];
405 self.i2c.write(LM36011_I2C_ADDRESS, &buffer).
406 map_err(LM36011Error::I2CError)
407 }
408
409 /// Reads all the registers of the LM36011 and saves the register states to the respective bitflag structs.
410 ///
411 /// This function performs a single I2C read operation starting from the `EnableRegister` and reads 6 bytes,
412 /// which correspond to the 6 registers of the LM36011. The read values are then saved to the respective
413 /// bitflag structs for easy access and manipulation.
414 ///
415 /// # Returns
416 ///
417 /// * `Ok(())` if the I2C read operation is successful.
418 /// * `Err(E)` if the I2C read operation fails, where `E` is the error type of the I2C operations.
419 ///
420 /// # Usage
421 ///
422 /// ```rust
423 /// let mut driver = LM36011::new(i2c_instance);
424 ///
425 /// if let Err(e) = driver.read_status() {
426 /// // Handle the error `e` here.
427 /// }
428 /// ```
429 pub fn read_status(&mut self) -> Result<(), LM36011Error<E>> {
430 // Read all 6 LM36011 registers
431 let mut buffer = [0u8; 6];
432 self.i2c.write_read(LM36011_I2C_ADDRESS,
433 &[Register::EnableRegister as u8], &mut buffer).
434 map_err(LM36011Error::I2CError)?;
435
436 // Save registers to the struct
437 self.enable_flags = EnableRegisterFlags::from_bits_truncate(buffer[0]);
438 self.config_flags = ConfigurationRegisterFlags::from_bits_truncate(buffer[1]);
439 self.flash_brightness_flags = LedFlashBrightnessFlags::from_bits_truncate(buffer[2]);
440 self.torch_brightness_flags = LedTorchBrightnessFlags::from_bits_truncate(buffer[3]);
441 self.flag_register_flags = FlagRegisterFlags::from_bits_truncate(buffer[4]);
442 self.device_id = DeviceIdFlags::from_bits_truncate(buffer[5]);
443
444 Ok(())
445 }
446
447 /// Writes the bitflags settings to the LM36011 device.
448 ///
449 /// This function will take the current settings stored in the bitflag structs and write them to the
450 /// respective registers on the LM36011 device using I2C.
451 ///
452 /// # Examples
453 ///
454 /// ```rust
455 /// // Assuming `i2c` is an initialized I2C instance`
456 /// let mut driver = LM36011::new(i2c_instance);
457 /// // Modify some settings
458 /// driver.enable_flags.insert(EnableRegisterFlags::MODE_TORCH);
459 /// driver.config_flags.insert(ConfigurationRegisterFlags::IVFM_3_4V);
460 ///
461 /// // Write the modified settings to the device
462 /// match lm36011.write_status() {
463 /// Ok(_) => println!("Settings written successfully!"),
464 /// Err(e) => println!("Failed to write settings: {:?}", e),
465 /// }
466 /// ```
467 ///
468 pub fn write_status(&mut self) -> Result<(), LM36011Error<E>> {
469 // create a buffer with all of the settings
470 let buffer = [0x01,
471 self.enable_flags.bits(),
472 self.config_flags.bits(),
473 self.flash_brightness_flags.bits(),
474 self.torch_brightness_flags.bits(),
475 //self.flag_register_flags.bits(),
476 //self.device_id.bits(),
477 ];
478
479 self.i2c.write(LM36011_I2C_ADDRESS, &buffer)
480 .map_err(LM36011Error::I2CError)
481 }
482
483 /// Performs a software reset on the LM36011 device.
484 ///
485 /// This function sends a specific command to the LM36011 device to initiate a software reset.
486 /// The reset command is sent to the address `0x06` with the data `0b1000_0000`.
487 ///
488 /// # Examples
489 ///
490 /// ```rust
491 /// let mut device = LM36011::new(i2c_instance);
492 ///
493 /// match device.software_reset() {
494 /// Ok(_) => println!("Software reset successful!"),
495 /// Err(e) => println!("Software reset failed with error: {:?}", e),
496 /// }
497 /// ```
498 ///
499 /// # Errors
500 ///
501 /// Returns an `Err` variant of `LM36011Error` if there's an I2C communication error.
502
503 pub fn software_reset(&mut self) -> Result<(), LM36011Error<E>> {
504 let buffer = [0x06,0b1000_0000];
505 self.i2c.write(LM36011_I2C_ADDRESS, &buffer)
506 .map_err(LM36011Error::I2CError)
507 }
508
509 /// Verifies the device ID of the LM36011.
510 ///
511 /// This function reads the current status of the LM36011, including its device ID,
512 /// and then checks if the silicon revision mask matches the expected value.
513 ///
514 /// # Returns
515 ///
516 /// * `Ok(true)` if the device ID matches the expected value.
517 /// * `Err(LM36011Error::InvalidInput)` if the device ID does not match the expected value.
518 /// * `Err(LM36011Error::I2CError(E))` if there's an error during the I2C communication.
519 ///
520 /// # Example
521 ///
522 /// ```rust
523 /// let mut driver = LM36011::new(i2c);
524 /// match driver.verify_device_id() {
525 /// Ok(true) => println!("Device ID verified!"),
526 /// Err(LM36011Error::DeviceIDError) => println!("Device ID does not match!"),
527 /// Err(LM36011Error::I2CError(_)) => println!("Error verifying device ID due to I2C communication"),
528 /// _ => println!("Some other error occurred"),
529 /// }
530 /// ```
531 pub fn verify_device_id(&mut self) -> Result<bool, LM36011Error<E>> {
532 match self.read_status() {
533 Ok(_) => (),
534 Err(e) => return Err(e),
535 }
536
537 // Check if the read value matches the expected device ID
538 if self.device_id & DeviceIdFlags::SILICON_REVISION_MASK ==
539 DeviceIdFlags::from_bits_truncate(0x01) {
540 Ok(true)
541 } else {
542 Err(LM36011Error::DeviceIDError)
543 }
544 }
545// similarly, you can add other methods with detailed documentation.
546}