Skip to content

Commit 914110b

Browse files
committed
STM32: USART: Change eager_reads from bool to Option<usize>
1 parent d384400 commit 914110b

File tree

4 files changed

+77
-40
lines changed

4 files changed

+77
-40
lines changed

embassy-hal-internal/src/atomic_ring_buffer.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ impl RingBuffer {
133133
self.len.load(Ordering::Relaxed)
134134
}
135135

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+
136148
/// Check if buffer is full.
137149
pub fn is_full(&self) -> bool {
138150
let len = self.len.load(Ordering::Relaxed);
@@ -144,15 +156,7 @@ impl RingBuffer {
144156

145157
/// Check if buffer is at least half full.
146158
pub fn is_half_full(&self) -> bool {
147-
let len = self.len.load(Ordering::Relaxed);
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
159+
self.available() >= self.len.load(Ordering::Relaxed) / 2
156160
}
157161

158162
/// Check if buffer is empty.

embassy-stm32/src/usart/buffered.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::future::poll_fn;
22
use core::marker::PhantomData;
33
use core::slice;
4-
use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
4+
use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
55
use core::task::Poll;
66

77
use embassy_embedded_hal::SetConfig;
@@ -68,8 +68,9 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) {
6868
// FIXME: Should we disable any further RX interrupts when the buffer becomes full.
6969
}
7070

71-
if state.eager_reads.load(Ordering::Relaxed) {
72-
if !state.rx_buf.is_empty() {
71+
let eager = state.eager_reads.load(Ordering::Relaxed);
72+
if eager > 0 {
73+
if state.rx_buf.available() >= eager {
7374
state.rx_waker.wake();
7475
}
7576
} else {
@@ -138,7 +139,7 @@ pub(super) struct State {
138139
tx_done: AtomicBool,
139140
tx_rx_refcount: AtomicU8,
140141
half_duplex_readback: AtomicBool,
141-
eager_reads: AtomicBool,
142+
eager_reads: AtomicUsize,
142143
}
143144

144145
impl State {
@@ -151,7 +152,7 @@ impl State {
151152
tx_done: AtomicBool::new(true),
152153
tx_rx_refcount: AtomicU8::new(0),
153154
half_duplex_readback: AtomicBool::new(false),
154-
eager_reads: AtomicBool::new(false),
155+
eager_reads: AtomicUsize::new(0),
155156
}
156157
}
157158
}
@@ -427,7 +428,9 @@ impl<'d> BufferedUart<'d> {
427428
let state = T::buffered_state();
428429
let kernel_clock = T::frequency();
429430

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);
431434
state.half_duplex_readback.store(
432435
config.duplex == Duplex::Half(HalfDuplexReadback::Readback),
433436
Ordering::Relaxed,
@@ -465,7 +468,9 @@ impl<'d> BufferedUart<'d> {
465468
let info = self.rx.info;
466469
let state = self.rx.state;
467470
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);
469474

470475
info.rcc.enable_and_reset();
471476

@@ -537,7 +542,10 @@ impl<'d> BufferedUart<'d> {
537542
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
538543
reconfigure(self.rx.info, self.rx.kernel_clock, config)?;
539544

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);
541549

542550
self.rx.info.regs.cr1().modify(|w| {
543551
w.set_rxneie(true);
@@ -654,7 +662,9 @@ impl<'d> BufferedUartRx<'d> {
654662
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
655663
reconfigure(self.info, self.kernel_clock, config)?;
656664

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);
658668

659669
self.info.regs.cr1().modify(|w| {
660670
w.set_rxneie(true);

embassy-stm32/src/usart/mod.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use core::future::poll_fn;
66
use core::marker::PhantomData;
7-
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, Ordering};
7+
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
88
use core::task::Poll;
99

1010
use embassy_embedded_hal::SetConfig;
@@ -212,17 +212,20 @@ pub struct Config {
212212
/// If false: the error is ignored and cleared
213213
pub detect_previous_overrun: bool,
214214

215-
/// If true then read-like calls on `BufferedUartRx` and `RingBufferedUartRx`
216-
/// are woken/return as soon as any data is available in the buffer.
215+
/// If `None` (the default) then read-like calls on `BufferedUartRx` and `RingBufferedUartRx`
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.
217219
///
218-
/// If false (the default) then reads started typically only wake/return after
219-
/// line idle or after the buffer is at least half full (`BufferedUartRx`) or
220-
/// the DMA buffer is written at the half or full positions (`RingBufferedUartRx`),
221-
/// though it may also wake/return earlier in some circumstances.
220+
/// If `Some(n)` then such reads are also woken/return as soon as at least `n` words are
221+
/// available in the buffer, in addition to waking/returning when the conditions described
222+
/// above are met. `Some(0)` is treated as `None`. Setting this for `RingBufferedUartRx`
223+
/// will trigger an interrupt for every received word to check the buffer level, which may
224+
/// impact performance at high data rates.
222225
///
223226
/// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either
224227
/// return a single word, a full buffer, or after line idle.
225-
pub eager_reads: bool,
228+
pub eager_reads: Option<usize>,
226229

227230
/// Set this to true if the line is considered noise free.
228231
/// This will increase the receiver’s tolerance to clock deviations,
@@ -296,7 +299,7 @@ impl Default for Config {
296299
parity: Parity::ParityNone,
297300
// historical behavior
298301
detect_previous_overrun: false,
299-
eager_reads: false,
302+
eager_reads: None,
300303
#[cfg(not(usart_v1))]
301304
assume_noise_free: false,
302305
#[cfg(any(usart_v3, usart_v4))]
@@ -997,7 +1000,9 @@ impl<'d, M: Mode> UartRx<'d, M> {
9971000
let info = self.info;
9981001
let state = self.state;
9991002
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);
10011006

10021007
info.rcc.enable_and_reset();
10031008

@@ -1014,7 +1019,9 @@ impl<'d, M: Mode> UartRx<'d, M> {
10141019

10151020
/// Reconfigure the driver
10161021
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);
10181025
reconfigure(self.info, self.kernel_clock, config)
10191026
}
10201027

@@ -1495,7 +1502,9 @@ impl<'d, M: Mode> Uart<'d, M> {
14951502
let info = self.rx.info;
14961503
let state = self.rx.state;
14971504
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);
14991508

15001509
info.rcc.enable_and_reset();
15011510

@@ -2080,7 +2089,7 @@ struct State {
20802089
rx_waker: AtomicWaker,
20812090
tx_waker: AtomicWaker,
20822091
tx_rx_refcount: AtomicU8,
2083-
eager_reads: AtomicBool,
2092+
eager_reads: AtomicUsize,
20842093
}
20852094

20862095
impl State {
@@ -2089,7 +2098,7 @@ impl State {
20892098
rx_waker: AtomicWaker::new(),
20902099
tx_waker: AtomicWaker::new(),
20912100
tx_rx_refcount: AtomicU8::new(0),
2092-
eager_reads: AtomicBool::new(false),
2101+
eager_reads: AtomicUsize::new(0),
20932102
}
20942103
}
20952104
}

embassy-stm32/src/usart/ringbuffered.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ impl<'d> UartRx<'d, Async> {
132132
impl<'d> RingBufferedUartRx<'d> {
133133
/// Reconfigure the driver
134134
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);
136138
reconfigure(self.info, self.kernel_clock, config)
137139
}
138140

@@ -149,7 +151,7 @@ impl<'d> RingBufferedUartRx<'d> {
149151
// clear all interrupts and DMA Rx Request
150152
r.cr1().modify(|w| {
151153
// 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);
153155
// enable parity interrupt if not ParityNone
154156
w.set_peie(w.pce());
155157
// enable idle line interrupt
@@ -261,11 +263,12 @@ impl<'d> RingBufferedUartRx<'d> {
261263
// However, DMA will clear RXNE, so we can't check directly, and because
262264
// the other future borrows `ring_buf`, we can't check `len()` here either.
263265
// 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;
265267

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) {
267270
// Idle line is detected, or eager reads is set and some data is available.
268-
Poll::Ready(Ok(()))
271+
Poll::Ready(Ok(idle))
269272
} else {
270273
uart_init = true;
271274
Poll::Pending
@@ -288,13 +291,24 @@ impl<'d> RingBufferedUartRx<'d> {
288291
});
289292

290293
match select(uart, dma).await {
291-
Either::Left((result, _)) => {
292-
if self.ring_buf.len().unwrap_or(0) > 0 || result.is_err() {
293-
return result;
294+
// UART woke with line idle
295+
Either::Left((Ok(true), _)) => {
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(());
294303
} else {
295304
continue;
296305
}
297306
}
307+
// UART woke with error
308+
Either::Left((Err(e), _)) => {
309+
return Err(e);
310+
}
311+
// DMA woke
298312
Either::Right(((), _)) => return Ok(()),
299313
}
300314
}

0 commit comments

Comments
 (0)