Skip to content

Commit

Permalink
Merge pull request #3379 from qwerty19106/stm32_async_flush
Browse files Browse the repository at this point in the history
Stm32: implement async flush for UART
  • Loading branch information
Dirbaio authored Oct 14, 2024
2 parents c84495e + 014583a commit 4f08d5b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 13 deletions.
69 changes: 58 additions & 11 deletions embassy-stm32/src/usart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ unsafe fn on_interrupt(r: Regs, s: &'static State) {
// disable idle line detection
w.set_idleie(false);
});
} else if cr1.tcie() && sr.tc() {
// Transmission complete detected
r.cr1().modify(|w| {
// disable Transmission complete interrupt
w.set_tcie(false);
});
} else if cr1.rxneie() {
// We cannot check the RXNE flag as it is auto-cleared by the DMA controller

Expand Down Expand Up @@ -420,7 +426,7 @@ impl<'d> UartTx<'d, Async> {

/// Wait until transmission complete
pub async fn flush(&mut self) -> Result<(), Error> {
self.blocking_flush()
flush(&self.info, &self.state).await
}
}

Expand Down Expand Up @@ -531,16 +537,40 @@ impl<'d, M: Mode> UartTx<'d, M> {
}
}

fn blocking_flush(info: &Info) -> Result<(), Error> {
/// Wait until transmission complete
async fn flush(info: &Info, state: &State) -> Result<(), Error> {
let r = info.regs;
while !sr(r).read().tc() {}
if r.cr1().read().te() && !sr(r).read().tc() {
r.cr1().modify(|w| {
// enable Transmission Complete interrupt
w.set_tcie(true);
});

compiler_fence(Ordering::SeqCst);

// Disable Transmitter and enable receiver after transmission complete for Half-Duplex mode
if r.cr3().read().hdsel() {
r.cr1().modify(|reg| {
reg.set_te(false);
reg.set_re(true);
// future which completes when Transmission complete is detected
let abort = poll_fn(move |cx| {
state.rx_waker.register(cx.waker());

let sr = sr(r).read();
if sr.tc() {
// Transmission complete detected
return Poll::Ready(());
}

Poll::Pending
});

abort.await;
}

Ok(())
}

fn blocking_flush(info: &Info) -> Result<(), Error> {
let r = info.regs;
if r.cr1().read().te() {
while !sr(r).read().tc() {}
}

Ok(())
Expand Down Expand Up @@ -621,7 +651,13 @@ impl<'d> UartRx<'d, Async> {
// Call flush for Half-Duplex mode if some bytes were written and flush was not called.
// It prevents reading of bytes which have just been written.
if r.cr3().read().hdsel() && r.cr1().read().te() {
blocking_flush(self.info)?;
flush(&self.info, &self.state).await?;

// Disable Transmitter and enable Receiver after flush
r.cr1().modify(|reg| {
reg.set_re(true);
reg.set_te(false);
});
}

// make sure USART state is restored to neutral state when this future is dropped
Expand Down Expand Up @@ -960,6 +996,12 @@ impl<'d, M: Mode> UartRx<'d, M> {
// It prevents reading of bytes which have just been written.
if r.cr3().read().hdsel() && r.cr1().read().te() {
blocking_flush(self.info)?;

// Disable Transmitter and enable Receiver after flush
r.cr1().modify(|reg| {
reg.set_re(true);
reg.set_te(false);
});
}

for b in buffer {
Expand Down Expand Up @@ -1155,6 +1197,11 @@ impl<'d> Uart<'d, Async> {
self.tx.write(buffer).await
}

/// Wait until transmission complete
pub async fn flush(&mut self) -> Result<(), Error> {
self.tx.flush().await
}

/// Perform an asynchronous read into `buffer`
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.read(buffer).await
Expand Down Expand Up @@ -1733,7 +1780,7 @@ impl embedded_io_async::Write for Uart<'_, Async> {
}

async fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
self.flush().await
}
}

Expand All @@ -1744,7 +1791,7 @@ impl embedded_io_async::Write for UartTx<'_, Async> {
}

async fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
self.flush().await
}
}

Expand Down
1 change: 0 additions & 1 deletion tests/rp/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ runner = "teleprobe client run"
rustflags = [
# Code-size optimizations.
#"-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]

Expand Down
1 change: 0 additions & 1 deletion tests/stm32/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ runner = "teleprobe client run"
rustflags = [
# Code-size optimizations.
#"-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]

Expand Down
7 changes: 7 additions & 0 deletions tests/stm32/src/bin/usart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ async fn main(_spawner: Spawner) {
let mut buf = [0; 2];
usart.blocking_read(&mut buf).unwrap();
assert_eq!(buf, data);

// Test flush doesn't hang.
usart.blocking_write(&data).unwrap();
usart.blocking_flush().unwrap();

// Test flush doesn't hang if there's nothing to flush
usart.blocking_flush().unwrap();
}

// Test error handling with with an overflow error
Expand Down
17 changes: 17 additions & 0 deletions tests/stm32/src/bin/usart_dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ async fn main(_spawner: Spawner) {
assert_eq!(tx_buf, rx_buf);
}

// Test flush doesn't hang. Check multiple combinations of async+blocking.
tx.write(&tx_buf).await.unwrap();
tx.flush().await.unwrap();
tx.flush().await.unwrap();

tx.write(&tx_buf).await.unwrap();
tx.blocking_flush().unwrap();
tx.flush().await.unwrap();

tx.blocking_write(&tx_buf).unwrap();
tx.blocking_flush().unwrap();
tx.flush().await.unwrap();

tx.blocking_write(&tx_buf).unwrap();
tx.flush().await.unwrap();
tx.blocking_flush().unwrap();

info!("Test OK");
cortex_m::asm::bkpt();
}

0 comments on commit 4f08d5b

Please sign in to comment.