Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions embassy-hal-internal/src/atomic_ring_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ impl RingBuffer {
self.len.load(Ordering::Relaxed)
}

/// Return number of items available to read.
pub fn available(&self) -> usize {
let end = self.end.load(Ordering::Relaxed);
let len = self.len.load(Ordering::Relaxed);
let start = self.start.load(Ordering::Relaxed);
if end >= start {
end - start
} else {
2 * len - start + end
}
}

/// Check if buffer is full.
pub fn is_full(&self) -> bool {
let len = self.len.load(Ordering::Relaxed);
Expand All @@ -142,6 +154,11 @@ impl RingBuffer {
self.wrap(start + len) == end
}

/// Check if buffer is at least half full.
pub fn is_half_full(&self) -> bool {
self.available() >= self.len.load(Ordering::Relaxed) / 2
}

/// Check if buffer is empty.
pub fn is_empty(&self) -> bool {
let start = self.start.load(Ordering::Relaxed);
Expand Down Expand Up @@ -394,6 +411,7 @@ mod tests {
rb.init(b.as_mut_ptr(), 4);

assert_eq!(rb.is_empty(), true);
assert_eq!(rb.is_half_full(), false);
assert_eq!(rb.is_full(), false);

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

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), true);

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

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), true);

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

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), false);

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

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), false);

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

assert_eq!(rb.is_empty(), true);
assert_eq!(rb.is_half_full(), false);
assert_eq!(rb.is_full(), false);

rb.reader().pop(|buf| {
Expand All @@ -460,14 +483,28 @@ mod tests {
1
});

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), false);
assert_eq!(rb.is_full(), false);

rb.writer().push(|buf| {
assert_eq!(3, buf.len());
buf[0] = 11;
buf[1] = 12;
2
1
});

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), false);

rb.writer().push(|buf| {
assert_eq!(2, buf.len());
buf[0] = 12;
1
});

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), false);

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

assert_eq!(rb.is_empty(), false);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), true);
}
}
Expand All @@ -490,6 +528,7 @@ mod tests {
rb.init(b.as_mut_ptr(), b.len());

assert_eq!(rb.is_empty(), true);
assert_eq!(rb.is_half_full(), true);
assert_eq!(rb.is_full(), true);

rb.writer().push(|buf| {
Expand Down
4 changes: 4 additions & 0 deletions embassy-stm32/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt.
- fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode.
- 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))
- feat: derive Clone, Copy and defmt::Format for all SPI-related configs
- 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))
- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer

## 0.4.0 - 2025-08-26

Expand Down
67 changes: 50 additions & 17 deletions embassy-stm32/src/usart/buffered.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::slice;
use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
use core::task::Poll;

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

if !state.rx_buf.is_empty() {
state.rx_waker.wake();
let eager = state.eager_reads.load(Ordering::Relaxed);
if eager > 0 {
if state.rx_buf.available() >= eager {
state.rx_waker.wake();
}
} else {
if state.rx_buf.is_half_full() {
state.rx_waker.wake();
}
}
}

Expand Down Expand Up @@ -132,6 +139,7 @@ pub(super) struct State {
tx_done: AtomicBool,
tx_rx_refcount: AtomicU8,
half_duplex_readback: AtomicBool,
eager_reads: AtomicUsize,
}

impl State {
Expand All @@ -144,6 +152,7 @@ impl State {
tx_done: AtomicBool::new(true),
tx_rx_refcount: AtomicU8::new(0),
half_duplex_readback: AtomicBool::new(false),
eager_reads: AtomicUsize::new(0),
}
}
}
Expand Down Expand Up @@ -419,6 +428,9 @@ impl<'d> BufferedUart<'d> {
let state = T::buffered_state();
let kernel_clock = T::frequency();

state
.eager_reads
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
state.half_duplex_readback.store(
config.duplex == Duplex::Half(HalfDuplexReadback::Readback),
Ordering::Relaxed,
Expand Down Expand Up @@ -456,6 +468,9 @@ impl<'d> BufferedUart<'d> {
let info = self.rx.info;
let state = self.rx.state;
state.tx_rx_refcount.store(2, Ordering::Relaxed);
state
.eager_reads
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);

info.rcc.enable_and_reset();

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

self.rx
.state
.eager_reads
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);

self.rx.info.regs.cr1().modify(|w| {
w.set_rxneie(true);
w.set_idleie(true);
Expand All @@ -553,24 +573,30 @@ impl<'d> BufferedUartRx<'d> {
poll_fn(move |cx| {
let state = self.state;
let mut rx_reader = unsafe { state.rx_buf.reader() };
let data = rx_reader.pop_slice();
let mut buf_len = 0;
let mut data = rx_reader.pop_slice();

if !data.is_empty() {
let len = data.len().min(buf.len());
buf[..len].copy_from_slice(&data[..len]);
while !data.is_empty() && buf_len < buf.len() {
let data_len = data.len().min(buf.len() - buf_len);
buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]);
buf_len += data_len;

let do_pend = state.rx_buf.is_full();
rx_reader.pop_done(len);
rx_reader.pop_done(data_len);

if do_pend {
self.info.interrupt.pend();
}

return Poll::Ready(Ok(len));
data = rx_reader.pop_slice();
}

state.rx_waker.register(cx.waker());
Poll::Pending
if buf_len != 0 {
Poll::Ready(Ok(buf_len))
} else {
state.rx_waker.register(cx.waker());
Poll::Pending
}
})
.await
}
Expand All @@ -579,21 +605,24 @@ impl<'d> BufferedUartRx<'d> {
loop {
let state = self.state;
let mut rx_reader = unsafe { state.rx_buf.reader() };
let data = rx_reader.pop_slice();
let mut buf_len = 0;
let mut data = rx_reader.pop_slice();

if !data.is_empty() {
let len = data.len().min(buf.len());
buf[..len].copy_from_slice(&data[..len]);
while !data.is_empty() && buf_len < buf.len() {
let data_len = data.len().min(buf.len() - buf_len);
buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]);
buf_len += data_len;

let do_pend = state.rx_buf.is_full();
rx_reader.pop_done(len);
rx_reader.pop_done(data_len);

if do_pend {
self.info.interrupt.pend();
}

return Ok(len);
data = rx_reader.pop_slice();
}
return Ok(buf_len);
}
}

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

self.state
.eager_reads
.store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);

self.info.regs.cr1().modify(|w| {
w.set_rxneie(true);
w.set_idleie(true);
Expand Down
Loading