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 // disable idle line detection
w.set_idleie(false); 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() { } else if cr1.rxneie() {
// We cannot check the RXNE flag as it is auto-cleared by the DMA controller // 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 /// Wait until transmission complete
pub async fn flush(&mut self) -> Result<(), Error> { 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> { fn blocking_flush(info: &Info) -> Result<(), Error> {
let r = info.regs; let r = info.regs;
while !sr(r).read().tc() {} if r.cr1().read().te() {
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);
});
} }
Ok(()) 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. // 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. // It prevents reading of bytes which have just been written.
if r.cr3().read().hdsel() && r.cr1().read().te() { 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 // 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. // It prevents reading of bytes which have just been written.
if r.cr3().read().hdsel() && r.cr1().read().te() { if r.cr3().read().hdsel() && r.cr1().read().te() {
blocking_flush(self.info)?; 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 { for b in buffer {
@ -1155,6 +1197,11 @@ impl<'d> Uart<'d, Async> {
self.tx.write(buffer).await 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` /// Perform an asynchronous read into `buffer`
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.read(buffer).await 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> { 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> { 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 = [ rustflags = [
# Code-size optimizations. # Code-size optimizations.
#"-Z", "trap-unreachable=no", #"-Z", "trap-unreachable=no",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops", "-C", "no-vectorize-loops",
] ]

View File

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

View File

@ -33,6 +33,13 @@ async fn main(_spawner: Spawner) {
let mut buf = [0; 2]; let mut buf = [0; 2];
usart.blocking_read(&mut buf).unwrap(); usart.blocking_read(&mut buf).unwrap();
assert_eq!(buf, data); 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 // 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); 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"); info!("Test OK");
cortex_m::asm::bkpt(); cortex_m::asm::bkpt();
} }