1#![warn(unsafe_code, missing_docs)]
31#![no_std]
32
33use core::result::Result;
34
35use bitflags::bitflags;
36use byteorder::{BigEndian, ByteOrder};
37use embedded_hal::{
38 blocking::spi::{Transfer, Write},
39 digital::v2::OutputPin,
40 spi::{Mode, MODE_1},
41};
42
43bitflags! {
44 #[repr(C)]
46 pub struct Configuration: u16 {
47 const RST = 0b1000_0000_0000_0000;
54
55 const RSTACC = 0b0100_0000_0000_0000;
60
61 const CONVDLY = 0b0011_1111_1100_0000;
67
68 const TEMPCOMP = 0b0000_0000_0010_0000;
73
74 const ADCRANGE = 0b0000_0000_0001_0000;
79
80 const RESERVED = 0b0000_0000_0000_1111;
82 }
83}
84
85pub const MODE: Mode = MODE_1;
87
88#[repr(u8)]
89enum Register {
90 Configuration = 0x00,
91 ShuntCalibration = 0x02,
92 ShuntVoltage = 0x04,
93 BusVoltage = 0x05,
94 DieTemperature = 0x06,
95 Current = 0x07,
96 Power = 0x08,
97 ManufacturerID = 0x3E,
98 DeviceID = 0x3F,
99}
100
101enum Command {
102 Read,
103 Write,
104}
105
106#[derive(Debug)]
108pub enum Error<SPIError, CSError> {
109 NotConfigured,
111
112 SPIError(SPIError),
114
115 ChipSelectError(CSError),
117}
118
119const BUS_VOLTAGE_UV_PER_LSB: f64 = 195.3125;
121const SHUNT_VOLTAGE_NV_PER_LSB_MODE_0: f64 = 312.5;
122const SHUNT_VOLTAGE_NV_PER_LSB_MODE_1: f64 = 78.125;
123const TEMPERATURE_MC_PER_LSB: f64 = 7.8125;
124const POWER_SCALING_FACTOR: f64 = 3.2;
125
126const DENOMINATOR: f64 = (1 << 19) as f64; const INTERNAL_SCALING: f64 = 13107200000.0; #[inline(always)]
131fn calculate_calibration_value(
132 configuration: Configuration,
133 shunt_resistance: f64,
134 current_expected_max: f64,
135) -> (f64, u16) {
136 let scale = if configuration.contains(Configuration::ADCRANGE) {
137 4.0
138 } else {
139 1.0
140 };
141 let current_lsb = calculate_current_lsb(current_expected_max);
142 let shunt_cal = INTERNAL_SCALING * current_lsb * shunt_resistance * scale;
143 (current_lsb, shunt_cal as u16)
144}
145
146#[inline(always)]
147fn calculate_current_lsb(current_expected_max: f64) -> f64 {
148 current_expected_max / DENOMINATOR
149}
150
151pub struct INA229<SPI, NCS> {
153 spi: SPI,
154 ncs: NCS,
155 config: Option<Configuration>,
156 current_lsb: Option<f64>,
157}
158
159impl<SPI, NCS, SPIError, CSError> INA229<SPI, NCS>
160where
161 SPI: Transfer<u8, Error = SPIError> + Write<u8, Error = SPIError>,
162 NCS: OutputPin<Error = CSError>,
163{
164 pub fn new(spi: SPI, ncs: NCS) -> Self {
166 INA229 {
167 spi,
168 ncs,
169 config: None,
170 current_lsb: None,
171 }
172 }
173
174 pub fn release(self) -> (SPI, NCS) {
176 (self.spi, self.ncs)
177 }
178
179 fn read_register_u16(&mut self, register: Register) -> Result<u16, Error<SPIError, CSError>> {
180 let mut buffer = [get_frame(register, Command::Read), 0x00, 0x00];
181 self.ncs.set_low().map_err(Error::ChipSelectError)?;
182 self.spi.transfer(&mut buffer).map_err(Error::SPIError)?;
183 self.ncs.set_high().map_err(Error::ChipSelectError)?;
184 let value = BigEndian::read_u16(&buffer[1..3]);
185 Ok(value)
186 }
187
188 fn write_register_u16(
189 &mut self,
190 register: Register,
191 value: u16,
192 ) -> Result<(), Error<SPIError, CSError>> {
193 let mut buffer = [get_frame(register, Command::Write), 0x00, 0x00];
194 BigEndian::write_u16_into(&[value], &mut buffer[1..3]);
195 self.ncs.set_low().map_err(Error::ChipSelectError)?;
196 self.spi.write(&buffer).map_err(Error::SPIError)?;
197 self.ncs.set_high().map_err(Error::ChipSelectError)?;
198 Ok(())
199 }
200
201 fn read_register_i16(&mut self, register: Register) -> Result<i16, Error<SPIError, CSError>> {
202 let mut buffer = [get_frame(register, Command::Read), 0x00, 0x00];
203 self.ncs.set_low().map_err(Error::ChipSelectError)?;
204 self.spi.transfer(&mut buffer).map_err(Error::SPIError)?;
205 self.ncs.set_high().map_err(Error::ChipSelectError)?;
206 let value = BigEndian::read_i16(&buffer[1..3]);
207 Ok(value)
208 }
209
210 fn read_register_u24(&mut self, register: Register) -> Result<u32, Error<SPIError, CSError>> {
211 let mut buffer = [get_frame(register, Command::Read), 0x00, 0x00, 0x00];
212 self.ncs.set_low().map_err(Error::ChipSelectError)?;
213 self.spi.transfer(&mut buffer).map_err(Error::SPIError)?;
214 self.ncs.set_high().map_err(Error::ChipSelectError)?;
215 let value = BigEndian::read_u24(&buffer[1..4]);
216 Ok(value)
217 }
218
219 fn read_register_i24(&mut self, register: Register) -> Result<i32, Error<SPIError, CSError>> {
220 let mut buffer = [get_frame(register, Command::Read), 0x00, 0x00, 0x00];
221 self.ncs.set_low().map_err(Error::ChipSelectError)?;
222 self.spi.transfer(&mut buffer).map_err(Error::SPIError)?;
223 self.ncs.set_high().map_err(Error::ChipSelectError)?;
224 let value = BigEndian::read_i24(&buffer[1..4]);
225 Ok(value)
226 }
227
228 pub fn set_configuration(
230 &mut self,
231 configuration: Configuration,
232 ) -> Result<(), Error<SPIError, CSError>> {
233 self.write_register_u16(Register::Configuration, configuration.bits())?;
234 self.config = Some(configuration);
235 Ok(())
236 }
237
238 pub fn configuration(&mut self) -> Result<Configuration, Error<SPIError, CSError>> {
240 self.read_register_u16(Register::Configuration)
241 .map(Configuration::from_bits_truncate)
242 .map(|config| {
243 self.config = Some(config);
244 config
245 })
246 }
247
248 pub fn shunt_calibration(&mut self) -> Result<u16, Error<SPIError, CSError>> {
250 self.read_register_u16(Register::ShuntCalibration)
251 }
252
253 pub fn set_shunt_calibration(&mut self, value: u16) -> Result<(), Error<SPIError, CSError>> {
255 self.write_register_u16(Register::ShuntCalibration, value)
256 }
257
258 pub fn calibrate(
260 &mut self,
261 shunt_resistance: f64,
262 current_expected_max: f64,
263 ) -> Result<(), Error<SPIError, CSError>> {
264 if let Some(config) = self.config {
265 let (current_lsb, value) =
266 calculate_calibration_value(config, shunt_resistance, current_expected_max);
267 self.set_shunt_calibration(value)?;
268 self.current_lsb = Some(current_lsb);
269 Ok(())
270 } else {
271 Err(Error::NotConfigured)
272 }
273 }
274
275 pub fn configure_and_calibrate(
277 &mut self,
278 configuration: Configuration,
279 shunt_resistance: f64,
280 current_expected_max: f64,
281 ) -> Result<(), Error<SPIError, CSError>> {
282 self.set_configuration(configuration)
283 .and_then(|_| self.calibrate(shunt_resistance, current_expected_max))
284 }
285
286 pub fn bus_voltage_raw(&mut self) -> Result<i32, Error<SPIError, CSError>> {
288 self.read_register_i24(Register::BusVoltage).map(|x| x >> 4) }
290
291 pub fn bus_voltage_microvolts(&mut self) -> Result<f64, Error<SPIError, CSError>> {
293 self.bus_voltage_raw()
294 .map(|x| (x as f64) * BUS_VOLTAGE_UV_PER_LSB)
295 }
296
297 pub fn shunt_voltage_raw(&mut self) -> Result<i32, Error<SPIError, CSError>> {
299 self.read_register_i24(Register::ShuntVoltage)
300 .map(|x| x >> 4)
301 }
302
303 pub fn shunt_voltage_nanovolts(&mut self) -> Result<f64, Error<SPIError, CSError>> {
305 if let Some(config) = self.config {
306 self.shunt_voltage_raw().map(|value| {
307 if config.contains(Configuration::ADCRANGE) {
308 (value as f64) * SHUNT_VOLTAGE_NV_PER_LSB_MODE_1
309 } else {
310 (value as f64) * SHUNT_VOLTAGE_NV_PER_LSB_MODE_0
311 }
312 })
313 } else {
314 Err(Error::NotConfigured)
315 }
316 }
317
318 pub fn temperature_raw(&mut self) -> Result<i16, Error<SPIError, CSError>> {
320 self.read_register_i16(Register::DieTemperature)
321 }
322
323 pub fn temperature_millidegrees_celsius(&mut self) -> Result<f64, Error<SPIError, CSError>> {
325 self.temperature_raw()
326 .map(|x| (x as f64) * TEMPERATURE_MC_PER_LSB)
327 }
328
329 pub fn current_raw(&mut self) -> Result<i32, Error<SPIError, CSError>> {
331 self.read_register_i24(Register::Current).map(|x| x >> 4) }
333
334 pub fn current_amps(&mut self) -> Result<f64, Error<SPIError, CSError>> {
336 if let Some(current_lsb) = self.current_lsb {
337 self.current_raw().map(|x| (x as f64) * current_lsb)
338 } else {
339 Err(Error::NotConfigured)
340 }
341 }
342
343 pub fn power_raw(&mut self) -> Result<u32, Error<SPIError, CSError>> {
345 self.read_register_u24(Register::Power)
346 }
347
348 pub fn power_watts(&mut self) -> Result<f64, Error<SPIError, CSError>> {
350 if let Some(current_lsb) = self.current_lsb {
351 self.power_raw()
352 .map(|x| (x as f64) * current_lsb * POWER_SCALING_FACTOR)
353 } else {
354 Err(Error::NotConfigured)
355 }
356 }
357
358 pub fn manufacturer_id(&mut self) -> Result<u16, Error<SPIError, CSError>> {
360 self.read_register_u16(Register::ManufacturerID)
361 }
362
363 pub fn device_id(&mut self) -> Result<u16, Error<SPIError, CSError>> {
365 self.read_register_u16(Register::DeviceID)
366 }
367}
368
369fn get_frame(register: Register, command: Command) -> u8 {
370 let frame = (register as u8) << 2u8;
371 match command {
372 Command::Write => frame & !0b00000001,
373 Command::Read => frame | 0b00000001,
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::{
380 calculate_calibration_value, calculate_current_lsb, get_frame, Command, Configuration,
381 Register,
382 };
383 use approx::assert_relative_eq;
384
385 #[test]
386 fn get_frame_manufacturer_read() {
387 let result = get_frame(Register::ManufacturerID, Command::Read);
388 assert_eq!(result, 0b1111_1001);
389 }
390
391 #[test]
392 fn get_frame_device_read() {
393 let result = get_frame(Register::DeviceID, Command::Read);
394 assert_eq!(result, 0b1111_1101);
395 }
396
397 #[test]
398 fn calculate_current_lsb_works() {
399 let lsb = calculate_current_lsb(10.0); assert_relative_eq!(lsb, 0.0000190735, max_relative = 0.00001);
401 }
402
403 #[test]
404 fn calculate_calibration_value_works() {
405 let (_, value) =
406 calculate_calibration_value(Configuration::from_bits_truncate(0), 0.0162, 10.0);
407 assert_eq!(value, 4050);
408 }
409}