pac194x/
lib.rs

1//! A platform-agnostic driver crate for the Microchip PAC194X single/multi channel power monitor using the embedded-hal traits.
2//!
3//! # Usage
4//!
5//! ```rust
6//! use linux_embedded_hal::I2cdev;
7//! use pac194x::{PAC194X, AddrSelect};
8//!
9//! const SENSE_RESISTOR: f32 = 0.5;
10//!
11//! fn main() {
12//!     let i2c = I2cdev::new("/dev/i2c-1").unwrap();
13//!     let mut sensor = PAC194X::new(i2c, AddrSelect::GND).unwrap();
14//!     loop {
15//!         let bus_voltage_1 = sensor.read_bus_voltage_n(1).unwrap();
16//!         let sense_voltage_1 = sensor.read_sense_voltage_n(1).unwrap();
17//!         println!("Channel 1 has a bus voltage of: {:.2} V", bus_voltage_1);
18//!         println!("Channel 1 is pulling a current of: {:.2} A", sense_voltage_1 / SENSE_RESISTOR);
19//!     }
20//! }
21//! ```
22//!
23//! # Reference
24//!
25//!- [Datasheet](https://ww1.microchip.com/downloads/en/DeviceDoc/PAC194X-Data-Sheet-20006543.pdf)
26
27#![cfg_attr(not(test), no_std)]
28
29pub mod regs;
30
31use embedded_hal::blocking::i2c;
32use packed_struct::prelude::*;
33use paste::paste;
34use regs::*;
35
36#[repr(u8)]
37#[derive(Debug, Copy, Clone)]
38/// Address select resistor value.
39///
40/// `GND` is a zero-ohm resistor where `ADDRSEL` is connected to ground.
41/// `VDD` is when `ADDRSEL` is connected directly to the power rail.
42pub enum AddrSelect {
43    GND = 0b10000,
44    _499 = 0b10001,
45    _806 = 0b10010,
46    _1270 = 0b10011,
47    _2050 = 0b10100,
48    _3240 = 0b10101,
49    _5230 = 0b10110,
50    _8450 = 0b10111,
51    _13300 = 0b11000,
52    _21500 = 0b11001,
53    _34000 = 0b11010,
54    _54900 = 0b11011,
55    _88700 = 0b11100,
56    _140000 = 0b11101,
57    _226000 = 0b11110,
58    VDD = 0b11111,
59}
60
61/// The Product ID of the connected part
62#[derive(Debug, Copy, Clone)]
63pub enum ProductId {
64    PAC1941_1,
65    PAC1942_1,
66    PAC1943_1,
67    PAC1944_1,
68    PAC1941_2,
69    PAC1942_2,
70}
71
72/// A PAC194X power monitor on the I2C bus `I`.
73pub struct PAC194X<I>
74where
75    I: i2c::Read + i2c::Write + i2c::WriteRead,
76{
77    i2c: I,
78    address: u8,
79}
80
81/// Driver errors.
82#[derive(Debug, PartialEq)]
83pub enum Error<E> {
84    /// I2C bus error
85    I2c(E),
86    /// Errors such as overflowing the stack.
87    Internal,
88}
89
90macro_rules! read_fn {
91    ($var:ident: $type:ty) => {
92        paste! {
93            #[doc = stringify!(Reads the $type register and deserializes into the appropriate struct)]
94            pub fn [<read_ $var>](&mut self) -> Result<$type, Error<E>> {
95                Ok($type::unpack(&self.block_read($type::addr())?).unwrap())
96            }
97        }
98    };
99}
100
101macro_rules! read_n_fn {
102    ($var:ident: $type:ty) => {
103        paste! {
104            #[doc = stringify!(Reads the $type register and deserializes into the appropriate struct)]
105            pub fn [<read_ $var>](&mut self, n: u8) -> Result<$type, Error<E>> {
106                assert!((1..=4).contains(&n),"Channel n must be between 1 and 4");
107                Ok($type::unpack(&self.block_read_n($type::addr(),n)?).unwrap())
108            }
109        }
110    };
111}
112
113macro_rules! write_fn {
114    ($var:ident: $type:ty) => {
115        paste! {
116            #[doc = stringify!(Writes out the $type register)]
117            pub fn [<write_ $var>](&mut self, $var: $type) -> Result<(), Error<E>> {
118                const PACKED_SIZE_WITH_ADDR: usize = core::mem::size_of::<<$type as PackedStruct>::ByteArray>() + 1;
119                let mut bytes = [0u8; PACKED_SIZE_WITH_ADDR];
120                bytes[0] = $type::addr() as u8;
121                $var.pack_to_slice(&mut bytes[1..]).unwrap();
122                self.block_write(&bytes)?;
123                Ok(())
124            }
125        }
126    };
127}
128
129macro_rules! write_n_fn {
130    ($var:ident: $type:ty) => {
131        paste! {
132            #[doc = stringify!(Writes out the $type register)]
133            pub fn [<write_ $var>](&mut self, $var: $type, n: u8) -> Result<(), Error<E>> {
134                assert!((1..=4).contains(&n),"Channel n must be between 1 and 4");
135                const PACKED_SIZE_WITH_ADDR: usize = core::mem::size_of::<<$type as PackedStruct>::ByteArray>() + 1;
136                let mut bytes = [0u8; PACKED_SIZE_WITH_ADDR];
137                bytes[0] = ($type::addr() as u8) + n - 1;
138                $var.pack_to_slice(&mut bytes[1..]).unwrap();
139                self.block_write(&bytes)?;
140                Ok(())
141            }
142        }
143    };
144}
145
146macro_rules! read_write {
147    ($var:ident: $type:ty) => {
148        write_fn!($var: $type);
149        read_fn!($var: $type);
150    };
151}
152
153macro_rules! read_write_n {
154    ($var:ident: $type:ty) => {
155        write_n_fn!($var: $type);
156        read_n_fn!($var: $type);
157    };
158}
159
160fn vbus_to_real(raw: u16, fsr: VBusFSR) -> f32 {
161    9.0 * match fsr {
162        VBusFSR::Unipolar => (raw as f32) / 65536.0,
163        VBusFSR::BipolarHV => (i16::from_ne_bytes(raw.to_le_bytes()) as f32) / 65536.0,
164        VBusFSR::BipolarLV => (i16::from_ne_bytes(raw.to_le_bytes()) as f32) / 32768.0,
165    }
166}
167
168fn vsense_to_real(raw: u16, fsr: VSenseFSR) -> f32 {
169    0.1 * match fsr {
170        VSenseFSR::Unipolar => (raw as f32) / 65536.0,
171        VSenseFSR::BipolarHV => (i16::from_ne_bytes(raw.to_le_bytes()) as f32) / 65536.0,
172        VSenseFSR::BipolarLV => (i16::from_ne_bytes(raw.to_le_bytes()) as f32) / 32768.0,
173    }
174}
175
176impl<E, I> PAC194X<I>
177where
178    I: i2c::Read<Error = E> + i2c::Write<Error = E> + i2c::WriteRead<Error = E>,
179{
180    /// Initializes the driver.
181    ///
182    /// This consumes the I2C bus `I`.
183    /// To use this driver with other I2C crates, check out [shared-bus](https://github.com/Rahix/shared-bus)
184    pub fn new(i2c: I, addr_sel: AddrSelect) -> Self {
185        Self {
186            i2c,
187            address: addr_sel as u8,
188        }
189    }
190
191    /// The send byte protocol is used to set the internal address register pointer to the correct address
192    /// location. No data is transferred.
193    fn send_byte(&mut self, addr: Address) -> Result<(), Error<E>> {
194        self.i2c
195            .write(self.address, &[addr as u8])
196            .map_err(Error::I2c)?;
197        Ok(())
198    }
199
200    /// The receive byte protocol is used to read data from a register where the internal register addr pointer is
201    /// known to be at the right location (e.g. set via `send_byte`)
202    fn receive_byte(&mut self) -> Result<u8, Error<E>> {
203        let mut buf = [0u8; 1];
204        self.i2c.read(self.address, &mut buf).map_err(Error::I2c)?;
205        Ok(buf[0])
206    }
207
208    /// Block write is used to write multiple data bytes from a register that contains more than one byte of data
209    /// of from a group of contiguous registers
210    fn block_write(&mut self, bytes: &[u8]) -> Result<(), Error<E>> {
211        self.i2c.write(self.address, bytes).map_err(Error::I2c)?;
212        Ok(())
213    }
214
215    /// Block read is used to read multiple data bytes from a register that contains more than one byte of data or from a group
216    /// of contiguous registers
217    fn block_read<const N: usize>(&mut self, addr: Address) -> Result<[u8; N], Error<E>> {
218        let mut buf = [0u8; N];
219        self.i2c
220            .write_read(self.address, &[addr as u8], &mut buf)
221            .map_err(Error::I2c)?;
222        Ok(buf)
223    }
224
225    /// Same behavior as `block_read` but adds the channel offset to the address
226    fn block_read_n<const N: usize>(&mut self, addr: Address, n: u8) -> Result<[u8; N], Error<E>> {
227        let mut buf = [0u8; N];
228        self.i2c
229            .write_read(self.address, &[(addr as u8) + (n - 1)], &mut buf)
230            .map_err(Error::I2c)?;
231        Ok(buf)
232    }
233
234    /// Refreshes the device
235    ///
236    /// The accumulator data, accumulator count, Vbus and Vsense measurements are all refreshed and
237    /// the accumulators are reset. The host must wait 1ms before reading accumulator or Vbus/Vsense data
238    pub fn refresh(&mut self) -> Result<(), Error<E>> {
239        self.send_byte(Address::Refresh)
240    }
241
242    /// Refreshes the device without resetting the accumulators
243    ///
244    /// Same behavior as `refresh`, but without resetting the accumulators.
245    pub fn refresh_v(&mut self) -> Result<(), Error<E>> {
246        self.send_byte(Address::RefreshV)
247    }
248
249    /// Refreshes every PAC194X device on the bus by transmitting REFRESH_G to the
250    /// general call address of 0
251    pub fn regresh_g(&mut self) -> Result<(), Error<E>> {
252        self.i2c
253            .write(0u8, &[Address::RefreshG as u8])
254            .map_err(Error::I2c)?;
255        Ok(())
256    }
257
258    /// Retrieves the Product ID of the connected component
259    pub fn product_id(&mut self) -> Result<ProductId, Error<E>> {
260        self.send_byte(regs::Address::ProductId)?;
261        Ok(match self.receive_byte()? {
262            0b0110_1000 => ProductId::PAC1941_1,
263            0b0110_1001 => ProductId::PAC1942_1,
264            0b0110_1010 => ProductId::PAC1943_1,
265            0b0110_1011 => ProductId::PAC1944_1,
266            0b0110_1100 => ProductId::PAC1941_2,
267            0b0110_1101 => ProductId::PAC1942_2,
268            _ => unreachable!(),
269        })
270    }
271
272    /// The Manufacturer ID register identifies Microchip as the manufacturer of the PAC194X.
273    /// This should return 0x54
274    pub fn manufacturer_id(&mut self) -> Result<u8, Error<E>> {
275        self.send_byte(regs::Address::ManufacturerId)?;
276        self.receive_byte()
277    }
278
279    /// The Revision register identifies the die revision.
280    /// This should return 0b00000010
281    pub fn revision_id(&mut self) -> Result<u8, Error<E>> {
282        self.send_byte(regs::Address::RevisionId)?;
283        self.receive_byte()
284    }
285
286    /// High level API for retrieving the bus voltage of channel `n`
287    pub fn read_bus_voltage_n(&mut self, n: u8) -> Result<f32, Error<E>> {
288        assert!((1..=4).contains(&n), "Channel n must be between 1 and 4");
289        let fsr_reg = self.read_neg_pwr_fsr_lat()?;
290        let fsr = match n {
291            1 => fsr_reg.cfg_vb1,
292            2 => fsr_reg.cfg_vb2,
293            3 => fsr_reg.cfg_vb3,
294            4 => fsr_reg.cfg_vb4,
295            _ => unreachable!(),
296        };
297        Ok(vbus_to_real(self.read_vbusn(n)?.voltage, fsr))
298    }
299
300    /// High level API for retrieving the sense voltage of channel `n`
301    /// Use Ohm's law with your sense resistor value (V/R) to get the sense current
302    pub fn read_sense_voltage_n(&mut self, n: u8) -> Result<f32, Error<E>> {
303        assert!((1..=4).contains(&n), "Channel n must be between 1 and 4");
304        let fsr_reg = self.read_neg_pwr_fsr_lat()?;
305        let fsr = match n {
306            1 => fsr_reg.cfg_vs1,
307            2 => fsr_reg.cfg_vs2,
308            3 => fsr_reg.cfg_vs3,
309            4 => fsr_reg.cfg_vs4,
310            _ => unreachable!(),
311        };
312        Ok(vsense_to_real(self.read_vsensen(n)?.voltage, fsr))
313    }
314
315    /// Same as [read_bus_voltage_n()], but using the accumulator-based rolling average
316    pub fn read_avg_bus_voltage_n(&mut self, n: u8) -> Result<f32, Error<E>> {
317        assert!((1..=4).contains(&n), "Channel n must be between 1 and 4");
318        let fsr_reg = self.read_neg_pwr_fsr_lat()?;
319        let fsr = match n {
320            1 => fsr_reg.cfg_vb1,
321            2 => fsr_reg.cfg_vb2,
322            3 => fsr_reg.cfg_vb3,
323            4 => fsr_reg.cfg_vb4,
324            _ => unreachable!(),
325        };
326        Ok(vbus_to_real(self.read_vbusn_avg(n)?.voltage, fsr))
327    }
328
329    /// Same as [read_sense_voltage_n()], but using the accumulator-based rolling average
330    pub fn read_avg_sense_voltage_n(&mut self, n: u8) -> Result<f32, Error<E>> {
331        assert!((1..=4).contains(&n), "Channel n must be between 1 and 4");
332        let fsr_reg = self.read_neg_pwr_fsr_lat()?;
333        let fsr = match n {
334            1 => fsr_reg.cfg_vs1,
335            2 => fsr_reg.cfg_vs2,
336            3 => fsr_reg.cfg_vs3,
337            4 => fsr_reg.cfg_vs4,
338            _ => unreachable!(),
339        };
340        Ok(vsense_to_real(self.read_vsensen(n)?.voltage, fsr))
341    }
342
343    // Auto generated functions for reading and writing all of our registers
344    read_write!(ctrl: Ctrl);
345    read_write!(acc_count: AccCount);
346    read_n_fn!(vaccn: Vaccn);
347    read_n_fn!(vbusn: Vbusn);
348    read_n_fn!(vsensen: Vsensen);
349    read_n_fn!(vbusn_avg: VbusnAvg);
350    read_n_fn!(vsensen_avg: VsensenAvg);
351    read_n_fn!(vpowern: Vpowern);
352    read_write!(smub_settings: SmbusSettings);
353    read_write!(neg_pwr_fsr: NegPwrFsr);
354    read_fn!(slow: Slow);
355    read_fn!(ctrl_act: CtrlAct);
356    read_write!(neg_pwr_fsr_act: NegPwrFsrAct);
357    read_fn!(ctrl_lat: CtrlLat);
358    read_write!(neg_pwr_fsr_lat: NegPwrFsrLat);
359    read_write!(accum_config: AccumConfig);
360    read_fn!(alert_statuc: AlertStatus);
361    read_write!(slow_alert1: SlowAlert1);
362    read_write!(gpio_alert2: GpioAlert2);
363    read_write!(acc_fullness_limits: AccFullnessLimits);
364    read_write_n!(oc_limitn: OcLimitn);
365    read_write_n!(uc_limitn: UcLimitn);
366    read_write_n!(op_limitn: OpLimitn);
367    read_write_n!(ov_limitn: OvLimitn);
368    read_write_n!(uv_limitn: UvLimitn);
369    read_write!(oc_limit_n_samples: OcLimitNSamples);
370    read_write!(uc_limit_n_samples: UcLimitNSamples);
371    read_write!(op_limit_n_samples: OpLimitNSamples);
372    read_write!(ov_limit_n_samples: OvLimitNSamples);
373    read_write!(uv_limit_n_samples: UvLimitNSamples);
374    read_write!(alert_enable: AlertEnable);
375    read_write!(accum_config_act: AccumConfigAct);
376    read_write!(accum_config_lat: AccumConfigLat);
377}
378
379#[cfg(test)]
380mod tests {
381    use super::*;
382    #[test]
383    fn addr_values() {
384        assert_eq!(Address::AlertEnable as u8, 0x49);
385        assert_eq!(Address::RevisionId as u8, 0xFF);
386    }
387}