Skip to main content

pamoja_sensors/
ina226.rs

1//! Texas Instruments INA226 high-side or low-side current, voltage, and power monitor.
2//!
3//! The INA226 measures the voltage across a shunt resistor and the bus voltage on
4//! common-mode voltages from 0 V to 36 V and, once its calibration register is
5//! programmed, computes current and power on the chip. It adds programmable
6//! conversion times and averaging, and an alert pin driven by a single limit
7//! register. This module builds the calibration and configuration values and decodes
8//! each register into a physical quantity, following the datasheet's equations and
9//! its worked example, so a battery-balancer or rack-power node reads amps and watts
10//! directly.
11//!
12//! Shunt voltages are returned in nanovolts, bus voltages in microvolts, and current
13//! and power in microamps and microwatts, so the 2.5 µV and 1.25 mV register LSBs
14//! convert exactly without floating point. `f32` conveniences sit beside them.
15
16use crate::SensorError;
17
18/// The INA226 register pointer addresses.
19pub mod register {
20    /// Configuration register: reset, averaging, conversion times, and operating mode.
21    pub const CONFIGURATION: u8 = 0x00;
22    /// Shunt voltage register, signed, 2.5 µV per count.
23    pub const SHUNT_VOLTAGE: u8 = 0x01;
24    /// Bus voltage register, positive only, 1.25 mV per count.
25    pub const BUS_VOLTAGE: u8 = 0x02;
26    /// Power register, scaled by the calibration register.
27    pub const POWER: u8 = 0x03;
28    /// Current register, signed, scaled by the calibration register.
29    pub const CURRENT: u8 = 0x04;
30    /// Calibration register, sets the current and power scale.
31    pub const CALIBRATION: u8 = 0x05;
32    /// Mask/Enable register: alert function selection and status flags.
33    pub const MASK_ENABLE: u8 = 0x06;
34    /// Alert limit register, compared against the selected alert function.
35    pub const ALERT_LIMIT: u8 = 0x07;
36    /// Manufacturer ID register, reads [`MANUFACTURER_ID`](super::MANUFACTURER_ID).
37    pub const MANUFACTURER_ID: u8 = 0xFE;
38    /// Die ID register: the device ID in bits 15:4 and the die revision in bits 3:0.
39    pub const DIE_ID: u8 = 0xFF;
40}
41
42/// The value the manufacturer ID register always reads (0x5449, "TI").
43pub const MANUFACTURER_ID: u16 = 0x5449;
44
45/// The 12-bit device ID carried in bits 15:4 of the die ID register.
46pub const DEVICE_ID: u16 = 0x226;
47
48/// The power-on value of the configuration register (0x4127): averaging off, 1.1 ms
49/// conversion time for both bus and shunt, and continuous shunt-and-bus conversion.
50pub const CONFIG_RESET: u16 = 0x4127;
51
52/// The shunt voltage register LSB, 2.5 µV, in nanovolts.
53pub const SHUNT_LSB_NANOVOLTS: i32 = 2_500;
54
55/// The bus voltage register LSB, 1.25 mV, in microvolts.
56pub const BUS_LSB_MICROVOLTS: u32 = 1_250;
57
58/// The fixed ratio between the power LSB and the programmed current LSB.
59pub const POWER_LSB_RATIO: u32 = 25;
60
61/// The base I2C address, selected when both address pins are tied to ground.
62pub const BASE_ADDRESS: u8 = 0x40;
63
64/// What an address pin is tied to; each of A1 and A0 takes one of four levels.
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66#[repr(u8)]
67pub enum AddressPin {
68    /// Tied to GND.
69    Ground = 0,
70    /// Tied to VS.
71    Supply = 1,
72    /// Tied to SDA.
73    Sda = 2,
74    /// Tied to SCL.
75    Scl = 3,
76}
77
78/// Returns the 7-bit I2C address selected by the A1 and A0 pins.
79///
80/// The address is `1 0 0 A1 A0` where each pin contributes two bits in the order
81/// GND, VS, SDA, SCL, giving the sixteen addresses 0x40 through 0x4F.
82///
83/// # Arguments
84///
85/// * `a1` - what the A1 pin is tied to.
86/// * `a0` - what the A0 pin is tied to.
87///
88/// # Returns
89///
90/// The 7-bit target address, before the read/write bit is appended.
91pub fn address(a1: AddressPin, a0: AddressPin) -> u8 {
92    BASE_ADDRESS | ((a1 as u8) << 2) | a0 as u8
93}
94
95/// The number of samples the ADC averages before updating the result registers.
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97#[repr(u8)]
98pub enum Averaging {
99    /// No averaging, every conversion is reported.
100    Samples1 = 0,
101    /// Average of 4 samples.
102    Samples4 = 1,
103    /// Average of 16 samples.
104    Samples16 = 2,
105    /// Average of 64 samples.
106    Samples64 = 3,
107    /// Average of 128 samples.
108    Samples128 = 4,
109    /// Average of 256 samples.
110    Samples256 = 5,
111    /// Average of 512 samples.
112    Samples512 = 6,
113    /// Average of 1024 samples.
114    Samples1024 = 7,
115}
116
117impl Averaging {
118    fn from_bits(bits: u16) -> Self {
119        match bits & 0x07 {
120            0 => Self::Samples1,
121            1 => Self::Samples4,
122            2 => Self::Samples16,
123            3 => Self::Samples64,
124            4 => Self::Samples128,
125            5 => Self::Samples256,
126            6 => Self::Samples512,
127            _ => Self::Samples1024,
128        }
129    }
130
131    /// Returns the number of samples this setting averages.
132    ///
133    /// # Returns
134    ///
135    /// The sample count, from 1 to 1024.
136    pub fn samples(self) -> u16 {
137        match self {
138            Self::Samples1 => 1,
139            Self::Samples4 => 4,
140            Self::Samples16 => 16,
141            Self::Samples64 => 64,
142            Self::Samples128 => 128,
143            Self::Samples256 => 256,
144            Self::Samples512 => 512,
145            Self::Samples1024 => 1024,
146        }
147    }
148}
149
150/// The ADC conversion time for one shunt or bus voltage measurement.
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152#[repr(u8)]
153pub enum ConversionTime {
154    /// 140 µs.
155    Us140 = 0,
156    /// 204 µs.
157    Us204 = 1,
158    /// 332 µs.
159    Us332 = 2,
160    /// 588 µs.
161    Us588 = 3,
162    /// 1.1 ms.
163    Us1100 = 4,
164    /// 2.116 ms.
165    Us2116 = 5,
166    /// 4.156 ms.
167    Us4156 = 6,
168    /// 8.244 ms.
169    Us8244 = 7,
170}
171
172impl ConversionTime {
173    fn from_bits(bits: u16) -> Self {
174        match bits & 0x07 {
175            0 => Self::Us140,
176            1 => Self::Us204,
177            2 => Self::Us332,
178            3 => Self::Us588,
179            4 => Self::Us1100,
180            5 => Self::Us2116,
181            6 => Self::Us4156,
182            _ => Self::Us8244,
183        }
184    }
185
186    /// Returns the typical conversion time this setting selects.
187    ///
188    /// # Returns
189    ///
190    /// The conversion time in microseconds.
191    pub fn microseconds(self) -> u32 {
192        match self {
193            Self::Us140 => 140,
194            Self::Us204 => 204,
195            Self::Us332 => 332,
196            Self::Us588 => 588,
197            Self::Us1100 => 1_100,
198            Self::Us2116 => 2_116,
199            Self::Us4156 => 4_156,
200            Self::Us8244 => 8_244,
201        }
202    }
203}
204
205/// The operating mode: which inputs are converted, and whether once or continuously.
206///
207/// Both `000` and `100` select power-down; this type reads either as
208/// [`Mode::PowerDown`] and writes it back as `000`.
209#[derive(Clone, Copy, Debug, PartialEq, Eq)]
210#[repr(u8)]
211pub enum Mode {
212    /// Power-down (shutdown); registers stay readable and writable.
213    PowerDown = 0,
214    /// One shunt voltage conversion, then idle.
215    ShuntTriggered = 1,
216    /// One bus voltage conversion, then idle.
217    BusTriggered = 2,
218    /// One shunt and one bus voltage conversion, then idle.
219    ShuntAndBusTriggered = 3,
220    /// Shunt voltage conversions back to back.
221    ShuntContinuous = 5,
222    /// Bus voltage conversions back to back.
223    BusContinuous = 6,
224    /// Shunt and bus voltage conversions back to back, the power-on mode.
225    ShuntAndBusContinuous = 7,
226}
227
228impl Mode {
229    fn from_bits(bits: u16) -> Self {
230        match bits & 0x07 {
231            1 => Self::ShuntTriggered,
232            2 => Self::BusTriggered,
233            3 => Self::ShuntAndBusTriggered,
234            5 => Self::ShuntContinuous,
235            6 => Self::BusContinuous,
236            7 => Self::ShuntAndBusContinuous,
237            _ => Self::PowerDown,
238        }
239    }
240
241    /// Returns whether this mode converts the shunt voltage.
242    ///
243    /// # Returns
244    ///
245    /// `true` for the shunt-only and shunt-and-bus modes.
246    pub fn measures_shunt(self) -> bool {
247        self as u8 & 0x01 != 0
248    }
249
250    /// Returns whether this mode converts the bus voltage.
251    ///
252    /// # Returns
253    ///
254    /// `true` for the bus-only and shunt-and-bus modes.
255    pub fn measures_bus(self) -> bool {
256        self as u8 & 0x02 != 0
257    }
258
259    /// Returns whether this mode keeps converting after the first result.
260    ///
261    /// # Returns
262    ///
263    /// `true` for the three continuous modes.
264    pub fn is_continuous(self) -> bool {
265        matches!(
266            self,
267            Self::ShuntContinuous | Self::BusContinuous | Self::ShuntAndBusContinuous
268        )
269    }
270}
271
272/// The configuration register (0x00), decoded into its fields.
273///
274/// Bit 14 is reserved and reads as `1` after reset; [`Configuration::to_register`]
275/// writes it that way so the reset value round-trips unchanged.
276#[derive(Clone, Copy, Debug, PartialEq, Eq)]
277pub struct Configuration {
278    /// Set to trigger a system reset equivalent to power-on; the bit self-clears.
279    pub reset: bool,
280    /// How many samples are averaged before the result registers update.
281    pub averaging: Averaging,
282    /// The conversion time for each bus voltage measurement.
283    pub bus_conversion_time: ConversionTime,
284    /// The conversion time for each shunt voltage measurement.
285    pub shunt_conversion_time: ConversionTime,
286    /// Which inputs are converted, and whether once or continuously.
287    pub mode: Mode,
288}
289
290impl Configuration {
291    /// The power-on configuration, the decoded form of [`CONFIG_RESET`].
292    pub const RESET: Self = Self {
293        reset: false,
294        averaging: Averaging::Samples1,
295        bus_conversion_time: ConversionTime::Us1100,
296        shunt_conversion_time: ConversionTime::Us1100,
297        mode: Mode::ShuntAndBusContinuous,
298    };
299
300    /// Decodes a configuration register value.
301    ///
302    /// # Arguments
303    ///
304    /// * `raw` - the 16-bit configuration register.
305    ///
306    /// # Returns
307    ///
308    /// The decoded fields; reserved bits are ignored.
309    pub fn from_register(raw: u16) -> Self {
310        Self {
311            reset: raw & 0x8000 != 0,
312            averaging: Averaging::from_bits(raw >> 9),
313            bus_conversion_time: ConversionTime::from_bits(raw >> 6),
314            shunt_conversion_time: ConversionTime::from_bits(raw >> 3),
315            mode: Mode::from_bits(raw),
316        }
317    }
318
319    /// Encodes the fields as a configuration register value.
320    ///
321    /// # Returns
322    ///
323    /// The 16-bit register to write, with reserved bit 14 set as at reset.
324    pub fn to_register(self) -> u16 {
325        (if self.reset { 0x8000 } else { 0 })
326            | 0x4000
327            | ((self.averaging as u16) << 9)
328            | ((self.bus_conversion_time as u16) << 6)
329            | ((self.shunt_conversion_time as u16) << 3)
330            | self.mode as u16
331    }
332
333    /// Returns how often the result registers update with these settings.
334    ///
335    /// One update takes the averaged sample count times the sum of the conversion
336    /// times of the inputs the mode measures, as the datasheet's timing examples
337    /// work it.
338    ///
339    /// # Returns
340    ///
341    /// The update interval in microseconds, or `0` in power-down.
342    pub fn update_microseconds(self) -> u32 {
343        let shunt = if self.mode.measures_shunt() {
344            self.shunt_conversion_time.microseconds()
345        } else {
346            0
347        };
348        let bus = if self.mode.measures_bus() {
349            self.bus_conversion_time.microseconds()
350        } else {
351            0
352        };
353        u32::from(self.averaging.samples()) * (shunt + bus)
354    }
355}
356
357impl Default for Configuration {
358    fn default() -> Self {
359        Self::RESET
360    }
361}
362
363/// The five limit comparisons the alert pin can be assigned to.
364#[derive(Clone, Copy, Debug, PartialEq, Eq)]
365pub enum AlertFunction {
366    /// Shunt voltage above the alert limit (SOL, bit 15).
367    ShuntOverLimit,
368    /// Shunt voltage below the alert limit (SUL, bit 14).
369    ShuntUnderLimit,
370    /// Bus voltage above the alert limit (BOL, bit 13).
371    BusOverLimit,
372    /// Bus voltage below the alert limit (BUL, bit 12).
373    BusUnderLimit,
374    /// Power above the alert limit (POL, bit 11).
375    PowerOverLimit,
376}
377
378/// The Mask/Enable register (0x06): which alert function drives the pin, how the
379/// pin behaves, and the status flags the chip reports back.
380///
381/// The alert limit register is compared in the units of the selected function, so
382/// build it with [`shunt_register`], [`bus_register`], or [`power_register`].
383#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
384pub struct MaskEnable {
385    /// Assert the alert pin when the shunt voltage exceeds the limit (SOL).
386    pub shunt_over_limit: bool,
387    /// Assert the alert pin when the shunt voltage drops below the limit (SUL).
388    pub shunt_under_limit: bool,
389    /// Assert the alert pin when the bus voltage exceeds the limit (BOL).
390    pub bus_over_limit: bool,
391    /// Assert the alert pin when the bus voltage drops below the limit (BUL).
392    pub bus_under_limit: bool,
393    /// Assert the alert pin when the power exceeds the limit (POL).
394    pub power_over_limit: bool,
395    /// Also assert the alert pin when a conversion completes (CNVR).
396    pub conversion_ready: bool,
397    /// Status: the selected alert function, not conversion-ready, caused the last
398    /// alert (AFF).
399    pub alert_function_flag: bool,
400    /// Status: all conversions, averaging, and multiplications are complete (CVRF).
401    pub conversion_ready_flag: bool,
402    /// Status: an arithmetic overflow occurred and current and power may be invalid
403    /// (OVF).
404    pub math_overflow: bool,
405    /// Alert pin polarity: `true` for inverted (active-high), `false` for the default
406    /// active-low (APOL).
407    pub alert_active_high: bool,
408    /// Latch the alert pin and flag until this register is read, rather than clearing
409    /// when the fault clears (LEN).
410    pub alert_latch: bool,
411}
412
413impl MaskEnable {
414    /// Decodes a Mask/Enable register value.
415    ///
416    /// # Arguments
417    ///
418    /// * `raw` - the 16-bit Mask/Enable register.
419    ///
420    /// # Returns
421    ///
422    /// The decoded enables and flags; reserved bits are ignored.
423    pub fn from_register(raw: u16) -> Self {
424        Self {
425            shunt_over_limit: raw & 0x8000 != 0,
426            shunt_under_limit: raw & 0x4000 != 0,
427            bus_over_limit: raw & 0x2000 != 0,
428            bus_under_limit: raw & 0x1000 != 0,
429            power_over_limit: raw & 0x0800 != 0,
430            conversion_ready: raw & 0x0400 != 0,
431            alert_function_flag: raw & 0x0010 != 0,
432            conversion_ready_flag: raw & 0x0008 != 0,
433            math_overflow: raw & 0x0004 != 0,
434            alert_active_high: raw & 0x0002 != 0,
435            alert_latch: raw & 0x0001 != 0,
436        }
437    }
438
439    /// Encodes the enables and flags as a Mask/Enable register value.
440    ///
441    /// # Returns
442    ///
443    /// The 16-bit register to write.
444    pub fn to_register(self) -> u16 {
445        let mut raw = 0;
446        for (set, bit) in [
447            (self.shunt_over_limit, 0x8000),
448            (self.shunt_under_limit, 0x4000),
449            (self.bus_over_limit, 0x2000),
450            (self.bus_under_limit, 0x1000),
451            (self.power_over_limit, 0x0800),
452            (self.conversion_ready, 0x0400),
453            (self.alert_function_flag, 0x0010),
454            (self.conversion_ready_flag, 0x0008),
455            (self.math_overflow, 0x0004),
456            (self.alert_active_high, 0x0002),
457            (self.alert_latch, 0x0001),
458        ] {
459            if set {
460                raw |= bit;
461            }
462        }
463        raw
464    }
465
466    /// Returns the alert function the pin actually responds to.
467    ///
468    /// Only one limit function can drive the pin at a time; when several are
469    /// enabled the chip honours the most significant bit.
470    ///
471    /// # Returns
472    ///
473    /// The highest-priority enabled function, or `None` if no limit function is
474    /// enabled.
475    pub fn active_alert_function(self) -> Option<AlertFunction> {
476        if self.shunt_over_limit {
477            Some(AlertFunction::ShuntOverLimit)
478        } else if self.shunt_under_limit {
479            Some(AlertFunction::ShuntUnderLimit)
480        } else if self.bus_over_limit {
481            Some(AlertFunction::BusOverLimit)
482        } else if self.bus_under_limit {
483            Some(AlertFunction::BusUnderLimit)
484        } else if self.power_over_limit {
485            Some(AlertFunction::PowerOverLimit)
486        } else {
487            None
488        }
489    }
490}
491
492/// The die ID register (0xFF), split into its device and revision fields.
493#[derive(Clone, Copy, Debug, PartialEq, Eq)]
494pub struct DieId {
495    /// The 12-bit device identifier, [`DEVICE_ID`] for an INA226.
496    pub device: u16,
497    /// The 4-bit die revision.
498    pub revision: u8,
499}
500
501impl DieId {
502    /// Splits a die ID register value into its fields.
503    ///
504    /// # Arguments
505    ///
506    /// * `raw` - the 16-bit die ID register.
507    ///
508    /// # Returns
509    ///
510    /// The device ID from bits 15:4 and the revision from bits 3:0.
511    pub fn from_register(raw: u16) -> Self {
512        Self {
513            device: raw >> 4,
514            revision: (raw & 0x0F) as u8,
515        }
516    }
517}
518
519/// Checks that the identification registers belong to an INA226.
520///
521/// A node reads both registers at start-up so a wrong part on the address, or a bus
522/// that reads all ones, is caught before its readings are trusted.
523///
524/// # Arguments
525///
526/// * `manufacturer_id` - the manufacturer ID register (0xFE).
527/// * `die_id` - the die ID register (0xFF).
528///
529/// # Returns
530///
531/// The decoded die ID when both registers match.
532///
533/// # Errors
534///
535/// [`SensorError::Identity`] if the manufacturer ID is not [`MANUFACTURER_ID`] or
536/// the device field of the die ID is not [`DEVICE_ID`].
537pub fn identify(manufacturer_id: u16, die_id: u16) -> Result<DieId, SensorError> {
538    let die = DieId::from_register(die_id);
539    if manufacturer_id != MANUFACTURER_ID || die.device != DEVICE_ID {
540        return Err(SensorError::Identity);
541    }
542    Ok(die)
543}
544
545/// Computes the calibration register value for a chosen current resolution and shunt.
546///
547/// This is the datasheet's calibration equation, `CAL = 0.00512 / (Current_LSB *
548/// R_SHUNT)`, expressed in integer micro-units: with the current LSB in microamps
549/// and the shunt in milliohms, the fixed `0.00512` becomes `5_120_000`.
550///
551/// # Arguments
552///
553/// * `current_lsb_microamps` - the amps-per-count the current register should carry.
554/// * `shunt_milliohms` - the shunt resistor value, in milliohms.
555///
556/// # Returns
557///
558/// The value to program into the calibration register, truncated as the datasheet
559/// does and capped at the register's 15-bit width. Returns `0` if either argument
560/// is `0`, which is the chip's own uncalibrated state.
561pub fn calibration(current_lsb_microamps: u32, shunt_milliohms: u32) -> u16 {
562    let denominator = current_lsb_microamps.saturating_mul(shunt_milliohms);
563    if denominator == 0 {
564        return 0;
565    }
566    (5_120_000 / denominator).min(0x7FFF) as u16
567}
568
569/// Returns the smallest current LSB, in microamps, that still spans a full-scale
570/// current.
571///
572/// The current register is 15 bits of magnitude, so the minimum resolution is the
573/// maximum expected current divided by 2^15, rounded up to the next whole microamp.
574/// The datasheet then rounds this up further to a convenient round number.
575///
576/// # Arguments
577///
578/// * `max_expected_microamps` - the largest current the application will measure.
579///
580/// # Returns
581///
582/// The minimum current LSB in microamps.
583pub fn minimum_current_lsb_microamps(max_expected_microamps: u32) -> u32 {
584    max_expected_microamps.div_ceil(32_768)
585}
586
587/// Decodes the shunt voltage register to nanovolts.
588///
589/// # Arguments
590///
591/// * `raw` - the signed shunt voltage register.
592///
593/// # Returns
594///
595/// The shunt voltage in nanovolts, at 2.5 µV per count.
596pub fn shunt_nanovolts(raw: i16) -> i32 {
597    i32::from(raw) * SHUNT_LSB_NANOVOLTS
598}
599
600/// Decodes the shunt voltage register to millivolts as a float.
601///
602/// # Arguments
603///
604/// * `raw` - the signed shunt voltage register.
605///
606/// # Returns
607///
608/// The shunt voltage in millivolts.
609pub fn shunt_millivolts_f32(raw: i16) -> f32 {
610    f32::from(raw) * 0.0025
611}
612
613/// Decodes the bus voltage register to microvolts.
614///
615/// Bit 15 is always zero on the chip, since the bus voltage is positive only, and
616/// is ignored here.
617///
618/// # Arguments
619///
620/// * `raw` - the bus voltage register.
621///
622/// # Returns
623///
624/// The bus voltage in microvolts, at 1.25 mV per count.
625pub fn bus_microvolts(raw: u16) -> u32 {
626    u32::from(raw & 0x7FFF) * BUS_LSB_MICROVOLTS
627}
628
629/// Decodes the bus voltage register to volts as a float.
630///
631/// # Arguments
632///
633/// * `raw` - the bus voltage register.
634///
635/// # Returns
636///
637/// The bus voltage in volts.
638pub fn bus_volts_f32(raw: u16) -> f32 {
639    f32::from(raw & 0x7FFF) * 0.00125
640}
641
642/// Decodes the current register to microamps for a given current LSB.
643///
644/// # Arguments
645///
646/// * `raw` - the signed current register.
647/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
648///
649/// # Returns
650///
651/// The current in microamps.
652pub fn current_microamps(raw: i16, current_lsb_microamps: u32) -> i32 {
653    i32::from(raw) * current_lsb_microamps as i32
654}
655
656/// Decodes the current register to amps as a float.
657///
658/// # Arguments
659///
660/// * `raw` - the signed current register.
661/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
662///
663/// # Returns
664///
665/// The current in amps.
666pub fn current_amps_f32(raw: i16, current_lsb_microamps: u32) -> f32 {
667    current_microamps(raw, current_lsb_microamps) as f32 * 1e-6
668}
669
670/// Decodes the power register to microwatts for a given current LSB.
671///
672/// The power LSB is fixed by the datasheet at twenty-five times the current LSB.
673///
674/// # Arguments
675///
676/// * `raw` - the power register.
677/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
678///
679/// # Returns
680///
681/// The power in microwatts.
682pub fn power_microwatts(raw: u16, current_lsb_microamps: u32) -> u32 {
683    u32::from(raw) * (POWER_LSB_RATIO * current_lsb_microamps)
684}
685
686/// Decodes the power register to watts as a float.
687///
688/// # Arguments
689///
690/// * `raw` - the power register.
691/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
692///
693/// # Returns
694///
695/// The power in watts.
696pub fn power_watts_f32(raw: u16, current_lsb_microamps: u32) -> f32 {
697    power_microwatts(raw, current_lsb_microamps) as f32 * 1e-6
698}
699
700/// Builds the shunt voltage register a monitor reports for a shunt voltage.
701///
702/// The inverse of [`shunt_nanovolts`], so a node can be written and tested against
703/// what a monitor sends without one attached. The same value serves as the alert
704/// limit for the shunt over- and under-limit functions.
705///
706/// # Arguments
707///
708/// * `nanovolts` - the shunt voltage in nanovolts.
709///
710/// # Returns
711///
712/// The signed shunt voltage register, at 2.5 µV per count.
713pub fn shunt_register(nanovolts: i32) -> i16 {
714    (nanovolts / SHUNT_LSB_NANOVOLTS) as i16
715}
716
717/// Builds the bus voltage register a monitor reports for a bus voltage.
718///
719/// The inverse of [`bus_microvolts`]. The same value serves as the alert limit for
720/// the bus over- and under-limit functions.
721///
722/// # Arguments
723///
724/// * `microvolts` - the bus voltage in microvolts.
725///
726/// # Returns
727///
728/// The bus voltage register, at 1.25 mV per count.
729pub fn bus_register(microvolts: u32) -> u16 {
730    (microvolts / BUS_LSB_MICROVOLTS) as u16
731}
732
733/// Builds the current register a monitor reports for a current.
734///
735/// The inverse of [`current_microamps`].
736///
737/// # Arguments
738///
739/// * `microamps` - the current in microamps.
740/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
741///
742/// # Returns
743///
744/// The signed current register, or zero if `current_lsb_microamps` is zero.
745pub fn current_register(microamps: i32, current_lsb_microamps: u32) -> i16 {
746    if current_lsb_microamps == 0 {
747        return 0;
748    }
749    (microamps / current_lsb_microamps as i32) as i16
750}
751
752/// Builds the power register a monitor reports for a power.
753///
754/// The inverse of [`power_microwatts`]. The same value serves as the alert limit for
755/// the power over-limit function.
756///
757/// # Arguments
758///
759/// * `microwatts` - the power in microwatts.
760/// * `current_lsb_microamps` - the current LSB the calibration register was set for.
761///
762/// # Returns
763///
764/// The power register, or zero if `current_lsb_microamps` is zero.
765pub fn power_register(microwatts: u32, current_lsb_microamps: u32) -> u16 {
766    if current_lsb_microamps == 0 {
767        return 0;
768    }
769    (microwatts / (POWER_LSB_RATIO * current_lsb_microamps)) as u16
770}
771
772/// Reproduces the chip's current calculation from a shunt reading and calibration.
773///
774/// This is the datasheet's `Current = ShuntVoltage * CalibrationRegister / 2048`,
775/// so a simulator can fill the current register the way the chip would.
776///
777/// # Arguments
778///
779/// * `shunt` - the signed shunt voltage register.
780/// * `calibration` - the programmed calibration register.
781///
782/// # Returns
783///
784/// The signed current register the chip would hold.
785pub fn current_register_from_shunt(shunt: i16, calibration: u16) -> i16 {
786    (i32::from(shunt) * i32::from(calibration) / 2048) as i16
787}
788
789/// Reproduces the chip's power calculation from the current and bus registers.
790///
791/// This is the datasheet's `Power = Current * BusVoltage / 20000`. The power
792/// register is unsigned, so the magnitude of the current is used.
793///
794/// # Arguments
795///
796/// * `current` - the signed current register.
797/// * `bus` - the bus voltage register.
798///
799/// # Returns
800///
801/// The power register the chip would hold.
802pub fn power_register_from_current(current: i16, bus: u16) -> u16 {
803    (u32::from(current.unsigned_abs()) * u32::from(bus & 0x7FFF) / 20_000) as u16
804}
805
806#[cfg(test)]
807mod tests {
808    use super::*;
809
810    // The datasheet's worked example (section 6.5.1, Table 6-1): a 10 A load across
811    // a 2 mΩ shunt, 12 V common mode, current LSB rounded to 1 mA/bit.
812    const CURRENT_LSB: u32 = 1_000; // 1 mA in microamps
813
814    #[test]
815    fn every_register_survives_a_round_trip_through_its_builder() {
816        // Table 6-1: 20 mV shunt, 11.98 V bus, 10 A, 119.8 W.
817        assert_eq!(shunt_register(20_000_000), 0x1F40);
818        assert_eq!(shunt_nanovolts(shunt_register(20_000_000)), 20_000_000);
819        assert_eq!(shunt_nanovolts(shunt_register(-80_000_000)), -80_000_000);
820        assert_eq!(bus_register(11_980_000), 0x2570);
821        assert_eq!(bus_microvolts(bus_register(11_980_000)), 11_980_000);
822        assert_eq!(current_register(10_000_000, CURRENT_LSB), 0x2710);
823        assert_eq!(
824            current_microamps(current_register(10_000_000, CURRENT_LSB), CURRENT_LSB),
825            10_000_000
826        );
827        assert_eq!(current_register(-2_500_000, CURRENT_LSB), -2_500);
828        assert_eq!(power_register(119_800_000, CURRENT_LSB), 0x12B8);
829        assert_eq!(
830            power_microwatts(power_register(119_800_000, CURRENT_LSB), CURRENT_LSB),
831            119_800_000
832        );
833        assert_eq!(current_register(1, 0), 0);
834        assert_eq!(power_register(1, 0), 0);
835    }
836
837    #[test]
838    fn calibration_matches_the_datasheet_example() {
839        // Equation 1 with Current_LSB = 1 mA/bit and R_SHUNT = 2 mΩ: 2560 = A00h.
840        assert_eq!(calibration(CURRENT_LSB, 2), 2_560);
841        assert_eq!(calibration(CURRENT_LSB, 2), 0xA00);
842    }
843
844    #[test]
845    fn calibration_is_capped_at_the_register_width() {
846        // Table 7-11: the calibration value occupies FS14..FS0.
847        assert_eq!(calibration(1, 1), 0x7FFF);
848    }
849
850    #[test]
851    fn minimum_current_lsb_matches_the_datasheet_example() {
852        // Equation 2, section 6.5.1: 15 A / 2^15 = 457.7 µA/bit, up to the next
853        // whole microamp.
854        assert_eq!(minimum_current_lsb_microamps(15_000_000), 458);
855    }
856
857    #[test]
858    fn shunt_register_decodes_per_the_datasheet() {
859        // Table 6-1: 1F40h = 8000 at 2.5 µV per count is 20 mV.
860        assert_eq!(shunt_nanovolts(0x1F40), 20_000_000);
861        // Section 7.1.2 worked two's complement: -80 mV is 8300h.
862        assert_eq!(
863            shunt_nanovolts(i16::from_be_bytes([0x83, 0x00])),
864            -80_000_000
865        );
866        // Section 7.1.2: full scale 7FFFh is 81.92 mV (81.9175 mV exactly, per the
867        // electrical characteristics input range).
868        assert_eq!(shunt_nanovolts(0x7FFF), 81_917_500);
869        assert!((shunt_millivolts_f32(0x1F40) - 20.0).abs() < 1e-4);
870    }
871
872    #[test]
873    fn bus_register_decodes_per_the_datasheet() {
874        // Table 6-1: 2570h = 9584 at 1.25 mV per count is 11.98 V.
875        assert_eq!(bus_microvolts(0x2570), 11_980_000);
876        // Section 7.1.3: full scale 7FFFh is 40.96 V.
877        assert_eq!(bus_microvolts(0x7FFF), 40_958_750);
878        // Table 7-8: D15 is always zero, so it carries no value if it ever reads set.
879        assert_eq!(bus_microvolts(0xA570), bus_microvolts(0x2570));
880        assert!((bus_volts_f32(0x2570) - 11.98).abs() < 1e-4);
881    }
882
883    #[test]
884    fn current_register_decodes_to_the_example_load() {
885        // Table 6-1: 2710h = 10000 at 1 mA per count is 10 A.
886        assert_eq!(current_microamps(0x2710, CURRENT_LSB), 10_000_000);
887        assert!((current_amps_f32(0x2710, CURRENT_LSB) - 10.0).abs() < 1e-5);
888    }
889
890    #[test]
891    fn power_register_decodes_to_the_example_load() {
892        // Table 6-1: 12B8h = 4792 at 25 mW per count (25 times the 1 mA current
893        // LSB) is 119.8 W; the table prints the figure as 119.82 W.
894        assert_eq!(power_microwatts(0x12B8, CURRENT_LSB), 119_800_000);
895        assert!((power_watts_f32(0x12B8, CURRENT_LSB) - 119.8).abs() < 1e-3);
896    }
897
898    #[test]
899    fn chip_arithmetic_reproduces_the_datasheet_example() {
900        // Equation 3: 8000 * 2560 / 2048 = 10000 = 2710h.
901        assert_eq!(current_register_from_shunt(0x1F40, 0xA00), 0x2710);
902        // Equation 4: 10000 * 9584 / 20000 = 4792 = 12B8h.
903        assert_eq!(power_register_from_current(0x2710, 0x2570), 0x12B8);
904        // A reversed current gives the same power magnitude.
905        assert_eq!(current_register_from_shunt(-0x1F40, 0xA00), -0x2710);
906        assert_eq!(power_register_from_current(-0x2710, 0x2570), 0x12B8);
907        // Nothing is computed until the calibration register is programmed
908        // (Table 7-1, note 2).
909        assert_eq!(current_register_from_shunt(0x1F40, 0), 0);
910    }
911
912    #[test]
913    fn configuration_reset_value_matches_the_datasheet() {
914        // Table 7-1 / Table 7-2: power-on reset 4127h, averaging 1, 1.1 ms for both
915        // conversions, shunt and bus continuous. Table 6-1 step 1 writes the same.
916        let config = Configuration::from_register(CONFIG_RESET);
917        assert_eq!(config, Configuration::RESET);
918        assert_eq!(config, Configuration::default());
919        assert!(!config.reset);
920        assert_eq!(config.averaging, Averaging::Samples1);
921        assert_eq!(config.bus_conversion_time, ConversionTime::Us1100);
922        assert_eq!(config.shunt_conversion_time, ConversionTime::Us1100);
923        assert_eq!(config.mode, Mode::ShuntAndBusContinuous);
924        assert_eq!(config.to_register(), 0x4127);
925    }
926
927    #[test]
928    fn configuration_fields_land_on_their_datasheet_bits() {
929        // Table 7-2: RST in D15, AVG in D11:9, VBUSCT in D8:6, VSHCT in D5:3, MODE
930        // in D2:0.
931        let config = Configuration {
932            reset: true,
933            averaging: Averaging::Samples1024,
934            bus_conversion_time: ConversionTime::Us8244,
935            shunt_conversion_time: ConversionTime::Us8244,
936            mode: Mode::ShuntAndBusContinuous,
937        };
938        assert_eq!(
939            config.to_register(),
940            0x8000 | 0x4000 | 0x0E00 | 0x01C0 | 0x0038 | 0x0007
941        );
942        assert_eq!(Configuration::from_register(config.to_register()), config);
943
944        let config = Configuration {
945            reset: false,
946            averaging: Averaging::Samples16,
947            bus_conversion_time: ConversionTime::Us140,
948            shunt_conversion_time: ConversionTime::Us588,
949            mode: Mode::ShuntTriggered,
950        };
951        assert_eq!(config.to_register(), 0x4000 | 0x0400 | 0x0018 | 0x0001);
952        assert_eq!(Configuration::from_register(config.to_register()), config);
953
954        // Table 7-6: both 000 and 100 are power-down.
955        assert_eq!(Configuration::from_register(0x4120).mode, Mode::PowerDown);
956        assert_eq!(Configuration::from_register(0x4124).mode, Mode::PowerDown);
957    }
958
959    #[test]
960    fn averaging_table_decodes_per_the_datasheet() {
961        // Table 7-3.
962        let table = [1, 4, 16, 64, 128, 256, 512, 1024];
963        for (bits, samples) in table.into_iter().enumerate() {
964            let averaging = Averaging::from_bits(bits as u16);
965            assert_eq!(averaging as u16, bits as u16);
966            assert_eq!(averaging.samples(), samples, "AVG = {bits:03b}");
967        }
968    }
969
970    #[test]
971    fn conversion_time_table_decodes_per_the_datasheet() {
972        // Tables 7-4 and 7-5, the same eight settings for bus and shunt.
973        let table = [140, 204, 332, 588, 1_100, 2_116, 4_156, 8_244];
974        for (bits, microseconds) in table.into_iter().enumerate() {
975            let time = ConversionTime::from_bits(bits as u16);
976            assert_eq!(time as u16, bits as u16);
977            assert_eq!(time.microseconds(), microseconds, "CT = {bits:03b}");
978        }
979    }
980
981    #[test]
982    fn mode_table_decodes_per_the_datasheet() {
983        // Table 7-6.
984        assert_eq!(Mode::from_bits(0b000), Mode::PowerDown);
985        assert_eq!(Mode::from_bits(0b001), Mode::ShuntTriggered);
986        assert_eq!(Mode::from_bits(0b010), Mode::BusTriggered);
987        assert_eq!(Mode::from_bits(0b011), Mode::ShuntAndBusTriggered);
988        assert_eq!(Mode::from_bits(0b100), Mode::PowerDown);
989        assert_eq!(Mode::from_bits(0b101), Mode::ShuntContinuous);
990        assert_eq!(Mode::from_bits(0b110), Mode::BusContinuous);
991        assert_eq!(Mode::from_bits(0b111), Mode::ShuntAndBusContinuous);
992        assert!(Mode::ShuntContinuous.measures_shunt());
993        assert!(!Mode::ShuntContinuous.measures_bus());
994        assert!(Mode::BusTriggered.measures_bus());
995        assert!(!Mode::BusTriggered.is_continuous());
996        assert!(Mode::ShuntAndBusContinuous.is_continuous());
997        assert!(!Mode::PowerDown.measures_shunt());
998        assert!(!Mode::PowerDown.measures_bus());
999    }
1000
1001    #[test]
1002    fn update_interval_matches_the_datasheet_timing_examples() {
1003        // Section 6.4.1: 588 µs for both conversions averaged over 4 samples updates
1004        // about every 4.7 ms, as does 4.156 ms shunt with 588 µs bus and no averaging.
1005        let config = Configuration {
1006            averaging: Averaging::Samples4,
1007            bus_conversion_time: ConversionTime::Us588,
1008            shunt_conversion_time: ConversionTime::Us588,
1009            ..Configuration::RESET
1010        };
1011        assert_eq!(config.update_microseconds(), 4_704);
1012        let config = Configuration {
1013            averaging: Averaging::Samples1,
1014            bus_conversion_time: ConversionTime::Us588,
1015            shunt_conversion_time: ConversionTime::Us4156,
1016            ..Configuration::RESET
1017        };
1018        assert_eq!(config.update_microseconds(), 4_744);
1019        assert_eq!(Configuration::RESET.update_microseconds(), 2_200);
1020        let config = Configuration {
1021            mode: Mode::ShuntContinuous,
1022            ..config
1023        };
1024        assert_eq!(config.update_microseconds(), 4_156);
1025        let config = Configuration {
1026            mode: Mode::PowerDown,
1027            ..config
1028        };
1029        assert_eq!(config.update_microseconds(), 0);
1030    }
1031
1032    #[test]
1033    fn address_table_matches_the_datasheet() {
1034        // Table 6-2, in its row order GND, VS, SDA, SCL for each pin.
1035        use AddressPin::*;
1036        let pins = [Ground, Supply, Sda, Scl];
1037        let mut expected = 0x40;
1038        for a1 in pins {
1039            for a0 in pins {
1040                assert_eq!(address(a1, a0), expected, "A1 = {a1:?}, A0 = {a0:?}");
1041                expected += 1;
1042            }
1043        }
1044        assert_eq!(address(Ground, Ground), 0b1000000);
1045        assert_eq!(address(Supply, Ground), 0b1000100);
1046        assert_eq!(address(Sda, Scl), 0b1001011);
1047        assert_eq!(address(Scl, Scl), 0b1001111);
1048    }
1049
1050    #[test]
1051    fn identification_registers_match_the_datasheet() {
1052        // Table 7-1: manufacturer ID 5449h; die ID 2260h or 2261h (note 3).
1053        assert_eq!(MANUFACTURER_ID, 0x5449);
1054        assert_eq!(
1055            identify(0x5449, 0x2260),
1056            Ok(DieId {
1057                device: 0x226,
1058                revision: 0
1059            })
1060        );
1061        assert_eq!(
1062            identify(0x5449, 0x2261),
1063            Ok(DieId {
1064                device: 0x226,
1065                revision: 1
1066            })
1067        );
1068        // An unpowered or absent part reads all ones.
1069        assert_eq!(identify(0xFFFF, 0xFFFF), Err(SensorError::Identity));
1070        // Another TI part on the same address.
1071        assert_eq!(identify(0x5449, 0x2280), Err(SensorError::Identity));
1072        assert_eq!(identify(0x0000, 0x2260), Err(SensorError::Identity));
1073    }
1074
1075    #[test]
1076    fn mask_enable_decodes_the_alert_bits() {
1077        // Table 7-12: SOL D15, SUL D14, BOL D13, BUL D12, POL D11, CNVR D10, AFF D4,
1078        // CVRF D3, OVF D2, APOL D1, LEN D0.
1079        let mask = MaskEnable::from_register(0x8001);
1080        assert!(mask.shunt_over_limit);
1081        assert!(mask.alert_latch);
1082        assert_eq!(
1083            mask.active_alert_function(),
1084            Some(AlertFunction::ShuntOverLimit)
1085        );
1086        assert_eq!(mask.to_register(), 0x8001);
1087
1088        let mask = MaskEnable::from_register(0x0418);
1089        assert!(mask.conversion_ready);
1090        assert!(mask.alert_function_flag);
1091        assert!(mask.conversion_ready_flag);
1092        assert!(!mask.math_overflow);
1093        assert_eq!(mask.active_alert_function(), None);
1094        assert_eq!(mask.to_register(), 0x0418);
1095
1096        assert!(MaskEnable::from_register(0x0004).math_overflow);
1097        assert!(MaskEnable::from_register(0x0002).alert_active_high);
1098        assert_eq!(MaskEnable::from_register(0x0000), MaskEnable::default());
1099        assert_eq!(MaskEnable::default().to_register(), 0x0000);
1100
1101        for bit in 0..16 {
1102            let raw = 1u16 << bit;
1103            let reserved = (5..=9).contains(&bit);
1104            assert_eq!(
1105                MaskEnable::from_register(raw).to_register(),
1106                if reserved { 0 } else { raw },
1107                "bit {bit}"
1108            );
1109        }
1110    }
1111
1112    #[test]
1113    fn the_highest_alert_function_bit_takes_priority() {
1114        // Section 7.1.7: with several functions enabled, the most significant wins.
1115        assert_eq!(
1116            MaskEnable::from_register(0xF800).active_alert_function(),
1117            Some(AlertFunction::ShuntOverLimit)
1118        );
1119        assert_eq!(
1120            MaskEnable::from_register(0x7800).active_alert_function(),
1121            Some(AlertFunction::ShuntUnderLimit)
1122        );
1123        assert_eq!(
1124            MaskEnable::from_register(0x3800).active_alert_function(),
1125            Some(AlertFunction::BusOverLimit)
1126        );
1127        assert_eq!(
1128            MaskEnable::from_register(0x1800).active_alert_function(),
1129            Some(AlertFunction::BusUnderLimit)
1130        );
1131        assert_eq!(
1132            MaskEnable::from_register(0x0800).active_alert_function(),
1133            Some(AlertFunction::PowerOverLimit)
1134        );
1135        assert_eq!(
1136            MaskEnable::from_register(0x0400).active_alert_function(),
1137            None
1138        );
1139    }
1140
1141    #[test]
1142    fn integer_conversions_track_the_floating_point_reference() {
1143        // Equation 1 in its floating-point form, CAL = 0.00512 / (Current_LSB *
1144        // R_SHUNT), swept over current LSBs and shunts a design would choose.
1145        for &lsb in &[50u32, 100, 250, 458, 500, 1_000, 2_000, 5_000, 10_000] {
1146            for &shunt in &[1u32, 2, 5, 10, 25, 100, 500, 1_000] {
1147                let reference = 0.00512 / (f64::from(lsb) * 1e-6 * f64::from(shunt) * 1e-3);
1148                let expected = reference.trunc().min(32_767.0);
1149                let actual = f64::from(calibration(lsb, shunt));
1150                assert!(
1151                    (actual - expected).abs() <= 1.0,
1152                    "lsb {lsb} µA, shunt {shunt} mΩ: {actual} vs {expected}"
1153                );
1154            }
1155        }
1156
1157        // The 2.5 µV, 1.25 mV, and 25 x Current_LSB scalings against their float
1158        // forms across the register range.
1159        for raw in (-32_768..=32_767).step_by(997) {
1160            let shunt = f64::from(shunt_nanovolts(raw as i16)) * 1e-9;
1161            assert!((shunt - f64::from(raw) * 2.5e-6).abs() < 1e-12);
1162            let current = f64::from(current_microamps(raw as i16, 458)) * 1e-6;
1163            assert!((current - f64::from(raw) * 458e-6).abs() < 1e-9);
1164        }
1165        for raw in (0..=0x7FFF).step_by(499) {
1166            let bus = f64::from(bus_microvolts(raw as u16)) * 1e-6;
1167            assert!((bus - f64::from(raw) * 1.25e-3).abs() < 1e-9);
1168            let power = f64::from(power_microwatts(raw as u16, 458)) * 1e-6;
1169            assert!((power - f64::from(raw) * 25.0 * 458e-6).abs() < 1e-9);
1170        }
1171    }
1172
1173    #[test]
1174    fn an_uncalibrated_request_returns_zero() {
1175        assert_eq!(calibration(0, 2), 0);
1176        assert_eq!(calibration(CURRENT_LSB, 0), 0);
1177    }
1178}