diff options
| author | Adam Greig <[email protected]> | 2025-09-19 02:10:56 +0100 |
|---|---|---|
| committer | Adam Greig <[email protected]> | 2025-10-01 21:11:09 +0100 |
| commit | 775123467b0ce6e5daede0795493df9577077a09 (patch) | |
| tree | 4bceb093dc9f367a233c4ea6638499bda9410366 | |
| parent | ef06ff43a14fd016d271c491bd830823ee96b740 (diff) | |
STM32: USART: Change eager_reads from bool to Option<usize>
| -rw-r--r-- | embassy-hal-internal/src/atomic_ring_buffer.rs | 22 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/buffered.rs | 28 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 37 | ||||
| -rw-r--r-- | embassy-stm32/src/usart/ringbuffered.rs | 30 |
4 files changed, 77 insertions, 40 deletions
diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs index 7de96e4e2..8c3889b85 100644 --- a/embassy-hal-internal/src/atomic_ring_buffer.rs +++ b/embassy-hal-internal/src/atomic_ring_buffer.rs | |||
| @@ -133,6 +133,18 @@ impl RingBuffer { | |||
| 133 | self.len.load(Ordering::Relaxed) | 133 | self.len.load(Ordering::Relaxed) |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | /// Return number of items available to read. | ||
| 137 | pub fn available(&self) -> usize { | ||
| 138 | let end = self.end.load(Ordering::Relaxed); | ||
| 139 | let len = self.len.load(Ordering::Relaxed); | ||
| 140 | let start = self.start.load(Ordering::Relaxed); | ||
| 141 | if end >= start { | ||
| 142 | end - start | ||
| 143 | } else { | ||
| 144 | 2 * len - start + end | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 136 | /// Check if buffer is full. | 148 | /// Check if buffer is full. |
| 137 | pub fn is_full(&self) -> bool { | 149 | pub fn is_full(&self) -> bool { |
| 138 | let len = self.len.load(Ordering::Relaxed); | 150 | let len = self.len.load(Ordering::Relaxed); |
| @@ -144,15 +156,7 @@ impl RingBuffer { | |||
| 144 | 156 | ||
| 145 | /// Check if buffer is at least half full. | 157 | /// Check if buffer is at least half full. |
| 146 | pub fn is_half_full(&self) -> bool { | 158 | pub fn is_half_full(&self) -> bool { |
| 147 | let len = self.len.load(Ordering::Relaxed); | 159 | self.available() >= self.len.load(Ordering::Relaxed) / 2 |
| 148 | let start = self.start.load(Ordering::Relaxed); | ||
| 149 | let end = self.end.load(Ordering::Relaxed); | ||
| 150 | let n = if end >= start { | ||
| 151 | end - start | ||
| 152 | } else { | ||
| 153 | 2 * len - start + end | ||
| 154 | }; | ||
| 155 | n >= len / 2 | ||
| 156 | } | 160 | } |
| 157 | 161 | ||
| 158 | /// Check if buffer is empty. | 162 | /// Check if buffer is empty. |
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 165f2e88e..10dc02334 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::slice; | 3 | use core::slice; |
| 4 | use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; |
| 5 | use core::task::Poll; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| @@ -68,8 +68,9 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 68 | // FIXME: Should we disable any further RX interrupts when the buffer becomes full. | 68 | // FIXME: Should we disable any further RX interrupts when the buffer becomes full. |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | if state.eager_reads.load(Ordering::Relaxed) { | 71 | let eager = state.eager_reads.load(Ordering::Relaxed); |
| 72 | if !state.rx_buf.is_empty() { | 72 | if eager > 0 { |
| 73 | if state.rx_buf.available() >= eager { | ||
| 73 | state.rx_waker.wake(); | 74 | state.rx_waker.wake(); |
| 74 | } | 75 | } |
| 75 | } else { | 76 | } else { |
| @@ -138,7 +139,7 @@ pub(super) struct State { | |||
| 138 | tx_done: AtomicBool, | 139 | tx_done: AtomicBool, |
| 139 | tx_rx_refcount: AtomicU8, | 140 | tx_rx_refcount: AtomicU8, |
| 140 | half_duplex_readback: AtomicBool, | 141 | half_duplex_readback: AtomicBool, |
| 141 | eager_reads: AtomicBool, | 142 | eager_reads: AtomicUsize, |
| 142 | } | 143 | } |
| 143 | 144 | ||
| 144 | impl State { | 145 | impl State { |
| @@ -151,7 +152,7 @@ impl State { | |||
| 151 | tx_done: AtomicBool::new(true), | 152 | tx_done: AtomicBool::new(true), |
| 152 | tx_rx_refcount: AtomicU8::new(0), | 153 | tx_rx_refcount: AtomicU8::new(0), |
| 153 | half_duplex_readback: AtomicBool::new(false), | 154 | half_duplex_readback: AtomicBool::new(false), |
| 154 | eager_reads: AtomicBool::new(false), | 155 | eager_reads: AtomicUsize::new(0), |
| 155 | } | 156 | } |
| 156 | } | 157 | } |
| 157 | } | 158 | } |
| @@ -427,7 +428,9 @@ impl<'d> BufferedUart<'d> { | |||
| 427 | let state = T::buffered_state(); | 428 | let state = T::buffered_state(); |
| 428 | let kernel_clock = T::frequency(); | 429 | let kernel_clock = T::frequency(); |
| 429 | 430 | ||
| 430 | state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 431 | state |
| 432 | .eager_reads | ||
| 433 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 431 | state.half_duplex_readback.store( | 434 | state.half_duplex_readback.store( |
| 432 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), | 435 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), |
| 433 | Ordering::Relaxed, | 436 | Ordering::Relaxed, |
| @@ -465,7 +468,9 @@ impl<'d> BufferedUart<'d> { | |||
| 465 | let info = self.rx.info; | 468 | let info = self.rx.info; |
| 466 | let state = self.rx.state; | 469 | let state = self.rx.state; |
| 467 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 470 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 468 | state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 471 | state |
| 472 | .eager_reads | ||
| 473 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 469 | 474 | ||
| 470 | info.rcc.enable_and_reset(); | 475 | info.rcc.enable_and_reset(); |
| 471 | 476 | ||
| @@ -537,7 +542,10 @@ impl<'d> BufferedUart<'d> { | |||
| 537 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 542 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 538 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; | 543 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; |
| 539 | 544 | ||
| 540 | self.rx.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 545 | self.rx |
| 546 | .state | ||
| 547 | .eager_reads | ||
| 548 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 541 | 549 | ||
| 542 | self.rx.info.regs.cr1().modify(|w| { | 550 | self.rx.info.regs.cr1().modify(|w| { |
| 543 | w.set_rxneie(true); | 551 | w.set_rxneie(true); |
| @@ -654,7 +662,9 @@ impl<'d> BufferedUartRx<'d> { | |||
| 654 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 662 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 655 | reconfigure(self.info, self.kernel_clock, config)?; | 663 | reconfigure(self.info, self.kernel_clock, config)?; |
| 656 | 664 | ||
| 657 | self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 665 | self.state |
| 666 | .eager_reads | ||
| 667 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 658 | 668 | ||
| 659 | self.info.regs.cr1().modify(|w| { | 669 | self.info.regs.cr1().modify(|w| { |
| 660 | w.set_rxneie(true); | 670 | w.set_rxneie(true); |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c8012a9b4..0d2d86aca 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, Ordering}; | 7 | use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; |
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| @@ -212,17 +212,20 @@ pub struct Config { | |||
| 212 | /// If false: the error is ignored and cleared | 212 | /// If false: the error is ignored and cleared |
| 213 | pub detect_previous_overrun: bool, | 213 | pub detect_previous_overrun: bool, |
| 214 | 214 | ||
| 215 | /// If true then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` | 215 | /// If `None` (the default) then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` |
| 216 | /// are woken/return as soon as any data is available in the buffer. | 216 | /// typically only wake/return after line idle or after the buffer is at least half full |
| 217 | /// (for `BufferedUartRx`) or the DMA buffer is written at the half or full positions | ||
| 218 | /// (for `RingBufferedUartRx`), though it may also wake/return earlier in some circumstances. | ||
| 217 | /// | 219 | /// |
| 218 | /// If false (the default) then reads started typically only wake/return after | 220 | /// If `Some(n)` then such reads are also woken/return as soon as at least `n` words are |
| 219 | /// line idle or after the buffer is at least half full (`BufferedUartRx`) or | 221 | /// available in the buffer, in addition to waking/returning when the conditions described |
| 220 | /// the DMA buffer is written at the half or full positions (`RingBufferedUartRx`), | 222 | /// above are met. `Some(0)` is treated as `None`. Setting this for `RingBufferedUartRx` |
| 221 | /// though it may also wake/return earlier in some circumstances. | 223 | /// will trigger an interrupt for every received word to check the buffer level, which may |
| 224 | /// impact performance at high data rates. | ||
| 222 | /// | 225 | /// |
| 223 | /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either | 226 | /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either |
| 224 | /// return a single word, a full buffer, or after line idle. | 227 | /// return a single word, a full buffer, or after line idle. |
| 225 | pub eager_reads: bool, | 228 | pub eager_reads: Option<usize>, |
| 226 | 229 | ||
| 227 | /// Set this to true if the line is considered noise free. | 230 | /// Set this to true if the line is considered noise free. |
| 228 | /// This will increase the receiver’s tolerance to clock deviations, | 231 | /// This will increase the receiver’s tolerance to clock deviations, |
| @@ -296,7 +299,7 @@ impl Default for Config { | |||
| 296 | parity: Parity::ParityNone, | 299 | parity: Parity::ParityNone, |
| 297 | // historical behavior | 300 | // historical behavior |
| 298 | detect_previous_overrun: false, | 301 | detect_previous_overrun: false, |
| 299 | eager_reads: false, | 302 | eager_reads: None, |
| 300 | #[cfg(not(usart_v1))] | 303 | #[cfg(not(usart_v1))] |
| 301 | assume_noise_free: false, | 304 | assume_noise_free: false, |
| 302 | #[cfg(any(usart_v3, usart_v4))] | 305 | #[cfg(any(usart_v3, usart_v4))] |
| @@ -997,7 +1000,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 997 | let info = self.info; | 1000 | let info = self.info; |
| 998 | let state = self.state; | 1001 | let state = self.state; |
| 999 | state.tx_rx_refcount.store(1, Ordering::Relaxed); | 1002 | state.tx_rx_refcount.store(1, Ordering::Relaxed); |
| 1000 | state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 1003 | state |
| 1004 | .eager_reads | ||
| 1005 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 1001 | 1006 | ||
| 1002 | info.rcc.enable_and_reset(); | 1007 | info.rcc.enable_and_reset(); |
| 1003 | 1008 | ||
| @@ -1014,7 +1019,9 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 1014 | 1019 | ||
| 1015 | /// Reconfigure the driver | 1020 | /// Reconfigure the driver |
| 1016 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 1021 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 1017 | self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 1022 | self.state |
| 1023 | .eager_reads | ||
| 1024 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 1018 | reconfigure(self.info, self.kernel_clock, config) | 1025 | reconfigure(self.info, self.kernel_clock, config) |
| 1019 | } | 1026 | } |
| 1020 | 1027 | ||
| @@ -1495,7 +1502,9 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1495 | let info = self.rx.info; | 1502 | let info = self.rx.info; |
| 1496 | let state = self.rx.state; | 1503 | let state = self.rx.state; |
| 1497 | state.tx_rx_refcount.store(2, Ordering::Relaxed); | 1504 | state.tx_rx_refcount.store(2, Ordering::Relaxed); |
| 1498 | state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 1505 | state |
| 1506 | .eager_reads | ||
| 1507 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 1499 | 1508 | ||
| 1500 | info.rcc.enable_and_reset(); | 1509 | info.rcc.enable_and_reset(); |
| 1501 | 1510 | ||
| @@ -2080,7 +2089,7 @@ struct State { | |||
| 2080 | rx_waker: AtomicWaker, | 2089 | rx_waker: AtomicWaker, |
| 2081 | tx_waker: AtomicWaker, | 2090 | tx_waker: AtomicWaker, |
| 2082 | tx_rx_refcount: AtomicU8, | 2091 | tx_rx_refcount: AtomicU8, |
| 2083 | eager_reads: AtomicBool, | 2092 | eager_reads: AtomicUsize, |
| 2084 | } | 2093 | } |
| 2085 | 2094 | ||
| 2086 | impl State { | 2095 | impl State { |
| @@ -2089,7 +2098,7 @@ impl State { | |||
| 2089 | rx_waker: AtomicWaker::new(), | 2098 | rx_waker: AtomicWaker::new(), |
| 2090 | tx_waker: AtomicWaker::new(), | 2099 | tx_waker: AtomicWaker::new(), |
| 2091 | tx_rx_refcount: AtomicU8::new(0), | 2100 | tx_rx_refcount: AtomicU8::new(0), |
| 2092 | eager_reads: AtomicBool::new(false), | 2101 | eager_reads: AtomicUsize::new(0), |
| 2093 | } | 2102 | } |
| 2094 | } | 2103 | } |
| 2095 | } | 2104 | } |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index d818e0bcc..27071fb31 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -132,7 +132,9 @@ impl<'d> UartRx<'d, Async> { | |||
| 132 | impl<'d> RingBufferedUartRx<'d> { | 132 | impl<'d> RingBufferedUartRx<'d> { |
| 133 | /// Reconfigure the driver | 133 | /// Reconfigure the driver |
| 134 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 134 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 135 | self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); | 135 | self.state |
| 136 | .eager_reads | ||
| 137 | .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); | ||
| 136 | reconfigure(self.info, self.kernel_clock, config) | 138 | reconfigure(self.info, self.kernel_clock, config) |
| 137 | } | 139 | } |
| 138 | 140 | ||
| @@ -149,7 +151,7 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 149 | // clear all interrupts and DMA Rx Request | 151 | // clear all interrupts and DMA Rx Request |
| 150 | r.cr1().modify(|w| { | 152 | r.cr1().modify(|w| { |
| 151 | // use RXNE only when returning reads early | 153 | // use RXNE only when returning reads early |
| 152 | w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed)); | 154 | w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed) > 0); |
| 153 | // enable parity interrupt if not ParityNone | 155 | // enable parity interrupt if not ParityNone |
| 154 | w.set_peie(w.pce()); | 156 | w.set_peie(w.pce()); |
| 155 | // enable idle line interrupt | 157 | // enable idle line interrupt |
| @@ -261,11 +263,12 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 261 | // However, DMA will clear RXNE, so we can't check directly, and because | 263 | // However, DMA will clear RXNE, so we can't check directly, and because |
| 262 | // the other future borrows `ring_buf`, we can't check `len()` here either. | 264 | // the other future borrows `ring_buf`, we can't check `len()` here either. |
| 263 | // Instead, return from this future and we'll check the length afterwards. | 265 | // Instead, return from this future and we'll check the length afterwards. |
| 264 | let eager = s.eager_reads.load(Ordering::Relaxed); | 266 | let eager = s.eager_reads.load(Ordering::Relaxed) > 0; |
| 265 | 267 | ||
| 266 | if check_idle_and_errors(self.info.regs)? || (eager && uart_init) { | 268 | let idle = check_idle_and_errors(self.info.regs)?; |
| 269 | if idle || (eager && uart_init) { | ||
| 267 | // Idle line is detected, or eager reads is set and some data is available. | 270 | // Idle line is detected, or eager reads is set and some data is available. |
| 268 | Poll::Ready(Ok(())) | 271 | Poll::Ready(Ok(idle)) |
| 269 | } else { | 272 | } else { |
| 270 | uart_init = true; | 273 | uart_init = true; |
| 271 | Poll::Pending | 274 | Poll::Pending |
| @@ -288,13 +291,24 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 288 | }); | 291 | }); |
| 289 | 292 | ||
| 290 | match select(uart, dma).await { | 293 | match select(uart, dma).await { |
| 291 | Either::Left((result, _)) => { | 294 | // UART woke with line idle |
| 292 | if self.ring_buf.len().unwrap_or(0) > 0 || result.is_err() { | 295 | Either::Left((Ok(true), _)) => { |
| 293 | return result; | 296 | return Ok(()); |
| 297 | } | ||
| 298 | // UART woke without idle or error: word received | ||
| 299 | Either::Left((Ok(false), _)) => { | ||
| 300 | let eager = self.state.eager_reads.load(Ordering::Relaxed); | ||
| 301 | if eager > 0 && self.ring_buf.len().unwrap_or(0) >= eager { | ||
| 302 | return Ok(()); | ||
| 294 | } else { | 303 | } else { |
| 295 | continue; | 304 | continue; |
| 296 | } | 305 | } |
| 297 | } | 306 | } |
| 307 | // UART woke with error | ||
| 308 | Either::Left((Err(e), _)) => { | ||
| 309 | return Err(e); | ||
| 310 | } | ||
| 311 | // DMA woke | ||
| 298 | Either::Right(((), _)) => return Ok(()), | 312 | Either::Right(((), _)) => return Ok(()), |
| 299 | } | 313 | } |
| 300 | } | 314 | } |
