Skip to content

Commit 194a721

Browse files
authored
Merge pull request #4668 from adamgreig/stm32-uart-eager-reads
STM32: USART: Add `eager_reads` config option
2 parents d382bf0 + 7751234 commit 194a721

File tree

5 files changed

+239
-64
lines changed

5 files changed

+239
-64
lines changed

embassy-hal-internal/src/atomic_ring_buffer.rs

Lines changed: 41 additions & 2 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);
@@ -142,6 +154,11 @@ impl RingBuffer {
142154
self.wrap(start + len) == end
143155
}
144156

157+
/// Check if buffer is at least half full.
158+
pub fn is_half_full(&self) -> bool {
159+
self.available() >= self.len.load(Ordering::Relaxed) / 2
160+
}
161+
145162
/// Check if buffer is empty.
146163
pub fn is_empty(&self) -> bool {
147164
let start = self.start.load(Ordering::Relaxed);
@@ -394,6 +411,7 @@ mod tests {
394411
rb.init(b.as_mut_ptr(), 4);
395412

396413
assert_eq!(rb.is_empty(), true);
414+
assert_eq!(rb.is_half_full(), false);
397415
assert_eq!(rb.is_full(), false);
398416

399417
rb.writer().push(|buf| {
@@ -406,6 +424,7 @@ mod tests {
406424
});
407425

408426
assert_eq!(rb.is_empty(), false);
427+
assert_eq!(rb.is_half_full(), true);
409428
assert_eq!(rb.is_full(), true);
410429

411430
rb.writer().push(|buf| {
@@ -415,6 +434,7 @@ mod tests {
415434
});
416435

417436
assert_eq!(rb.is_empty(), false);
437+
assert_eq!(rb.is_half_full(), true);
418438
assert_eq!(rb.is_full(), true);
419439

420440
rb.reader().pop(|buf| {
@@ -424,6 +444,7 @@ mod tests {
424444
});
425445

426446
assert_eq!(rb.is_empty(), false);
447+
assert_eq!(rb.is_half_full(), true);
427448
assert_eq!(rb.is_full(), false);
428449

429450
rb.reader().pop(|buf| {
@@ -432,6 +453,7 @@ mod tests {
432453
});
433454

434455
assert_eq!(rb.is_empty(), false);
456+
assert_eq!(rb.is_half_full(), true);
435457
assert_eq!(rb.is_full(), false);
436458

437459
rb.reader().pop(|buf| {
@@ -447,6 +469,7 @@ mod tests {
447469
});
448470

449471
assert_eq!(rb.is_empty(), true);
472+
assert_eq!(rb.is_half_full(), false);
450473
assert_eq!(rb.is_full(), false);
451474

452475
rb.reader().pop(|buf| {
@@ -460,14 +483,28 @@ mod tests {
460483
1
461484
});
462485

486+
assert_eq!(rb.is_empty(), false);
487+
assert_eq!(rb.is_half_full(), false);
488+
assert_eq!(rb.is_full(), false);
489+
463490
rb.writer().push(|buf| {
464491
assert_eq!(3, buf.len());
465492
buf[0] = 11;
466-
buf[1] = 12;
467-
2
493+
1
494+
});
495+
496+
assert_eq!(rb.is_empty(), false);
497+
assert_eq!(rb.is_half_full(), true);
498+
assert_eq!(rb.is_full(), false);
499+
500+
rb.writer().push(|buf| {
501+
assert_eq!(2, buf.len());
502+
buf[0] = 12;
503+
1
468504
});
469505

470506
assert_eq!(rb.is_empty(), false);
507+
assert_eq!(rb.is_half_full(), true);
471508
assert_eq!(rb.is_full(), false);
472509

473510
rb.writer().push(|buf| {
@@ -477,6 +514,7 @@ mod tests {
477514
});
478515

479516
assert_eq!(rb.is_empty(), false);
517+
assert_eq!(rb.is_half_full(), true);
480518
assert_eq!(rb.is_full(), true);
481519
}
482520
}
@@ -490,6 +528,7 @@ mod tests {
490528
rb.init(b.as_mut_ptr(), b.len());
491529

492530
assert_eq!(rb.is_empty(), true);
531+
assert_eq!(rb.is_half_full(), true);
493532
assert_eq!(rb.is_full(), true);
494533

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

embassy-stm32/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt.
2828
- fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode.
2929
- feat: stm32/rcc/mco: Added support for IO driver strength when using Master Clock Out IO. This changes signature on Mco::new taking a McoConfig struct ([#4679](https://github.com/embassy-rs/embassy/pull/4679))
30+
- feat: derive Clone, Copy and defmt::Format for all SPI-related configs
31+
- 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))
32+
- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
33+
- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer
3034

3135
## 0.4.0 - 2025-08-26
3236

embassy-stm32/src/usart/buffered.rs

Lines changed: 50 additions & 17 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,15 @@ 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+
let eager = state.eager_reads.load(Ordering::Relaxed);
72+
if eager > 0 {
73+
if state.rx_buf.available() >= eager {
74+
state.rx_waker.wake();
75+
}
76+
} else {
77+
if state.rx_buf.is_half_full() {
78+
state.rx_waker.wake();
79+
}
7380
}
7481
}
7582

@@ -132,6 +139,7 @@ pub(super) struct State {
132139
tx_done: AtomicBool,
133140
tx_rx_refcount: AtomicU8,
134141
half_duplex_readback: AtomicBool,
142+
eager_reads: AtomicUsize,
135143
}
136144

137145
impl State {
@@ -144,6 +152,7 @@ impl State {
144152
tx_done: AtomicBool::new(true),
145153
tx_rx_refcount: AtomicU8::new(0),
146154
half_duplex_readback: AtomicBool::new(false),
155+
eager_reads: AtomicUsize::new(0),
147156
}
148157
}
149158
}
@@ -419,6 +428,9 @@ impl<'d> BufferedUart<'d> {
419428
let state = T::buffered_state();
420429
let kernel_clock = T::frequency();
421430

431+
state
432+
.eager_reads
433+
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
422434
state.half_duplex_readback.store(
423435
config.duplex == Duplex::Half(HalfDuplexReadback::Readback),
424436
Ordering::Relaxed,
@@ -456,6 +468,9 @@ impl<'d> BufferedUart<'d> {
456468
let info = self.rx.info;
457469
let state = self.rx.state;
458470
state.tx_rx_refcount.store(2, Ordering::Relaxed);
471+
state
472+
.eager_reads
473+
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
459474

460475
info.rcc.enable_and_reset();
461476

@@ -527,6 +542,11 @@ impl<'d> BufferedUart<'d> {
527542
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
528543
reconfigure(self.rx.info, self.rx.kernel_clock, config)?;
529544

545+
self.rx
546+
.state
547+
.eager_reads
548+
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
549+
530550
self.rx.info.regs.cr1().modify(|w| {
531551
w.set_rxneie(true);
532552
w.set_idleie(true);
@@ -553,24 +573,30 @@ impl<'d> BufferedUartRx<'d> {
553573
poll_fn(move |cx| {
554574
let state = self.state;
555575
let mut rx_reader = unsafe { state.rx_buf.reader() };
556-
let data = rx_reader.pop_slice();
576+
let mut buf_len = 0;
577+
let mut data = rx_reader.pop_slice();
557578

558-
if !data.is_empty() {
559-
let len = data.len().min(buf.len());
560-
buf[..len].copy_from_slice(&data[..len]);
579+
while !data.is_empty() && buf_len < buf.len() {
580+
let data_len = data.len().min(buf.len() - buf_len);
581+
buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]);
582+
buf_len += data_len;
561583

562584
let do_pend = state.rx_buf.is_full();
563-
rx_reader.pop_done(len);
585+
rx_reader.pop_done(data_len);
564586

565587
if do_pend {
566588
self.info.interrupt.pend();
567589
}
568590

569-
return Poll::Ready(Ok(len));
591+
data = rx_reader.pop_slice();
570592
}
571593

572-
state.rx_waker.register(cx.waker());
573-
Poll::Pending
594+
if buf_len != 0 {
595+
Poll::Ready(Ok(buf_len))
596+
} else {
597+
state.rx_waker.register(cx.waker());
598+
Poll::Pending
599+
}
574600
})
575601
.await
576602
}
@@ -579,21 +605,24 @@ impl<'d> BufferedUartRx<'d> {
579605
loop {
580606
let state = self.state;
581607
let mut rx_reader = unsafe { state.rx_buf.reader() };
582-
let data = rx_reader.pop_slice();
608+
let mut buf_len = 0;
609+
let mut data = rx_reader.pop_slice();
583610

584-
if !data.is_empty() {
585-
let len = data.len().min(buf.len());
586-
buf[..len].copy_from_slice(&data[..len]);
611+
while !data.is_empty() && buf_len < buf.len() {
612+
let data_len = data.len().min(buf.len() - buf_len);
613+
buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]);
614+
buf_len += data_len;
587615

588616
let do_pend = state.rx_buf.is_full();
589-
rx_reader.pop_done(len);
617+
rx_reader.pop_done(data_len);
590618

591619
if do_pend {
592620
self.info.interrupt.pend();
593621
}
594622

595-
return Ok(len);
623+
data = rx_reader.pop_slice();
596624
}
625+
return Ok(buf_len);
597626
}
598627
}
599628

@@ -633,6 +662,10 @@ impl<'d> BufferedUartRx<'d> {
633662
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
634663
reconfigure(self.info, self.kernel_clock, config)?;
635664

665+
self.state
666+
.eager_reads
667+
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
668+
636669
self.info.regs.cr1().modify(|w| {
637670
w.set_rxneie(true);
638671
w.set_idleie(true);

0 commit comments

Comments
 (0)