Skip to main content

bmi323_driver/
transport.rs

1use embedded_hal::spi::Operation;
2
3#[cfg(feature = "blocking")]
4use embedded_hal::i2c::I2c as HalI2c;
5#[cfg(feature = "blocking")]
6use embedded_hal::spi::SpiDevice as HalSpiDevice;
7#[cfg(not(feature = "blocking"))]
8use embedded_hal_async::i2c::I2c as HalI2c;
9#[cfg(not(feature = "blocking"))]
10use embedded_hal_async::spi::SpiDevice as HalSpiDevice;
11
12use crate::Bmi323;
13
14/// Maximum number of 16-bit words that a single `read_words` call may request.
15///
16/// Limited by the internal transfer scratch buffer. Callers that need more
17/// data must split the request into chunks of at most this size.
18pub const MAX_WORDS_PER_READ: usize = 64;
19
20/// I2C transport wrapper used by [`Bmi323`].
21///
22/// Most users do not need to construct this directly. Prefer
23/// [`Bmi323::new_i2c`](crate::Bmi323::new_i2c).
24pub struct I2cTransport<I2C> {
25    pub(crate) bus: I2C,
26    pub(crate) address: u8,
27}
28
29/// SPI transport wrapper used by [`Bmi323`].
30///
31/// Most users do not need to construct this directly. Prefer
32/// [`Bmi323::new_spi`](crate::Bmi323::new_spi).
33pub struct SpiTransport<SPI> {
34    pub(crate) bus: SPI,
35}
36
37/// Low-level register access contract used by the driver.
38///
39/// This trait is public so the driver can remain transport-agnostic, but most
40/// users will rely on the built-in I2C and SPI implementations.
41///
42/// In async mode (`features = ["async"]`) the methods are `async fn`.
43/// In blocking mode (`features = ["blocking"]`, the default) they are regular `fn`.
44#[cfg(feature = "blocking")]
45pub trait Access {
46    /// Underlying bus error type returned by the transport.
47    type BusError;
48    /// Read a single 16-bit register payload from the BMI323.
49    fn read_word(&mut self, reg: u8) -> Result<u16, Self::BusError>;
50    /// Write a single 16-bit register payload to the BMI323.
51    fn write_word(&mut self, reg: u8, word: u16) -> Result<(), Self::BusError>;
52    /// Read multiple consecutive 16-bit register payloads starting at `reg`.
53    fn read_words(&mut self, reg: u8, words: &mut [u16]) -> Result<(), Self::BusError>;
54}
55
56#[cfg(not(feature = "blocking"))]
57#[allow(async_fn_in_trait)]
58pub trait Access {
59    /// Underlying bus error type returned by the transport.
60    type BusError;
61    /// Read a single 16-bit register payload from the BMI323.
62    async fn read_word(&mut self, reg: u8) -> Result<u16, Self::BusError>;
63    /// Write a single 16-bit register payload to the BMI323.
64    async fn write_word(&mut self, reg: u8, word: u16) -> Result<(), Self::BusError>;
65    /// Read multiple consecutive 16-bit register payloads starting at `reg`.
66    async fn read_words(&mut self, reg: u8, words: &mut [u16]) -> Result<(), Self::BusError>;
67}
68
69// ---- Blocking I2C impl ----
70
71#[cfg(feature = "blocking")]
72impl<I2C: HalI2c> Access for Bmi323<I2cTransport<I2C>> {
73    type BusError = I2C::Error;
74
75    fn read_word(&mut self, reg: u8) -> Result<u16, Self::BusError> {
76        // 2 dummy bytes precede payload on I2C reads (§7.2.4.2)
77        let mut bytes = [0u8; 4];
78        self.transport
79            .bus
80            .write_read(self.transport.address, &[reg], &mut bytes)?;
81        Ok(u16::from_le_bytes([bytes[2], bytes[3]]))
82    }
83
84    fn write_word(&mut self, reg: u8, word: u16) -> Result<(), Self::BusError> {
85        let [lo, hi] = word.to_le_bytes();
86        self.transport
87            .bus
88            .write(self.transport.address, &[reg, lo, hi])
89    }
90
91    fn read_words(&mut self, reg: u8, words: &mut [u16]) -> Result<(), Self::BusError> {
92        // 2 dummy bytes precede payload on I2C reads (§7.2.4.2); 2 bytes per word
93        let mut bytes = [0u8; 2 + MAX_WORDS_PER_READ * 2];
94        let byte_len = words.len() * 2 + 2;
95        self.transport
96            .bus
97            .write_read(self.transport.address, &[reg], &mut bytes[..byte_len])?;
98        for (index, word) in words.iter_mut().enumerate() {
99            let offset = 2 + index * 2;
100            *word = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
101        }
102        Ok(())
103    }
104}
105
106// ---- Async I2C impl ----
107
108#[cfg(not(feature = "blocking"))]
109impl<I2C: HalI2c> Access for Bmi323<I2cTransport<I2C>> {
110    type BusError = I2C::Error;
111
112    async fn read_word(&mut self, reg: u8) -> Result<u16, Self::BusError> {
113        // 2 dummy bytes precede payload on I2C reads (§7.2.4.2)
114        let mut bytes = [0u8; 4];
115        self.transport
116            .bus
117            .write_read(self.transport.address, &[reg], &mut bytes)
118            .await?;
119        Ok(u16::from_le_bytes([bytes[2], bytes[3]]))
120    }
121
122    async fn write_word(&mut self, reg: u8, word: u16) -> Result<(), Self::BusError> {
123        let [lo, hi] = word.to_le_bytes();
124        self.transport
125            .bus
126            .write(self.transport.address, &[reg, lo, hi])
127            .await
128    }
129
130    async fn read_words(&mut self, reg: u8, words: &mut [u16]) -> Result<(), Self::BusError> {
131        // 2 dummy bytes precede payload on I2C reads (§7.2.4.2); 2 bytes per word
132        let mut bytes = [0u8; 2 + MAX_WORDS_PER_READ * 2];
133        let byte_len = words.len() * 2 + 2;
134        self.transport
135            .bus
136            .write_read(self.transport.address, &[reg], &mut bytes[..byte_len])
137            .await?;
138        for (index, word) in words.iter_mut().enumerate() {
139            let offset = 2 + index * 2;
140            *word = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
141        }
142        Ok(())
143    }
144}
145
146// ---- Blocking SPI impl ----
147
148#[cfg(feature = "blocking")]
149impl<SPI: HalSpiDevice<u8>> Access for Bmi323<SpiTransport<SPI>> {
150    type BusError = SPI::Error;
151
152    fn read_word(&mut self, reg: u8) -> Result<u16, Self::BusError> {
153        // MSB of address byte set to 1 for reads (§7.2.3)
154        let cmd = 0x80 | (reg & 0x7F);
155        // 1 dummy byte precedes the 2 payload bytes on SPI reads (§7.2.3)
156        let mut bytes = [0u8; 3];
157        let cmd_buf = [cmd];
158        let mut ops = [Operation::Write(&cmd_buf), Operation::Read(&mut bytes)];
159        self.transport.bus.transaction(&mut ops)?;
160        Ok(u16::from_le_bytes([bytes[1], bytes[2]]))
161    }
162
163    fn write_word(&mut self, reg: u8, word: u16) -> Result<(), Self::BusError> {
164        let [lo, hi] = word.to_le_bytes();
165        let payload = [reg & 0x7F, lo, hi];
166        self.transport.bus.write(&payload)
167    }
168
169    fn read_words(&mut self, reg: u8, words: &mut [u16]) -> Result<(), Self::BusError> {
170        // MSB of address byte set to 1 for reads; 1 dummy byte precedes payload (§7.2.3)
171        let cmd = 0x80 | (reg & 0x7F);
172        // 1 dummy byte + 2 bytes per word
173        let mut bytes = [0u8; 1 + MAX_WORDS_PER_READ * 2];
174        let byte_len = words.len() * 2 + 1;
175        let cmd_buf = [cmd];
176        let mut ops = [
177            Operation::Write(&cmd_buf),
178            Operation::Read(&mut bytes[..byte_len]),
179        ];
180        self.transport.bus.transaction(&mut ops)?;
181        for (index, word) in words.iter_mut().enumerate() {
182            let offset = 1 + index * 2;
183            *word = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
184        }
185        Ok(())
186    }
187}
188
189// ---- Async SPI impl ----
190
191#[cfg(not(feature = "blocking"))]
192impl<SPI: HalSpiDevice<u8>> Access for Bmi323<SpiTransport<SPI>> {
193    type BusError = SPI::Error;
194
195    async fn read_word(&mut self, reg: u8) -> Result<u16, Self::BusError> {
196        // MSB of address byte set to 1 for reads (§7.2.3)
197        let cmd = 0x80 | (reg & 0x7F);
198        // 1 dummy byte precedes the 2 payload bytes on SPI reads (§7.2.3)
199        let mut bytes = [0u8; 3];
200        let cmd_buf = [cmd];
201        let mut ops = [Operation::Write(&cmd_buf), Operation::Read(&mut bytes)];
202        self.transport.bus.transaction(&mut ops).await?;
203        Ok(u16::from_le_bytes([bytes[1], bytes[2]]))
204    }
205
206    async fn write_word(&mut self, reg: u8, word: u16) -> Result<(), Self::BusError> {
207        let [lo, hi] = word.to_le_bytes();
208        let payload = [reg & 0x7F, lo, hi];
209        self.transport.bus.write(&payload).await
210    }
211
212    async fn read_words(&mut self, reg: u8, words: &mut [u16]) -> Result<(), Self::BusError> {
213        // MSB of address byte set to 1 for reads; 1 dummy byte precedes payload (§7.2.3)
214        let cmd = 0x80 | (reg & 0x7F);
215        // 1 dummy byte + 2 bytes per word
216        let mut bytes = [0u8; 1 + MAX_WORDS_PER_READ * 2];
217        let byte_len = words.len() * 2 + 1;
218        let cmd_buf = [cmd];
219        let mut ops = [
220            Operation::Write(&cmd_buf),
221            Operation::Read(&mut bytes[..byte_len]),
222        ];
223        self.transport.bus.transaction(&mut ops).await?;
224        for (index, word) in words.iter_mut().enumerate() {
225            let offset = 1 + index * 2;
226            *word = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
227        }
228        Ok(())
229    }
230}