Skip to content

Commit 9c884a4

Browse files
committed
Add read_from_break method to STM32 UartRx
1 parent de5dd10 commit 9c884a4

File tree

2 files changed

+24
-9
lines changed

2 files changed

+24
-9
lines changed

embassy-stm32/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- feat: Allow OSPI DMA writes larger than 64kB using chunking
2424
- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times
2525
- feat: Add USB CRS sync support for STM32C071
26+
- feat: Add read_from_break method to STM32 UartRx ([#4702](https://github.com/embassy-rs/embassy/pull/4702))
2627

2728
## 0.4.0 - 2025-08-26
2829

embassy-stm32/src/usart/mod.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
4141
unsafe fn on_interrupt(r: Regs, s: &'static State) {
4242
let (sr, cr1, cr3) = (sr(r).read(), r.cr1().read(), r.cr3().read());
4343

44-
let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie());
44+
let has_errors = (sr.pe() && cr1.peie()) || ((sr.ne() || sr.ore()) && cr3.eie()) || (sr.fe() && cr3.eie() && cr3.dmar());
4545
if has_errors {
4646
// clear all interrupts and DMA Rx Request
4747
r.cr1().modify(|w| {
@@ -74,6 +74,13 @@ unsafe fn on_interrupt(r: Regs, s: &'static State) {
7474
// We cannot check the RXNE flag as it is auto-cleared by the DMA controller
7575

7676
// It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection
77+
} else if sr.fe() && cr3.eie() {
78+
// Break detected (frame error)
79+
if sr.fe() {
80+
r.icr().write(|w| w.set_fe(true)); // clear framing error
81+
unsafe { rdr(r).read_volatile() }; // clear byte so DMA doesn't pick it up
82+
r.cr3().modify(|w| w.set_dmar(true)); // start DMA
83+
}
7784
} else {
7885
return;
7986
}
@@ -692,20 +699,27 @@ impl<'d> UartRx<'d, Async> {
692699

693700
/// Initiate an asynchronous UART read
694701
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
695-
self.inner_read(buffer, false).await?;
702+
self.inner_read(buffer, false, false).await?;
696703

697704
Ok(())
698705
}
699706

700707
/// Initiate an asynchronous read with idle line detection enabled
701708
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
702-
self.inner_read(buffer, true).await
709+
self.inner_read(buffer, true, false).await
710+
}
711+
712+
/// Initiate an asynchronous read, waiting for a break character (frame error) before starting the read
713+
/// Returns Err(Framing) if a second break occurs before buffer.len() characters have been received.
714+
pub async fn read_from_break(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
715+
self.inner_read(buffer, false, true).await
703716
}
704717

705718
async fn inner_read_run(
706719
&mut self,
707720
buffer: &mut [u8],
708721
enable_idle_line_detection: bool,
722+
wait_for_break: bool,
709723
) -> Result<ReadCompletionEvent, Error> {
710724
let r = self.info.regs;
711725

@@ -767,8 +781,8 @@ impl<'d> UartRx<'d, Async> {
767781
r.cr3().modify(|w| {
768782
// enable Error Interrupt: (Frame error, Noise error, Overrun error)
769783
w.set_eie(true);
770-
// enable DMA Rx Request
771-
w.set_dmar(true);
784+
// enable DMA Rx Request unless waiting for break first
785+
if !wait_for_break { w.set_dmar(true); }
772786
});
773787

774788
compiler_fence(Ordering::SeqCst);
@@ -779,7 +793,7 @@ impl<'d> UartRx<'d, Async> {
779793

780794
let cr3 = r.cr3().read();
781795

782-
if !cr3.dmar() {
796+
if !wait_for_break && !cr3.dmar() {
783797
// something went wrong
784798
// because the only way to get this flag cleared is to have an interrupt
785799

@@ -841,7 +855,7 @@ impl<'d> UartRx<'d, Async> {
841855

842856
compiler_fence(Ordering::SeqCst);
843857

844-
let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore();
858+
let has_errors = sr.pe() || sr.ne() || sr.fe() || (sr.ore() && !wait_for_break);
845859

846860
if has_errors {
847861
// all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler
@@ -889,7 +903,7 @@ impl<'d> UartRx<'d, Async> {
889903
r
890904
}
891905

892-
async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error> {
906+
async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool, wait_for_break: bool) -> Result<usize, Error> {
893907
if buffer.is_empty() {
894908
return Ok(0);
895909
} else if buffer.len() > 0xFFFF {
@@ -899,7 +913,7 @@ impl<'d> UartRx<'d, Async> {
899913
let buffer_len = buffer.len();
900914

901915
// wait for DMA to complete or IDLE line detection if requested
902-
let res = self.inner_read_run(buffer, enable_idle_line_detection).await;
916+
let res = self.inner_read_run(buffer, enable_idle_line_detection, wait_for_break).await;
903917

904918
match res {
905919
Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),

0 commit comments

Comments
 (0)