diff options
| author | Tobias Naumann <[email protected]> | 2025-03-21 14:04:33 +0100 |
|---|---|---|
| committer | Tobias Naumann <[email protected]> | 2025-03-21 14:23:25 +0100 |
| commit | 402b78138b76ae125bb9be0a336e5d0bc17e7833 (patch) | |
| tree | 73cc0ddf570954ab0aac1e69398b0c6d8a652376 | |
| parent | ac7c44c1d1465bd125ef9a2c6da7e3b42a52569c (diff) | |
Check and clear idle and error flags together
| -rw-r--r-- | embassy-stm32/src/usart/ringbuffered.rs | 79 |
1 files changed, 39 insertions, 40 deletions
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index d76a52d1e..3ab9f95e0 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -99,8 +99,6 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 99 | // enable idle line interrupt | 99 | // enable idle line interrupt |
| 100 | w.set_idleie(true); | 100 | w.set_idleie(true); |
| 101 | }); | 101 | }); |
| 102 | // Clear all potential error interrupt flags | ||
| 103 | clear_interrupt_flags(r, sr(r).read()); | ||
| 104 | r.cr3().modify(|w| { | 102 | r.cr3().modify(|w| { |
| 105 | // enable Error Interrupt: (Frame error, Noise error, Overrun error) | 103 | // enable Error Interrupt: (Frame error, Noise error, Overrun error) |
| 106 | w.set_eie(true); | 104 | w.set_eie(true); |
| @@ -134,17 +132,15 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 134 | } | 132 | } |
| 135 | 133 | ||
| 136 | /// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and | 134 | /// (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 | 135 | /// 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> { | 136 | fn start_dma_or_check_errors(&mut self) -> Result<(), Error> { |
| 140 | let r = self.info.regs; | 137 | let r = self.info.regs; |
| 141 | 138 | ||
| 142 | let sr = clear_idle_flag(r); | 139 | check_idle_and_errors(r)?; |
| 143 | let res = check_for_errors(sr); | ||
| 144 | if !r.cr3().read().dmar() { | 140 | if !r.cr3().read().dmar() { |
| 145 | self.start_uart(); | 141 | self.start_uart(); |
| 146 | } | 142 | } |
| 147 | res | 143 | Ok(()) |
| 148 | } | 144 | } |
| 149 | 145 | ||
| 150 | /// Read bytes that are readily available in the ring buffer. | 146 | /// Read bytes that are readily available in the ring buffer. |
| @@ -191,13 +187,7 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 191 | 187 | ||
| 192 | compiler_fence(Ordering::SeqCst); | 188 | compiler_fence(Ordering::SeqCst); |
| 193 | 189 | ||
| 194 | // Critical section is needed so that IDLE isn't set after | 190 | 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 | 191 | // Idle line is detected |
| 202 | Poll::Ready(Ok(())) | 192 | Poll::Ready(Ok(())) |
| 203 | } else { | 193 | } else { |
| @@ -240,40 +230,49 @@ impl Drop for RingBufferedUartRx<'_> { | |||
| 240 | } | 230 | } |
| 241 | } | 231 | } |
| 242 | 232 | ||
| 243 | /// Return an error result if the Sr register has errors | 233 | /// Check and clear idle and error interrupts, return true if idle, Err(e) on error |
| 244 | fn check_for_errors(s: Sr) -> Result<(), Error> { | 234 | /// |
| 245 | if s.pe() { | 235 | /// All flags are read and cleared in a single step, respectively. When more than one flag is set |
| 236 | /// at the same time, all flags will be cleared but only one flag will be reported. So the other | ||
| 237 | /// flag(s) will gone missing unnoticed. The error flags are checked first, the idle flag last. | ||
| 238 | /// | ||
| 239 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | ||
| 240 | /// are cleared by a single read to the RDR register. | ||
| 241 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | ||
| 242 | // Critical section is required so that the flags aren't set after read and before clear | ||
| 243 | let sr = critical_section::with(|_| { | ||
| 244 | // SAFETY: read only and we only use Rx related flags | ||
| 245 | let sr = sr(r).read(); | ||
| 246 | |||
| 247 | #[cfg(any(usart_v3, usart_v4))] | ||
| 248 | r.icr().write(|w| { | ||
| 249 | w.set_idle(true); | ||
| 250 | w.set_pe(true); | ||
| 251 | w.set_fe(true); | ||
| 252 | w.set_ne(true); | ||
| 253 | w.set_ore(true); | ||
| 254 | }); | ||
| 255 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 256 | unsafe { | ||
| 257 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) | ||
| 258 | rdr(r).read_volatile() | ||
| 259 | }; | ||
| 260 | sr | ||
| 261 | }); | ||
| 262 | if sr.pe() { | ||
| 246 | Err(Error::Parity) | 263 | Err(Error::Parity) |
| 247 | } else if s.fe() { | 264 | } else if sr.fe() { |
| 248 | Err(Error::Framing) | 265 | Err(Error::Framing) |
| 249 | } else if s.ne() { | 266 | } else if sr.ne() { |
| 250 | Err(Error::Noise) | 267 | Err(Error::Noise) |
| 251 | } else if s.ore() { | 268 | } else if sr.ore() { |
| 252 | Err(Error::Overrun) | 269 | Err(Error::Overrun) |
| 253 | } else { | 270 | } else { |
| 254 | Ok(()) | 271 | r.cr1().modify(|w| w.set_idleie(true)); |
| 272 | Ok(sr.idle()) | ||
| 255 | } | 273 | } |
| 256 | } | 274 | } |
| 257 | 275 | ||
| 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 | #[cfg(any(usart_v3, usart_v4))] | ||
| 265 | r.icr().write(|w| w.set_idle(true)); | ||
| 266 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 267 | unsafe { | ||
| 268 | // This read also clears the error and idle interrupt flags on v1. | ||
| 269 | rdr(r).read_volatile() | ||
| 270 | }; | ||
| 271 | |||
| 272 | r.cr1().modify(|w| w.set_idleie(true)); | ||
| 273 | |||
| 274 | sr | ||
| 275 | } | ||
| 276 | |||
| 277 | impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { | 276 | impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { |
| 278 | type Error = Error; | 277 | type Error = Error; |
| 279 | } | 278 | } |
