Skip to content

Commit 224ebef

Browse files
committed
STM32: USART: Add eager_reads config option
1 parent fe95e33 commit 224ebef

File tree

5 files changed

+131
-49
lines changed

5 files changed

+131
-49
lines changed

embassy-hal-internal/src/atomic_ring_buffer.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,19 @@ impl RingBuffer {
142142
self.wrap(start + len) == end
143143
}
144144

145+
/// Check if buffer is at least half full.
146+
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
156+
}
157+
145158
/// Check if buffer is empty.
146159
pub fn is_empty(&self) -> bool {
147160
let start = self.start.load(Ordering::Relaxed);
@@ -394,6 +407,7 @@ mod tests {
394407
rb.init(b.as_mut_ptr(), 4);
395408

396409
assert_eq!(rb.is_empty(), true);
410+
assert_eq!(rb.is_half_full(), false);
397411
assert_eq!(rb.is_full(), false);
398412

399413
rb.writer().push(|buf| {
@@ -406,6 +420,7 @@ mod tests {
406420
});
407421

408422
assert_eq!(rb.is_empty(), false);
423+
assert_eq!(rb.is_half_full(), true);
409424
assert_eq!(rb.is_full(), true);
410425

411426
rb.writer().push(|buf| {
@@ -415,6 +430,7 @@ mod tests {
415430
});
416431

417432
assert_eq!(rb.is_empty(), false);
433+
assert_eq!(rb.is_half_full(), true);
418434
assert_eq!(rb.is_full(), true);
419435

420436
rb.reader().pop(|buf| {
@@ -424,6 +440,7 @@ mod tests {
424440
});
425441

426442
assert_eq!(rb.is_empty(), false);
443+
assert_eq!(rb.is_half_full(), true);
427444
assert_eq!(rb.is_full(), false);
428445

429446
rb.reader().pop(|buf| {
@@ -432,6 +449,7 @@ mod tests {
432449
});
433450

434451
assert_eq!(rb.is_empty(), false);
452+
assert_eq!(rb.is_half_full(), true);
435453
assert_eq!(rb.is_full(), false);
436454

437455
rb.reader().pop(|buf| {
@@ -447,6 +465,7 @@ mod tests {
447465
});
448466

449467
assert_eq!(rb.is_empty(), true);
468+
assert_eq!(rb.is_half_full(), false);
450469
assert_eq!(rb.is_full(), false);
451470

452471
rb.reader().pop(|buf| {
@@ -460,14 +479,28 @@ mod tests {
460479
1
461480
});
462481

482+
assert_eq!(rb.is_empty(), false);
483+
assert_eq!(rb.is_half_full(), false);
484+
assert_eq!(rb.is_full(), false);
485+
463486
rb.writer().push(|buf| {
464487
assert_eq!(3, buf.len());
465488
buf[0] = 11;
466-
buf[1] = 12;
467-
2
489+
1
490+
});
491+
492+
assert_eq!(rb.is_empty(), false);
493+
assert_eq!(rb.is_half_full(), true);
494+
assert_eq!(rb.is_full(), false);
495+
496+
rb.writer().push(|buf| {
497+
assert_eq!(2, buf.len());
498+
buf[0] = 12;
499+
1
468500
});
469501

470502
assert_eq!(rb.is_empty(), false);
503+
assert_eq!(rb.is_half_full(), true);
471504
assert_eq!(rb.is_full(), false);
472505

473506
rb.writer().push(|buf| {
@@ -477,6 +510,7 @@ mod tests {
477510
});
478511

479512
assert_eq!(rb.is_empty(), false);
513+
assert_eq!(rb.is_half_full(), true);
480514
assert_eq!(rb.is_full(), true);
481515
}
482516
}
@@ -490,6 +524,7 @@ mod tests {
490524
rb.init(b.as_mut_ptr(), b.len());
491525

492526
assert_eq!(rb.is_empty(), true);
527+
assert_eq!(rb.is_half_full(), true);
493528
assert_eq!(rb.is_full(), true);
494529

495530
rb.writer().push(|buf| {

embassy-stm32/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes
2020
- feat: Configurable gpio speed for QSPI
2121
- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs
22+
- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))
2223

2324
## 0.4.0 - 2025-08-26
2425

embassy-stm32/src/usart/buffered.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ 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.rx_buf.is_empty() {
72-
state.rx_waker.wake();
71+
if state.eager_reads.load(Ordering::Relaxed) {
72+
if !state.rx_buf.is_empty() {
73+
state.rx_waker.wake();
74+
}
75+
} else {
76+
if state.rx_buf.is_half_full() {
77+
state.rx_waker.wake();
78+
}
7379
}
7480
}
7581

@@ -132,6 +138,7 @@ pub(super) struct State {
132138
tx_done: AtomicBool,
133139
tx_rx_refcount: AtomicU8,
134140
half_duplex_readback: AtomicBool,
141+
eager_reads: AtomicBool,
135142
}
136143

137144
impl State {
@@ -144,6 +151,7 @@ impl State {
144151
tx_done: AtomicBool::new(true),
145152
tx_rx_refcount: AtomicU8::new(0),
146153
half_duplex_readback: AtomicBool::new(false),
154+
eager_reads: AtomicBool::new(false),
147155
}
148156
}
149157
}
@@ -419,6 +427,7 @@ impl<'d> BufferedUart<'d> {
419427
let state = T::buffered_state();
420428
let kernel_clock = T::frequency();
421429

430+
state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
422431
state.half_duplex_readback.store(
423432
config.duplex == Duplex::Half(HalfDuplexReadback::Readback),
424433
Ordering::Relaxed,
@@ -456,6 +465,7 @@ impl<'d> BufferedUart<'d> {
456465
let info = self.rx.info;
457466
let state = self.rx.state;
458467
state.tx_rx_refcount.store(2, Ordering::Relaxed);
468+
state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
459469

460470
info.rcc.enable_and_reset();
461471

@@ -527,6 +537,8 @@ impl<'d> BufferedUart<'d> {
527537
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
528538
reconfigure(self.rx.info, self.rx.kernel_clock, config)?;
529539

540+
self.rx.state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
541+
530542
self.rx.info.regs.cr1().modify(|w| {
531543
w.set_rxneie(true);
532544
w.set_idleie(true);
@@ -633,6 +645,8 @@ impl<'d> BufferedUartRx<'d> {
633645
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
634646
reconfigure(self.info, self.kernel_clock, config)?;
635647

648+
self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
649+
636650
self.info.regs.cr1().modify(|w| {
637651
w.set_rxneie(true);
638652
w.set_idleie(true);

embassy-stm32/src/usart/mod.rs

Lines changed: 19 additions & 1 deletion
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, AtomicU8, Ordering};
7+
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, Ordering};
88
use core::task::Poll;
99

1010
use embassy_embedded_hal::SetConfig;
@@ -206,6 +206,18 @@ pub struct Config {
206206
/// If false: the error is ignored and cleared
207207
pub detect_previous_overrun: bool,
208208

209+
/// If true then read-like calls on `BufferedUartRx` and `RingBufferedUartRx`
210+
/// are woken/return as soon as any data is available in the buffer.
211+
///
212+
/// If false (the default) then reads started typically only wake/return after
213+
/// line idle or after the buffer is at least half full (`BufferedUartRx`) or
214+
/// the DMA buffer is written at the half or full positions (`RingBufferedUartRx`),
215+
/// though it may also wake/return earlier in some circumstances.
216+
///
217+
/// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either
218+
/// return a single word, a full buffer, or after line idle.
219+
pub eager_reads: bool,
220+
209221
/// Set this to true if the line is considered noise free.
210222
/// This will increase the receiver’s tolerance to clock deviations,
211223
/// but will effectively disable noise detection.
@@ -270,6 +282,7 @@ impl Default for Config {
270282
parity: Parity::ParityNone,
271283
// historical behavior
272284
detect_previous_overrun: false,
285+
eager_reads: false,
273286
#[cfg(not(usart_v1))]
274287
assume_noise_free: false,
275288
#[cfg(any(usart_v3, usart_v4))]
@@ -966,6 +979,7 @@ impl<'d, M: Mode> UartRx<'d, M> {
966979
let info = self.info;
967980
let state = self.state;
968981
state.tx_rx_refcount.store(1, Ordering::Relaxed);
982+
state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
969983

970984
info.rcc.enable_and_reset();
971985

@@ -982,6 +996,7 @@ impl<'d, M: Mode> UartRx<'d, M> {
982996

983997
/// Reconfigure the driver
984998
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
999+
self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
9851000
reconfigure(self.info, self.kernel_clock, config)
9861001
}
9871002

@@ -1462,6 +1477,7 @@ impl<'d, M: Mode> Uart<'d, M> {
14621477
let info = self.rx.info;
14631478
let state = self.rx.state;
14641479
state.tx_rx_refcount.store(2, Ordering::Relaxed);
1480+
state.eager_reads.store(config.eager_reads, Ordering::Relaxed);
14651481

14661482
info.rcc.enable_and_reset();
14671483

@@ -2022,6 +2038,7 @@ struct State {
20222038
rx_waker: AtomicWaker,
20232039
tx_waker: AtomicWaker,
20242040
tx_rx_refcount: AtomicU8,
2041+
eager_reads: AtomicBool,
20252042
}
20262043

20272044
impl State {
@@ -2030,6 +2047,7 @@ impl State {
20302047
rx_waker: AtomicWaker::new(),
20312048
tx_waker: AtomicWaker::new(),
20322049
tx_rx_refcount: AtomicU8::new(0),
2050+
eager_reads: AtomicBool::new(false),
20332051
}
20342052
}
20352053
}

0 commit comments

Comments
 (0)