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}