diff options
| author | Dario Nieuwenhuis <[email protected]> | 2025-03-31 15:09:01 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-03-31 15:09:01 +0000 |
| commit | a44abaf7e4562fa5393087fd845bf0d02141325b (patch) | |
| tree | 667bc74b504a65592c805fd3f66197bd2126801f | |
| parent | 4d9b41714da77d82811f39bd6feabe161e93552c (diff) | |
| parent | c29fc3532b34633b2234c26a7e41e8ba6d628e7f (diff) | |
Merge pull request #3989 from embedded-rust-iml/fix/ringbuffered-error-handling
Rework status handling (idle and errors) in ringbuffered uart
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 11 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/ringbuffered.rs | 88 |
2 files changed, 44 insertions, 55 deletions
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 675e90c7f..5b7f8dc26 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -18,11 +18,6 @@ use crate::gpio::{self, AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed} | |||
| 18 | use crate::interrupt::typelevel::Interrupt as _; | 18 | use crate::interrupt::typelevel::Interrupt as _; |
| 19 | use crate::interrupt::{self, Interrupt, InterruptExt}; | 19 | use crate::interrupt::{self, Interrupt, InterruptExt}; |
| 20 | use crate::mode::{Async, Blocking, Mode}; | 20 | use crate::mode::{Async, Blocking, Mode}; |
| 21 | #[allow(unused_imports)] | ||
| 22 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 23 | use crate::pac::usart::regs::Isr as Sr; | ||
| 24 | #[cfg(any(usart_v1, usart_v2))] | ||
| 25 | use crate::pac::usart::regs::Sr; | ||
| 26 | #[cfg(not(any(usart_v1, usart_v2)))] | 21 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 27 | use crate::pac::usart::Lpuart as Regs; | 22 | use crate::pac::usart::Lpuart as Regs; |
| 28 | #[cfg(any(usart_v1, usart_v2))] | 23 | #[cfg(any(usart_v1, usart_v2))] |
| @@ -403,7 +398,7 @@ pub struct UartRx<'d, M: Mode> { | |||
| 403 | rx_dma: Option<ChannelAndRequest<'d>>, | 398 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 404 | detect_previous_overrun: bool, | 399 | detect_previous_overrun: bool, |
| 405 | #[cfg(any(usart_v1, usart_v2))] | 400 | #[cfg(any(usart_v1, usart_v2))] |
| 406 | buffered_sr: stm32_metapac::usart::regs::Sr, | 401 | buffered_sr: regs::Sr, |
| 407 | _phantom: PhantomData<M>, | 402 | _phantom: PhantomData<M>, |
| 408 | } | 403 | } |
| 409 | 404 | ||
| @@ -950,7 +945,7 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 950 | rx_dma, | 945 | rx_dma, |
| 951 | detect_previous_overrun: config.detect_previous_overrun, | 946 | detect_previous_overrun: config.detect_previous_overrun, |
| 952 | #[cfg(any(usart_v1, usart_v2))] | 947 | #[cfg(any(usart_v1, usart_v2))] |
| 953 | buffered_sr: stm32_metapac::usart::regs::Sr(0), | 948 | buffered_sr: regs::Sr(0), |
| 954 | }; | 949 | }; |
| 955 | this.enable_and_configure(&config)?; | 950 | this.enable_and_configure(&config)?; |
| 956 | Ok(this) | 951 | Ok(this) |
| @@ -1449,7 +1444,7 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1449 | rx_dma, | 1444 | rx_dma, |
| 1450 | detect_previous_overrun: config.detect_previous_overrun, | 1445 | detect_previous_overrun: config.detect_previous_overrun, |
| 1451 | #[cfg(any(usart_v1, usart_v2))] | 1446 | #[cfg(any(usart_v1, usart_v2))] |
| 1452 | buffered_sr: stm32_metapac::usart::regs::Sr(0), | 1447 | buffered_sr: regs::Sr(0), |
| 1453 | }, | 1448 | }, |
| 1454 | }; | 1449 | }; |
| 1455 | this.enable_and_configure(&config)?; | 1450 | this.enable_and_configure(&config)?; |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 3a7c3adf4..8e42d5917 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -7,16 +7,12 @@ use embassy_embedded_hal::SetConfig; | |||
| 7 | use embedded_io_async::ReadReady; | 7 | use embedded_io_async::ReadReady; |
| 8 | use futures_util::future::{select, Either}; | 8 | use futures_util::future::{select, Either}; |
| 9 | 9 | ||
| 10 | use super::{ | 10 | use super::{rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx}; |
| 11 | clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx, | ||
| 12 | }; | ||
| 13 | use crate::dma::ReadableRingBuffer; | 11 | use crate::dma::ReadableRingBuffer; |
| 14 | use crate::gpio::{AnyPin, SealedPin as _}; | 12 | use crate::gpio::{AnyPin, SealedPin as _}; |
| 15 | use crate::mode::Async; | 13 | use crate::mode::Async; |
| 16 | #[cfg(any(usart_v3, usart_v4))] | ||
| 17 | use crate::pac::usart::regs; | ||
| 18 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 19 | use crate::usart::{Regs, Sr}; | 15 | use crate::usart::Regs; |
| 20 | use crate::Peri; | 16 | use crate::Peri; |
| 21 | 17 | ||
| 22 | /// Rx-only Ring-buffered UART Driver | 18 | /// Rx-only Ring-buffered UART Driver |
| @@ -99,8 +95,6 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 99 | // enable idle line interrupt | 95 | // enable idle line interrupt |
| 100 | w.set_idleie(true); | 96 | w.set_idleie(true); |
| 101 | }); | 97 | }); |
| 102 | // Clear all potential error interrupt flags | ||
| 103 | clear_interrupt_flags(r, sr(r).read()); | ||
| 104 | r.cr3().modify(|w| { | 98 | r.cr3().modify(|w| { |
| 105 | // enable Error Interrupt: (Frame error, Noise error, Overrun error) | 99 | // enable Error Interrupt: (Frame error, Noise error, Overrun error) |
| 106 | w.set_eie(true); | 100 | w.set_eie(true); |
| @@ -134,17 +128,15 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 134 | } | 128 | } |
| 135 | 129 | ||
| 136 | /// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and | 130 | /// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and |
| 137 | /// check for errors in status register. Error flags are cleared in `start_uart()` so they need | 131 | /// check for errors in status register. Error flags are checked/cleared first. |
| 138 | /// to be read first without returning yet. | ||
| 139 | fn start_dma_or_check_errors(&mut self) -> Result<(), Error> { | 132 | fn start_dma_or_check_errors(&mut self) -> Result<(), Error> { |
| 140 | let r = self.info.regs; | 133 | let r = self.info.regs; |
| 141 | 134 | ||
| 142 | let sr = clear_idle_flag(r); | 135 | check_idle_and_errors(r)?; |
| 143 | let res = check_for_errors(sr); | ||
| 144 | if !r.cr3().read().dmar() { | 136 | if !r.cr3().read().dmar() { |
| 145 | self.start_uart(); | 137 | self.start_uart(); |
| 146 | } | 138 | } |
| 147 | res | 139 | Ok(()) |
| 148 | } | 140 | } |
| 149 | 141 | ||
| 150 | /// Read bytes that are readily available in the ring buffer. | 142 | /// Read bytes that are readily available in the ring buffer. |
| @@ -191,13 +183,7 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 191 | 183 | ||
| 192 | compiler_fence(Ordering::SeqCst); | 184 | compiler_fence(Ordering::SeqCst); |
| 193 | 185 | ||
| 194 | // Critical section is needed so that IDLE isn't set after | 186 | if check_idle_and_errors(self.info.regs)? { |
| 195 | // our read but before we clear it. | ||
| 196 | let sr = critical_section::with(|_| clear_idle_flag(self.info.regs)); | ||
| 197 | |||
| 198 | check_for_errors(sr)?; | ||
| 199 | |||
| 200 | if sr.idle() { | ||
| 201 | // Idle line is detected | 187 | // Idle line is detected |
| 202 | Poll::Ready(Ok(())) | 188 | Poll::Ready(Ok(())) |
| 203 | } else { | 189 | } else { |
| @@ -240,41 +226,49 @@ impl Drop for RingBufferedUartRx<'_> { | |||
| 240 | } | 226 | } |
| 241 | } | 227 | } |
| 242 | 228 | ||
| 243 | /// Return an error result if the Sr register has errors | 229 | /// Check and clear idle and error interrupts, return true if idle, Err(e) on error |
| 244 | fn check_for_errors(s: Sr) -> Result<(), Error> { | 230 | /// |
| 245 | if s.pe() { | 231 | /// All flags are read and cleared in a single step, respectively. When more than one flag is set |
| 232 | /// at the same time, all flags will be cleared but only one flag will be reported. So the other | ||
| 233 | /// flag(s) will gone missing unnoticed. The error flags are checked first, the idle flag last. | ||
| 234 | /// | ||
| 235 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | ||
| 236 | /// are cleared by a single read to the RDR register. | ||
| 237 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | ||
| 238 | // Critical section is required so that the flags aren't set after read and before clear | ||
| 239 | let sr = critical_section::with(|_| { | ||
| 240 | // SAFETY: read only and we only use Rx related flags | ||
| 241 | let sr = sr(r).read(); | ||
| 242 | |||
| 243 | #[cfg(any(usart_v3, usart_v4))] | ||
| 244 | r.icr().write(|w| { | ||
| 245 | w.set_idle(true); | ||
| 246 | w.set_pe(true); | ||
| 247 | w.set_fe(true); | ||
| 248 | w.set_ne(true); | ||
| 249 | w.set_ore(true); | ||
| 250 | }); | ||
| 251 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 252 | unsafe { | ||
| 253 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) | ||
| 254 | rdr(r).read_volatile() | ||
| 255 | }; | ||
| 256 | sr | ||
| 257 | }); | ||
| 258 | if sr.pe() { | ||
| 246 | Err(Error::Parity) | 259 | Err(Error::Parity) |
| 247 | } else if s.fe() { | 260 | } else if sr.fe() { |
| 248 | Err(Error::Framing) | 261 | Err(Error::Framing) |
| 249 | } else if s.ne() { | 262 | } else if sr.ne() { |
| 250 | Err(Error::Noise) | 263 | Err(Error::Noise) |
| 251 | } else if s.ore() { | 264 | } else if sr.ore() { |
| 252 | Err(Error::Overrun) | 265 | Err(Error::Overrun) |
| 253 | } else { | 266 | } else { |
| 254 | Ok(()) | 267 | r.cr1().modify(|w| w.set_idleie(true)); |
| 268 | Ok(sr.idle()) | ||
| 255 | } | 269 | } |
| 256 | } | 270 | } |
| 257 | 271 | ||
| 258 | /// Clear IDLE and return the Sr register | ||
| 259 | fn clear_idle_flag(r: Regs) -> Sr { | ||
| 260 | // SAFETY: read only and we only use Rx related flags | ||
| 261 | |||
| 262 | let sr = sr(r).read(); | ||
| 263 | |||
| 264 | // This read also clears the error and idle interrupt flags on v1. | ||
| 265 | unsafe { rdr(r).read_volatile() }; | ||
| 266 | #[cfg(any(usart_v3, usart_v4))] | ||
| 267 | { | ||
| 268 | let mut clear_idle = regs::Icr(0); | ||
| 269 | clear_idle.set_idle(true); | ||
| 270 | r.icr().write_value(clear_idle); | ||
| 271 | } | ||
| 272 | |||
| 273 | r.cr1().modify(|w| w.set_idleie(true)); | ||
| 274 | |||
| 275 | sr | ||
| 276 | } | ||
| 277 | |||
| 278 | impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { | 272 | impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { |
| 279 | type Error = Error; | 273 | type Error = Error; |
| 280 | } | 274 | } |
