Skip to content

Commit 89a782a

Browse files
authored
Fix address nack race condition (#308)I
According to NXP, mststate is only accurate and reflect actual HW state when pending is set. This causes race condition where if the previous transaction gets an address nack, the next transaction still sees address nack in the mststate. Checking pending bit first ensure that mststate is updated already.
2 parents 66949e8 + 5f377a5 commit 89a782a

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

examples/rt685s-evk/src/bin/i2c-master-async.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use embassy_imxrt::{bind_interrupts, i2c, peripherals};
99
use embassy_time::Timer;
1010
use embedded_hal_async::i2c::I2c;
1111

12+
const NACK_ADDR: u8 = 0x07;
13+
1214
const ACC_ADDR: u8 = 0x1E;
1315

1416
const ACC_ID_REG: u8 = 0x0D;
@@ -94,7 +96,33 @@ async fn main(_spawner: Spawner) {
9496
)
9597
.unwrap();
9698

97-
// Read WHO_AM_I register, 0x0D to get value 0xC7 (1100 0111)
99+
info!("i2c example - write nack check");
100+
let result = i2c.write(NACK_ADDR, &[ACC_ID_REG]).await;
101+
if result.is_err_and(|e| e == i2c::TransferError::AddressNack.into()) {
102+
info!("i2c example - write nack check gets the right error");
103+
} else {
104+
error!("i2c example - write nack check error did not get the error {}", result);
105+
}
106+
107+
info!("i2c example - read nack check");
108+
let mut reg = [0u8; 1];
109+
let result = i2c.read(NACK_ADDR, &mut reg).await;
110+
if result.is_err_and(|e| e == i2c::TransferError::AddressNack.into()) {
111+
info!("i2c example - write nack check gets the right error");
112+
} else {
113+
error!("i2c example - write nack check error did not get the error {}", result);
114+
}
115+
116+
info!("i2c example - write_read nack check");
117+
let mut reg = [0u8; 1];
118+
reg[0] = 0xAA;
119+
let result = i2c.write_read(NACK_ADDR, &[ACC_ID_REG], &mut reg).await;
120+
if result.is_err_and(|e| e == i2c::TransferError::AddressNack.into()) {
121+
info!("i2c example - write nack check gets the right error");
122+
} else {
123+
error!("i2c example - write nack check error did not get the error {}", result);
124+
}
125+
98126
info!("i2c example - ACC WHO_AM_I register check");
99127
let mut reg = [0u8; 1];
100128
reg[0] = 0xAA;

src/i2c/master.rs

+15-10
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,20 @@ impl<'a> I2cMaster<'a, Async> {
321321
|me| {
322322
let stat = me.info.regs.stat().read();
323323

324-
if is_read && stat.mststate().is_receive_ready() || !is_read && stat.mststate().is_transmit_ready()
325-
{
326-
Poll::Ready(Ok::<(), Error>(()))
327-
} else if stat.mststate().is_nack_address() {
328-
Poll::Ready(Err(TransferError::AddressNack.into()))
329-
} else if is_read && stat.mstpending().is_pending() && !stat.mststate().is_receive_ready() {
330-
Poll::Ready(Err(TransferError::ReadFail.into()))
331-
} else if !is_read && stat.mstpending().is_pending() && !stat.mststate().is_transmit_ready() {
332-
Poll::Ready(Err(TransferError::WriteFail.into()))
324+
if stat.mstpending().is_pending() {
325+
if is_read && stat.mststate().is_receive_ready()
326+
|| !is_read && stat.mststate().is_transmit_ready()
327+
{
328+
Poll::Ready(Ok::<(), Error>(()))
329+
} else if stat.mststate().is_nack_address() {
330+
Poll::Ready(Err(TransferError::AddressNack.into()))
331+
} else if is_read && !stat.mststate().is_receive_ready() {
332+
Poll::Ready(Err(TransferError::ReadFail.into()))
333+
} else if !is_read && !stat.mststate().is_transmit_ready() {
334+
Poll::Ready(Err(TransferError::WriteFail.into()))
335+
} else {
336+
Poll::<Result<()>>::Pending
337+
}
333338
} else if stat.mstarbloss().is_arbitration_loss() {
334339
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
335340
} else if stat.mstststperr().is_error() {
@@ -543,7 +548,7 @@ impl<'a> I2cMaster<'a, Async> {
543548
|me| {
544549
let stat = me.info.regs.stat().read();
545550

546-
if stat.mststate().is_idle() {
551+
if stat.mstpending().is_pending() && stat.mststate().is_idle() {
547552
Poll::Ready(Ok(()))
548553
} else if stat.mstarbloss().is_arbitration_loss() {
549554
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))

0 commit comments

Comments
 (0)