From 1c1aea62d00fcf920c8c756eb9a8518c1d9338cd Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Wed, 12 Feb 2025 20:54:07 +0800 Subject: [PATCH] stm32: i2c-v2: Add error handling Currently when error occurres, we have to wait for timeout, which is less than ideal. Enable related interrupts, and return Err when error occurres. --- embassy-stm32/src/i2c/v2.rs | 67 +++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 05ac9afcc..e2eb6f367 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -13,13 +13,17 @@ pub(crate) unsafe fn on_interrupt() { let regs = T::info().regs; let isr = regs.isr().read(); - if isr.tcr() || isr.tc() { + if isr.tcr() || isr.tc() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() { T::state().waker.wake(); } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt critical_section::with(|_| { - regs.cr1().modify(|w| w.set_tcie(false)); + regs.cr1().modify(|w| { + // The flag can only be cleared by writting to nbytes, we won't do that here + w.set_tcie(false); + // Error flags are to be read in the routines, so we also don't clear them here + w.set_nackie(false); + w.set_errie(false); + }); }); } @@ -449,6 +453,8 @@ impl<'d> I2c<'d, Async> { if first_slice { w.set_tcie(true); } + w.set_nackie(true); + w.set_errie(true); }); let dst = regs.txdr().as_ptr() as *mut u8; @@ -459,18 +465,41 @@ impl<'d> I2c<'d, Async> { let on_drop = OnDrop::new(|| { let regs = self.info.regs; + let isr = regs.isr().read(); regs.cr1().modify(|w| { - if last_slice { + if last_slice || isr.nackf() || isr.arlo() || isr.berr() || isr.ovr() { w.set_txdmaen(false); } w.set_tcie(false); - }) + w.set_nackie(false); + w.set_errie(false); + }); + regs.icr().write(|w| { + w.set_nackcf(true); + w.set_berrcf(true); + w.set_arlocf(true); + w.set_ovrcf(true); + }); }); poll_fn(|cx| { self.state.waker.register(cx.waker()); let isr = self.info.regs.isr().read(); + + if isr.nackf() { + return Poll::Ready(Err(Error::Nack)); + } + if isr.arlo() { + return Poll::Ready(Err(Error::Arbitration)); + } + if isr.berr() { + return Poll::Ready(Err(Error::Bus)); + } + if isr.ovr() { + return Poll::Ready(Err(Error::Overrun)); + } + if remaining_len == total_len { if first_slice { Self::master_write( @@ -531,6 +560,8 @@ impl<'d> I2c<'d, Async> { regs.cr1().modify(|w| { w.set_rxdmaen(true); w.set_tcie(true); + w.set_nackie(true); + w.set_errie(true); }); let src = regs.rxdr().as_ptr() as *mut u8; @@ -544,13 +575,35 @@ impl<'d> I2c<'d, Async> { regs.cr1().modify(|w| { w.set_rxdmaen(false); w.set_tcie(false); - }) + w.set_nackie(false); + w.set_errie(false); + }); + regs.icr().write(|w| { + w.set_nackcf(true); + w.set_berrcf(true); + w.set_arlocf(true); + w.set_ovrcf(true); + }); }); poll_fn(|cx| { self.state.waker.register(cx.waker()); let isr = self.info.regs.isr().read(); + + if isr.nackf() { + return Poll::Ready(Err(Error::Nack)); + } + if isr.arlo() { + return Poll::Ready(Err(Error::Arbitration)); + } + if isr.berr() { + return Poll::Ready(Err(Error::Bus)); + } + if isr.ovr() { + return Poll::Ready(Err(Error::Overrun)); + } + if remaining_len == total_len { Self::master_read( self.info,