aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Naumann <[email protected]>2025-03-21 14:04:33 +0100
committerTobias Naumann <[email protected]>2025-03-21 14:23:25 +0100
commit402b78138b76ae125bb9be0a336e5d0bc17e7833 (patch)
tree73cc0ddf570954ab0aac1e69398b0c6d8a652376
parentac7c44c1d1465bd125ef9a2c6da7e3b42a52569c (diff)
Check and clear idle and error flags together
-rw-r--r--embassy-stm32/src/usart/ringbuffered.rs79
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
244fn 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.
241fn 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
259fn 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
277impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { 276impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> {
278 type Error = Error; 277 type Error = Error;
279} 278}