Skip to main content

pamoja_sensors/
ina219.rs

1//! Texas Instruments INA219 high-side current, voltage, and power monitor.
2//!
3//! The INA219 measures the voltage across a shunt resistor and the bus voltage, and,
4//! once its calibration register is programmed, computes current and power on the
5//! chip. This module builds the calibration value and decodes each register into a
6//! physical quantity, following the datasheet's equations and its worked design
7//! example, so a solar-battery or microgrid node reads amps and watts directly.
8//!
9//! Currents, voltages, and powers are returned in integer micro-units (microvolts,
10//! microamps, microwatts) so the conversions stay exact without floating point.
11
12#[cfg(feature = "embedded-hal")]
13mod driver;
14#[cfg(feature = "embedded-hal")]
15pub mod sim;
16
17#[cfg(feature = "embedded-hal")]
18pub use driver::{Ina219, STATUS_POLLS};
19
20/// The I2C address with both address pins tied to GND; A1 and A0 select the rest.
21pub const BASE_ADDRESS: u8 = 0x40;
22
23/// What an address pin is tied to: GND, VS+, SDA, or SCL. The INA219 and the INA226 share
24/// one address table, so they share this type.
25pub use crate::ina226::AddressPin;
26
27/// Returns the 7-bit I2C address the A1 and A0 pins select.
28///
29/// This is Table 1 of the datasheet: the address is `1 0 0 A1 A0`, each pin contributing two
30/// bits in the order GND, VS+, SDA, SCL, which gives the sixteen addresses 0x40 through
31/// 0x4F. Several monitors share a bus by tying their pins differently.
32///
33/// # Arguments
34///
35/// * `a1` - what the A1 pin is tied to.
36/// * `a0` - what the A0 pin is tied to.
37///
38/// # Returns
39///
40/// The 7-bit target address, before the read/write bit is appended.
41///
42/// # Examples
43///
44/// ```
45/// use pamoja_sensors::ina219::{address, AddressPin};
46///
47/// // A1 to ground and A0 to VS+: the first address above the base.
48/// assert_eq!(address(AddressPin::Ground, AddressPin::Supply), 0x41);
49/// ```
50pub fn address(a1: AddressPin, a0: AddressPin) -> u8 {
51    BASE_ADDRESS | ((a1 as u8) << 2) | a0 as u8
52}
53
54/// The INA219 register addresses.
55pub mod register {
56    /// Configuration register: bus-voltage range, gain, ADC settings, and mode.
57    pub const CONFIGURATION: u8 = 0x00;
58    /// Shunt voltage register, signed, 10 µV per count.
59    pub const SHUNT_VOLTAGE: u8 = 0x01;
60    /// Bus voltage register, value in bits 15:3, 4 mV per count.
61    pub const BUS_VOLTAGE: u8 = 0x02;
62    /// Power register, scaled by the calibration register.
63    pub const POWER: u8 = 0x03;
64    /// Current register, scaled by the calibration register.
65    pub const CURRENT: u8 = 0x04;
66    /// Calibration register, sets the current and power scale.
67    pub const CALIBRATION: u8 = 0x05;
68}
69
70/// The power-on value of the configuration register (0x399F): 32 V bus range, gain
71/// /8, 12-bit ADCs, and continuous shunt-and-bus conversion.
72pub const CONFIG_RESET: u16 = 0x399F;
73
74/// Computes the calibration register value for a chosen current resolution and shunt.
75///
76/// This is the datasheet's calibration equation, `Cal = trunc(0.04096 / (Current_LSB
77/// * R_shunt))`, expressed in integer micro-units: with the current LSB in microamps
78/// and the shunt in milliohms, the fixed `0.04096` becomes `40_960_000`.
79///
80/// # Arguments
81///
82/// * `current_lsb_microamps` - the amps-per-count the current register should carry.
83/// * `shunt_milliohms` - the shunt resistor value, in milliohms.
84///
85/// # Returns
86///
87/// The 16-bit value to program into the calibration register. Returns `0` if either
88/// argument is `0`, which is the chip's own uncalibrated state.
89pub fn calibration(current_lsb_microamps: u32, shunt_milliohms: u32) -> u16 {
90    let denominator = current_lsb_microamps.saturating_mul(shunt_milliohms);
91    if denominator == 0 {
92        return 0;
93    }
94    (40_960_000 / denominator) as u16
95}
96
97/// Returns the smallest current LSB, in microamps, that still spans a full-scale
98/// current.
99///
100/// The current register is 15 bits of magnitude, so the minimum resolution is the
101/// maximum expected current divided by 32768, rounded up to the next whole microamp.
102/// The datasheet then rounds this up further to a convenient round number.
103///
104/// # Arguments
105///
106/// * `max_expected_microamps` - the largest current the application will measure.
107///
108/// # Returns
109///
110/// The minimum current LSB in microamps.
111pub fn minimum_current_lsb_microamps(max_expected_microamps: u32) -> u32 {
112    max_expected_microamps.div_ceil(32_768)
113}
114
115/// Decodes the shunt voltage register to microvolts.
116///
117/// # Arguments
118///
119/// * `raw` - the signed shunt voltage register.
120///
121/// # Returns
122///
123/// The shunt voltage in microvolts, at 10 µV per count.
124pub fn shunt_microvolts(raw: i16) -> i32 {
125    raw as i32 * 10
126}
127
128/// Decodes the bus voltage register to millivolts.
129///
130/// The voltage occupies bits 15:3, so the register is shifted right by three before
131/// scaling by the 4 mV LSB; the low bits are the conversion-ready and overflow flags.
132///
133/// # Arguments
134///
135/// * `raw` - the bus voltage register.
136///
137/// # Returns
138///
139/// The bus voltage in millivolts.
140pub fn bus_millivolts(raw: u16) -> u32 {
141    (raw >> 3) as u32 * 4
142}
143
144/// Returns whether the bus voltage register's conversion-ready (CNVR) flag is set.
145///
146/// # Arguments
147///
148/// * `raw` - the bus voltage register.
149///
150/// # Returns
151///
152/// `true` if a conversion has completed and the data is ready to read.
153pub fn conversion_ready(raw: u16) -> bool {
154    raw & 0x0002 != 0
155}
156
157/// Returns whether the bus voltage register's math-overflow (OVF) flag is set.
158///
159/// # Arguments
160///
161/// * `raw` - the bus voltage register.
162///
163/// # Returns
164///
165/// `true` if the power or current calculation overflowed and the readings are invalid.
166pub fn math_overflow(raw: u16) -> bool {
167    raw & 0x0001 != 0
168}
169
170/// Decodes the current register to microamps for a given current LSB.
171///
172/// # Arguments
173///
174/// * `raw` - the signed current register.
175/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
176///
177/// # Returns
178///
179/// The current in microamps.
180pub fn current_microamps(raw: i16, current_lsb_microamps: u32) -> i32 {
181    raw as i32 * current_lsb_microamps as i32
182}
183
184/// Decodes the power register to microwatts for a given current LSB.
185///
186/// The power LSB is fixed by the datasheet at twenty times the current LSB.
187///
188/// # Arguments
189///
190/// * `raw` - the power register.
191/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
192///
193/// # Returns
194///
195/// The power in microwatts.
196pub fn power_microwatts(raw: u16, current_lsb_microamps: u32) -> u32 {
197    raw as u32 * (20 * current_lsb_microamps)
198}
199
200/// Builds the shunt voltage register a monitor reports for a shunt voltage.
201///
202/// The inverse of [`shunt_microvolts`], so a node can be written and tested against
203/// what a monitor sends without one attached.
204///
205/// # Arguments
206///
207/// * `microvolts` - the shunt voltage in microvolts.
208///
209/// # Returns
210///
211/// The signed shunt voltage register, at 10 µV per count.
212pub fn shunt_register(microvolts: i32) -> i16 {
213    (microvolts / 10) as i16
214}
215
216/// Builds the bus voltage register a monitor reports for a bus voltage.
217///
218/// The inverse of [`bus_millivolts`], with the conversion-ready flag set and the
219/// overflow flag clear, which is what a completed conversion reads as.
220///
221/// # Arguments
222///
223/// * `millivolts` - the bus voltage in millivolts.
224///
225/// # Returns
226///
227/// The bus voltage register, the voltage in bits 15:3 at 4 mV per count.
228pub fn bus_register(millivolts: u32) -> u16 {
229    (((millivolts / 4) as u16) << 3) | 0x0002
230}
231
232/// Builds the current register a monitor reports for a current.
233///
234/// The inverse of [`current_microamps`].
235///
236/// # Arguments
237///
238/// * `microamps` - the current in microamps.
239/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
240///
241/// # Returns
242///
243/// The signed current register, or zero if `current_lsb_microamps` is zero.
244pub fn current_register(microamps: i32, current_lsb_microamps: u32) -> i16 {
245    if current_lsb_microamps == 0 {
246        return 0;
247    }
248    (microamps / current_lsb_microamps as i32) as i16
249}
250
251/// Builds the power register a monitor reports for a power.
252///
253/// The inverse of [`power_microwatts`]; the power LSB is fixed by the datasheet at
254/// twenty times the current LSB.
255///
256/// # Arguments
257///
258/// * `microwatts` - the power in microwatts.
259/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
260///
261/// # Returns
262///
263/// The power register, or zero if `current_lsb_microamps` is zero.
264pub fn power_register(microwatts: u32, current_lsb_microamps: u32) -> u16 {
265    if current_lsb_microamps == 0 {
266        return 0;
267    }
268    (microwatts / (20 * current_lsb_microamps)) as u16
269}
270
271/// The bus voltage full-scale range, the `BRNG` bit of the Configuration register.
272#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
273pub enum BusRange {
274    /// 0 to 16 V.
275    V16,
276    /// 0 to 32 V, the reset setting.
277    #[default]
278    V32,
279}
280
281impl BusRange {
282    /// Returns the `BRNG` bit value.
283    pub fn code(self) -> u8 {
284        match self {
285            BusRange::V16 => 0,
286            BusRange::V32 => 1,
287        }
288    }
289
290    /// Decodes the `BRNG` bit value.
291    ///
292    /// # Arguments
293    ///
294    /// * `code` - the bit; only the low bit is used.
295    ///
296    /// # Returns
297    ///
298    /// The range.
299    pub fn from_code(code: u8) -> BusRange {
300        if code & 1 == 0 {
301            BusRange::V16
302        } else {
303            BusRange::V32
304        }
305    }
306}
307
308/// The shunt voltage gain and range, the `PG` bits of the Configuration register.
309#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
310pub enum Gain {
311    /// Gain 1, ±40 mV.
312    Div1,
313    /// Gain 1/2, ±80 mV.
314    Div2,
315    /// Gain 1/4, ±160 mV.
316    Div4,
317    /// Gain 1/8, ±320 mV, the reset setting.
318    #[default]
319    Div8,
320}
321
322impl Gain {
323    /// Returns the two-bit `PG` field value.
324    pub fn code(self) -> u8 {
325        match self {
326            Gain::Div1 => 0b00,
327            Gain::Div2 => 0b01,
328            Gain::Div4 => 0b10,
329            Gain::Div8 => 0b11,
330        }
331    }
332
333    /// Decodes a two-bit `PG` field value.
334    ///
335    /// # Arguments
336    ///
337    /// * `code` - the field value; only the low two bits are used.
338    ///
339    /// # Returns
340    ///
341    /// The gain.
342    pub fn from_code(code: u8) -> Gain {
343        match code & 0b11 {
344            0b00 => Gain::Div1,
345            0b01 => Gain::Div2,
346            0b10 => Gain::Div4,
347            _ => Gain::Div8,
348        }
349    }
350
351    /// Returns the shunt voltage range in millivolts, either side of zero.
352    pub fn range_millivolts(self) -> u16 {
353        match self {
354            Gain::Div1 => 40,
355            Gain::Div2 => 80,
356            Gain::Div4 => 160,
357            Gain::Div8 => 320,
358        }
359    }
360}
361
362/// An ADC resolution or averaging setting, for the `BADC` and `SADC` fields.
363///
364/// The four-bit code selects either a resolution, when its high bit is clear, or a
365/// sample count averaged at 12 bits, when it is set. Each setting has the conversion
366/// time the datasheet tabulates.
367#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
368pub enum Adc {
369    /// 9-bit, 84 us.
370    Bits9,
371    /// 10-bit, 148 us.
372    Bits10,
373    /// 11-bit, 276 us.
374    Bits11,
375    /// 12-bit, 532 us, the reset setting.
376    #[default]
377    Bits12,
378    /// 2 samples averaged, 1.06 ms.
379    Samples2,
380    /// 4 samples averaged, 2.13 ms.
381    Samples4,
382    /// 8 samples averaged, 4.26 ms.
383    Samples8,
384    /// 16 samples averaged, 8.51 ms.
385    Samples16,
386    /// 32 samples averaged, 17.02 ms.
387    Samples32,
388    /// 64 samples averaged, 34.05 ms.
389    Samples64,
390    /// 128 samples averaged, 68.10 ms.
391    Samples128,
392}
393
394impl Adc {
395    /// Returns the four-bit field value.
396    pub fn code(self) -> u8 {
397        match self {
398            Adc::Bits9 => 0b0000,
399            Adc::Bits10 => 0b0001,
400            Adc::Bits11 => 0b0010,
401            Adc::Bits12 => 0b0011,
402            Adc::Samples2 => 0b1001,
403            Adc::Samples4 => 0b1010,
404            Adc::Samples8 => 0b1011,
405            Adc::Samples16 => 0b1100,
406            Adc::Samples32 => 0b1101,
407            Adc::Samples64 => 0b1110,
408            Adc::Samples128 => 0b1111,
409        }
410    }
411
412    /// Decodes a four-bit field value.
413    ///
414    /// With the high bit clear the second bit is ignored and the low two select the
415    /// resolution; `0b1000` is 12-bit as well.
416    ///
417    /// # Arguments
418    ///
419    /// * `code` - the field value; only the low four bits are used.
420    ///
421    /// # Returns
422    ///
423    /// The setting.
424    pub fn from_code(code: u8) -> Adc {
425        match code & 0b1111 {
426            0b1001 => Adc::Samples2,
427            0b1010 => Adc::Samples4,
428            0b1011 => Adc::Samples8,
429            0b1100 => Adc::Samples16,
430            0b1101 => Adc::Samples32,
431            0b1110 => Adc::Samples64,
432            0b1111 => Adc::Samples128,
433            0b1000 => Adc::Bits12,
434            low => match low & 0b11 {
435                0b00 => Adc::Bits9,
436                0b01 => Adc::Bits10,
437                0b10 => Adc::Bits11,
438                _ => Adc::Bits12,
439            },
440        }
441    }
442
443    /// Returns the conversion time in microseconds.
444    pub fn conversion_micros(self) -> u32 {
445        match self {
446            Adc::Bits9 => 84,
447            Adc::Bits10 => 148,
448            Adc::Bits11 => 276,
449            Adc::Bits12 => 532,
450            Adc::Samples2 => 1_060,
451            Adc::Samples4 => 2_130,
452            Adc::Samples8 => 4_260,
453            Adc::Samples16 => 8_510,
454            Adc::Samples32 => 17_020,
455            Adc::Samples64 => 34_050,
456            Adc::Samples128 => 68_100,
457        }
458    }
459}
460
461/// The operating mode, the `MODE` bits of the Configuration register.
462///
463/// Writing a triggered mode starts one conversion, even if that mode is already set.
464#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
465pub enum Mode {
466    /// No conversions, lowest power.
467    PowerDown = 0,
468    /// One shunt conversion.
469    ShuntTriggered = 1,
470    /// One bus conversion.
471    BusTriggered = 2,
472    /// One shunt and one bus conversion.
473    ShuntAndBusTriggered = 3,
474    /// The ADC disabled.
475    AdcOff = 4,
476    /// Shunt conversions back to back.
477    ShuntContinuous = 5,
478    /// Bus conversions back to back.
479    BusContinuous = 6,
480    /// Shunt and bus conversions back to back, the reset setting.
481    #[default]
482    ShuntAndBusContinuous = 7,
483}
484
485impl Mode {
486    /// Returns the three-bit `MODE` field value.
487    pub fn code(self) -> u8 {
488        self as u8
489    }
490
491    /// Decodes a three-bit `MODE` field value.
492    ///
493    /// # Arguments
494    ///
495    /// * `code` - the field value; only the low three bits are used.
496    ///
497    /// # Returns
498    ///
499    /// The mode.
500    pub fn from_code(code: u8) -> Mode {
501        match code & 0b111 {
502            0 => Mode::PowerDown,
503            1 => Mode::ShuntTriggered,
504            2 => Mode::BusTriggered,
505            3 => Mode::ShuntAndBusTriggered,
506            4 => Mode::AdcOff,
507            5 => Mode::ShuntContinuous,
508            6 => Mode::BusContinuous,
509            _ => Mode::ShuntAndBusContinuous,
510        }
511    }
512
513    /// Reports whether the mode converts the shunt voltage.
514    pub fn measures_shunt(self) -> bool {
515        matches!(
516            self,
517            Mode::ShuntTriggered
518                | Mode::ShuntAndBusTriggered
519                | Mode::ShuntContinuous
520                | Mode::ShuntAndBusContinuous
521        )
522    }
523
524    /// Reports whether the mode converts the bus voltage.
525    pub fn measures_bus(self) -> bool {
526        matches!(
527            self,
528            Mode::BusTriggered
529                | Mode::ShuntAndBusTriggered
530                | Mode::BusContinuous
531                | Mode::ShuntAndBusContinuous
532        )
533    }
534}
535
536/// The Configuration register (00h).
537///
538/// The default is the reset state, [`CONFIG_RESET`]: 32 V range, gain 1/8, 12-bit
539/// conversions, shunt and bus continuous.
540#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
541pub struct Configuration {
542    /// Reset the part as at power-on; the bit clears itself.
543    pub reset: bool,
544    /// Bus voltage range, `BRNG` in bit 13.
545    pub bus_range: BusRange,
546    /// Shunt gain and range, `PG` in bits 12:11.
547    pub gain: Gain,
548    /// Bus ADC resolution or averaging, `BADC` in bits 10:7.
549    pub bus_adc: Adc,
550    /// Shunt ADC resolution or averaging, `SADC` in bits 6:3.
551    pub shunt_adc: Adc,
552    /// Operating mode, `MODE` in bits 2:0.
553    pub mode: Mode,
554}
555
556impl Configuration {
557    /// Packs the settings into the register word.
558    ///
559    /// # Returns
560    ///
561    /// The word to write to [`register::CONFIGURATION`].
562    pub fn bits(self) -> u16 {
563        u16::from(self.reset) << 15
564            | u16::from(self.bus_range.code()) << 13
565            | u16::from(self.gain.code()) << 11
566            | u16::from(self.bus_adc.code()) << 7
567            | u16::from(self.shunt_adc.code()) << 3
568            | u16::from(self.mode.code())
569    }
570
571    /// Decodes a register word.
572    ///
573    /// # Arguments
574    ///
575    /// * `bits` - the word read from [`register::CONFIGURATION`].
576    ///
577    /// # Returns
578    ///
579    /// The decoded settings.
580    pub fn from_bits(bits: u16) -> Configuration {
581        Configuration {
582            reset: bits & (1 << 15) != 0,
583            bus_range: BusRange::from_code((bits >> 13) as u8),
584            gain: Gain::from_code((bits >> 11) as u8),
585            bus_adc: Adc::from_code((bits >> 7) as u8),
586            shunt_adc: Adc::from_code((bits >> 3) as u8),
587            mode: Mode::from_code(bits as u8),
588        }
589    }
590
591    /// Returns how long one conversion cycle of the mode takes, in microseconds.
592    ///
593    /// A shunt conversion and a bus conversion run one after the other, so the time
594    /// is the sum of whichever the mode measures.
595    pub fn conversion_micros(self) -> u32 {
596        let mut micros = 0;
597        if self.mode.measures_shunt() {
598            micros += self.shunt_adc.conversion_micros();
599        }
600        if self.mode.measures_bus() {
601            micros += self.bus_adc.conversion_micros();
602        }
603        micros
604    }
605}
606
607/// One set of results, with the current resolution they were taken at.
608///
609/// The four data registers are kept as read; the methods apply the datasheet's
610/// scaling. A [`Reading`] whose current and power are zero means the calibration
611/// register was never programmed.
612#[derive(Clone, Copy, Debug, PartialEq, Eq)]
613pub struct Reading {
614    /// The Shunt Voltage register.
615    pub shunt: i16,
616    /// The Bus Voltage register, flags included.
617    pub bus: u16,
618    /// The Current register.
619    pub current: i16,
620    /// The Power register.
621    pub power: u16,
622    /// The programmed current resolution, in microamps per count.
623    pub current_lsb_microamps: u32,
624}
625
626impl Reading {
627    /// Returns the shunt voltage in microvolts.
628    pub fn shunt_microvolts(&self) -> i32 {
629        shunt_microvolts(self.shunt)
630    }
631
632    /// Returns the bus voltage in millivolts.
633    pub fn bus_millivolts(&self) -> u32 {
634        bus_millivolts(self.bus)
635    }
636
637    /// Returns the current in microamps.
638    pub fn current_microamps(&self) -> i32 {
639        current_microamps(self.current, self.current_lsb_microamps)
640    }
641
642    /// Returns the power in microwatts.
643    pub fn power_microwatts(&self) -> u32 {
644        power_microwatts(self.power, self.current_lsb_microamps)
645    }
646
647    /// Reports the math overflow flag: the current and power may be meaningless.
648    pub fn math_overflow(&self) -> bool {
649        math_overflow(self.bus)
650    }
651}
652
653#[cfg(test)]
654mod driver_support_tests {
655    use super::*;
656
657    #[test]
658    fn the_address_pins_select_the_datasheet_table() {
659        // Table 1, row by row: A1, A0, and the seven address bits.
660        use AddressPin::{Ground, Scl, Sda, Supply};
661        let table = [
662            (Ground, Ground, 0b100_0000),
663            (Ground, Supply, 0b100_0001),
664            (Ground, Sda, 0b100_0010),
665            (Ground, Scl, 0b100_0011),
666            (Supply, Ground, 0b100_0100),
667            (Supply, Supply, 0b100_0101),
668            (Supply, Sda, 0b100_0110),
669            (Supply, Scl, 0b100_0111),
670            (Sda, Ground, 0b100_1000),
671            (Sda, Supply, 0b100_1001),
672            (Sda, Sda, 0b100_1010),
673            (Sda, Scl, 0b100_1011),
674            (Scl, Ground, 0b100_1100),
675            (Scl, Supply, 0b100_1101),
676            (Scl, Sda, 0b100_1110),
677            (Scl, Scl, 0b100_1111),
678        ];
679        for (a1, a0, expected) in table {
680            assert_eq!(address(a1, a0), expected, "A1 {a1:?}, A0 {a0:?}");
681        }
682    }
683
684    #[test]
685    fn the_default_configuration_is_the_reset_word() {
686        assert_eq!(Configuration::default().bits(), CONFIG_RESET);
687        assert_eq!(
688            Configuration::from_bits(CONFIG_RESET),
689            Configuration::default()
690        );
691    }
692
693    #[test]
694    fn fields_land_where_the_datasheet_puts_them() {
695        let configuration = Configuration {
696            reset: true,
697            bus_range: BusRange::V16,
698            gain: Gain::Div2,
699            bus_adc: Adc::Samples16,
700            shunt_adc: Adc::Bits9,
701            mode: Mode::ShuntAndBusTriggered,
702        };
703        let bits = configuration.bits();
704        assert_eq!(bits, 0x8000 | 0x0800 | (0b1100 << 7) | 0x0003);
705        assert_eq!(Configuration::from_bits(bits), configuration);
706    }
707
708    #[test]
709    fn adc_codes_and_times_follow_table_5() {
710        assert_eq!(Adc::from_code(0b0100), Adc::Bits9, "bit 2 is a do not care");
711        assert_eq!(Adc::from_code(0b1000), Adc::Bits12);
712        assert_eq!(Adc::Samples128.conversion_micros(), 68_100);
713        assert_eq!(Adc::Bits12.conversion_micros(), 532);
714        for adc in [Adc::Bits11, Adc::Samples2, Adc::Samples64] {
715            assert_eq!(Adc::from_code(adc.code()), adc);
716        }
717    }
718
719    #[test]
720    fn gains_and_modes_follow_tables_4_and_6() {
721        assert_eq!(Gain::Div8.range_millivolts(), 320);
722        assert_eq!(Gain::from_code(0b01), Gain::Div2);
723        assert_eq!(Mode::from_code(0b011), Mode::ShuntAndBusTriggered);
724        assert!(Mode::ShuntTriggered.measures_shunt());
725        assert!(!Mode::ShuntTriggered.measures_bus());
726        assert!(!Mode::AdcOff.measures_shunt());
727        let triggered = Configuration {
728            mode: Mode::ShuntAndBusTriggered,
729            ..Configuration::default()
730        };
731        assert_eq!(triggered.conversion_micros(), 1_064);
732    }
733
734    #[test]
735    fn a_reading_scales_its_registers_like_the_free_functions() {
736        let reading = Reading {
737            shunt: shunt_register(100_000),
738            bus: bus_register(12_000),
739            current: current_register(1_000_000, 100),
740            power: power_register(12_000_000, 100),
741            current_lsb_microamps: 100,
742        };
743        assert_eq!(reading.shunt_microvolts(), 100_000);
744        assert_eq!(reading.bus_millivolts(), 12_000);
745        assert_eq!(reading.current_microamps(), 1_000_000);
746        assert_eq!(reading.power_microwatts(), 12_000_000);
747        assert!(!reading.math_overflow());
748    }
749}
750
751#[cfg(test)]
752mod tests {
753    use super::*;
754
755    // The datasheet's worked design example (Table 8): max expected current 15 A,
756    // shunt 2 mΩ, current LSB rounded to 1 mA/bit, bus range 16 V.
757    const CURRENT_LSB: u32 = 1_000; // 1 mA in microamps
758
759    #[test]
760    fn every_register_survives_a_round_trip_through_its_builder() {
761        // The datasheet's worked design example reads 11.98 V, 10 A, and 119.8 W, and
762        // each builder reproduces the register that decodes back to it.
763        assert_eq!(bus_millivolts(bus_register(11_980)), 11_980);
764        assert_eq!(bus_register(11_980) >> 3, 0x5D98 >> 3);
765        assert!(conversion_ready(bus_register(11_980)));
766        assert!(!math_overflow(bus_register(11_980)));
767        assert_eq!(current_register(10_000_000, CURRENT_LSB), 0x2710);
768        assert_eq!(
769            current_microamps(current_register(10_000_000, CURRENT_LSB), CURRENT_LSB),
770            10_000_000
771        );
772        assert_eq!(power_register(119_800_000, CURRENT_LSB), 0x1766);
773        assert_eq!(
774            power_microwatts(power_register(119_800_000, CURRENT_LSB), CURRENT_LSB),
775            119_800_000
776        );
777        assert_eq!(shunt_microvolts(shunt_register(20_000)), 20_000);
778        assert_eq!(current_register(1, 0), 0);
779        assert_eq!(power_register(1, 0), 0);
780    }
781
782    #[test]
783    fn calibration_matches_the_datasheet_example() {
784        // Cal = trunc(0.04096 / (0.001 A * 0.002 Ω)) = 20480 = 0x5000.
785        assert_eq!(calibration(CURRENT_LSB, 2), 20_480);
786        assert_eq!(calibration(CURRENT_LSB, 2), 0x5000);
787    }
788
789    #[test]
790    fn minimum_current_lsb_matches_the_datasheet_example() {
791        // 15 A / 32768 = 457.76 µA, computed up to the next whole microamp.
792        assert_eq!(minimum_current_lsb_microamps(15_000_000), 458);
793    }
794
795    #[test]
796    fn shunt_register_decodes_per_the_datasheet() {
797        // Table 8: shunt register 0x07D0 = 2000 → 20 mV.
798        assert_eq!(shunt_microvolts(0x07D0), 20_000);
799        // Negative full scale at gain /8: -320 mV is register 0x8300 (Figure 20).
800        assert_eq!(shunt_microvolts(i16::from_be_bytes([0x83, 0x00])), -320_000);
801    }
802
803    #[test]
804    fn bus_register_decodes_per_the_datasheet() {
805        // Table 8: bus register 0x5D98 → shifted 0x0BB3 = 2995 → 11.98 V.
806        assert_eq!(bus_millivolts(0x5D98), 11_980);
807        assert!(!conversion_ready(0x5D98));
808        assert!(!math_overflow(0x5D98));
809        // A reading with both status flags set.
810        assert!(conversion_ready(0x1F43));
811        assert!(math_overflow(0x1F43));
812    }
813
814    #[test]
815    fn current_register_decodes_to_the_example_load() {
816        // Table 8: current register 0x2710 = 10000 → 10.0 A.
817        assert_eq!(current_microamps(0x2710, CURRENT_LSB), 10_000_000);
818    }
819
820    #[test]
821    fn power_register_decodes_to_the_example_load() {
822        // Table 8: power register 0x1766 = 5990 → 119.8 W (power LSB 20 mW).
823        assert_eq!(power_microwatts(0x1766, CURRENT_LSB), 119_800_000);
824    }
825
826    #[test]
827    fn an_uncalibrated_request_returns_zero() {
828        assert_eq!(calibration(0, 2), 0);
829        assert_eq!(calibration(CURRENT_LSB, 0), 0);
830    }
831}