Merge pull request #3379 from qwerty19106/stm32_async_flush

Stm32: implement async flush for UART
This commit is contained in:
Dario Nieuwenhuis 2024-10-14 10:52:32 +00:00 committed by GitHub
commit 4f08d5bc5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 14 deletions

View File

@ -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
@ -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
}
}
@ -531,16 +537,40 @@ impl<'d, M: Mode> UartTx<'d, M> {
}
}
/// Wait until transmission complete
async fn flush(info: &Info, state: &State) -> Result<(), Error> {
let r = info.regs;
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);
// 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;
while !sr(r).read().tc() {}
// 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);
});
if r.cr1().read().te() {
while !sr(r).read().tc() {}
}
Ok(())
@ -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
@ -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 {
@ -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
@ -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
}
}
@ -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
}
}

View File

@ -11,7 +11,6 @@ runner = "teleprobe client run"
rustflags = [
# Code-size optimizations.
#"-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]

View File

@ -9,7 +9,6 @@ runner = "teleprobe client run"
rustflags = [
# Code-size optimizations.
#"-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]

View File

@ -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

View File

@ -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();
}