stm32f1xx_hal/i2c/
blocking.rs

1use super::*;
2
3use crate::pac::DWT;
4
5/// embedded-hal compatible blocking I2C implementation
6///
7/// **NOTE**: Before using blocking I2C, you need to enable the DWT cycle counter using the
8/// [DWT::enable_cycle_counter] method.
9pub struct BlockingI2c<I2C: Instance> {
10    nb: I2c<I2C>,
11    start_retries: u8,
12    timeouts: DwtTimeouts,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub struct DwtTimeouts {
17    start: u32,
18    addr: u32,
19    data: u32,
20}
21
22impl<I2C: Instance> BlockingI2c<I2C> {
23    /// Creates a blocking I2C1 object on pins PB6 and PB7 or PB8 and PB9 using the embedded-hal `BlockingI2c` trait.
24    #[allow(clippy::too_many_arguments)]
25    pub fn new<const R: u8>(
26        i2c: impl Into<Rmp<I2C, R>>,
27        pins: (impl RInto<I2C::Scl, R>, impl RInto<I2C::Sda, R>),
28        mode: impl Into<Mode>,
29        rcc: &mut Rcc,
30        start_timeout_us: u32,
31        start_retries: u8,
32        addr_timeout_us: u32,
33        data_timeout_us: u32,
34    ) -> Self {
35        I2c::new(i2c, pins, mode, rcc).blocking(
36            start_timeout_us,
37            start_retries,
38            addr_timeout_us,
39            data_timeout_us,
40            &rcc.clocks,
41        )
42    }
43}
44
45impl<I2C: Instance> I2c<I2C> {
46    /// Generates a blocking I2C instance from a universal I2C object
47    pub fn blocking(
48        self,
49        start_timeout_us: u32,
50        start_retries: u8,
51        addr_timeout_us: u32,
52        data_timeout_us: u32,
53        clocks: &Clocks,
54    ) -> BlockingI2c<I2C> {
55        let sysclk_mhz = clocks.sysclk().to_MHz();
56        BlockingI2c {
57            nb: self,
58            start_retries,
59            timeouts: DwtTimeouts {
60                start: start_timeout_us * sysclk_mhz,
61                addr: addr_timeout_us * sysclk_mhz,
62                data: data_timeout_us * sysclk_mhz,
63            },
64        }
65    }
66    pub fn blocking_default(self, clocks: Clocks) -> BlockingI2c<I2C> {
67        let sysclk_mhz = clocks.sysclk().to_MHz();
68        BlockingI2c {
69            nb: self,
70            start_retries: 10,
71            timeouts: DwtTimeouts {
72                start: 1000 * sysclk_mhz,
73                addr: 1000 * sysclk_mhz,
74                data: 1000 * sysclk_mhz,
75            },
76        }
77    }
78}
79
80macro_rules! wait_for_flag {
81    ($i2c:expr, $flag:ident, $nack:ident) => {{
82        let sr1 = $i2c.sr1().read();
83
84        if sr1.berr().bit_is_set() {
85            $i2c.sr1().write(|w| w.berr().clear_bit());
86            Err(Error::Bus.into())
87        } else if sr1.arlo().bit_is_set() {
88            $i2c.sr1().write(|w| w.arlo().clear_bit());
89            Err(Error::ArbitrationLoss.into())
90        } else if sr1.af().bit_is_set() {
91            $i2c.sr1().write(|w| w.af().clear_bit());
92            Err(Error::NoAcknowledge(NoAcknowledgeSource::$nack).into())
93        } else if sr1.ovr().bit_is_set() {
94            $i2c.sr1().write(|w| w.ovr().clear_bit());
95            Err(Error::Overrun.into())
96        } else if sr1.$flag().bit_is_set() {
97            Ok(())
98        } else {
99            Err(nb::Error::WouldBlock)
100        }
101    }};
102}
103
104macro_rules! busy_wait {
105    ($nb_expr:expr, $exit_cond:expr) => {{
106        loop {
107            match $nb_expr {
108                Err(nb::Error::Other(e)) => {
109                    #[allow(unreachable_code)]
110                    break Err(e);
111                }
112                Err(nb::Error::WouldBlock) => {
113                    if $exit_cond {
114                        break Err(Error::Timeout);
115                    }
116                }
117                Ok(x) => break Ok(x),
118            }
119        }
120    }};
121}
122
123macro_rules! busy_wait_cycles {
124    ($nb_expr:expr, $cycles:expr) => {{
125        let started = DWT::cycle_count();
126        let cycles = $cycles;
127        busy_wait!($nb_expr, DWT::cycle_count().wrapping_sub(started) >= cycles)
128    }};
129}
130
131impl<I2C: Instance> BlockingI2c<I2C> {
132    /// Check if START condition is generated. If the condition is not generated, this
133    /// method returns `WouldBlock` so the program can act accordingly
134    /// (busy wait, async, ...)
135    fn wait_after_sent_start(&mut self) -> nb::Result<(), Error> {
136        wait_for_flag!(self.nb.i2c, sb, Unknown)
137    }
138
139    /// Check if STOP condition is generated. If the condition is not generated, this
140    /// method returns `WouldBlock` so the program can act accordingly
141    /// (busy wait, async, ...)
142    fn wait_for_stop(&mut self) -> nb::Result<(), Error> {
143        if self.nb.i2c.cr1().read().stop().is_no_stop() {
144            Ok(())
145        } else {
146            Err(nb::Error::WouldBlock)
147        }
148    }
149
150    fn send_start_and_wait(&mut self) -> Result<(), Error> {
151        // According to http://www.st.com/content/ccc/resource/technical/document/errata_sheet/f5/50/c9/46/56/db/4a/f6/CD00197763.pdf/files/CD00197763.pdf/jcr:content/translations/en.CD00197763.pdf
152        // 2.14.4 Wrong behavior of I2C peripheral in master mode after a misplaced STOP
153        let mut retries_left = self.start_retries;
154        let mut last_ret = Ok(());
155        while retries_left > 0 {
156            self.nb.send_start();
157            last_ret = busy_wait_cycles!(self.wait_after_sent_start(), self.timeouts.start);
158            if last_ret.is_err() {
159                self.nb.reset();
160            } else {
161                break;
162            }
163            retries_left -= 1;
164        }
165        last_ret
166    }
167
168    fn send_addr_and_wait(&mut self, addr: u8, read: bool) -> Result<(), Error> {
169        self.nb.i2c.sr1().read();
170        self.nb.send_addr(addr, read);
171        let ret = busy_wait_cycles!(
172            wait_for_flag!(self.nb.i2c, addr, Address),
173            self.timeouts.addr
174        );
175        if let Err(Error::NoAcknowledge(_)) = ret {
176            self.nb.send_stop();
177        }
178        ret
179    }
180
181    fn write_bytes_and_wait(&mut self, bytes: &[u8]) -> Result<(), Error> {
182        self.nb.i2c.sr1().read();
183        self.nb.i2c.sr2().read();
184
185        self.nb.i2c.dr().write(|w| w.dr().set(bytes[0]));
186
187        for byte in &bytes[1..] {
188            busy_wait_cycles!(wait_for_flag!(self.nb.i2c, tx_e, Data), self.timeouts.data)?;
189            self.nb.i2c.dr().write(|w| w.dr().set(*byte));
190        }
191        busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btf, Data), self.timeouts.data)?;
192
193        Ok(())
194    }
195
196    fn write_without_stop(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
197        self.send_start_and_wait()?;
198        self.send_addr_and_wait(addr, false)?;
199
200        let ret = self.write_bytes_and_wait(bytes);
201        if let Err(Error::NoAcknowledge(_)) = ret {
202            self.nb.send_stop();
203        }
204        ret
205    }
206
207    pub fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
208        self.write_without_stop(addr, bytes)?;
209        self.nb.send_stop();
210        busy_wait_cycles!(self.wait_for_stop(), self.timeouts.data)?;
211
212        Ok(())
213    }
214
215    pub fn reset(&mut self) {
216        self.nb.reset();
217    }
218
219    pub fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
220        self.send_start_and_wait()?;
221        self.send_addr_and_wait(addr, true)?;
222
223        match buffer.len() {
224            1 => {
225                self.nb.i2c.cr1().modify(|_, w| w.ack().clear_bit());
226                self.nb.i2c.sr1().read();
227                self.nb.i2c.sr2().read();
228                self.nb.send_stop();
229
230                busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rx_ne, Data), self.timeouts.data)?;
231                buffer[0] = self.nb.i2c.dr().read().dr().bits();
232
233                busy_wait_cycles!(self.wait_for_stop(), self.timeouts.data)?;
234                self.nb.i2c.cr1().modify(|_, w| w.ack().set_bit());
235            }
236            2 => {
237                self.nb
238                    .i2c
239                    .cr1()
240                    .modify(|_, w| w.pos().set_bit().ack().set_bit());
241                self.nb.i2c.sr1().read();
242                self.nb.i2c.sr2().read();
243                self.nb.i2c.cr1().modify(|_, w| w.ack().clear_bit());
244
245                busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btf, Data), self.timeouts.data)?;
246                self.nb.send_stop();
247                buffer[0] = self.nb.i2c.dr().read().dr().bits();
248                buffer[1] = self.nb.i2c.dr().read().dr().bits();
249
250                busy_wait_cycles!(self.wait_for_stop(), self.timeouts.data)?;
251                self.nb
252                    .i2c
253                    .cr1()
254                    .modify(|_, w| w.pos().clear_bit().ack().clear_bit());
255                self.nb.i2c.cr1().modify(|_, w| w.ack().set_bit());
256            }
257            buffer_len => {
258                self.nb.i2c.cr1().modify(|_, w| w.ack().set_bit());
259                self.nb.i2c.sr1().read();
260                self.nb.i2c.sr2().read();
261
262                let (first_bytes, last_two_bytes) = buffer.split_at_mut(buffer_len - 3);
263                for byte in first_bytes {
264                    busy_wait_cycles!(
265                        wait_for_flag!(self.nb.i2c, rx_ne, Data),
266                        self.timeouts.data
267                    )?;
268                    *byte = self.nb.i2c.dr().read().dr().bits();
269                }
270
271                busy_wait_cycles!(wait_for_flag!(self.nb.i2c, btf, Data), self.timeouts.data)?;
272                self.nb.i2c.cr1().modify(|_, w| w.ack().clear_bit());
273                last_two_bytes[0] = self.nb.i2c.dr().read().dr().bits();
274                self.nb.send_stop();
275                last_two_bytes[1] = self.nb.i2c.dr().read().dr().bits();
276                busy_wait_cycles!(wait_for_flag!(self.nb.i2c, rx_ne, Data), self.timeouts.data)?;
277                last_two_bytes[2] = self.nb.i2c.dr().read().dr().bits();
278
279                busy_wait_cycles!(self.wait_for_stop(), self.timeouts.data)?;
280                self.nb.i2c.cr1().modify(|_, w| w.ack().set_bit());
281            }
282        }
283
284        Ok(())
285    }
286
287    pub fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
288        if !bytes.is_empty() {
289            self.write_without_stop(addr, bytes)?;
290        }
291
292        if !buffer.is_empty() {
293            self.read(addr, buffer)?;
294        } else if !bytes.is_empty() {
295            self.nb.send_stop();
296            busy_wait_cycles!(self.wait_for_stop(), self.timeouts.data)?;
297        }
298
299        Ok(())
300    }
301
302    pub fn transaction_slice(
303        &mut self,
304        _addr: u8,
305        _ops_slice: &mut [embedded_hal::i2c::Operation<'_>],
306    ) -> Result<(), Error> {
307        todo!();
308    }
309
310    pub(crate) fn transaction_slice_hal_02(
311        &mut self,
312        _addr: u8,
313        _ops_slice: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],
314    ) -> Result<(), Error> {
315        todo!();
316    }
317}
318
319pub trait BlockingI2cExt: I2cExt {
320    #[allow(clippy::too_many_arguments)]
321    fn blocking_i2c(
322        self,
323        pins: (impl RInto<Self::Scl, 0>, impl RInto<Self::Sda, 0>),
324        mode: impl Into<Mode>,
325        rcc: &mut Rcc,
326        start_timeout_us: u32,
327        start_retries: u8,
328        addr_timeout_us: u32,
329        data_timeout_us: u32,
330    ) -> BlockingI2c<Self> {
331        Self::i2c(self, pins, mode, rcc).blocking(
332            start_timeout_us,
333            start_retries,
334            addr_timeout_us,
335            data_timeout_us,
336            &rcc.clocks,
337        )
338    }
339}
340
341impl<I2C: I2cExt> BlockingI2cExt for I2C {}
342
343impl<I2C: Instance, const R: u8> Rmp<I2C, R> {
344    #[allow(clippy::too_many_arguments)]
345    pub fn blocking_i2c(
346        self,
347        pins: (impl RInto<I2C::Scl, R>, impl RInto<I2C::Sda, R>),
348        mode: impl Into<Mode>,
349        rcc: &mut Rcc,
350        start_timeout_us: u32,
351        start_retries: u8,
352        addr_timeout_us: u32,
353        data_timeout_us: u32,
354    ) -> BlockingI2c<I2C> {
355        self.i2c(pins, mode, rcc).blocking(
356            start_timeout_us,
357            start_retries,
358            addr_timeout_us,
359            data_timeout_us,
360            &rcc.clocks,
361        )
362    }
363}