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}