Skip to main content

async_ltc681x/driver/
communication.rs

1use super::registers::base::RegisterBlock;
2use super::{
3    adi_isospi_pec::{calc_pec, cmd_calc_pec},
4    command::IsCmd,
5};
6use embedded_hal::digital::{InputPin, OutputPin};
7use embedded_hal_async::spi::SpiBus;
8
9/// Implementation of generic [a SPI Driver](super::Driver) using [`embedded_hal`] traits
10pub struct Ltc681xSpi<SPI: SpiBus, CS: OutputPin, MISO: InputPin> {
11    spi_hdl: SPI,
12    cs: CS,
13    miso_pin: MISO,
14}
15
16/// Error Types returned by functions of [`Ltc681xSpi`]
17#[derive(Debug, Copy, Clone)]
18pub enum Error<SPI: SpiBus> {
19    /// Data Read back from the Chip has a non matching PEC
20    PecError,
21    /// Wraps all internal [SPI Errors](embedded_hal::spi::Error) emitted by the used SPI implementation
22    SpiError(SPI::Error),
23}
24impl<SPI: SpiBus, CS: OutputPin, MISO: InputPin> super::Driver for Ltc681xSpi<SPI, CS, MISO> {
25    type ErrorType = Error<SPI>;
26
27    async fn send_cmd(&mut self, cmd: &dyn IsCmd) -> Result<(), Error<SPI>> {
28        self.send_cmd_internal(cmd.as_cmd_header(), cmd.has_poll_status())
29            .await
30    }
31    async fn wake(&mut self, nr_of_bytes: usize) -> Result<(), Error<SPI>> {
32        for _i in 0..nr_of_bytes {
33            self.write_byte().await?;
34        }
35        Ok(())
36    }
37    async fn write_register(&mut self, payload: RegisterBlock) -> Result<(), Error<SPI>> {
38        let mut buf = get_write_buffer(payload);
39        self.transfer(&mut buf).await?;
40        // Read Back Value
41        Ok(())
42    }
43
44    async fn read_register(&mut self) -> Result<RegisterBlock, Error<SPI>> {
45        let mut buf = [0; 8];
46        self.transfer(&mut buf).await?;
47        Self::get_payload_from_buffer(&buf)
48    }
49    async fn drive_stcomm(&mut self) -> Result<(), Self::ErrorType> {
50        unimplemented!()
51    }
52    fn enable_cs(&mut self) {
53        self.cs.set_low().unwrap_or_default();
54    }
55    fn disable_cs(&mut self) {
56        self.cs.set_high().unwrap_or_default();
57    }
58}
59
60impl<SPI: SpiBus, CS: OutputPin, MISO: InputPin> Ltc681xSpi<SPI, CS, MISO> {
61    /// Creates a new Ltc681x instance by consuming a [`SpiBus`](embedded_hal::spi::SpiBus) Instance and the necessary CS and MISO Pins
62    ///
63    /// IMPORTANT: This driver needs the MISO (Controller In Device Out- CIDO?) Pin as an Input in addition to its use in the SPI peripheral.
64    /// When building the SPI Struct this Pin is usally consumed. Typically the MISO Pin must be unsafely cloned to allow for creation of this driver.
65    /// Safety of this is handled internally as the MISO Pin is only polled as in [`InputPin`] during conversion of the ADCs. During that time no data is pushed out
66    /// by the chip.
67    pub fn new(spi: SPI, mut cs: CS, miso: MISO) -> Self {
68        // Deactivate CS in any case
69        cs.set_high().unwrap_or_default();
70        Self {
71            spi_hdl: spi,
72            cs,
73            miso_pin: miso,
74        }
75    }
76
77    async fn send_cmd_internal(&mut self, cmd: u16, with_poll: bool) -> Result<(), Error<SPI>> {
78        let mut buf = get_cmd_buffer(cmd);
79        self.transfer(&mut buf).await?;
80        if with_poll {
81            self.poll().await?;
82        }
83        Ok(())
84    }
85    // Internal Transfer Function as we cannot impl From<FullFuplex::Error> for Error<FullDuplex>
86    // we cannot use ? operator for conversion. This function performs conversion by matching
87    // in all other functions we can use this function and the ? operator
88    //
89    async fn transfer(&mut self, buffer: &mut [u8]) -> Result<(), Error<SPI>> {
90        match self.spi_hdl.transfer_in_place(buffer).await {
91            Ok(()) => Ok(()),
92            Err(err) => Err(Error::SpiError(err)),
93        }
94    }
95    async fn write_byte(&mut self) -> Result<(), Error<SPI>> {
96        let mut buf = [0xFF; 1];
97        self.transfer(&mut buf).await?;
98        Ok(())
99    }
100    async fn poll(&mut self) -> Result<(), Error<SPI>> {
101        self.write_byte().await?;
102        loop {
103            if let Ok(is_high) = self.miso_pin.is_high() {
104                if is_high {
105                    break;
106                }
107            }
108            self.write_byte().await?;
109            // in a Mock implementation where a future resolves on first poll
110            // we spin here forevery and cant change the mock state of miso pin
111            #[cfg(test)]
112            embassy_futures::yield_now().await;
113        }
114        Ok(())
115    }
116    fn get_payload_from_buffer(buf: &[u8]) -> Result<RegisterBlock, Error<SPI>> {
117        let pec = u16::from_be_bytes([buf[6], buf[7]]);
118        if pec != calc_pec(&buf[0..=5]) {
119            return Err(Error::PecError);
120        }
121        Ok(RegisterBlock {
122            reg_1: u16::from_le_bytes([buf[0], buf[1]]),
123            reg_2: u16::from_le_bytes([buf[2], buf[3]]),
124            reg_3: u16::from_le_bytes([buf[4], buf[5]]),
125        })
126    }
127}
128
129pub(crate) fn get_write_buffer(reg: RegisterBlock) -> [u8; 8] {
130    let mut buf = [0; 8];
131    copy_payload_to_buffer(reg, &mut buf);
132    buf
133}
134fn get_cmd_buffer(write_cmd: u16) -> [u8; 4] {
135    let mut buf = [0; 4];
136    copy_cmd_to_buffer(write_cmd, &mut buf);
137    buf
138}
139pub(crate) fn copy_cmd_to_buffer(cmd: u16, buf: &mut [u8]) {
140    let pec = cmd_calc_pec(cmd).to_be_bytes();
141    let cmd = cmd.to_be_bytes();
142    buf[0] = cmd[0];
143    buf[1] = cmd[1];
144    buf[2] = pec[0];
145    buf[3] = pec[1];
146}
147fn copy_payload_to_buffer(reg: RegisterBlock, buf: &mut [u8]) {
148    let reg_1 = reg.reg_1.to_le_bytes();
149    let reg_2 = reg.reg_2.to_le_bytes();
150    let reg_3 = reg.reg_3.to_le_bytes();
151
152    buf[0] = reg_1[0];
153    buf[1] = reg_1[1];
154    buf[2] = reg_2[0];
155    buf[3] = reg_2[1];
156    buf[4] = reg_3[0];
157    buf[5] = reg_3[1];
158    let pec = calc_pec(&buf[0..=5]).to_be_bytes();
159    buf[6] = pec[0];
160    buf[7] = pec[1];
161}