embedded_devices/devices/texas_instruments/ina228/
mod.rs1use self::address::Address;
100
101use embedded_devices_derive::{forward_register_fns, sensor};
102use embedded_interfaces::TransportError;
103use registers::AdcRange;
104use uom::si::electric_current::ampere;
105use uom::si::electric_potential::volt;
106use uom::si::electrical_resistance::ohm;
107use uom::si::f64::ThermodynamicTemperature;
108use uom::si::f64::{ElectricCharge, ElectricCurrent, ElectricPotential, ElectricalResistance, Energy, Power};
109
110pub mod address;
111pub mod registers;
112
113#[cfg_attr(feature = "defmt", derive(defmt::Format))]
114#[derive(Debug, thiserror::Error)]
115pub enum InitError<BusError> {
116 #[error("transport error")]
118 Transport(#[from] TransportError<(), BusError>),
119 #[error("invalid device id {0:#04x}")]
121 InvalidDeviceId(u16),
122 #[error("invalid manufacturer id {0:#04x}")]
124 InvalidManufacturerId(u16),
125}
126
127#[derive(Debug, thiserror::Error)]
128pub enum MeasurementError<BusError> {
129 #[error("transport error")]
131 Transport(#[from] TransportError<(), BusError>),
132 #[error("conversion timeout")]
134 Timeout,
135 #[error("overflow in measurement")]
138 Overflow(Measurement),
139}
140
141#[derive(Debug, thiserror::Error)]
142pub enum ContinuousMeasurementError<BusError> {
143 #[error("transport error")]
145 Transport(#[from] TransportError<(), BusError>),
146 #[error("overflow in measurement")]
149 Overflow(Measurement),
150}
151
152#[derive(Debug, embedded_devices_derive::Measurement)]
154pub struct Measurement {
155 pub shunt_voltage: ElectricPotential,
157 #[measurement(Voltage)]
159 pub bus_voltage: ElectricPotential,
160 #[measurement(Temperature)]
162 pub temperature: ThermodynamicTemperature,
163 #[measurement(Current)]
165 pub current: ElectricCurrent,
166 #[measurement(Power)]
168 pub power: Power,
169 #[measurement(Energy)]
171 pub energy: Energy,
172 #[measurement(Charge)]
174 pub charge: ElectricCharge,
175}
176
177#[maybe_async_cfg::maybe(
184 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
185 sync(feature = "sync"),
186 async(feature = "async")
187)]
188pub struct INA228<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> {
189 delay: D,
191 interface: I,
193 shunt_resistance: ElectricalResistance,
195 max_expected_current: ElectricCurrent,
197 pub current_lsb_na: i64,
199 pub adc_range: self::registers::AdcRange,
201}
202
203pub trait INA228Register {}
204
205#[maybe_async_cfg::maybe(
206 idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cDevice),
207 sync(feature = "sync"),
208 async(feature = "async")
209)]
210impl<D, I> INA228<D, embedded_interfaces::i2c::I2cDevice<I, hal::i2c::SevenBitAddress>>
211where
212 I: hal::i2c::I2c<hal::i2c::SevenBitAddress> + hal::i2c::ErrorType,
213 D: hal::delay::DelayNs,
214{
215 #[inline]
221 pub fn new_i2c(delay: D, interface: I, address: Address) -> Self {
222 Self {
223 delay,
224 interface: embedded_interfaces::i2c::I2cDevice::new(interface, address.into()),
225 shunt_resistance: Default::default(),
226 max_expected_current: Default::default(),
227 current_lsb_na: 1,
228 adc_range: AdcRange::Div4,
229 }
230 }
231}
232
233#[forward_register_fns]
234#[sensor(Voltage, Temperature, Current, Power, Energy, Charge)]
235#[maybe_async_cfg::maybe(
236 idents(
237 hal(sync = "embedded_hal", async = "embedded_hal_async"),
238 RegisterInterface,
239 ResettableDevice
240 ),
241 sync(feature = "sync"),
242 async(feature = "async")
243)]
244impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> INA228<D, I> {
245 pub async fn init(
250 &mut self,
251 shunt_resistance: ElectricalResistance,
252 max_expected_current: ElectricCurrent,
253 ) -> Result<(), InitError<I::BusError>> {
254 use crate::device::ResettableDevice;
255 use registers::{DeviceId, ManufacturerId};
256
257 self.reset().await?;
260 self.delay.delay_us(500).await;
261
262 let manufacturer_id = self.read_register::<ManufacturerId>().await?.read_id();
264 if manufacturer_id != ManufacturerId::default().read_id() {
265 return Err(InitError::InvalidManufacturerId(manufacturer_id));
266 }
267 let device_id = self.read_register::<DeviceId>().await?.read_id();
268 if device_id != DeviceId::default().read_id() {
269 return Err(InitError::InvalidDeviceId(device_id));
270 }
271
272 self.calibrate(shunt_resistance, max_expected_current).await?;
274 Ok(())
275 }
276
277 pub async fn calibrate(
281 &mut self,
282 shunt_resistance: ElectricalResistance,
283 max_expected_current: ElectricCurrent,
284 ) -> Result<(), TransportError<(), I::BusError>> {
285 self.shunt_resistance = shunt_resistance;
286 self.max_expected_current = max_expected_current;
287 let max_expected_shunt_voltage = shunt_resistance * max_expected_current;
288
289 let shunt_resistance = shunt_resistance.get::<ohm>();
290 let max_expected_current = max_expected_current.get::<ampere>();
291 let max_expected_shunt_voltage = max_expected_shunt_voltage.get::<volt>();
292
293 self.current_lsb_na = ((1_000_000_000f64 / (1 << 19) as f64) * max_expected_current) as i64;
294 let shunt_resistance_mohm = (1_000.0 * shunt_resistance) as i64;
295
296 let div4_threshold = 0.036; self.adc_range = if max_expected_shunt_voltage > div4_threshold {
299 self::registers::AdcRange::Div4
300 } else {
301 self::registers::AdcRange::Div1
302 };
303
304 let reg_conf = self.read_register::<self::registers::Configuration>().await?;
306 self.write_register(reg_conf.with_adc_range(self.adc_range)).await?;
307
308 let cal = 524_288 * self.current_lsb_na * shunt_resistance_mohm / (10_000_000 * self.adc_range.factor() as i64);
311 self.write_register(self::registers::ShuntCalibration::default().with_raw_value(cal as u16))
312 .await?;
313
314 Ok(())
315 }
316}
317
318#[maybe_async_cfg::maybe(
319 idents(
320 hal(sync = "embedded_hal", async = "embedded_hal_async"),
321 RegisterInterface,
322 ResettableDevice
323 ),
324 sync(feature = "sync"),
325 async(feature = "async")
326)]
327impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::device::ResettableDevice
328 for INA228<D, I>
329{
330 type Error = TransportError<(), I::BusError>;
331
332 async fn reset(&mut self) -> Result<(), Self::Error> {
334 self.write_register(self::registers::Configuration::default().with_reset(true))
335 .await?;
336 Ok(())
337 }
338}
339
340#[maybe_async_cfg::maybe(
341 idents(
342 hal(sync = "embedded_hal", async = "embedded_hal_async"),
343 RegisterInterface,
344 OneshotSensor
345 ),
346 sync(feature = "sync"),
347 async(feature = "async")
348)]
349impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
350 for INA228<D, I>
351{
352 type Error = MeasurementError<I::BusError>;
353 type Measurement = Measurement;
354
355 async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
359 let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
360
361 self.write_register(
363 reg_adc_conf
364 .with_operating_mode(self::registers::OperatingMode::Triggered)
365 .with_enable_temperature(true)
366 .with_enable_shunt(true)
367 .with_enable_bus(true),
368 )
369 .await?;
370
371 let measurement_time_us = reg_adc_conf.read_bus_conversion_time().us()
373 + reg_adc_conf.read_shunt_conversion_time().us()
374 + reg_adc_conf.read_temperature_conversion_time().us();
375 self.delay
376 .delay_us(100 + measurement_time_us * reg_adc_conf.read_average_count().factor() as u32)
377 .await;
378
379 const TRIES: u8 = 5;
381 for _ in 0..TRIES {
382 let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
383 if diag.read_conversion_ready() {
384 let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
385 let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
386 let temperature = self.read_register::<self::registers::Temperature>().await?;
387 let current = self.read_register::<self::registers::Current>().await?;
388 let energy = self.read_register::<self::registers::Energy>().await?;
389 let charge = self.read_register::<self::registers::Charge>().await?;
390 let power = self.read_register::<self::registers::Power>().await?;
392
393 let measurement = Measurement {
394 shunt_voltage: shunt_voltage.read_voltage(self.adc_range),
395 bus_voltage: bus_voltage.read_value(),
396 temperature: temperature.read_value(),
397 current: current.read_current(self.current_lsb_na),
398 power: power.read_power(self.current_lsb_na),
399 energy: energy.read_energy(self.current_lsb_na),
400 charge: charge.read_charge(self.current_lsb_na),
401 };
402
403 if diag.read_math_overflow() {
404 return Err(MeasurementError::Overflow(measurement));
405 } else {
406 return Ok(measurement);
407 }
408 }
409
410 self.delay.delay_us(100).await;
411 }
412
413 Err(MeasurementError::Timeout)
414 }
415}
416
417#[maybe_async_cfg::maybe(
418 idents(
419 hal(sync = "embedded_hal", async = "embedded_hal_async"),
420 RegisterInterface,
421 ContinuousSensor
422 ),
423 sync(feature = "sync"),
424 async(feature = "async")
425)]
426impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
427 for INA228<D, I>
428{
429 type Error = ContinuousMeasurementError<I::BusError>;
430 type Measurement = Measurement;
431
432 async fn start_measuring(&mut self) -> Result<(), Self::Error> {
434 let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
435 self.write_register(
436 reg_adc_conf
437 .with_operating_mode(self::registers::OperatingMode::Continuous)
438 .with_enable_temperature(true)
439 .with_enable_shunt(true)
440 .with_enable_bus(true),
441 )
442 .await?;
443 Ok(())
444 }
445
446 async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
448 let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
449 self.write_register(
450 reg_adc_conf
451 .with_operating_mode(self::registers::OperatingMode::Continuous)
452 .with_enable_temperature(false)
453 .with_enable_shunt(false)
454 .with_enable_bus(false),
455 )
456 .await?;
457 Ok(())
458 }
459
460 async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
462 let reg_adc_conf = self.read_register::<self::registers::AdcConfiguration>().await?;
463 let measurement_time_us = reg_adc_conf.read_bus_conversion_time().us()
464 + reg_adc_conf.read_shunt_conversion_time().us()
465 + reg_adc_conf.read_temperature_conversion_time().us();
466 Ok(measurement_time_us)
467 }
468
469 async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
471 let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
472 let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
473 let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
474 let temperature = self.read_register::<self::registers::Temperature>().await?;
475 let current = self.read_register::<self::registers::Current>().await?;
476 let energy = self.read_register::<self::registers::Energy>().await?;
477 let charge = self.read_register::<self::registers::Charge>().await?;
478 let power = self.read_register::<self::registers::Power>().await?;
480
481 let measurement = Measurement {
482 shunt_voltage: shunt_voltage.read_voltage(self.adc_range),
483 bus_voltage: bus_voltage.read_value(),
484 temperature: temperature.read_value(),
485 current: current.read_current(self.current_lsb_na),
486 power: power.read_power(self.current_lsb_na),
487 energy: energy.read_energy(self.current_lsb_na),
488 charge: charge.read_charge(self.current_lsb_na),
489 };
490
491 if diag.read_math_overflow() {
492 Err(ContinuousMeasurementError::Overflow(measurement))
493 } else {
494 Ok(Some(measurement))
495 }
496 }
497
498 async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
500 let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
501 Ok(diag.read_conversion_ready())
502 }
503
504 async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
507 loop {
508 let diag = self.read_register::<self::registers::DiagnosticsAndAlert>().await?;
509 if diag.read_conversion_ready() {
510 let bus_voltage = self.read_register::<self::registers::BusVoltage>().await?;
511 let shunt_voltage = self.read_register::<self::registers::ShuntVoltage>().await?;
512 let temperature = self.read_register::<self::registers::Temperature>().await?;
513 let current = self.read_register::<self::registers::Current>().await?;
514 let energy = self.read_register::<self::registers::Energy>().await?;
515 let charge = self.read_register::<self::registers::Charge>().await?;
516 let power = self.read_register::<self::registers::Power>().await?;
518
519 let measurement = Measurement {
520 shunt_voltage: shunt_voltage.read_voltage(self.adc_range),
521 bus_voltage: bus_voltage.read_value(),
522 temperature: temperature.read_value(),
523 current: current.read_current(self.current_lsb_na),
524 power: power.read_power(self.current_lsb_na),
525 energy: energy.read_energy(self.current_lsb_na),
526 charge: charge.read_charge(self.current_lsb_na),
527 };
528
529 if diag.read_math_overflow() {
530 return Err(ContinuousMeasurementError::Overflow(measurement));
531 } else {
532 return Ok(measurement);
533 }
534 }
535
536 self.delay.delay_us(100).await;
537 }
538 }
539}