Skip to content

Commit fb08190

Browse files
committed
Let I2C blocking methods wait until STOP is sent.
This commit brings back the while-loop waiting at the end of the blocking read/write methods on I2C. The while-loop waiting is necessary to ensure that the I2C interface has become idle before returning from the blocking read/write methods. As an example, the following code might break without the while-loop waiting. Assuming that `handle` is a DMA handle to an I2C interface. After the first write method returns, without the while-loop waiting, the I2C interface can be generating the stop condition when the code reaches the second `write` method, which then fails the busy test `self.busy_res()?` and causes the second `write` to return `WouldBlock`. ``` handle.write(addr, buf); // Ok(()) handle.write(addr, buf); // Err(WouldBlock) ``` The added while-loop affects only the blocking methods and will not affect the non-blocking methods with DMA.
1 parent 403fddb commit fb08190

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

src/i2c.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,12 @@ impl<I2C: Instance> I2c<I2C> {
285285
/// Sends START and Address for writing
286286
#[inline(always)]
287287
fn prepare_write(&self, addr: u8) -> Result<(), Error> {
288-
// Wait until a previous STOP condition finishes
288+
// Wait until a previous STOP condition finishes. When the previous
289+
// STOP was generated inside an ISR (e.g. DMA interrupt handler),
290+
// the ISR returns without waiting for the STOP condition to finish.
291+
// It is possible that the STOP condition is still being generated
292+
// when we reach here, so we wait until it finishes before proceeding
293+
// to start a new transaction.
289294
while self.i2c.cr1.read().stop().bit_is_set() {}
290295

291296
// Send a START condition
@@ -330,7 +335,12 @@ impl<I2C: Instance> I2c<I2C> {
330335

331336
/// Sends START and Address for reading
332337
fn prepare_read(&self, addr: u8) -> Result<(), Error> {
333-
// Wait until a previous STOP condition finishes
338+
// Wait until a previous STOP condition finishes. When the previous
339+
// STOP was generated inside an ISR (e.g. DMA interrupt handler),
340+
// the ISR returns without waiting for the STOP condition to finish.
341+
// It is possible that the STOP condition is still being generated
342+
// when we reach here, so we wait until it finishes before proceeding
343+
// to start a new transaction.
334344
while self.i2c.cr1.read().stop().bit_is_set() {}
335345

336346
// Send a START condition and set ACK bit
@@ -449,6 +459,13 @@ impl<I2C: Instance> I2c<I2C> {
449459
// Receive last byte
450460
*last = self.recv_byte()?;
451461

462+
// Wait for the STOP to be sent. Otherwise, the interface will still be
463+
// busy for a while after this function returns. Immediate following
464+
// operations through the DMA handle might thus encounter `WouldBlock`
465+
// error. Instead, we should make sure that the interface becomes idle
466+
// before returning.
467+
while self.i2c.cr1.read().stop().bit_is_set() {}
468+
452469
// Fallthrough is success
453470
Ok(())
454471
} else {
@@ -468,6 +485,13 @@ impl<I2C: Instance> I2c<I2C> {
468485
// Send a STOP condition
469486
self.i2c.cr1.modify(|_, w| w.stop().set_bit());
470487

488+
// Wait for the STOP to be sent. Otherwise, the interface will still be
489+
// busy for a while after this function returns. Immediate following
490+
// operations through the DMA handle might thus encounter `WouldBlock`
491+
// error. Instead, we should make sure that the interface becomes idle
492+
// before returning.
493+
while self.i2c.cr1.read().stop().bit_is_set() {}
494+
471495
// Fallthrough is success
472496
Ok(())
473497
}
@@ -482,6 +506,13 @@ impl<I2C: Instance> I2c<I2C> {
482506
// Send a STOP condition
483507
self.i2c.cr1.modify(|_, w| w.stop().set_bit());
484508

509+
// Wait for the STOP to be sent. Otherwise, the interface will still be
510+
// busy for a while after this function returns. Immediate following
511+
// operations through the DMA handle might thus encounter `WouldBlock`
512+
// error. Instead, we should make sure that the interface becomes idle
513+
// before returning.
514+
while self.i2c.cr1.read().stop().bit_is_set() {}
515+
485516
// Fallthrough is success
486517
Ok(())
487518
}

0 commit comments

Comments
 (0)