Skip to main content

async_ltc681x/device/
mod.rs

1use crate::device::error::DeviceErrorInfo;
2use crate::driver::Driver;
3use crate::driver::command;
4use crate::driver::command::{HasGpioChannelConfig, HasMDOption};
5use crate::driver::registers::{AuxRegisters, CellRegisters, auxreg, cells, cfga, cfgb};
6use error::Error;
7
8/// Error Definition
9pub mod error;
10#[cfg(test)]
11mod test;
12
13/// Trait defining all high-level functions of the `ltc681x` chip family
14pub trait Device<const NUM_OF_DEVICES: usize> {
15    type Error;
16    ///Reads content of all `CfgA` Registers
17    /// # Errors
18    /// Register reads are infallible from the `Device` point-of-view.
19    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
20    async fn read_cfga(&mut self) -> Result<[cfga::CfgA; NUM_OF_DEVICES], Self::Error>;
21    ///Reads content of all `CfgB` Registers
22    /// # Errors
23    /// Register reads are infallible from the `Device` point-of-view.
24    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
25    async fn read_cfgb(&mut self) -> Result<[cfgb::CfgB; NUM_OF_DEVICES], Self::Error>;
26
27    ///Reads content of all `CvA` Registers
28    /// # Errors
29    /// Register reads are infallible from the `Device` point-of-view.
30    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
31    async fn read_cva(&mut self) -> Result<[cells::CvA; NUM_OF_DEVICES], Self::Error>;
32    ///Reads content of all `CvB` Registers
33    /// # Errors
34    /// Register reads are infallible from the `Device` point-of-view.
35    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
36    async fn read_cvb(&mut self) -> Result<[cells::CvB; NUM_OF_DEVICES], Self::Error>;
37    ///Reads content of all `CvC` Registers
38    /// # Errors
39    /// Register reads are infallible from the `Device` point-of-view.
40    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
41    async fn read_cvc(&mut self) -> Result<[cells::CvC; NUM_OF_DEVICES], Self::Error>;
42    ///Reads content of all `CvD` Registers
43    /// # Errors
44    /// Register reads are infallible from the `Device` point-of-view.
45    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
46    async fn read_cvd(&mut self) -> Result<[cells::CvD; NUM_OF_DEVICES], Self::Error>;
47    #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
48    ///Reads content of all `CvE` Registers
49    /// # Errors
50    /// Register reads are infallible from the `Device` point-of-view.
51    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
52    async fn read_cve(&mut self) -> Result<[cells::CvE; NUM_OF_DEVICES], Self::Error>;
53    #[cfg(feature = "ltc6813")]
54    ///Reads content of all `CvF` Registers
55    /// # Errors
56    /// Register reads are infallible from the `Device` point-of-view.
57    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
58    async fn read_cvf(&mut self) -> Result<[cells::CvF; NUM_OF_DEVICES], Self::Error>;
59
60    ///Reads content of all `AuxA` Registers
61    /// # Errors
62    /// Register reads are infallible from the `Device` point-of-view.
63    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
64    async fn read_auxa(&mut self) -> Result<[auxreg::AuxA; NUM_OF_DEVICES], Self::Error>;
65    ///Reads content of all `AuxB` Registers
66    /// # Errors
67    /// Register reads are infallible from the `Device` point-of-view.
68    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
69    async fn read_auxb(&mut self) -> Result<[auxreg::AuxB; NUM_OF_DEVICES], Self::Error>;
70    #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
71    ///Reads content of all `AuxC` Registers
72    /// # Errors
73    /// Register reads are infallible from the `Device` point-of-view.
74    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
75    async fn read_auxc(&mut self) -> Result<[auxreg::AuxC; NUM_OF_DEVICES], Self::Error>;
76    #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
77    ///Reads content of all `AuxD` Registers
78    /// # Errors
79    /// Register reads are infallible from the `Device` point-of-view.
80    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
81    async fn read_auxd(&mut self) -> Result<[auxreg::AuxD; NUM_OF_DEVICES], Self::Error>;
82
83    /// Writes content to `CfgA` Register
84    ///
85    /// Pay attention to the "index to device logic relationship" of the array argument.
86    /// See crate-level documentation of [`NUM_OF_DEVICES`](crate#defining-the-number-of-devices)
87    ///
88    /// # Errors
89    /// Writing to a register is infallible apart from [`DriverErrors`](Error::DriverError)
90    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
91    async fn write_cfga(&mut self, reg: [cfga::CfgA; NUM_OF_DEVICES]) -> Result<(), Self::Error>;
92    /// Writes content to `CfgB` Register
93    ///
94    /// Pay attention to the index to device logic of the payload array argument. See chapter [Defining number of devices](crate#defining-the-number-of-devices)
95    ///
96    /// # Errors
97    /// Writing to a register should is infallible apart from [`DriverErrors`](Error::DriverError)
98    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
99    async fn write_cfgb(&mut self, reg: [cfgb::CfgB; NUM_OF_DEVICES]) -> Result<(), Self::Error>;
100
101    /// Writes content to `CfgB` Register
102    ///
103    /// Pay attention to the index to device logic of the payload array argument. See chapter [Defining number of devices](crate#defining-the-number-of-devices)
104    ///
105    /// # Errors
106    /// Performing a conversion is infallible apart from [`DriverErrors`](Error::DriverError)
107    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
108    async fn convert_cells(
109        &mut self,
110        cmd: command::Adcv,
111    ) -> Result<[CellRegisters; NUM_OF_DEVICES], Self::Error>;
112
113    /// Converts the gpio channels with the given [`Adax`](command::Adax) command.
114    ///
115    /// # Errors
116    /// Performing a conversion is infallible apart from [`DriverErrors`](Error::DriverError)
117    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
118    async fn convert_gpios(
119        &mut self,
120        cmd: command::Adax,
121    ) -> Result<[AuxRegisters; NUM_OF_DEVICES], Self::Error>;
122
123    /// Performs the overlapping diagnostic test as described in the datasheet
124    ///
125    /// Uses the given [`Adol`](command::Adol) and performs the overlapping conversion of Cell 7 and Cell 13 (in case of LTC6812/13).
126    /// Results of both conversion are read back and compared against a deviation of 100 digits between both ADCs.
127    /// This is inline with the definition from the datasheet
128    ///
129    /// # Errors
130    /// - Returns [`DriverErrors`](Error::DriverError) in case of communication issues
131    /// - Returns [`Overlap`](`error::DeviceErrorKind::Overlap`) when diagnostic logic is violated
132    async fn perform_overlap_tests(&mut self, cmd: command::Adol) -> Result<(), Self::Error>;
133
134    /// Performs the accuracy check with the given [`MDSettings`](command::MDSetting)
135    ///
136    /// Only [`ScndRef`](command::GpioChannelConfig::ScndRef) channel is converted with the given settings.
137    /// The result for the second reference is compared against the bounds of the datasheet.
138    ///
139    /// # Errors
140    /// - Returns [`AccuracyFailed`](`error::DeviceErrorKind::AccuracyFailed`) if accuracy selftest fails
141    /// - Returns [`Error::DriverError`] in case of communication problems
142    async fn perform_accuracy_check(
143        &mut self,
144        conv_setting: command::MDSetting,
145    ) -> Result<(), Self::Error>;
146    /// Combiniation of [`convert_cells`](Ltc681x::convert_cells) and [`convert_gpios`](Ltc681x::convert_gpios) called after another
147    ///
148    /// # Errors
149    /// Performing a conversion is infallible apart from [`DriverErrors`](Error::DriverError)
150    /// Only returns [`DriverErrors`](Error::DriverError) in case of communication issues
151    async fn convert_cells_and_gpios(
152        &mut self,
153        cmd_cells: command::Adcv,
154        cmd_gpios: command::Adax,
155    ) -> Result<
156        (
157            [CellRegisters; NUM_OF_DEVICES],
158            [AuxRegisters; NUM_OF_DEVICES],
159        ),
160        Self::Error,
161    >;
162    /// Wakes the chip and the ISO SPI port (if used) by sending 12 Dummy Bytes per device
163    ///
164    /// `12 * NUM_OF_DEVICES` = total number of bytes send
165    /// for details see timing in the [driver](crate::driver::Driver::wake) or in the datasheet
166    ///
167    /// # Errors
168    /// Writing to the SPI Bus is infallible apart from [`DriverErrors`](Error::DriverError)
169    /// Only returns [`DriverErrors`](Error::DriverError) in case of a hardware defect.
170    /// This command only writes dummy bytes to the SPI Bus with no reaction expected
171    async fn wake(&mut self) -> Result<(), Self::Error>;
172}
173
174macro_rules! read {
175    ($name:ident, $t:ty, $cmd:ident) => {
176        async fn $name(&mut self) -> Result<[$t; NUM_OF_DEVICES], Self::Error> {
177            self.driver.enable_cs();
178            Self::convert_error(self.send_cmd(&command::$cmd::default()).await)?;
179
180            let mut res: [$t; NUM_OF_DEVICES] = [<$t>::default(); NUM_OF_DEVICES];
181            for i in 0..NUM_OF_DEVICES {
182                let reg = self.driver.read_register().await;
183                match reg {
184                    Ok(r) => res[i] = r.into(),
185                    Err(e) => {
186                        self.driver.disable_cs();
187                        return Self::convert_error(Err(e));
188                    }
189                }
190            }
191            self.driver.disable_cs();
192            Ok(res)
193        }
194    };
195}
196
197impl<const NUM_OF_DEVICES: usize, DRIVER: Driver> Device<NUM_OF_DEVICES>
198    for Ltc681x<DRIVER, NUM_OF_DEVICES>
199{
200    type Error = Error<DRIVER>;
201
202    read!(read_cfga, cfga::CfgA, RdCfgA);
203    read!(read_cfgb, cfgb::CfgB, RdCfgB);
204    read!(read_cva, cells::CvA, RdCvA);
205    read!(read_cvb, cells::CvB, RdCvB);
206    read!(read_cvc, cells::CvC, RdCvC);
207    read!(read_cvd, cells::CvD, RdCvD);
208    #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
209    read!(read_cve, cells::CvE, RdCvE);
210    #[cfg(feature = "ltc6813")]
211    read!(read_cvf, cells::CvF, RdCvF);
212    read!(read_auxa, auxreg::AuxA, RdAuxA);
213    read!(read_auxb, auxreg::AuxB, RdAuxB);
214    #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
215    read!(read_auxc, auxreg::AuxC, RdAuxC);
216    #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
217    read!(read_auxd, auxreg::AuxD, RdAuxD);
218
219    /// See Trait documentation
220    async fn write_cfga(&mut self, reg: [cfga::CfgA; NUM_OF_DEVICES]) -> Result<(), Self::Error> {
221        self.driver.enable_cs();
222        Self::convert_error(self.send_cmd(&command::WrCfgA::default()).await)?;
223        for item in reg.into_iter().take(NUM_OF_DEVICES) {
224            let ret = self.driver.write_register(item.into()).await;
225            Self::convert_error(self.handle_ret(ret))?;
226        }
227        self.driver.disable_cs();
228        Ok(())
229    }
230    /// See Trait documentation
231    async fn write_cfgb(&mut self, reg: [cfgb::CfgB; NUM_OF_DEVICES]) -> Result<(), Self::Error> {
232        self.driver.enable_cs();
233        Self::convert_error(self.send_cmd(&command::WrCfgB::default()).await)?;
234        for item in reg.into_iter().take(NUM_OF_DEVICES) {
235            let ret = self.driver.write_register(item.into()).await;
236            Self::convert_error(self.handle_ret(ret))?;
237        }
238        self.driver.disable_cs();
239        Ok(())
240    }
241
242    /// See Trait documentation
243    async fn convert_cells(
244        &mut self,
245        cmd: command::Adcv,
246    ) -> Result<[CellRegisters; NUM_OF_DEVICES], Self::Error> {
247        Self::convert_error(self.convert(&cmd).await)?;
248
249        let cva = self.read_cva().await?;
250        let cvb = self.read_cvb().await?;
251        let cvc = self.read_cvc().await?;
252        let cvd = self.read_cvd().await?;
253        #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
254        let cve = self.read_cve().await?;
255        #[cfg(feature = "ltc6813")]
256        let cvf = self.read_cvf().await?;
257        let mut ret: [CellRegisters; NUM_OF_DEVICES] = [CellRegisters::default(); NUM_OF_DEVICES];
258        for i in 0..NUM_OF_DEVICES {
259            ret[i] = CellRegisters::new(
260                cva[i],
261                cvb[i],
262                cvc[i],
263                cvd[i],
264                #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
265                cve[i],
266                #[cfg(feature = "ltc6813")]
267                cvf[i],
268            );
269        }
270        Ok(ret)
271    }
272
273    /// See Trait documentation
274    async fn convert_gpios(
275        &mut self,
276        cmd: command::Adax,
277    ) -> Result<[AuxRegisters; NUM_OF_DEVICES], Self::Error> {
278        Self::convert_error(self.convert(&cmd).await)?;
279
280        let auxa = self.read_auxa().await?;
281        let auxb = self.read_auxb().await?;
282        #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
283        let auxc = self.read_auxc().await?;
284        #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
285        let auxd = self.read_auxd().await?;
286        let mut ret: [AuxRegisters; NUM_OF_DEVICES] = [AuxRegisters::default(); NUM_OF_DEVICES];
287        for i in 0..NUM_OF_DEVICES {
288            ret[i] = AuxRegisters::new(
289                auxa[i],
290                auxb[i],
291                #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
292                auxc[i],
293                #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
294                auxd[i],
295            );
296        }
297        Ok(ret)
298    }
299    /// See Trait documentation
300    async fn perform_overlap_tests(&mut self, cmd: command::Adol) -> Result<(), Error<DRIVER>> {
301        const ALLOWED_DEVIATION_DIGITS: u16 = 100; // 100 Digits => 10mV
302
303        Self::convert_error(self.convert(&cmd).await)?;
304        // 7 + 8  und 13+14 Register
305        let cvc = self.read_cvc().await?;
306
307        for (i, item) in cvc.iter().enumerate().take(NUM_OF_DEVICES) {
308            if item
309                .get_cell_7_voltage()
310                .abs_diff(cvc[i].get_cell_8_voltage())
311                .ge(&ALLOWED_DEVIATION_DIGITS)
312            {
313                return Err(Error::DeviceError(DeviceErrorInfo {
314                    err: error::DeviceErrorKind::Overlap,
315                    device_index: i,
316                }));
317            }
318            #[cfg(any(feature = "ltc6812", feature = "ltc6813"))]
319            {
320                let cve = self.read_cve().await?;
321                for (i, item) in cve.iter().enumerate().take(NUM_OF_DEVICES) {
322                    if item
323                        .get_cell_13_voltage()
324                        .abs_diff(item.get_cell_14_voltage())
325                        >= ALLOWED_DEVIATION_DIGITS
326                    {
327                        return Err(Error::DeviceError(DeviceErrorInfo {
328                            err: error::DeviceErrorKind::Overlap,
329                            device_index: i,
330                        }));
331                    }
332                }
333            }
334        }
335        Ok(())
336    }
337
338    /// See Trait documentation
339    async fn perform_accuracy_check(
340        &mut self,
341        conv_setting: command::MDSetting,
342    ) -> Result<(), Error<DRIVER>> {
343        // Bounds for a valid VREF from datasheet
344        const VREF_ALLOWED_UPPER_BOUND: u16 = 30140;
345        const VREF_ALLOWED_LOWER_BOUND: u16 = 29900;
346
347        Self::convert_error(
348            self.convert(
349                &command::Adax::default()
350                    .set_md_option(conv_setting)
351                    .set_channel_config(command::GpioChannelConfig::ScndRef),
352            )
353            .await,
354        )?;
355        let auxb = self.read_auxb().await?;
356        for (i, item) in auxb.iter().enumerate().take(NUM_OF_DEVICES) {
357            let vref = item.get_vref_voltage();
358            if !(VREF_ALLOWED_LOWER_BOUND..=VREF_ALLOWED_UPPER_BOUND).contains(&vref) {
359                let err_desc = DeviceErrorInfo {
360                    err: error::DeviceErrorKind::AccuracyFailed,
361                    device_index: i,
362                };
363                return Err(Error::DeviceError(err_desc));
364            }
365        }
366        Ok(())
367    }
368    /// See Trait documentation
369    async fn convert_cells_and_gpios(
370        &mut self,
371        cmd_cells: command::Adcv,
372        cmd_gpios: command::Adax,
373    ) -> Result<
374        (
375            [CellRegisters; NUM_OF_DEVICES],
376            [AuxRegisters; NUM_OF_DEVICES],
377        ),
378        Self::Error,
379    > {
380        Ok((
381            self.convert_cells(cmd_cells).await?,
382            self.convert_gpios(cmd_gpios).await?,
383        ))
384    }
385    /// See Trait documentation
386    async fn wake(&mut self) -> Result<(), Self::Error> {
387        for _i in 0..NUM_OF_DEVICES {
388            self.driver.enable_cs();
389            // 12 Bytes per Device => 12 Bytes is a normal cmd size 4 Cmd Bytes + 8 Bytes Payload
390            let ret = self.driver.wake(12).await;
391            Self::convert_error(self.handle_ret(ret))?;
392            self.driver.disable_cs();
393        }
394        Ok(())
395    }
396}
397/// Main LTC681X structure implementing typical chip functions need to run a battery-management-system and perform relevant diagnostic functions
398#[derive(Debug)]
399pub struct Ltc681x<DRIVER: Driver, const NUM_OF_DEVICES: usize> {
400    driver: DRIVER,
401}
402
403impl<DRIVER: Driver, const NUM_OF_DEVICES: usize> Ltc681x<DRIVER, NUM_OF_DEVICES> {
404    // internal send_cmd routine that handles disabling CS on error
405    async fn send_cmd(&mut self, cmd: &dyn command::IsCmd) -> Result<(), DRIVER::ErrorType> {
406        if let Err(err) = self.driver.send_cmd(cmd).await {
407            self.driver.disable_cs();
408            Err(err)
409        } else {
410            Ok(())
411        }
412    }
413    async fn convert(&mut self, cmd: &dyn command::IsCmd) -> Result<(), DRIVER::ErrorType> {
414        self.driver.enable_cs();
415        self.send_cmd(cmd).await?;
416        self.driver.disable_cs();
417        Ok(())
418    }
419    /// Constructs a new Ltc681x with the given driver
420    pub fn new(driver: DRIVER) -> Self {
421        Self { driver }
422    }
423
424    fn handle_ret(
425        &mut self,
426        ret: Result<(), <DRIVER as Driver>::ErrorType>,
427    ) -> Result<(), DRIVER::ErrorType> {
428        if let Err(err) = ret {
429            self.driver.disable_cs();
430            Err(err)
431        } else {
432            Ok(())
433        }
434    }
435    fn convert_error<T>(drv_err: Result<T, DRIVER::ErrorType>) -> Result<T, Error<DRIVER>> {
436        match drv_err {
437            Ok(x) => Ok(x),
438            Err(err) => Err(Error::DriverError(err)),
439        }
440    }
441}