afe4404/led_current/
low_level.rs

1//! This module contains the LED current and offset current low level functions.
2
3use embedded_hal::i2c::{I2c, SevenBitAddress};
4use uom::si::{
5    electric_current::{microampere, milliampere},
6    f32::ElectricCurrent,
7};
8
9use crate::{
10    device::AFE4404,
11    errors::AfeError,
12    modes::{LedMode, ThreeLedsMode, TwoLedsMode},
13    register_structs::R22h,
14};
15
16impl<I2C, MODE> AFE4404<I2C, MODE>
17where
18    I2C: I2c<SevenBitAddress>,
19    MODE: LedMode,
20{
21    /// Checks if the current range has changed and returns a scaled value.
22    fn scale_current(reg_value: u8, prev_2x: bool, curr_2x: bool) -> u8 {
23        if prev_2x == curr_2x {
24            reg_value
25        } else if curr_2x {
26            reg_value / 2
27        } else {
28            reg_value * 2
29        }
30    }
31
32    /// Sets the LED1 current.
33    ///
34    /// # Notes
35    ///
36    /// This function automatically expands the current range to 0-100 mA if the current is above 50 mA.
37    /// When the range is expanded to 0-100 mA, the unit step is doubled from 0.8 to 1.6 mA.
38    ///
39    /// # Errors
40    ///
41    /// This function returns an error if the I2C bus encounters an error.
42    /// Setting a current value outside the range 0-100mA will result in an error.
43    pub fn set_led1_current(
44        &mut self,
45        current: ElectricCurrent,
46    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
47        let r22h_prev = self.registers.r22h.read()?;
48        let r23h_prev = self.registers.r23h.read()?;
49
50        let high_current = current.get::<milliampere>() > 50.0
51            || (r23h_prev.iled_2x() && (r22h_prev.iled2() > 31 || r22h_prev.iled3() > 31));
52
53        let range = if high_current {
54            ElectricCurrent::new::<milliampere>(100.0)
55        } else {
56            ElectricCurrent::new::<milliampere>(50.0)
57        };
58
59        let quantisation = range / 63.0;
60
61        if current > range || current.get::<milliampere>() < 0.0 {
62            return Err(AfeError::LedCurrentOutsideAllowedRange);
63        }
64
65        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
66        let values = [
67            (current / quantisation).value.round() as u8,
68            Self::scale_current(r22h_prev.iled2(), r23h_prev.iled_2x(), high_current),
69            Self::scale_current(r22h_prev.iled3(), r23h_prev.iled_2x(), high_current),
70        ];
71
72        self.registers.r22h.write(
73            R22h::new()
74                .with_iled1(values[0])
75                .with_iled2(values[1])
76                .with_iled3(values[2]),
77        )?;
78        self.registers
79            .r23h
80            .write(r23h_prev.with_iled_2x(high_current))?;
81
82        Ok(f32::from(values[0]) * quantisation)
83    }
84
85    /// Sets the LED2 current.
86    ///
87    /// # Notes
88    ///
89    /// This function automatically expands the current range to 0-100 mA if the current is above 50 mA.
90    /// When the range is expanded to 0-100 mA, the unit step is doubled from 0.8 to 1.6 mA.
91    ///
92    /// # Errors
93    ///
94    /// This function returns an error if the I2C bus encounters an error.
95    /// Setting a current value outside the range 0-100mA will result in an error.
96    pub fn set_led2_current(
97        &mut self,
98        current: ElectricCurrent,
99    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
100        let r22h_prev = self.registers.r22h.read()?;
101        let r23h_prev = self.registers.r23h.read()?;
102
103        let high_current = current.get::<milliampere>() > 50.0
104            || (r23h_prev.iled_2x() && (r22h_prev.iled1() > 31 || r22h_prev.iled3() > 31));
105
106        let range = if high_current {
107            ElectricCurrent::new::<milliampere>(100.0)
108        } else {
109            ElectricCurrent::new::<milliampere>(50.0)
110        };
111
112        let quantisation = range / 63.0;
113
114        if current > range || current.get::<milliampere>() < 0.0 {
115            return Err(AfeError::LedCurrentOutsideAllowedRange);
116        }
117
118        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
119        let values = [
120            Self::scale_current(r22h_prev.iled1(), r23h_prev.iled_2x(), high_current),
121            (current / quantisation).value.round() as u8,
122            Self::scale_current(r22h_prev.iled3(), r23h_prev.iled_2x(), high_current),
123        ];
124
125        self.registers.r22h.write(
126            R22h::new()
127                .with_iled1(values[0])
128                .with_iled2(values[1])
129                .with_iled3(values[2]),
130        )?;
131        self.registers
132            .r23h
133            .write(r23h_prev.with_iled_2x(high_current))?;
134
135        Ok(f32::from(values[1]) * quantisation)
136    }
137
138    /// Gets the LED1 current.
139    ///
140    /// # Errors
141    ///
142    /// This function returns an error if the I2C bus encounters an error.
143    pub fn get_led1_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
144        let r22h_prev = self.registers.r22h.read()?;
145        let r23h_prev = self.registers.r23h.read()?;
146
147        let range = if r23h_prev.iled_2x() {
148            ElectricCurrent::new::<milliampere>(100.0)
149        } else {
150            ElectricCurrent::new::<milliampere>(50.0)
151        };
152        let quantisation = range / 63.0;
153
154        Ok(f32::from(r22h_prev.iled1()) * quantisation)
155    }
156
157    /// Gets the LED2 current.
158    ///
159    /// # Errors
160    ///
161    /// This function returns an error if the I2C bus encounters an error.
162    pub fn get_led2_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
163        let r22h_prev = self.registers.r22h.read()?;
164        let r23h_prev = self.registers.r23h.read()?;
165
166        let range = if r23h_prev.iled_2x() {
167            ElectricCurrent::new::<milliampere>(100.0)
168        } else {
169            ElectricCurrent::new::<milliampere>(50.0)
170        };
171        let quantisation = range / 63.0;
172
173        Ok(f32::from(r22h_prev.iled2()) * quantisation)
174    }
175
176    /// Sets the offset cancellation current of the LED1.
177    ///
178    /// # Errors
179    ///
180    /// This function returns an error if the I2C bus encounters an error.
181    /// Setting a current value outside the range -7-7uA will result in an error.
182    pub fn set_offset_led1_current(
183        &mut self,
184        offset: ElectricCurrent,
185    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
186        let r3ah_prev = self.registers.r3Ah.read()?;
187
188        let range = ElectricCurrent::new::<microampere>(7.0);
189        let quantisation = range / 15.0;
190
191        if offset > range || offset < -range {
192            return Err(AfeError::OffsetCurrentOutsideAllowedRange);
193        }
194
195        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
196        let value = (
197            (offset.abs() / quantisation).value.round() as u8,
198            offset.get::<microampere>() < 0.0,
199        );
200
201        self.registers.r3Ah.write(
202            r3ah_prev
203                .with_i_offdac_led1(value.0)
204                .with_pol_offdac_led1(value.1),
205        )?;
206
207        Ok(f32::from(value.0) * quantisation * if value.1 { -1.0 } else { 1.0 })
208    }
209
210    /// Sets the offset cancellation current of the LED2.
211    ///
212    /// # Errors
213    ///
214    /// This function returns an error if the I2C bus encounters an error.
215    /// Setting a current value outside the range -7-7uA will result in an error.
216    pub fn set_offset_led2_current(
217        &mut self,
218        offset: ElectricCurrent,
219    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
220        let r3ah_prev = self.registers.r3Ah.read()?;
221
222        let range = ElectricCurrent::new::<microampere>(7.0);
223        let quantisation = range / 15.0;
224
225        if offset > range || offset < -range {
226            return Err(AfeError::OffsetCurrentOutsideAllowedRange);
227        }
228
229        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
230        let value = (
231            (offset.abs() / quantisation).value.round() as u8,
232            offset.get::<microampere>() < 0.0,
233        );
234
235        self.registers.r3Ah.write(
236            r3ah_prev
237                .with_i_offdac_led2(value.0)
238                .with_pol_offdac_led2(value.1),
239        )?;
240
241        Ok(f32::from(value.0) * quantisation * if value.1 { -1.0 } else { 1.0 })
242    }
243
244    /// Gets the offset cancellation current of the LED1.
245    ///
246    /// # Errors
247    ///
248    /// This function returns an error if the I2C bus encounters an error.
249    pub fn get_offset_led1_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
250        let r3ah_prev = self.registers.r3Ah.read()?;
251
252        let range = ElectricCurrent::new::<microampere>(7.0);
253        let quantisation = range / 15.0;
254
255        Ok(f32::from(r3ah_prev.i_offdac_led1())
256            * quantisation
257            * if r3ah_prev.pol_offdac_led1() {
258                -1.0
259            } else {
260                1.0
261            })
262    }
263
264    /// Gets the offset cancellation current of the LED2.
265    ///
266    /// # Errors
267    ///
268    /// This function returns an error if the I2C bus encounters an error.
269    pub fn get_offset_led2_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
270        let r3ah_prev = self.registers.r3Ah.read()?;
271
272        let range = ElectricCurrent::new::<microampere>(7.0);
273        let quantisation = range / 15.0;
274
275        Ok(f32::from(r3ah_prev.i_offdac_led2())
276            * quantisation
277            * if r3ah_prev.pol_offdac_led2() {
278                -1.0
279            } else {
280                1.0
281            })
282    }
283}
284
285impl<I2C> AFE4404<I2C, ThreeLedsMode>
286where
287    I2C: I2c<SevenBitAddress>,
288{
289    /// Sets the LED3 current.
290    ///
291    /// # Notes
292    ///
293    /// This function automatically expands the current range to 0-100 mA if the current is above 50 mA.
294    /// When the range is expanded to 0-100 mA, the unit step is doubled from 0.8 to 1.6 mA.
295    ///
296    /// # Errors
297    ///
298    /// This function returns an error if the I2C bus encounters an error.
299    /// Setting a current value outside the range 0-100mA will result in an error.
300    pub fn set_led3_current(
301        &mut self,
302        current: ElectricCurrent,
303    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
304        let r22h_prev = self.registers.r22h.read()?;
305        let r23h_prev = self.registers.r23h.read()?;
306
307        let high_current = current.get::<milliampere>() > 50.0
308            || (r23h_prev.iled_2x() && (r22h_prev.iled1() > 31 || r22h_prev.iled2() > 31));
309
310        let range = if high_current {
311            ElectricCurrent::new::<milliampere>(100.0)
312        } else {
313            ElectricCurrent::new::<milliampere>(50.0)
314        };
315
316        let quantisation = range / 63.0;
317
318        if current > range || current.get::<milliampere>() < 0.0 {
319            return Err(AfeError::LedCurrentOutsideAllowedRange);
320        }
321
322        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
323        let values = [
324            Self::scale_current(r22h_prev.iled1(), r23h_prev.iled_2x(), high_current),
325            Self::scale_current(r22h_prev.iled2(), r23h_prev.iled_2x(), high_current),
326            (current / quantisation).value.round() as u8,
327        ];
328
329        self.registers.r22h.write(
330            R22h::new()
331                .with_iled1(values[0])
332                .with_iled2(values[1])
333                .with_iled3(values[2]),
334        )?;
335        self.registers
336            .r23h
337            .write(r23h_prev.with_iled_2x(high_current))?;
338
339        Ok(f32::from(values[2]) * quantisation)
340    }
341
342    /// Gets the LED3 current.
343    ///
344    /// # Errors
345    ///
346    /// This function returns an error if the I2C bus encounters an error.
347    pub fn get_led3_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
348        let r22h_prev = self.registers.r22h.read()?;
349        let r23h_prev = self.registers.r23h.read()?;
350
351        let range = if r23h_prev.iled_2x() {
352            ElectricCurrent::new::<milliampere>(100.0)
353        } else {
354            ElectricCurrent::new::<milliampere>(50.0)
355        };
356        let quantisation = range / 63.0;
357
358        Ok(f32::from(r22h_prev.iled3()) * quantisation)
359    }
360
361    /// Sets the offset cancellation current of the LED3.
362    ///
363    /// # Errors
364    ///
365    /// This function returns an error if the I2C bus encounters an error.
366    /// Setting a current value outside the range -7-7uA will result in an error.
367    pub fn set_offset_led3_current(
368        &mut self,
369        offset: ElectricCurrent,
370    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
371        let r3ah_prev = self.registers.r3Ah.read()?;
372
373        let range = ElectricCurrent::new::<microampere>(7.0);
374        let quantisation = range / 15.0;
375
376        if offset > range || offset < -range {
377            return Err(AfeError::OffsetCurrentOutsideAllowedRange);
378        }
379
380        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
381        let value = (
382            (offset.abs() / quantisation).value.round() as u8,
383            offset.get::<microampere>() < 0.0,
384        );
385
386        self.registers.r3Ah.write(
387            r3ah_prev
388                .with_i_offdac_amb2_or_i_offdac_led3(value.0)
389                .with_pol_offdac_amb2_or_pol_offdac_led3(value.1),
390        )?;
391
392        Ok(f32::from(value.0) * quantisation * if value.1 { -1.0 } else { 1.0 })
393    }
394
395    /// Sets the offset cancellation current of the Ambient.
396    ///
397    /// # Errors
398    ///
399    /// This function returns an error if the I2C bus encounters an error.
400    /// Setting a current value outside the range -7-7uA will result in an error.
401    pub fn set_offset_amb_current(
402        &mut self,
403        offset: ElectricCurrent,
404    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
405        let r3ah_prev = self.registers.r3Ah.read()?;
406
407        let range = ElectricCurrent::new::<microampere>(7.0);
408        let quantisation = range / 15.0;
409
410        if offset > range || offset < -range {
411            return Err(AfeError::OffsetCurrentOutsideAllowedRange);
412        }
413
414        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
415        let value = (
416            (offset.abs() / quantisation).value.round() as u8,
417            offset.get::<microampere>() < 0.0,
418        );
419
420        self.registers.r3Ah.write(
421            r3ah_prev
422                .with_i_offdac_amb1(value.0)
423                .with_pol_offdac_amb1(value.1),
424        )?;
425
426        Ok(f32::from(value.0) * quantisation * if value.1 { -1.0 } else { 1.0 })
427    }
428
429    /// Sets the offset cancellation current of the LED3.
430    ///
431    /// # Errors
432    ///
433    /// This function returns an error if the I2C bus encounters an error.
434    pub fn get_offset_led3_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
435        let r3ah_prev = self.registers.r3Ah.read()?;
436
437        let range = ElectricCurrent::new::<microampere>(7.0);
438        let quantisation = range / 15.0;
439
440        Ok(f32::from(r3ah_prev.i_offdac_amb2_or_i_offdac_led3())
441            * quantisation
442            * if r3ah_prev.pol_offdac_amb2_or_pol_offdac_led3() {
443                -1.0
444            } else {
445                1.0
446            })
447    }
448
449    /// Sets the offset cancellation current of the Ambient.
450    ///
451    /// # Errors
452    ///
453    /// This function returns an error if the I2C bus encounters an error.
454    pub fn get_offset_amb_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
455        let r3ah_prev = self.registers.r3Ah.read()?;
456
457        let range = ElectricCurrent::new::<microampere>(7.0);
458        let quantisation = range / 15.0;
459
460        Ok(f32::from(r3ah_prev.i_offdac_amb1())
461            * quantisation
462            * if r3ah_prev.pol_offdac_amb1() {
463                -1.0
464            } else {
465                1.0
466            })
467    }
468}
469
470impl<I2C> AFE4404<I2C, TwoLedsMode>
471where
472    I2C: I2c<SevenBitAddress>,
473{
474    /// Sets the offset cancellation current of the Ambient1.
475    ///
476    /// # Errors
477    ///
478    /// This function returns an error if the I2C bus encounters an error.
479    /// Setting a current value outside the range -7-7uA will result in an error.
480    pub fn set_offset_amb1_current(
481        &mut self,
482        offset: ElectricCurrent,
483    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
484        let r3ah_prev = self.registers.r3Ah.read()?;
485
486        let range = ElectricCurrent::new::<microampere>(7.0);
487        let quantisation = range / 15.0;
488
489        if offset > range || offset < -range {
490            return Err(AfeError::OffsetCurrentOutsideAllowedRange);
491        }
492
493        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
494        let value = (
495            (offset.abs() / quantisation).value.round() as u8,
496            offset.get::<microampere>() < 0.0,
497        );
498
499        self.registers.r3Ah.write(
500            r3ah_prev
501                .with_i_offdac_amb1(value.0)
502                .with_pol_offdac_amb1(value.1),
503        )?;
504
505        Ok(f32::from(value.0) * quantisation * if value.1 { -1.0 } else { 1.0 })
506    }
507
508    /// Sets the offset cancellation current of the Ambient2.
509    ///
510    /// # Errors
511    ///
512    /// This function returns an error if the I2C bus encounters an error.
513    /// Setting a current value outside the range -7-7uA will result in an error.
514    pub fn set_offset_amb2_current(
515        &mut self,
516        offset: ElectricCurrent,
517    ) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
518        let r3ah_prev = self.registers.r3Ah.read()?;
519
520        let range = ElectricCurrent::new::<microampere>(7.0);
521        let quantisation = range / 15.0;
522
523        if offset > range || offset < -range {
524            return Err(AfeError::OffsetCurrentOutsideAllowedRange);
525        }
526
527        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
528        let value = (
529            (offset.abs() / quantisation).value.round() as u8,
530            offset.get::<microampere>() < 0.0,
531        );
532
533        self.registers.r3Ah.write(
534            r3ah_prev
535                .with_i_offdac_amb2_or_i_offdac_led3(value.0)
536                .with_pol_offdac_amb2_or_pol_offdac_led3(value.1),
537        )?;
538
539        Ok(f32::from(value.0) * quantisation * if value.1 { -1.0 } else { 1.0 })
540    }
541
542    /// Sets the offset cancellation current of the Ambient1.
543    ///
544    /// # Errors
545    ///
546    /// This function returns an error if the I2C bus encounters an error.
547    pub fn get_offset_amb1_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
548        let r3ah_prev = self.registers.r3Ah.read()?;
549
550        let range = ElectricCurrent::new::<microampere>(7.0);
551        let quantisation = range / 15.0;
552
553        Ok(f32::from(r3ah_prev.i_offdac_amb1())
554            * quantisation
555            * if r3ah_prev.pol_offdac_amb1() {
556                -1.0
557            } else {
558                1.0
559            })
560    }
561
562    /// Sets the offset cancellation current of the Ambient2.
563    ///
564    /// # Errors
565    ///
566    /// This function returns an error if the I2C bus encounters an error.
567    pub fn get_offset_amb2_current(&mut self) -> Result<ElectricCurrent, AfeError<I2C::Error>> {
568        let r3ah_prev = self.registers.r3Ah.read()?;
569
570        let range = ElectricCurrent::new::<microampere>(7.0);
571        let quantisation = range / 15.0;
572
573        Ok(f32::from(r3ah_prev.i_offdac_amb2_or_i_offdac_led3())
574            * quantisation
575            * if r3ah_prev.pol_offdac_amb2_or_pol_offdac_led3() {
576                -1.0
577            } else {
578                1.0
579            })
580    }
581}