embedded_devices/devices/texas_instruments/ina226/
mod.rs

1//! The INA226 is a current shunt and power monitor with an I2C- or SMBUS-compatible interface.
2//! The device monitors both a shunt voltage drop and bus supply voltage.
3//! Programmable calibration value, conversion times, and averaging,combined with an
4//! internal multiplier, enable direct readouts of current in amperes and power in watts.
5//!
6//! The INA226 senses current on common-mode bus voltages that can vary from 0V to 36V,
7//! independent of the supply voltage. The device operates from a single 2.7V to 5.5V supply,
8//! drawing a typical of 330μA of supply current. The device is specified over the
9//! operating temperature range between –40°C and 125°C and features
10//! up to 16 programmable addresses on the I2C-compatible interface.
11//!
12//! ## Usage (sync)
13//!
14//! ```rust
15//! # #[cfg(feature = "sync")] mod test {
16//! # fn test<I, D>(mut i2c: I, delay: D) -> Result<(), I::Error>
17//! # where
18//! #   I: embedded_hal::i2c::I2c + embedded_hal::i2c::ErrorType,
19//! #   D: embedded_hal::delay::DelayNs
20//! # {
21//! use embedded_devices::devices::texas_instruments::ina226::{INA226Sync, address::Address, address::Pin};
22//! use embedded_devices::sensor::OneshotSensorSync;
23//! use uom::si::electric_current::{ampere, milliampere};
24//! use uom::si::electric_potential::millivolt;
25//! use uom::si::electrical_resistance::ohm;
26//! use uom::si::power::milliwatt;
27//! use uom::si::f64::{ElectricCurrent, ElectricalResistance};
28//!
29//! // Create and initialize the device
30//! let mut ina226 = INA226Sync::new_i2c(delay, i2c, Address::A0A1(Pin::Gnd, Pin::Gnd));
31//! ina226.init(
32//!   // Most units use a 100mΩ shunt resistor
33//!   ElectricalResistance::new::<ohm>(0.1),
34//!   // Maximum expected current 3A
35//!   ElectricCurrent::new::<ampere>(3.0),
36//! ).unwrap();
37//!
38//! // One-shot read all values
39//! let measurement = ina226.measure().unwrap();
40//! let bus_voltage = measurement.bus_voltage.get::<millivolt>();
41//! let current = measurement.current.get::<milliampere>();
42//! let power = measurement.power.get::<milliwatt>();
43//! println!("Current measurement: {:?}mV, {:?}mA, {:?}mW", bus_voltage, current, power);
44//! # Ok(())
45//! # }
46//! # }
47//! ```
48//!
49//! ## Usage (async)
50//!
51//! ```rust
52//! # #[cfg(feature = "async")] mod test {
53//! # async fn test<I, D>(mut i2c: I, delay: D) -> Result<(), I::Error>
54//! # where
55//! #   I: embedded_hal_async::i2c::I2c + embedded_hal_async::i2c::ErrorType,
56//! #   D: embedded_hal_async::delay::DelayNs
57//! # {
58//! use embedded_devices::devices::texas_instruments::ina226::{INA226Async, address::Address, address::Pin};
59//! use embedded_devices::sensor::OneshotSensorAsync;
60//! use uom::si::electric_current::{ampere, milliampere};
61//! use uom::si::electric_potential::millivolt;
62//! use uom::si::electrical_resistance::ohm;
63//! use uom::si::power::milliwatt;
64//! use uom::si::f64::{ElectricCurrent, ElectricalResistance};
65//!
66//! // Create and initialize the device
67//! let mut ina226 = INA226Async::new_i2c(delay, i2c, Address::A0A1(Pin::Gnd, Pin::Gnd));
68//! ina226.init(
69//!   // Most units use a 100mΩ shunt resistor
70//!   ElectricalResistance::new::<ohm>(0.1),
71//!   // Maximum expected current 3A
72//!   ElectricCurrent::new::<ampere>(3.0),
73//! ).await.unwrap();
74//!
75//! // One-shot read all values
76//! let measurement = ina226.measure().await.unwrap();
77//! let bus_voltage = measurement.bus_voltage.get::<millivolt>();
78//! let current = measurement.current.get::<milliampere>();
79//! let power = measurement.power.get::<milliwatt>();
80//! println!("Current measurement: {:?}mV, {:?}mA, {:?}mW", bus_voltage, current, power);
81//! # Ok(())
82//! # }
83//! # }
84//! ```
85
86use self::address::Address;
87
88use embedded_devices_derive::{forward_register_fns, sensor};
89use embedded_interfaces::TransportError;
90use uom::si::electric_current::ampere;
91use uom::si::electrical_resistance::ohm;
92use uom::si::f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Power};
93
94pub mod address;
95pub mod registers;
96
97#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98#[derive(Debug, thiserror::Error)]
99pub enum InitError<BusError> {
100    /// Transport error
101    #[error("transport error")]
102    Transport(#[from] TransportError<(), BusError>),
103    /// Invalid Die Id was encountered
104    #[error("invalid die id {0:#04x}")]
105    InvalidDieId(u16),
106    /// Invalid Manufacturer Id was encountered
107    #[error("invalid manufacturer id {0:#04x}")]
108    InvalidManufacturerId(u16),
109}
110
111#[derive(Debug, thiserror::Error)]
112pub enum MeasurementError<BusError> {
113    /// Transport error
114    #[error("transport error")]
115    Transport(#[from] TransportError<(), BusError>),
116    /// The conversion ready flag was not set within the expected time frame.
117    #[error("conversion timeout")]
118    Timeout,
119    /// Measurement was ready, but an overflow occurred. The power and
120    /// current measurement may be incorrect.
121    #[error("overflow in measurement")]
122    Overflow(Measurement),
123}
124
125#[derive(Debug, thiserror::Error)]
126pub enum ContinuousMeasurementError<BusError> {
127    /// Transport error
128    #[error("transport error")]
129    Transport(#[from] TransportError<(), BusError>),
130    /// Measurement was ready, but an overflow occurred. The power and
131    /// current measurement may be incorrect.
132    #[error("overflow in measurement")]
133    Overflow(Measurement),
134}
135
136/// Measurement data
137#[derive(Debug, embedded_devices_derive::Measurement)]
138pub struct Measurement {
139    /// Measured voltage across the shunt
140    pub shunt_voltage: ElectricPotential,
141    /// Measured voltage on the bus
142    #[measurement(Voltage)]
143    pub bus_voltage: ElectricPotential,
144    /// Measured current
145    #[measurement(Current)]
146    pub current: ElectricCurrent,
147    /// Measured power
148    #[measurement(Power)]
149    pub power: Power,
150}
151
152/// The INA226 is an ultra-precise digital power monitor with a 16-bit delta-sigma ADC
153/// for high- or low-side current-sensing applications. The device can measure a full-scale
154/// differential input of ±81.92 mV across a resistive shunt sense element with bus
155/// voltage support from 0 V to +36 V.
156///
157/// For a full description and usage examples, refer to the [module documentation](self).
158#[maybe_async_cfg::maybe(
159    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
160    sync(feature = "sync"),
161    async(feature = "async")
162)]
163pub struct INA226<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> {
164    /// The delay provider
165    delay: D,
166    /// The interface to communicate with the device
167    interface: I,
168    /// Shunt resistance
169    shunt_resistance: ElectricalResistance,
170    /// Maximum expected current
171    max_expected_current: ElectricCurrent,
172    /// Configured nA/LSB for current readings
173    current_lsb_na: i64,
174}
175
176pub trait INA226Register {}
177
178#[maybe_async_cfg::maybe(
179    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
180    sync(feature = "sync"),
181    async(feature = "async")
182)]
183impl<D, I> INA226<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>>
184where
185    I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
186    D: hal::delay::DelayNs,
187{
188    /// Initializes a new device with the given address on the specified bus.
189    /// This consumes the I2C bus `I`.
190    ///
191    /// Before using this device, you should call the [`Self::init`] method which
192    /// saves the calibration values to enable current and power output.
193    #[inline]
194    pub fn new_i2c(delay: D, interface: I, address: Address) -> Self {
195        Self {
196            delay,
197            interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
198            shunt_resistance: Default::default(),
199            max_expected_current: Default::default(),
200            current_lsb_na: 1,
201        }
202    }
203}
204
205#[forward_register_fns]
206#[sensor(Voltage, Current, Power)]
207#[maybe_async_cfg::maybe(
208    idents(
209        hal(sync = "embedded_hal", async = "embedded_hal_async"),
210        RegisterInterface,
211        ResettableDevice
212    ),
213    sync(feature = "sync"),
214    async(feature = "async")
215)]
216impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> INA226<D, I> {
217    /// Soft-resets the device, calibrates it with the given shunt resistor
218    /// value and maximum expected current.
219    ///
220    /// You can change the values later using [`Self::calibrate`].
221    pub async fn init(
222        &mut self,
223        shunt_resistance: ElectricalResistance,
224        max_expected_current: ElectricCurrent,
225    ) -> Result<(), InitError<I::BusError>> {
226        use crate::device::ResettableDevice;
227        use registers::{DieId, ManufacturerId};
228
229        // Reset the device and wait for 0.5ms. The datasheet does not define
230        // a specific startup time so we use a similar value as the INA228.
231        self.reset().await?;
232        self.delay.delay_us(500).await;
233
234        // Verify we are talking to the correct device
235        let manufacturer_id = self.read_register::<ManufacturerId>().await?.read_id();
236        if manufacturer_id != ManufacturerId::default().read_id() {
237            return Err(InitError::InvalidManufacturerId(manufacturer_id));
238        }
239        let die_id = self.read_register::<DieId>().await?.read_id();
240        if die_id != DieId::default().read_id() {
241            return Err(InitError::InvalidDieId(die_id));
242        }
243
244        self.calibrate(shunt_resistance, max_expected_current).await?;
245
246        Ok(())
247    }
248
249    /// Writes the given shunt resistance and maximum expected current into
250    /// the device configuration register. This enables power and current output.
251    pub async fn calibrate(
252        &mut self,
253        shunt_resistance: ElectricalResistance,
254        max_expected_current: ElectricCurrent,
255    ) -> Result<(), TransportError<(), I::BusError>> {
256        self.shunt_resistance = shunt_resistance;
257        self.max_expected_current = max_expected_current;
258
259        let shunt_resistance = shunt_resistance.get::<ohm>();
260        let max_expected_current = max_expected_current.get::<ampere>();
261
262        self.current_lsb_na = ((1_000_000_000f64 / (1 << 15) as f64) * max_expected_current) as i64;
263        let shunt_resistance_mohm = (1_000.0 * shunt_resistance) as i64;
264
265        // Calibration Register = 0.00512 / (current_lsb (A) * shunt_resistance (Ω))
266        let cal = 5_120_000_000 / (self.current_lsb_na * shunt_resistance_mohm);
267        self.write_register(self::registers::Calibration::default().with_raw_value(cal as u16))
268            .await?;
269
270        Ok(())
271    }
272
273    /// Returns the currently stored measurement values without triggering a new measurement.
274    /// A timeout error cannot occur here.
275    pub async fn read_measurements(&mut self) -> Result<Measurement, MeasurementError<I::BusError>> {
276        let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
277        let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
278        let current = self.read_register::<self::registers::Current>().await?;
279        let power = self.read_register::<self::registers::Power>().await?;
280        let flags = self.read_register::<self::registers::MaskEnable>().await?;
281
282        let measurement = Measurement {
283            shunt_voltage: shunt_voltage.read_value(),
284            bus_voltage: bus_voltage.read_value(),
285            current: current.read_current(self.current_lsb_na),
286            power: power.read_power(self.current_lsb_na),
287        };
288
289        if flags.read_math_overflow_flag() {
290            Err(MeasurementError::Overflow(measurement))
291        } else {
292            Ok(measurement)
293        }
294    }
295}
296
297#[maybe_async_cfg::maybe(
298    idents(
299        hal(sync = "embedded_hal", async = "embedded_hal_async"),
300        RegisterInterface,
301        ResettableDevice
302    ),
303    sync(feature = "sync"),
304    async(feature = "async")
305)]
306impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::device::ResettableDevice
307    for INA226<D, I>
308{
309    type Error = TransportError<(), I::BusError>;
310
311    /// Performs a soft-reset of the device, restoring internal registers to power-on reset values.
312    async fn reset(&mut self) -> Result<(), Self::Error> {
313        self.write_register(self::registers::Configuration::default().with_reset(true))
314            .await?;
315        Ok(())
316    }
317}
318
319#[maybe_async_cfg::maybe(
320    idents(
321        hal(sync = "embedded_hal", async = "embedded_hal_async"),
322        RegisterInterface,
323        OneshotSensor
324    ),
325    sync(feature = "sync"),
326    async(feature = "async")
327)]
328impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
329    for INA226<D, I>
330{
331    type Error = MeasurementError<I::BusError>;
332    type Measurement = Measurement;
333
334    /// Performs a one-shot measurement. This will set the operating mode to
335    /// [`self::registers::OperatingMode::ShuntAndBusTriggered´] and enable all conversion outputs causing the
336    /// device to perform a single conversion a return to sleep afterwards.
337    async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
338        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
339
340        // Initiate measurement
341        self.write_register(reg_conf.with_operating_mode(self::registers::OperatingMode::ShuntAndBusTriggered))
342            .await?;
343
344        // Wait until measurement is ready, plus 10% and 1ms extra
345        let mut measurement_time_us = reg_conf.total_conversion_time_us();
346        measurement_time_us += measurement_time_us / 10; // 10% extra (data sheet conversion times state ~10% higher maximum times)
347        self.delay.delay_us(1000 + measurement_time_us).await;
348
349        // Wait for the conversion ready flag to be set
350        const TRIES: u8 = 5;
351        for _ in 0..TRIES {
352            let flags = self.read_register::<self::registers::MaskEnable>().await?;
353            if !flags.read_conversion_ready_flag() {
354                self.delay.delay_us(1000).await;
355                continue;
356            }
357
358            let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
359            let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
360            let current = self.read_register::<self::registers::Current>().await?;
361            // Reading this register clears the conversion_ready flag
362            let power = self.read_register::<self::registers::Power>().await?;
363
364            let measurement = Measurement {
365                shunt_voltage: shunt_voltage.read_value(),
366                bus_voltage: bus_voltage.read_value(),
367                current: current.read_current(self.current_lsb_na),
368                power: power.read_power(self.current_lsb_na),
369            };
370
371            if flags.read_math_overflow_flag() {
372                return Err(MeasurementError::Overflow(measurement));
373            } else {
374                return Ok(measurement);
375            }
376        }
377
378        Err(MeasurementError::Timeout)
379    }
380}
381
382#[maybe_async_cfg::maybe(
383    idents(
384        hal(sync = "embedded_hal", async = "embedded_hal_async"),
385        RegisterInterface,
386        ContinuousSensor
387    ),
388    sync(feature = "sync"),
389    async(feature = "async")
390)]
391impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
392    for INA226<D, I>
393{
394    type Error = ContinuousMeasurementError<I::BusError>;
395    type Measurement = Measurement;
396
397    /// Starts continuous measurement.
398    async fn start_measuring(&mut self) -> Result<(), Self::Error> {
399        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
400        self.write_register(reg_conf.with_operating_mode(self::registers::OperatingMode::ShuntAndBusContinuous))
401            .await?;
402        Ok(())
403    }
404
405    /// Stops continuous measurement.
406    async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
407        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
408        self.write_register(reg_conf.with_operating_mode(self::registers::OperatingMode::PowerDown))
409            .await?;
410        Ok(())
411    }
412
413    /// Expected amount of time between measurements in microseconds.
414    async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
415        let reg_conf = self.read_register::<self::registers::Configuration>().await?;
416        let mut measurement_time_us = reg_conf.total_conversion_time_us();
417        measurement_time_us += measurement_time_us / 10; // 10% extra (data sheet conversion times state ~10% higher maximum times)
418        Ok(measurement_time_us)
419    }
420
421    /// Returns the most recent measurement. Will never return None.
422    async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
423        let flags = self.read_register::<self::registers::MaskEnable>().await?;
424        let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
425        let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
426        let current = self.read_register::<self::registers::Current>().await?;
427        // Reading this register clears the conversion_ready flag
428        let power = self.read_register::<self::registers::Power>().await?;
429
430        let measurement = Measurement {
431            shunt_voltage: shunt_voltage.read_value(),
432            bus_voltage: bus_voltage.read_value(),
433            current: current.read_current(self.current_lsb_na),
434            power: power.read_power(self.current_lsb_na),
435        };
436
437        if flags.read_math_overflow_flag() {
438            Err(ContinuousMeasurementError::Overflow(measurement))
439        } else {
440            Ok(Some(measurement))
441        }
442    }
443
444    /// Check if new measurements are available.
445    async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
446        let flags = self.read_register::<self::registers::MaskEnable>().await?;
447        Ok(flags.read_conversion_ready_flag())
448    }
449
450    /// Wait indefinitely until new measurements are available and return them. Checks whether data
451    /// is ready in intervals of 100us.
452    async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
453        loop {
454            let flags = self.read_register::<self::registers::MaskEnable>().await?;
455            if flags.read_conversion_ready_flag() {
456                let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
457                let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
458                let current = self.read_register::<self::registers::Current>().await?;
459                // Reading this register clears the conversion_ready flag
460                let power = self.read_register::<self::registers::Power>().await?;
461
462                let measurement = Measurement {
463                    shunt_voltage: shunt_voltage.read_value(),
464                    bus_voltage: bus_voltage.read_value(),
465                    current: current.read_current(self.current_lsb_na),
466                    power: power.read_power(self.current_lsb_na),
467                };
468
469                if flags.read_math_overflow_flag() {
470                    return Err(ContinuousMeasurementError::Overflow(measurement));
471                } else {
472                    return Ok(measurement);
473                }
474            }
475
476            self.delay.delay_us(100).await;
477        }
478    }
479}