From 7d0de86ce93deb91def4a77bbb618c5573098504 Mon Sep 17 00:00:00 2001 From: decaday Date: Thu, 14 Nov 2024 16:35:41 +0800 Subject: [PATCH 1/4] feat: init usart mod --- src/lib.rs | 1 + src/usart/buffered.rs | 877 +++++++++++++++++++ src/usart/mod.rs | 1941 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2819 insertions(+) create mode 100644 src/usart/buffered.rs create mode 100644 src/usart/mod.rs diff --git a/src/lib.rs b/src/lib.rs index ce159f3..9647d9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ pub mod rcc; pub mod i2c; pub mod adc; pub mod dma; +pub mod usart; pub mod timer; #[cfg(feature = "_time-driver")] pub mod time_driver; diff --git a/src/usart/buffered.rs b/src/usart/buffered.rs new file mode 100644 index 0000000..7ba2090 --- /dev/null +++ b/src/usart/buffered.rs @@ -0,0 +1,877 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::slice; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::{Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; + +#[cfg(not(any(usart_v1, usart_v2)))] +use super::DePin; +use super::{ + clear_interrupt_flags, configure, rdr, reconfigure, send_break, sr, tdr, Config, ConfigError, CtsPin, Error, Info, + Instance, Regs, RtsPin, RxPin, TxPin, +}; +use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; +use crate::interrupt::{self, InterruptExt}; +use crate::time::Hertz; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + on_interrupt(T::info().regs, T::buffered_state()) + } +} + +unsafe fn on_interrupt(r: Regs, state: &'static State) { + // RX + let sr_val = sr(r).read(); + // On v1 & v2, reading DR clears the rxne, error and idle interrupt + // flags. Keep this close to the SR read to reduce the chance of a + // flag being set in-between. + let dr = if sr_val.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr_val.ore() || sr_val.idle()) { + Some(rdr(r).read_volatile()) + } else { + None + }; + clear_interrupt_flags(r, sr_val); + + if sr_val.pe() { + warn!("Parity error"); + } + if sr_val.fe() { + warn!("Framing error"); + } + if sr_val.ne() { + warn!("Noise error"); + } + if sr_val.ore() { + warn!("Overrun error"); + } + if sr_val.rxne() { + let mut rx_writer = state.rx_buf.writer(); + let buf = rx_writer.push_slice(); + if !buf.is_empty() { + if let Some(byte) = dr { + buf[0] = byte; + rx_writer.push_done(1); + } + } else { + // FIXME: Should we disable any further RX interrupts when the buffer becomes full. + } + + if !state.rx_buf.is_empty() { + state.rx_waker.wake(); + } + } + + if sr_val.idle() { + state.rx_waker.wake(); + } + + // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) + // indicates that all bytes are pushed out from the FIFO. + // For other usart variants it shows that last byte from the buffer was just sent. + if sr_val.tc() { + // For others it is cleared above with `clear_interrupt_flags`. + #[cfg(any(usart_v1, usart_v2))] + sr(r).modify(|w| w.set_tc(false)); + + r.cr1().modify(|w| { + w.set_tcie(false); + }); + + state.tx_done.store(true, Ordering::Release); + state.tx_waker.wake(); + } + + // TX + if sr(r).read().txe() { + let mut tx_reader = state.tx_buf.reader(); + let buf = tx_reader.pop_slice(); + if !buf.is_empty() { + r.cr1().modify(|w| { + w.set_txeie(true); + }); + + // Enable transmission complete interrupt when last byte is going to be sent out. + if buf.len() == 1 { + r.cr1().modify(|w| { + w.set_tcie(true); + }); + } + + tdr(r).write_volatile(buf[0].into()); + tx_reader.pop_done(1); + } else { + // Disable interrupt until we have something to transmit again. + r.cr1().modify(|w| { + w.set_txeie(false); + }); + } + } +} + +pub(super) struct State { + rx_waker: AtomicWaker, + rx_buf: RingBuffer, + tx_waker: AtomicWaker, + tx_buf: RingBuffer, + tx_done: AtomicBool, + tx_rx_refcount: AtomicU8, +} + +impl State { + pub(super) const fn new() -> Self { + Self { + rx_buf: RingBuffer::new(), + tx_buf: RingBuffer::new(), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + tx_done: AtomicBool::new(true), + tx_rx_refcount: AtomicU8::new(0), + } + } +} + +/// Bidirectional buffered UART +pub struct BufferedUart<'d> { + rx: BufferedUartRx<'d>, + tx: BufferedUartTx<'d>, +} + +/// Tx-only buffered UART +/// +/// Created with [BufferedUart::split] +pub struct BufferedUartTx<'d> { + info: &'static Info, + state: &'static State, + kernel_clock: Hertz, + tx: Option>, + cts: Option>, + de: Option>, +} + +/// Rx-only buffered UART +/// +/// Created with [BufferedUart::split] +pub struct BufferedUartRx<'d> { + info: &'static Info, + state: &'static State, + kernel_clock: Hertz, + rx: Option>, + rts: Option>, +} + +impl<'d> SetConfig for BufferedUart<'d> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> SetConfig for BufferedUartRx<'d> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> SetConfig for BufferedUartTx<'d> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> BufferedUart<'d> { + /// Create a new bidirectional buffered UART driver + pub fn new( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(config.rx_pull)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + None, + None, + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins + pub fn new_with_rtscts( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(Pull::None)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(cts, AfType::input(Pull::None)), + None, + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin + pub fn new_with_rts_as_de( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(Pull::None)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + None, + new_pin!(rts, AfType::input(Pull::None)), // RTS mapped used as DE + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART driver with only the request-to-send pin + pub fn new_with_rts( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(Pull::None)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(rts, AfType::input(Pull::None)), + None, // no CTS + None, // no DE + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART driver with a driver-enable pin + #[cfg(not(any(usart_v1, usart_v2)))] + pub fn new_with_de( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + de: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(config.rx_pull)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + None, + new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), + tx_buffer, + rx_buffer, + config, + ) + } + + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + de: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let info = T::info(); + let state = T::buffered_state(); + let kernel_clock = T::frequency(); + + let mut this = Self { + rx: BufferedUartRx { + info, + state, + kernel_clock, + rx, + rts, + }, + tx: BufferedUartTx { + info, + state, + kernel_clock, + tx, + cts, + de, + }, + }; + this.enable_and_configure(tx_buffer, rx_buffer, &config)?; + Ok(this) + } + + fn enable_and_configure( + &mut self, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: &Config, + ) -> Result<(), ConfigError> { + let info = self.rx.info; + let state = self.rx.state; + state.tx_rx_refcount.store(2, Ordering::Relaxed); + + info.rcc.enable_and_reset(); + + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + info.regs.cr3().write(|w| { + w.set_rtse(self.rx.rts.is_some()); + w.set_ctse(self.tx.cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(self.tx.de.is_some()); + }); + configure(info, self.rx.kernel_clock, &config, true, true)?; + + info.regs.cr1().modify(|w| { + w.set_rxneie(true); + w.set_idleie(true); + }); + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } + + /// Split the driver into a Tx and Rx part (useful for sending to separate tasks) + pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) { + (self.tx, self.rx) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure(self.rx.info, self.rx.kernel_clock, config)?; + + self.rx.info.regs.cr1().modify(|w| { + w.set_rxneie(true); + w.set_idleie(true); + }); + + Ok(()) + } + + /// Send break character + pub fn send_break(&self) { + self.tx.send_break() + } +} + +impl<'d> BufferedUartRx<'d> { + async fn read(&self, buf: &mut [u8]) -> Result { + poll_fn(move |cx| { + let state = self.state; + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let data = rx_reader.pop_slice(); + + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + let do_pend = state.rx_buf.is_full(); + rx_reader.pop_done(len); + + if do_pend { + self.info.interrupt.pend(); + } + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }) + .await + } + + fn blocking_read(&self, buf: &mut [u8]) -> Result { + loop { + let state = self.state; + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let data = rx_reader.pop_slice(); + + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + let do_pend = state.rx_buf.is_full(); + rx_reader.pop_done(len); + + if do_pend { + self.info.interrupt.pend(); + } + + return Ok(len); + } + } + } + + async fn fill_buf(&self) -> Result<&[u8], Error> { + poll_fn(move |cx| { + let state = self.state; + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let (p, n) = rx_reader.pop_buf(); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + + let buf = unsafe { slice::from_raw_parts(p, n) }; + Poll::Ready(Ok(buf)) + }) + .await + } + + fn consume(&self, amt: usize) { + let state = self.state; + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let full = state.rx_buf.is_full(); + rx_reader.pop_done(amt); + if full { + self.info.interrupt.pend(); + } + } + + /// we are ready to read if there is data in the buffer + fn read_ready(&mut self) -> Result { + let state = self.state; + Ok(!state.rx_buf.is_empty()) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure(self.info, self.kernel_clock, config)?; + + self.info.regs.cr1().modify(|w| { + w.set_rxneie(true); + w.set_idleie(true); + }); + + Ok(()) + } +} + +impl<'d> BufferedUartTx<'d> { + async fn write(&self, buf: &[u8]) -> Result { + poll_fn(move |cx| { + let state = self.state; + state.tx_done.store(false, Ordering::Release); + + let empty = state.tx_buf.is_empty(); + + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let data = tx_writer.push_slice(); + if data.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + tx_writer.push_done(n); + + if empty { + self.info.interrupt.pend(); + } + + Poll::Ready(Ok(n)) + }) + .await + } + + async fn flush(&self) -> Result<(), Error> { + poll_fn(move |cx| { + let state = self.state; + + if !state.tx_done.load(Ordering::Acquire) { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + .await + } + + fn blocking_write(&self, buf: &[u8]) -> Result { + loop { + let state = self.state; + let empty = state.tx_buf.is_empty(); + + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let data = tx_writer.push_slice(); + if !data.is_empty() { + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + tx_writer.push_done(n); + + if empty { + self.info.interrupt.pend(); + } + + return Ok(n); + } + } + } + + fn blocking_flush(&self) -> Result<(), Error> { + loop { + let state = self.state; + if state.tx_buf.is_empty() { + return Ok(()); + } + } + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure(self.info, self.kernel_clock, config)?; + + self.info.regs.cr1().modify(|w| { + w.set_rxneie(true); + w.set_idleie(true); + }); + + Ok(()) + } + + /// Send break character + pub fn send_break(&self) { + send_break(&self.info.regs); + } +} + +impl<'d> Drop for BufferedUartRx<'d> { + fn drop(&mut self) { + let state = self.state; + unsafe { + state.rx_buf.deinit(); + + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + self.info.interrupt.disable(); + } + } + + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); + drop_tx_rx(self.info, state); + } +} + +impl<'d> Drop for BufferedUartTx<'d> { + fn drop(&mut self) { + let state = self.state; + unsafe { + state.tx_buf.deinit(); + + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + self.info.interrupt.disable(); + } + } + + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + self.de.as_ref().map(|x| x.set_as_disconnected()); + drop_tx_rx(self.info, state); + } +} + +fn drop_tx_rx(info: &Info, state: &State) { + // We cannot use atomic subtraction here, because it's not supported for all targets + let is_last_drop = critical_section::with(|_| { + let refcount = state.tx_rx_refcount.load(Ordering::Relaxed); + assert!(refcount >= 1); + state.tx_rx_refcount.store(refcount - 1, Ordering::Relaxed); + refcount == 1 + }); + if is_last_drop { + info.rcc.disable(); + } +} + +impl<'d> embedded_io_async::ErrorType for BufferedUart<'d> { + type Error = Error; +} + +impl<'d> embedded_io_async::ErrorType for BufferedUartRx<'d> { + type Error = Error; +} + +impl<'d> embedded_io_async::ErrorType for BufferedUartTx<'d> { + type Error = Error; +} + +impl<'d> embedded_io_async::Read for BufferedUart<'d> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf).await + } +} + +impl<'d> embedded_io_async::Read for BufferedUartRx<'d> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + Self::read(self, buf).await + } +} + +impl<'d> embedded_io_async::ReadReady for BufferedUart<'d> { + fn read_ready(&mut self) -> Result { + BufferedUartRx::<'d>::read_ready(&mut self.rx) + } +} + +impl<'d> embedded_io_async::ReadReady for BufferedUartRx<'d> { + fn read_ready(&mut self) -> Result { + Self::read_ready(self) + } +} + +impl<'d> embedded_io_async::BufRead for BufferedUart<'d> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.rx.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.rx.consume(amt) + } +} + +impl<'d> embedded_io_async::BufRead for BufferedUartRx<'d> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Self::fill_buf(self).await + } + + fn consume(&mut self, amt: usize) { + Self::consume(self, amt) + } +} + +impl<'d> embedded_io_async::Write for BufferedUart<'d> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.flush().await + } +} + +impl<'d> embedded_io_async::Write for BufferedUartTx<'d> { + async fn write(&mut self, buf: &[u8]) -> Result { + Self::write(self, buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush(self).await + } +} + +impl<'d> embedded_io::Read for BufferedUart<'d> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.blocking_read(buf) + } +} + +impl<'d> embedded_io::Read for BufferedUartRx<'d> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.blocking_read(buf) + } +} + +impl<'d> embedded_io::Write for BufferedUart<'d> { + fn write(&mut self, buf: &[u8]) -> Result { + self.tx.blocking_write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } +} + +impl<'d> embedded_io::Write for BufferedUartTx<'d> { + fn write(&mut self, buf: &[u8]) -> Result { + Self::blocking_write(self, buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Self::blocking_flush(self) + } +} + +impl<'d> embedded_hal_02::serial::Read for BufferedUartRx<'d> { + type Error = Error; + + fn read(&mut self) -> Result> { + let r = self.info.regs; + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } +} + +impl<'d> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d> { + type Error = Error; + + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl<'d> embedded_hal_02::serial::Read for BufferedUart<'d> { + type Error = Error; + + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } +} + +impl<'d> embedded_hal_02::blocking::serial::Write for BufferedUart<'d> { + type Error = Error; + + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.tx.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } +} + +impl<'d> embedded_hal_nb::serial::ErrorType for BufferedUart<'d> { + type Error = Error; +} + +impl<'d> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d> { + type Error = Error; +} + +impl<'d> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d> { + type Error = Error; +} + +impl<'d> embedded_hal_nb::serial::Read for BufferedUartRx<'d> { + fn read(&mut self) -> nb::Result { + embedded_hal_02::serial::Read::read(self) + } +} + +impl<'d> embedded_hal_nb::serial::Write for BufferedUartTx<'d> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } +} + +impl<'d> embedded_hal_nb::serial::Read for BufferedUart<'d> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } +} + +impl<'d> embedded_hal_nb::serial::Write for BufferedUart<'d> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.blocking_flush().map_err(nb::Error::Other) + } +} diff --git a/src/usart/mod.rs b/src/usart/mod.rs new file mode 100644 index 0000000..333e01e --- /dev/null +++ b/src/usart/mod.rs @@ -0,0 +1,1941 @@ +//! Universal Synchronous/Asynchronous Receiver Transmitter (USART, UART, LPUART) +#![macro_use] +#![warn(missing_docs)] + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::PeripheralRef; +use embassy_sync::waitqueue::AtomicWaker; +use futures_util::future::{select, Either}; + +use crate::dma::ChannelAndRequest; +use crate::gpio::{self, AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; +use crate::interrupt::typelevel::Interrupt as _; +use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::mode::{Async, Blocking, Mode}; +#[allow(unused_imports)] +#[cfg(not(any(usart_v1, usart_v2)))] +use crate::pac::usart::regs::Isr as Sr; +#[cfg(any(usart_v1, usart_v2))] +use crate::pac::usart::regs::Sr; +#[cfg(not(any(usart_v1, usart_v2)))] +use crate::pac::usart::Lpuart as Regs; +#[cfg(any(usart_v1, usart_v2))] +use crate::pac::usart::Usart as Regs; +use crate::pac::usart::{regs, vals}; +use crate::rcc::{RccInfo, SealedRccPeripheral}; +use crate::time::Hertz; +use crate::Peripheral; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + on_interrupt(T::info().regs, T::state()) + } +} + +unsafe fn on_interrupt(r: Regs, s: &'static State) { + let (sr, cr1, cr3) = (sr(r).read(), r.cr1().read(), r.cr3().read()); + + let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); + if has_errors { + // clear all interrupts and DMA Rx Request + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } else if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + r.cr1().modify(|w| { + // 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 + + // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection + } else { + return; + } + + compiler_fence(Ordering::SeqCst); + s.rx_waker.wake(); +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Number of data bits +pub enum DataBits { + /// 8 Data Bits + DataBits8, + /// 9 Data Bits + DataBits9, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Parity +pub enum Parity { + /// No parity + ParityNone, + /// Even Parity + ParityEven, + /// Odd Parity + ParityOdd, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Number of stop bits +pub enum StopBits { + #[doc = "1 stop bit"] + STOP1, + #[doc = "0.5 stop bits"] + STOP0P5, + #[doc = "2 stop bits"] + STOP2, + #[doc = "1.5 stop bits"] + STOP1P5, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Config Error +pub enum ConfigError { + /// Baudrate too low + BaudrateTooLow, + /// Baudrate too high + BaudrateTooHigh, + /// Rx or Tx not enabled + RxOrTxNotEnabled, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Config +pub struct Config { + /// Baud rate + pub baudrate: u32, + /// Number of data bits + pub data_bits: DataBits, + /// Number of stop bits + pub stop_bits: StopBits, + /// Parity type + pub parity: Parity, + + /// If true: on a read-like method, if there is a latent error pending, + /// the read will abort and the error will be reported and cleared + /// + /// If false: the error is ignored and cleared + pub detect_previous_overrun: bool, + + /// Set this to true if the line is considered noise free. + /// This will increase the receiver’s tolerance to clock deviations, + /// but will effectively disable noise detection. + #[cfg(not(usart_v1))] + pub assume_noise_free: bool, + + /// Set this to true to swap the RX and TX pins. + #[cfg(any(usart_v3, usart_v4))] + pub swap_rx_tx: bool, + + /// Set this to true to invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle). + #[cfg(any(usart_v3, usart_v4))] + pub invert_tx: bool, + + /// Set this to true to invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle). + #[cfg(any(usart_v3, usart_v4))] + pub invert_rx: bool, + + /// Set the pull configuration for the RX pin. + pub rx_pull: Pull, + + // private: set by new_half_duplex, not by the user. + half_duplex: bool, +} + +impl Config { + fn tx_af(&self) -> AfType { + #[cfg(any(usart_v3, usart_v4))] + if self.swap_rx_tx { + return AfType::input(self.rx_pull); + }; + AfType::output(OutputType::PushPull, Speed::Medium) + } + + fn rx_af(&self) -> AfType { + #[cfg(any(usart_v3, usart_v4))] + if self.swap_rx_tx { + return AfType::output(OutputType::PushPull, Speed::Medium); + }; + AfType::input(self.rx_pull) + } +} + +impl Default for Config { + fn default() -> Self { + Self { + baudrate: 115200, + data_bits: DataBits::DataBits8, + stop_bits: StopBits::STOP1, + parity: Parity::ParityNone, + // historical behavior + detect_previous_overrun: false, + #[cfg(not(usart_v1))] + assume_noise_free: false, + #[cfg(any(usart_v3, usart_v4))] + swap_rx_tx: false, + #[cfg(any(usart_v3, usart_v4))] + invert_tx: false, + #[cfg(any(usart_v3, usart_v4))] + invert_rx: false, + rx_pull: Pull::None, + half_duplex: false, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Half duplex IO mode +pub enum HalfDuplexConfig { + /// Push pull allows for faster baudrates, may require series resistor + PushPull, + /// Open drain output using external pull up resistor + OpenDrainExternal, + #[cfg(not(gpio_v1))] + /// Open drain output using internal pull up resistor + OpenDrainInternal, +} + +impl HalfDuplexConfig { + fn af_type(self) -> gpio::AfType { + match self { + HalfDuplexConfig::PushPull => AfType::output(OutputType::PushPull, Speed::Medium), + HalfDuplexConfig::OpenDrainExternal => AfType::output(OutputType::OpenDrain, Speed::Medium), + #[cfg(not(gpio_v1))] + HalfDuplexConfig::OpenDrainInternal => AfType::output_pull(OutputType::OpenDrain, Speed::Medium, Pull::Up), + } + } +} + +/// Serial error +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Framing error + Framing, + /// Noise error + Noise, + /// RX buffer overrun + Overrun, + /// Parity check error + Parity, + /// Buffer too large for DMA + BufferTooLong, +} + +enum ReadCompletionEvent { + // DMA Read transfer completed first + DmaCompleted, + // Idle line detected first + Idle(usize), +} + +/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`]. +/// +/// ### Notes on [`embedded_io::Read`] +/// +/// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide. +/// +/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] +/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. +pub struct Uart<'d, M: Mode> { + tx: UartTx<'d, M>, + rx: UartRx<'d, M>, +} + +impl<'d, M: Mode> SetConfig for Uart<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.tx.set_config(config)?; + self.rx.set_config(config) + } +} + +/// Tx-only UART Driver. +/// +/// Can be obtained from [`Uart::split`], or can be constructed independently, +/// if you do not need the receiving half of the driver. +pub struct UartTx<'d, M: Mode> { + info: &'static Info, + state: &'static State, + kernel_clock: Hertz, + tx: Option>, + cts: Option>, + de: Option>, + tx_dma: Option>, + _phantom: PhantomData, +} + +impl<'d, M: Mode> SetConfig for UartTx<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +/// Rx-only UART Driver. +/// +/// Can be obtained from [`Uart::split`], or can be constructed independently, +/// if you do not need the transmitting half of the driver. +/// +/// ### Notes on [`embedded_io::Read`] +/// +/// `embedded_io::Read` requires guarantees that this struct cannot provide: +/// +/// - Any data received between calls to [`UartRx::read`] or [`UartRx::blocking_read`] +/// will be thrown away, as `UartRx` is unbuffered. +/// Users of `embedded_io::Read` are likely to not expect this behavior +/// (for instance if they read multiple small chunks in a row). +/// - [`UartRx::read`] and [`UartRx::blocking_read`] only return once the entire buffer has been +/// filled, whereas `embedded_io::Read` requires us to fill the buffer with what we already +/// received, and only block/wait until the first byte arrived. +///
+/// While [`UartRx::read_until_idle`] does return early, it will still eagerly wait for data until +/// the buffer is full or no data has been transmitted in a while, +/// which may not be what users of `embedded_io::Read` expect. +/// +/// [`UartRx::into_ring_buffered`] can be called to equip `UartRx` with a buffer, +/// that it can then use to store data received between calls to `read`, +/// provided you are using DMA already. +/// +/// Alternatively, you can use [`BufferedUartRx`], which is interrupt-based and which can also +/// store data received between calls. +/// +/// Also see [this github comment](https://github.com/embassy-rs/embassy/pull/2185#issuecomment-1810047043). +pub struct UartRx<'d, M: Mode> { + info: &'static Info, + state: &'static State, + kernel_clock: Hertz, + rx: Option>, + rts: Option>, + rx_dma: Option>, + detect_previous_overrun: bool, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr, + _phantom: PhantomData, +} + +impl<'d, M: Mode> SetConfig for UartRx<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> UartTx<'d, Async> { + /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. + pub fn new( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + new_dma!(tx_dma), + config, + ) + } + + /// Create a new tx-only UART with a clear-to-send pin + pub fn new_with_cts( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(cts, AfType::input(Pull::None)), + new_dma!(tx_dma), + config, + ) + } + + /// Initiate an asynchronous UART write + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let r = self.info.regs; + + // Enable Transmitter and disable Receiver for Half-Duplex mode + let mut cr1 = r.cr1().read(); + if r.cr3().read().hdsel() && !cr1.te() { + cr1.set_te(true); + cr1.set_re(false); + r.cr1().write_value(cr1); + } + + let ch = self.tx_dma.as_mut().unwrap(); + r.cr3().modify(|reg| { + reg.set_dmat(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + let transfer = unsafe { ch.write(buffer, tdr(r), Default::default()) }; + transfer.await; + Ok(()) + } + + /// Wait until transmission complete + pub async fn flush(&mut self) -> Result<(), Error> { + flush(&self.info, &self.state).await + } +} + +impl<'d> UartTx<'d, Blocking> { + /// Create a new blocking tx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + None, + config, + ) + } + + /// Create a new blocking tx-only UART with a clear-to-send pin + pub fn new_blocking_with_cts( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(cts, AfType::input(config.rx_pull)), + None, + config, + ) + } +} + +impl<'d, M: Mode> UartTx<'d, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + tx: Option>, + cts: Option>, + tx_dma: Option>, + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::state(), + kernel_clock: T::frequency(), + tx, + cts, + de: None, + tx_dma, + _phantom: PhantomData, + }; + this.enable_and_configure(&config)?; + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + state.tx_rx_refcount.store(1, Ordering::Relaxed); + + info.rcc.enable_and_reset(); + + info.regs.cr3().modify(|w| { + w.set_ctse(self.cts.is_some()); + }); + configure(info, self.kernel_clock, config, false, true)?; + + Ok(()) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure(self.info, self.kernel_clock, config) + } + + /// Perform a blocking UART write + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let r = self.info.regs; + + // Enable Transmitter and disable Receiver for Half-Duplex mode + let mut cr1 = r.cr1().read(); + if r.cr3().read().hdsel() && !cr1.te() { + cr1.set_te(true); + cr1.set_re(false); + r.cr1().write_value(cr1); + } + + for &b in buffer { + while !sr(r).read().txe() {} + unsafe { tdr(r).write_volatile(b) }; + } + Ok(()) + } + + /// Block until transmission complete + pub fn blocking_flush(&mut self) -> Result<(), Error> { + blocking_flush(self.info) + } + + /// Send break character + pub fn send_break(&self) { + send_break(&self.info.regs); + } +} + +/// 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; + if r.cr1().read().te() { + while !sr(r).read().tc() {} + } + + Ok(()) +} + +/// Send break character +pub fn send_break(regs: &Regs) { + // Busy wait until previous break has been sent + #[cfg(any(usart_v1, usart_v2))] + while regs.cr1().read().sbk() {} + #[cfg(any(usart_v3, usart_v4))] + while regs.isr().read().sbkf() {} + + // Send break right after completing the current character transmission + #[cfg(any(usart_v1, usart_v2))] + regs.cr1().modify(|w| w.set_sbk(true)); + #[cfg(any(usart_v3, usart_v4))] + regs.rqr().write(|w| w.set_sbkrq(true)); +} + +impl<'d> UartRx<'d, Async> { + /// Create a new rx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. + pub fn new( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(config.rx_pull)), + None, + new_dma!(rx_dma), + config, + ) + } + + /// Create a new rx-only UART with a request-to-send pin + pub fn new_with_rts( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(config.rx_pull)), + new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), + new_dma!(rx_dma), + config, + ) + } + + /// Initiate an asynchronous UART read + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.inner_read(buffer, false).await?; + + Ok(()) + } + + /// Initiate an asynchronous read with idle line detection enabled + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + self.inner_read(buffer, true).await + } + + async fn inner_read_run( + &mut self, + buffer: &mut [u8], + enable_idle_line_detection: bool, + ) -> Result { + let r = self.info.regs; + + // 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() { + 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 + let on_drop = OnDrop::new(move || { + // defmt::trace!("Clear all USART interrupts and DMA Read Request"); + // clear all interrupts and DMA Rx Request + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + }); + + let ch = self.rx_dma.as_mut().unwrap(); + + let buffer_len = buffer.len(); + + // Start USART DMA + // will not do anything yet because DMAR is not yet set + // future which will complete when DMA Read request completes + let transfer = unsafe { ch.read(rdr(r), buffer, Default::default()) }; + + // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer + if !self.detect_previous_overrun { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + } + + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + }); + + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); + + compiler_fence(Ordering::SeqCst); + + // In case of errors already pending when reception started, interrupts may have already been raised + // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts + // have been disabled in interrupt handler and DMA Rx Request has been disabled. + + let cr3 = r.cr3().read(); + + if !cr3.dmar() { + // something went wrong + // because the only way to get this flag cleared is to have an interrupt + + // DMA will be stopped when transfer is dropped + + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + + if sr.pe() { + return Err(Error::Parity); + } + if sr.fe() { + return Err(Error::Framing); + } + if sr.ne() { + return Err(Error::Noise); + } + if sr.ore() { + return Err(Error::Overrun); + } + + unreachable!(); + } + + if enable_idle_line_detection { + // clear idle flag + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); + } + + compiler_fence(Ordering::SeqCst); + + // future which completes when idle line or error is detected + let s = self.state; + let abort = poll_fn(move |cx| { + s.rx_waker.register(cx.waker()); + + let sr = sr(r).read(); + + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + + if enable_idle_line_detection { + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); + } + + compiler_fence(Ordering::SeqCst); + + let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); + + if has_errors { + // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler + + if sr.pe() { + return Poll::Ready(Err(Error::Parity)); + } + if sr.fe() { + return Poll::Ready(Err(Error::Framing)); + } + if sr.ne() { + return Poll::Ready(Err(Error::Noise)); + } + if sr.ore() { + return Poll::Ready(Err(Error::Overrun)); + } + } + + if enable_idle_line_detection && sr.idle() { + // Idle line detected + return Poll::Ready(Ok(())); + } + + Poll::Pending + }); + + // wait for the first of DMA request or idle line detected to completes + // select consumes its arguments + // when transfer is dropped, it will stop the DMA request + let r = match select(transfer, abort).await { + // DMA transfer completed first + Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted), + + // Idle line detected first + Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle( + buffer_len - transfer.get_remaining_transfers() as usize, + )), + + // error occurred + Either::Right((Err(e), _)) => Err(e), + }; + + drop(on_drop); + + r + } + + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result { + if buffer.is_empty() { + return Ok(0); + } else if buffer.len() > 0xFFFF { + return Err(Error::BufferTooLong); + } + + let buffer_len = buffer.len(); + + // wait for DMA to complete or IDLE line detection if requested + let res = self.inner_read_run(buffer, enable_idle_line_detection).await; + + match res { + Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), + Ok(ReadCompletionEvent::Idle(n)) => Ok(n), + Err(e) => Err(e), + } + } +} + +impl<'d> UartRx<'d, Blocking> { + /// Create a new rx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(rx, AfType::input(config.rx_pull)), None, None, config) + } + + /// Create a new rx-only UART with a request-to-send pin + pub fn new_blocking_with_rts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(config.rx_pull)), + new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + config, + ) + } +} + +impl<'d, M: Mode> UartRx<'d, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + rts: Option>, + rx_dma: Option>, + config: Config, + ) -> Result { + let mut this = Self { + _phantom: PhantomData, + info: T::info(), + state: T::state(), + kernel_clock: T::frequency(), + rx, + rts, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr(0), + }; + this.enable_and_configure(&config)?; + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + state.tx_rx_refcount.store(1, Ordering::Relaxed); + + info.rcc.enable_and_reset(); + + info.regs.cr3().write(|w| { + w.set_rtse(self.rts.is_some()); + }); + configure(info, self.kernel_clock, &config, true, false)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure(self.info, self.kernel_clock, config) + } + + #[cfg(any(usart_v1, usart_v2))] + fn check_rx_flags(&mut self) -> Result { + let r = self.info.regs; + loop { + // Handle all buffered error flags. + if self.buffered_sr.pe() { + self.buffered_sr.set_pe(false); + return Err(Error::Parity); + } else if self.buffered_sr.fe() { + self.buffered_sr.set_fe(false); + return Err(Error::Framing); + } else if self.buffered_sr.ne() { + self.buffered_sr.set_ne(false); + return Err(Error::Noise); + } else if self.buffered_sr.ore() { + self.buffered_sr.set_ore(false); + return Err(Error::Overrun); + } else if self.buffered_sr.rxne() { + self.buffered_sr.set_rxne(false); + return Ok(true); + } else { + // No error flags from previous iterations were set: Check the actual status register + let sr = r.sr().read(); + if !sr.rxne() { + return Ok(false); + } + + // Buffer the status register and let the loop handle the error flags. + self.buffered_sr = sr; + } + } + } + + #[cfg(any(usart_v3, usart_v4))] + fn check_rx_flags(&mut self) -> Result { + let r = self.info.regs; + let sr = r.isr().read(); + if sr.pe() { + r.icr().write(|w| w.set_pe(true)); + return Err(Error::Parity); + } else if sr.fe() { + r.icr().write(|w| w.set_fe(true)); + return Err(Error::Framing); + } else if sr.ne() { + r.icr().write(|w| w.set_ne(true)); + return Err(Error::Noise); + } else if sr.ore() { + r.icr().write(|w| w.set_ore(true)); + return Err(Error::Overrun); + } + Ok(sr.rxne()) + } + + /// Read a single u8 if there is one available, otherwise return WouldBlock + pub(crate) fn nb_read(&mut self) -> Result> { + let r = self.info.regs; + if self.check_rx_flags()? { + Ok(unsafe { rdr(r).read_volatile() }) + } else { + Err(nb::Error::WouldBlock) + } + } + + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let r = self.info.regs; + + // 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)?; + + // Disable Transmitter and enable Receiver after flush + r.cr1().modify(|reg| { + reg.set_re(true); + reg.set_te(false); + }); + } + + for b in buffer { + while !self.check_rx_flags()? {} + unsafe { *b = rdr(r).read_volatile() } + } + Ok(()) + } +} + +impl<'d, M: Mode> Drop for UartTx<'d, M> { + fn drop(&mut self) { + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + self.de.as_ref().map(|x| x.set_as_disconnected()); + drop_tx_rx(self.info, self.state); + } +} + +impl<'d, M: Mode> Drop for UartRx<'d, M> { + fn drop(&mut self) { + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); + drop_tx_rx(self.info, self.state); + } +} + +fn drop_tx_rx(info: &Info, state: &State) { + // We cannot use atomic subtraction here, because it's not supported for all targets + let is_last_drop = critical_section::with(|_| { + let refcount = state.tx_rx_refcount.load(Ordering::Relaxed); + assert!(refcount >= 1); + state.tx_rx_refcount.store(refcount - 1, Ordering::Relaxed); + refcount == 1 + }); + if is_last_drop { + info.rcc.disable(); + } +} + +impl<'d> Uart<'d, Async> { + /// Create a new bidirectional UART + pub fn new( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + /// Create a new bidirectional UART with request-to-send and clear-to-send pins + pub fn new_with_rtscts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(cts, AfType::input(Pull::None)), + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + #[cfg(not(any(usart_v1, usart_v2)))] + /// Create a new bidirectional UART with a driver-enable pin + pub fn new_with_de( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + de: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. + /// + /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin + /// (when it is available for your chip). There is no functional difference between these methods, as both + /// allow bidirectional communication. + /// + /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. It means that the I/O must be configured so that TX is + /// configured as alternate function open-drain with an external pull-up + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[doc(alias("HDSEL"))] + pub fn new_half_duplex( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + mut config: Config, + half_duplex: HalfDuplexConfig, + ) -> Result { + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } + config.half_duplex = true; + + Self::new_inner( + peri, + None, + new_pin!(tx, half_duplex.af_type()), + None, + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. + /// + /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_half_duplex_on_rx( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + mut config: Config, + half_duplex: HalfDuplexConfig, + ) -> Result { + config.swap_rx_tx = true; + config.half_duplex = true; + + Self::new_inner( + peri, + None, + None, + new_pin!(rx, half_duplex.af_type()), + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + /// Perform an asynchronous write + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + 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 + } + + /// Perform an an asynchronous read with idle line detection enabled + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + self.rx.read_until_idle(buffer).await + } +} + +impl<'d> Uart<'d, Blocking> { + /// Create a new blocking bidirectional UART. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + None, + None, + None, + config, + ) + } + + /// Create a new bidirectional UART with request-to-send and clear-to-send pins + pub fn new_blocking_with_rtscts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(cts, AfType::input(Pull::None)), + None, + None, + None, + config, + ) + } + + #[cfg(not(any(usart_v1, usart_v2)))] + /// Create a new bidirectional UART with a driver-enable pin + pub fn new_blocking_with_de( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + de: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + None, + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. + /// + /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin + /// (when it is available for your chip). There is no functional difference between these methods, as both + /// allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[doc(alias("HDSEL"))] + pub fn new_blocking_half_duplex( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + mut config: Config, + half_duplex: HalfDuplexConfig, + ) -> Result { + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } + config.half_duplex = true; + + Self::new_inner( + peri, + None, + new_pin!(tx, half_duplex.af_type()), + None, + None, + None, + None, + None, + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. + /// + /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_blocking_half_duplex_on_rx( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + mut config: Config, + half_duplex: HalfDuplexConfig, + ) -> Result { + config.swap_rx_tx = true; + config.half_duplex = true; + + Self::new_inner( + peri, + None, + None, + new_pin!(rx, half_duplex.af_type()), + None, + None, + None, + None, + config, + ) + } +} + +impl<'d, M: Mode> Uart<'d, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + de: Option>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Result { + let info = T::info(); + let state = T::state(); + let kernel_clock = T::frequency(); + + let mut this = Self { + tx: UartTx { + _phantom: PhantomData, + info, + state, + kernel_clock, + tx, + cts, + de, + tx_dma, + }, + rx: UartRx { + _phantom: PhantomData, + info, + state, + kernel_clock, + rx, + rts, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr(0), + }, + }; + this.enable_and_configure(&config)?; + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.rx.info; + let state = self.rx.state; + state.tx_rx_refcount.store(2, Ordering::Relaxed); + + info.rcc.enable_and_reset(); + + info.regs.cr3().write(|w| { + w.set_rtse(self.rx.rts.is_some()); + w.set_ctse(self.tx.cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(self.tx.de.is_some()); + }); + configure(info, self.rx.kernel_clock, config, true, true)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } + + /// Perform a blocking write + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.blocking_write(buffer) + } + + /// Block until transmission complete + pub fn blocking_flush(&mut self) -> Result<(), Error> { + self.tx.blocking_flush() + } + + /// Read a single `u8` or return `WouldBlock` + pub(crate) fn nb_read(&mut self) -> Result> { + self.rx.nb_read() + } + + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.blocking_read(buffer) + } + + /// Split the Uart into a transmitter and receiver, which is + /// particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { + (self.tx, self.rx) + } + + /// Send break character + pub fn send_break(&self) { + self.tx.send_break(); + } +} + +fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> { + info.interrupt.disable(); + let r = info.regs; + + let cr = r.cr1().read(); + configure(info, kernel_clock, config, cr.re(), cr.te())?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +fn configure( + info: &Info, + kernel_clock: Hertz, + config: &Config, + enable_rx: bool, + enable_tx: bool, +) -> Result<(), ConfigError> { + let r = info.regs; + let kind = info.kind; + + if !enable_rx && !enable_tx { + return Err(ConfigError::RxOrTxNotEnabled); + } + + #[cfg(not(usart_v4))] + static DIVS: [(u16, ()); 1] = [(1, ())]; + + #[cfg(usart_v4)] + static DIVS: [(u16, vals::Presc); 12] = [ + (1, vals::Presc::DIV1), + (2, vals::Presc::DIV2), + (4, vals::Presc::DIV4), + (6, vals::Presc::DIV6), + (8, vals::Presc::DIV8), + (10, vals::Presc::DIV10), + (12, vals::Presc::DIV12), + (16, vals::Presc::DIV16), + (32, vals::Presc::DIV32), + (64, vals::Presc::DIV64), + (128, vals::Presc::DIV128), + (256, vals::Presc::DIV256), + ]; + + let (mul, brr_min, brr_max) = match kind { + #[cfg(any(usart_v3, usart_v4))] + Kind::Lpuart => { + trace!("USART: Kind::Lpuart"); + (256, 0x300, 0x10_0000) + } + Kind::Uart => { + trace!("USART: Kind::Uart"); + (1, 0x10, 0x1_0000) + } + }; + + fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { + // The calculation to be done to get the BRR is `mul * pclk / presc / baud` + // To do this in 32-bit only we can't multiply `mul` and `pclk` + let clock = pclk / presc; + + // The mul is applied as the last operation to prevent overflow + let brr = clock / baud * mul; + + // The BRR calculation will be a bit off because of integer rounding. + // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. + let rounding = ((clock % baud) * mul + (baud / 2)) / baud; + + brr + rounding + } + + // UART must be disabled during configuration. + r.cr1().modify(|w| { + w.set_ue(false); + }); + + #[cfg(not(usart_v1))] + let mut over8 = false; + let mut found_brr = None; + for &(presc, _presc_val) in &DIVS { + let brr = calculate_brr(config.baudrate, kernel_clock.0, presc as u32, mul); + trace!( + "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", + presc, + brr, + brr >> 4, + brr & 0x0F + ); + + if brr < brr_min { + #[cfg(not(usart_v1))] + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + over8 = true; + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + found_brr = Some(brr); + break; + } + return Err(ConfigError::BaudrateTooHigh); + } + + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + found_brr = Some(brr); + break; + } + } + + let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; + + #[cfg(not(usart_v1))] + let oversampling = if over8 { "8 bit" } else { "16 bit" }; + #[cfg(usart_v1)] + let oversampling = "default"; + trace!( + "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", + oversampling, + config.baudrate, + kernel_clock.0 / brr * mul + ); + + r.cr2().write(|w| { + w.set_stop(match config.stop_bits { + StopBits::STOP0P5 => vals::Stop::STOP0P5, + StopBits::STOP1 => vals::Stop::STOP1, + StopBits::STOP1P5 => vals::Stop::STOP1P5, + StopBits::STOP2 => vals::Stop::STOP2, + }); + + #[cfg(any(usart_v3, usart_v4))] + { + w.set_txinv(config.invert_tx); + w.set_rxinv(config.invert_rx); + w.set_swap(config.swap_rx_tx); + } + }); + + r.cr3().modify(|w| { + #[cfg(not(usart_v1))] + w.set_onebit(config.assume_noise_free); + w.set_hdsel(config.half_duplex); + }); + + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + + if config.half_duplex { + // The te and re bits will be set by write, read and flush methods. + // Receiver should be enabled by default for Half-Duplex. + w.set_te(false); + w.set_re(true); + } else { + // enable transceiver + w.set_te(enable_tx); + // enable receiver + w.set_re(enable_rx); + } + + // configure word size + // if using odd or even parity it must be configured to 9bits + w.set_m0(if config.parity != Parity::ParityNone { + trace!("USART: m0: vals::M0::BIT9"); + vals::M0::BIT9 + } else { + trace!("USART: m0: vals::M0::BIT8"); + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => { + trace!("USART: set_ps: vals::Ps::ODD"); + vals::Ps::ODD + } + Parity::ParityEven => { + trace!("USART: set_ps: vals::Ps::EVEN"); + vals::Ps::EVEN + } + _ => { + trace!("USART: set_ps: vals::Ps::EVEN"); + vals::Ps::EVEN + } + }); + #[cfg(not(usart_v1))] + w.set_over8(vals::Over8::from_bits(over8 as _)); + #[cfg(usart_v4)] + { + trace!("USART: set_fifoen: true (usart_v4)"); + w.set_fifoen(true); + } + }); + + Ok(()) +} + +impl<'d, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, M> { + type Error = Error; + fn read(&mut self) -> Result> { + self.nb_read() + } +} + +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, M> { + type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl<'d, M: Mode> embedded_hal_02::serial::Read for Uart<'d, M> { + type Error = Error; + fn read(&mut self) -> Result> { + self.nb_read() + } +} + +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, M> { + type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { + match *self { + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other, + } + } +} + +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, M> { + type Error = Error; +} + +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, M> { + type Error = Error; +} + +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, M> { + type Error = Error; +} + +impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> { + fn read(&mut self) -> nb::Result { + self.nb_read() + } +} + +impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } +} + +impl<'d, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, M> { + fn read(&mut self) -> Result> { + self.nb_read() + } +} + +impl<'d, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl embedded_io::ErrorType for Uart<'_, M> { + type Error = Error; +} + +impl embedded_io::ErrorType for UartTx<'_, M> { + type Error = Error; +} + +impl embedded_io::Write for Uart<'_, M> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl embedded_io::Write for UartTx<'_, M> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + +impl embedded_io_async::Write for Uart<'_, Async> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush().await + } +} + +impl embedded_io_async::Write for UartTx<'_, Async> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush().await + } +} + +pub use buffered::*; + +pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; +mod buffered; + +#[cfg(not(gpdma))] +mod ringbuffered; +#[cfg(not(gpdma))] +pub use ringbuffered::RingBufferedUartRx; + +#[cfg(any(usart_v1, usart_v2))] +fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { + r.dr().as_ptr() as _ +} + +#[cfg(any(usart_v1, usart_v2))] +fn rdr(r: crate::pac::usart::Usart) -> *mut u8 { + r.dr().as_ptr() as _ +} + +#[cfg(any(usart_v1, usart_v2))] +fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg { + r.sr() +} + +#[cfg(any(usart_v1, usart_v2))] +#[allow(unused)] +fn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) { + // On v1 the flags are cleared implicitly by reads and writes to DR. +} + +#[cfg(any(usart_v3, usart_v4))] +fn tdr(r: Regs) -> *mut u8 { + r.tdr().as_ptr() as _ +} + +#[cfg(any(usart_v3, usart_v4))] +fn rdr(r: Regs) -> *mut u8 { + r.rdr().as_ptr() as _ +} + +#[cfg(any(usart_v3, usart_v4))] +fn sr(r: Regs) -> crate::pac::common::Reg { + r.isr() +} + +#[cfg(any(usart_v3, usart_v4))] +#[allow(unused)] +fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { + r.icr().write(|w| *w = regs::Icr(sr.0)); +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Kind { + Uart, + #[cfg(any(usart_v3, usart_v4))] + #[allow(unused)] + Lpuart, +} + +struct State { + rx_waker: AtomicWaker, + tx_rx_refcount: AtomicU8, +} + +impl State { + const fn new() -> Self { + Self { + rx_waker: AtomicWaker::new(), + tx_rx_refcount: AtomicU8::new(0), + } + } +} + +struct Info { + regs: Regs, + rcc: RccInfo, + interrupt: Interrupt, + kind: Kind, +} + +#[allow(private_interfaces)] +pub(crate) trait SealedInstance: crate::rcc::RccPeripheral { + fn info() -> &'static Info; + fn state() -> &'static State; + fn buffered_state() -> &'static buffered::State; +} + +/// USART peripheral instance trait. +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +pin_trait!(RxPin, Instance); +pin_trait!(TxPin, Instance); +pin_trait!(CtsPin, Instance); +pin_trait!(RtsPin, Instance); +pin_trait!(CkPin, Instance); +pin_trait!(DePin, Instance); + +dma_trait!(TxDma, Instance); +dma_trait!(RxDma, Instance); + +macro_rules! impl_usart { + ($inst:ident, $irq:ident, $kind:expr) => { + #[allow(private_interfaces)] + impl SealedInstance for crate::peripherals::$inst { + fn info() -> &'static Info { + static INFO: Info = Info { + regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }, + rcc: crate::peripherals::$inst::RCC_INFO, + interrupt: crate::interrupt::typelevel::$irq::IRQ, + kind: $kind, + }; + &INFO + } + + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } + + fn buffered_state() -> &'static buffered::State { + static BUFFERED_STATE: buffered::State = buffered::State::new(); + &BUFFERED_STATE + } + } + + impl Instance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} + +foreach_interrupt!( + ($inst:ident, usart, LPUART, $signal_name:ident, $irq:ident) => { + impl_usart!($inst, $irq, Kind::Lpuart); + }; + ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { + impl_usart!($inst, $irq, Kind::Uart); + }; +); From 7970419e9b840552444fa068094ec7f386bb07f8 Mon Sep 17 00:00:00 2001 From: decaday Date: Thu, 14 Nov 2024 20:16:38 +0800 Subject: [PATCH 2/4] feat: blocking usart --- Cargo.toml | 3 + src/usart/mod.rs | 1299 ++++++++++++++++++---------------------------- 2 files changed, 516 insertions(+), 786 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f5bd56a..a980a99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,9 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } embedded-hal-nb = { version = "1.0" } embedded-can = "0.4" +embedded-io = { version = "0.6.0" } +embedded-io-async = { version = "0.6.1" } +nb = "1.0.0" defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.4", optional = true } diff --git a/src/usart/mod.rs b/src/usart/mod.rs index 333e01e..f53baec 100644 --- a/src/usart/mod.rs +++ b/src/usart/mod.rs @@ -2,30 +2,26 @@ #![macro_use] #![warn(missing_docs)] -use core::future::poll_fn; +// use core::future::poll_fn; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; -use core::task::Poll; +// use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::drop::OnDrop; +// use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::PeripheralRef; use embassy_sync::waitqueue::AtomicWaker; +#[allow(unused_imports)] use futures_util::future::{select, Either}; -use crate::dma::ChannelAndRequest; +// use crate::dma::ChannelAndRequest; use crate::gpio::{self, AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::interrupt::typelevel::Interrupt as _; use crate::interrupt::{self, Interrupt, InterruptExt}; +#[allow(unused_imports)] use crate::mode::{Async, Blocking, Mode}; #[allow(unused_imports)] -#[cfg(not(any(usart_v1, usart_v2)))] -use crate::pac::usart::regs::Isr as Sr; -#[cfg(any(usart_v1, usart_v2))] use crate::pac::usart::regs::Sr; -#[cfg(not(any(usart_v1, usart_v2)))] -use crate::pac::usart::Lpuart as Regs; -#[cfg(any(usart_v1, usart_v2))] use crate::pac::usart::Usart as Regs; use crate::pac::usart::{regs, vals}; use crate::rcc::{RccInfo, SealedRccPeripheral}; @@ -115,12 +111,12 @@ pub enum Parity { pub enum StopBits { #[doc = "1 stop bit"] STOP1, - #[doc = "0.5 stop bits"] - STOP0P5, + // #[doc = "0.5 stop bits"] + // STOP0P5, #[doc = "2 stop bits"] STOP2, - #[doc = "1.5 stop bits"] - STOP1P5, + // #[doc = "1.5 stop bits"] + // STOP1P5, } #[non_exhaustive] @@ -155,24 +151,6 @@ pub struct Config { /// If false: the error is ignored and cleared pub detect_previous_overrun: bool, - /// Set this to true if the line is considered noise free. - /// This will increase the receiver’s tolerance to clock deviations, - /// but will effectively disable noise detection. - #[cfg(not(usart_v1))] - pub assume_noise_free: bool, - - /// Set this to true to swap the RX and TX pins. - #[cfg(any(usart_v3, usart_v4))] - pub swap_rx_tx: bool, - - /// Set this to true to invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle). - #[cfg(any(usart_v3, usart_v4))] - pub invert_tx: bool, - - /// Set this to true to invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle). - #[cfg(any(usart_v3, usart_v4))] - pub invert_rx: bool, - /// Set the pull configuration for the RX pin. pub rx_pull: Pull, @@ -182,18 +160,10 @@ pub struct Config { impl Config { fn tx_af(&self) -> AfType { - #[cfg(any(usart_v3, usart_v4))] - if self.swap_rx_tx { - return AfType::input(self.rx_pull); - }; AfType::output(OutputType::PushPull, Speed::Medium) } fn rx_af(&self) -> AfType { - #[cfg(any(usart_v3, usart_v4))] - if self.swap_rx_tx { - return AfType::output(OutputType::PushPull, Speed::Medium); - }; AfType::input(self.rx_pull) } } @@ -207,14 +177,6 @@ impl Default for Config { parity: Parity::ParityNone, // historical behavior detect_previous_overrun: false, - #[cfg(not(usart_v1))] - assume_noise_free: false, - #[cfg(any(usart_v3, usart_v4))] - swap_rx_tx: false, - #[cfg(any(usart_v3, usart_v4))] - invert_tx: false, - #[cfg(any(usart_v3, usart_v4))] - invert_rx: false, rx_pull: Pull::None, half_duplex: false, } @@ -229,7 +191,6 @@ pub enum HalfDuplexConfig { PushPull, /// Open drain output using external pull up resistor OpenDrainExternal, - #[cfg(not(gpio_v1))] /// Open drain output using internal pull up resistor OpenDrainInternal, } @@ -239,7 +200,6 @@ impl HalfDuplexConfig { match self { HalfDuplexConfig::PushPull => AfType::output(OutputType::PushPull, Speed::Medium), HalfDuplexConfig::OpenDrainExternal => AfType::output(OutputType::OpenDrain, Speed::Medium), - #[cfg(not(gpio_v1))] HalfDuplexConfig::OpenDrainInternal => AfType::output_pull(OutputType::OpenDrain, Speed::Medium, Pull::Up), } } @@ -262,12 +222,12 @@ pub enum Error { BufferTooLong, } -enum ReadCompletionEvent { - // DMA Read transfer completed first - DmaCompleted, - // Idle line detected first - Idle(usize), -} +// enum ReadCompletionEvent { +// // DMA Read transfer completed first +// DmaCompleted, +// // Idle line detected first +// Idle(usize), +// } /// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`]. /// @@ -303,7 +263,7 @@ pub struct UartTx<'d, M: Mode> { tx: Option>, cts: Option>, de: Option>, - tx_dma: Option>, + // tx_dma: Option>, _phantom: PhantomData, } @@ -351,10 +311,9 @@ pub struct UartRx<'d, M: Mode> { kernel_clock: Hertz, rx: Option>, rts: Option>, - rx_dma: Option>, - detect_previous_overrun: bool, - #[cfg(any(usart_v1, usart_v2))] - buffered_sr: stm32_metapac::usart::regs::Sr, + // rx_dma: Option>, + _detect_previous_overrun: bool, + buffered_sr: py32_metapac::usart::regs::Sr, _phantom: PhantomData, } @@ -367,68 +326,68 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> { } } -impl<'d> UartTx<'d, Async> { - /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. - pub fn new( - peri: impl Peripheral

+ 'd, - tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), - None, - new_dma!(tx_dma), - config, - ) - } - - /// Create a new tx-only UART with a clear-to-send pin - pub fn new_with_cts( - peri: impl Peripheral

+ 'd, - tx: impl Peripheral

> + 'd, - cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), - new_pin!(cts, AfType::input(Pull::None)), - new_dma!(tx_dma), - config, - ) - } - - /// Initiate an asynchronous UART write - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let r = self.info.regs; - - // Enable Transmitter and disable Receiver for Half-Duplex mode - let mut cr1 = r.cr1().read(); - if r.cr3().read().hdsel() && !cr1.te() { - cr1.set_te(true); - cr1.set_re(false); - r.cr1().write_value(cr1); - } - - let ch = self.tx_dma.as_mut().unwrap(); - r.cr3().modify(|reg| { - reg.set_dmat(true); - }); - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - let transfer = unsafe { ch.write(buffer, tdr(r), Default::default()) }; - transfer.await; - Ok(()) - } - - /// Wait until transmission complete - pub async fn flush(&mut self) -> Result<(), Error> { - flush(&self.info, &self.state).await - } -} +// impl<'d> UartTx<'d, Async> { +// /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. +// pub fn new( +// peri: impl Peripheral

+ 'd, +// tx: impl Peripheral

> + 'd, +// tx_dma: impl Peripheral

> + 'd, +// config: Config, +// ) -> Result { +// Self::new_inner( +// peri, +// new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), +// None, +// new_dma!(tx_dma), +// config, +// ) +// } + +// /// Create a new tx-only UART with a clear-to-send pin +// pub fn new_with_cts( +// peri: impl Peripheral

+ 'd, +// tx: impl Peripheral

> + 'd, +// cts: impl Peripheral

> + 'd, +// tx_dma: impl Peripheral

> + 'd, +// config: Config, +// ) -> Result { +// Self::new_inner( +// peri, +// new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), +// new_pin!(cts, AfType::input(Pull::None)), +// new_dma!(tx_dma), +// config, +// ) +// } + +// /// Initiate an asynchronous UART write +// pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { +// let r = self.info.regs; + +// // Enable Transmitter and disable Receiver for Half-Duplex mode +// let mut cr1 = r.cr1().read(); +// if r.cr3().read().hdsel() && !cr1.te() { +// cr1.set_te(true); +// cr1.set_re(false); +// r.cr1().write_value(cr1); +// } + +// let ch = self.tx_dma.as_mut().unwrap(); +// r.cr3().modify(|reg| { +// reg.set_dmat(true); +// }); +// // If we don't assign future to a variable, the data register pointer +// // is held across an await and makes the future non-Send. +// let transfer = unsafe { ch.write(buffer, tdr(r), Default::default()) }; +// transfer.await; +// Ok(()) +// } + +// /// Wait until transmission complete +// pub async fn flush(&mut self) -> Result<(), Error> { +// flush(&self.info, &self.state).await +// } +// } impl<'d> UartTx<'d, Blocking> { /// Create a new blocking tx-only UART with no hardware flow control. @@ -443,7 +402,7 @@ impl<'d> UartTx<'d, Blocking> { peri, new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), None, - None, + // None, config, ) } @@ -459,7 +418,7 @@ impl<'d> UartTx<'d, Blocking> { peri, new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), new_pin!(cts, AfType::input(config.rx_pull)), - None, + // None, config, ) } @@ -470,7 +429,7 @@ impl<'d, M: Mode> UartTx<'d, M> { _peri: impl Peripheral

+ 'd, tx: Option>, cts: Option>, - tx_dma: Option>, + // tx_dma: Option>, config: Config, ) -> Result { let mut this = Self { @@ -480,7 +439,7 @@ impl<'d, M: Mode> UartTx<'d, M> { tx, cts, de: None, - tx_dma, + // tx_dma, _phantom: PhantomData, }; this.enable_and_configure(&config)?; @@ -537,35 +496,35 @@ 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); - }); +// /// 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); +// compiler_fence(Ordering::SeqCst); - // future which completes when Transmission complete is detected - let abort = poll_fn(move |cx| { - state.rx_waker.register(cx.waker()); +// // 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(()); - } +// let sr = sr(r).read(); +// if sr.tc() { +// // Transmission complete detected +// return Poll::Ready(()); +// } - Poll::Pending - }); +// Poll::Pending +// }); - abort.await; - } +// abort.await; +// } - Ok(()) -} +// Ok(()) +// } fn blocking_flush(info: &Info) -> Result<(), Error> { let r = info.regs; @@ -579,275 +538,269 @@ fn blocking_flush(info: &Info) -> Result<(), Error> { /// Send break character pub fn send_break(regs: &Regs) { // Busy wait until previous break has been sent - #[cfg(any(usart_v1, usart_v2))] while regs.cr1().read().sbk() {} - #[cfg(any(usart_v3, usart_v4))] - while regs.isr().read().sbkf() {} // Send break right after completing the current character transmission - #[cfg(any(usart_v1, usart_v2))] regs.cr1().modify(|w| w.set_sbk(true)); - #[cfg(any(usart_v3, usart_v4))] - regs.rqr().write(|w| w.set_sbkrq(true)); } -impl<'d> UartRx<'d, Async> { - /// Create a new rx-only UART with no hardware flow control. - /// - /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. - pub fn new( - peri: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - rx: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, AfType::input(config.rx_pull)), - None, - new_dma!(rx_dma), - config, - ) - } - - /// Create a new rx-only UART with a request-to-send pin - pub fn new_with_rts( - peri: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - rx: impl Peripheral

> + 'd, - rts: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, AfType::input(config.rx_pull)), - new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), - new_dma!(rx_dma), - config, - ) - } - - /// Initiate an asynchronous UART read - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.inner_read(buffer, false).await?; - - Ok(()) - } - - /// Initiate an asynchronous read with idle line detection enabled - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.inner_read(buffer, true).await - } - - async fn inner_read_run( - &mut self, - buffer: &mut [u8], - enable_idle_line_detection: bool, - ) -> Result { - let r = self.info.regs; - - // 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() { - 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 - let on_drop = OnDrop::new(move || { - // defmt::trace!("Clear all USART interrupts and DMA Read Request"); - // clear all interrupts and DMA Rx Request - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); - }); - - let ch = self.rx_dma.as_mut().unwrap(); - - let buffer_len = buffer.len(); - - // Start USART DMA - // will not do anything yet because DMAR is not yet set - // future which will complete when DMA Read request completes - let transfer = unsafe { ch.read(rdr(r), buffer, Default::default()) }; - - // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer - if !self.detect_previous_overrun { - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - unsafe { rdr(r).read_volatile() }; - clear_interrupt_flags(r, sr); - } - - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // enable parity interrupt if not ParityNone - w.set_peie(w.pce()); - }); - - r.cr3().modify(|w| { - // enable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(true); - // enable DMA Rx Request - w.set_dmar(true); - }); - - compiler_fence(Ordering::SeqCst); - - // In case of errors already pending when reception started, interrupts may have already been raised - // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts - // have been disabled in interrupt handler and DMA Rx Request has been disabled. - - let cr3 = r.cr3().read(); - - if !cr3.dmar() { - // something went wrong - // because the only way to get this flag cleared is to have an interrupt - - // DMA will be stopped when transfer is dropped - - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - unsafe { rdr(r).read_volatile() }; - clear_interrupt_flags(r, sr); - - if sr.pe() { - return Err(Error::Parity); - } - if sr.fe() { - return Err(Error::Framing); - } - if sr.ne() { - return Err(Error::Noise); - } - if sr.ore() { - return Err(Error::Overrun); - } - - unreachable!(); - } - - if enable_idle_line_detection { - // clear idle flag - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - unsafe { rdr(r).read_volatile() }; - clear_interrupt_flags(r, sr); - - // enable idle interrupt - r.cr1().modify(|w| { - w.set_idleie(true); - }); - } - - compiler_fence(Ordering::SeqCst); - - // future which completes when idle line or error is detected - let s = self.state; - let abort = poll_fn(move |cx| { - s.rx_waker.register(cx.waker()); - - let sr = sr(r).read(); - - // This read also clears the error and idle interrupt flags on v1. - unsafe { rdr(r).read_volatile() }; - clear_interrupt_flags(r, sr); - - if enable_idle_line_detection { - // enable idle interrupt - r.cr1().modify(|w| { - w.set_idleie(true); - }); - } - - compiler_fence(Ordering::SeqCst); - - let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); - - if has_errors { - // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler - - if sr.pe() { - return Poll::Ready(Err(Error::Parity)); - } - if sr.fe() { - return Poll::Ready(Err(Error::Framing)); - } - if sr.ne() { - return Poll::Ready(Err(Error::Noise)); - } - if sr.ore() { - return Poll::Ready(Err(Error::Overrun)); - } - } - - if enable_idle_line_detection && sr.idle() { - // Idle line detected - return Poll::Ready(Ok(())); - } - - Poll::Pending - }); - - // wait for the first of DMA request or idle line detected to completes - // select consumes its arguments - // when transfer is dropped, it will stop the DMA request - let r = match select(transfer, abort).await { - // DMA transfer completed first - Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted), - - // Idle line detected first - Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle( - buffer_len - transfer.get_remaining_transfers() as usize, - )), - - // error occurred - Either::Right((Err(e), _)) => Err(e), - }; - - drop(on_drop); - - r - } - - async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result { - if buffer.is_empty() { - return Ok(0); - } else if buffer.len() > 0xFFFF { - return Err(Error::BufferTooLong); - } - - let buffer_len = buffer.len(); - - // wait for DMA to complete or IDLE line detection if requested - let res = self.inner_read_run(buffer, enable_idle_line_detection).await; - - match res { - Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), - Ok(ReadCompletionEvent::Idle(n)) => Ok(n), - Err(e) => Err(e), - } - } -} +// impl<'d> UartRx<'d, Async> { +// /// Create a new rx-only UART with no hardware flow control. +// /// +// /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. +// pub fn new( +// peri: impl Peripheral

+ 'd, +// _irq: impl interrupt::typelevel::Binding> + 'd, +// rx: impl Peripheral

> + 'd, +// rx_dma: impl Peripheral

> + 'd, +// config: Config, +// ) -> Result { +// Self::new_inner( +// peri, +// new_pin!(rx, AfType::input(config.rx_pull)), +// None, +// new_dma!(rx_dma), +// config, +// ) +// } + +// /// Create a new rx-only UART with a request-to-send pin +// pub fn new_with_rts( +// peri: impl Peripheral

+ 'd, +// _irq: impl interrupt::typelevel::Binding> + 'd, +// rx: impl Peripheral

> + 'd, +// rts: impl Peripheral

> + 'd, +// rx_dma: impl Peripheral

> + 'd, +// config: Config, +// ) -> Result { +// Self::new_inner( +// peri, +// new_pin!(rx, AfType::input(config.rx_pull)), +// new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), +// new_dma!(rx_dma), +// config, +// ) +// } + +// /// Initiate an asynchronous UART read +// pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { +// self.inner_read(buffer, false).await?; + +// Ok(()) +// } + +// /// Initiate an asynchronous read with idle line detection enabled +// pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { +// self.inner_read(buffer, true).await +// } + +// async fn inner_read_run( +// &mut self, +// buffer: &mut [u8], +// enable_idle_line_detection: bool, +// ) -> Result { +// let r = self.info.regs; + +// // 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() { +// 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 +// let on_drop = OnDrop::new(move || { +// // defmt::trace!("Clear all USART interrupts and DMA Read Request"); +// // clear all interrupts and DMA Rx Request +// r.cr1().modify(|w| { +// // disable RXNE interrupt +// w.set_rxneie(false); +// // disable parity interrupt +// w.set_peie(false); +// // disable idle line interrupt +// w.set_idleie(false); +// }); +// r.cr3().modify(|w| { +// // disable Error Interrupt: (Frame error, Noise error, Overrun error) +// w.set_eie(false); +// // disable DMA Rx Request +// w.set_dmar(false); +// }); +// }); + +// let ch = self.rx_dma.as_mut().unwrap(); + +// let buffer_len = buffer.len(); + +// // Start USART DMA +// // will not do anything yet because DMAR is not yet set +// // future which will complete when DMA Read request completes +// let transfer = unsafe { ch.read(rdr(r), buffer, Default::default()) }; + +// // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer +// if !self.detect_previous_overrun { +// let sr = sr(r).read(); +// // This read also clears the error and idle interrupt flags on v1. +// unsafe { rdr(r).read_volatile() }; +// clear_interrupt_flags(r, sr); +// } + +// r.cr1().modify(|w| { +// // disable RXNE interrupt +// w.set_rxneie(false); +// // enable parity interrupt if not ParityNone +// w.set_peie(w.pce()); +// }); + +// r.cr3().modify(|w| { +// // enable Error Interrupt: (Frame error, Noise error, Overrun error) +// w.set_eie(true); +// // enable DMA Rx Request +// w.set_dmar(true); +// }); + +// compiler_fence(Ordering::SeqCst); + +// // In case of errors already pending when reception started, interrupts may have already been raised +// // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts +// // have been disabled in interrupt handler and DMA Rx Request has been disabled. + +// let cr3 = r.cr3().read(); + +// if !cr3.dmar() { +// // something went wrong +// // because the only way to get this flag cleared is to have an interrupt + +// // DMA will be stopped when transfer is dropped + +// let sr = sr(r).read(); +// // This read also clears the error and idle interrupt flags on v1. +// unsafe { rdr(r).read_volatile() }; +// clear_interrupt_flags(r, sr); + +// if sr.pe() { +// return Err(Error::Parity); +// } +// if sr.fe() { +// return Err(Error::Framing); +// } +// if sr.ne() { +// return Err(Error::Noise); +// } +// if sr.ore() { +// return Err(Error::Overrun); +// } + +// unreachable!(); +// } + +// if enable_idle_line_detection { +// // clear idle flag +// let sr = sr(r).read(); +// // This read also clears the error and idle interrupt flags on v1. +// unsafe { rdr(r).read_volatile() }; +// clear_interrupt_flags(r, sr); + +// // enable idle interrupt +// r.cr1().modify(|w| { +// w.set_idleie(true); +// }); +// } + +// compiler_fence(Ordering::SeqCst); + +// // future which completes when idle line or error is detected +// let s = self.state; +// let abort = poll_fn(move |cx| { +// s.rx_waker.register(cx.waker()); + +// let sr = sr(r).read(); + +// // This read also clears the error and idle interrupt flags on v1. +// unsafe { rdr(r).read_volatile() }; +// clear_interrupt_flags(r, sr); + +// if enable_idle_line_detection { +// // enable idle interrupt +// r.cr1().modify(|w| { +// w.set_idleie(true); +// }); +// } + +// compiler_fence(Ordering::SeqCst); + +// let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); + +// if has_errors { +// // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler + +// if sr.pe() { +// return Poll::Ready(Err(Error::Parity)); +// } +// if sr.fe() { +// return Poll::Ready(Err(Error::Framing)); +// } +// if sr.ne() { +// return Poll::Ready(Err(Error::Noise)); +// } +// if sr.ore() { +// return Poll::Ready(Err(Error::Overrun)); +// } +// } + +// if enable_idle_line_detection && sr.idle() { +// // Idle line detected +// return Poll::Ready(Ok(())); +// } + +// Poll::Pending +// }); + +// // wait for the first of DMA request or idle line detected to completes +// // select consumes its arguments +// // when transfer is dropped, it will stop the DMA request +// let r = match select(transfer, abort).await { +// // DMA transfer completed first +// Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted), + +// // Idle line detected first +// Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle( +// buffer_len - transfer.get_remaining_transfers() as usize, +// )), + +// // error occurred +// Either::Right((Err(e), _)) => Err(e), +// }; + +// drop(on_drop); + +// r +// } + +// async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result { +// if buffer.is_empty() { +// return Ok(0); +// } else if buffer.len() > 0xFFFF { +// return Err(Error::BufferTooLong); +// } + +// let buffer_len = buffer.len(); + +// // wait for DMA to complete or IDLE line detection if requested +// let res = self.inner_read_run(buffer, enable_idle_line_detection).await; + +// match res { +// Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), +// Ok(ReadCompletionEvent::Idle(n)) => Ok(n), +// Err(e) => Err(e), +// } +// } +// } impl<'d> UartRx<'d, Blocking> { /// Create a new rx-only UART with no hardware flow control. @@ -858,7 +811,7 @@ impl<'d> UartRx<'d, Blocking> { rx: impl Peripheral

> + 'd, config: Config, ) -> Result { - Self::new_inner(peri, new_pin!(rx, AfType::input(config.rx_pull)), None, None, config) + Self::new_inner(peri, new_pin!(rx, AfType::input(config.rx_pull)), None, /*None,*/ config) } /// Create a new rx-only UART with a request-to-send pin @@ -872,7 +825,7 @@ impl<'d> UartRx<'d, Blocking> { peri, new_pin!(rx, AfType::input(config.rx_pull)), new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), - None, + // None, config, ) } @@ -883,7 +836,7 @@ impl<'d, M: Mode> UartRx<'d, M> { _peri: impl Peripheral

+ 'd, rx: Option>, rts: Option>, - rx_dma: Option>, + // rx_dma: Option>, config: Config, ) -> Result { let mut this = Self { @@ -893,10 +846,9 @@ impl<'d, M: Mode> UartRx<'d, M> { kernel_clock: T::frequency(), rx, rts, - rx_dma, - detect_previous_overrun: config.detect_previous_overrun, - #[cfg(any(usart_v1, usart_v2))] - buffered_sr: stm32_metapac::usart::regs::Sr(0), + // rx_dma, + _detect_previous_overrun: config.detect_previous_overrun, + buffered_sr: py32_metapac::usart::regs::Sr(0), }; this.enable_and_configure(&config)?; Ok(this) @@ -925,7 +877,6 @@ impl<'d, M: Mode> UartRx<'d, M> { reconfigure(self.info, self.kernel_clock, config) } - #[cfg(any(usart_v1, usart_v2))] fn check_rx_flags(&mut self) -> Result { let r = self.info.regs; loop { @@ -958,26 +909,6 @@ impl<'d, M: Mode> UartRx<'d, M> { } } - #[cfg(any(usart_v3, usart_v4))] - fn check_rx_flags(&mut self) -> Result { - let r = self.info.regs; - let sr = r.isr().read(); - if sr.pe() { - r.icr().write(|w| w.set_pe(true)); - return Err(Error::Parity); - } else if sr.fe() { - r.icr().write(|w| w.set_fe(true)); - return Err(Error::Framing); - } else if sr.ne() { - r.icr().write(|w| w.set_ne(true)); - return Err(Error::Noise); - } else if sr.ore() { - r.icr().write(|w| w.set_ore(true)); - return Err(Error::Overrun); - } - Ok(sr.rxne()) - } - /// Read a single u8 if there is one available, otherwise return WouldBlock pub(crate) fn nb_read(&mut self) -> Result> { let r = self.info.regs; @@ -1042,176 +973,111 @@ fn drop_tx_rx(info: &Info, state: &State) { } } -impl<'d> Uart<'d, Async> { - /// Create a new bidirectional UART - pub fn new( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_af()), - new_pin!(tx, config.tx_af()), - None, - None, - None, - new_dma!(tx_dma), - new_dma!(rx_dma), - config, - ) - } - - /// Create a new bidirectional UART with request-to-send and clear-to-send pins - pub fn new_with_rtscts( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - rts: impl Peripheral

> + 'd, - cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_af()), - new_pin!(tx, config.tx_af()), - new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), - new_pin!(cts, AfType::input(Pull::None)), - None, - new_dma!(tx_dma), - new_dma!(rx_dma), - config, - ) - } - - #[cfg(not(any(usart_v1, usart_v2)))] - /// Create a new bidirectional UART with a driver-enable pin - pub fn new_with_de( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - de: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_af()), - new_pin!(tx, config.tx_af()), - None, - None, - new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), - new_dma!(tx_dma), - new_dma!(rx_dma), - config, - ) - } - - /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. - /// - /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin - /// (when it is available for your chip). There is no functional difference between these methods, as both - /// allow bidirectional communication. - /// - /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard - /// I/O in idle or in reception. It means that the I/O must be configured so that TX is - /// configured as alternate function open-drain with an external pull-up - /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict - /// on the line must be managed by software (for instance by using a centralized arbiter). - #[doc(alias("HDSEL"))] - pub fn new_half_duplex( - peri: impl Peripheral

+ 'd, - tx: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - mut config: Config, - half_duplex: HalfDuplexConfig, - ) -> Result { - #[cfg(not(any(usart_v1, usart_v2)))] - { - config.swap_rx_tx = false; - } - config.half_duplex = true; - - Self::new_inner( - peri, - None, - new_pin!(tx, half_duplex.af_type()), - None, - None, - None, - new_dma!(tx_dma), - new_dma!(rx_dma), - config, - ) - } - - /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. - /// - /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. - /// There is no functional difference between these methods, as both allow bidirectional communication. - /// - /// The pin is always released when no data is transmitted. Thus, it acts as a standard - /// I/O in idle or in reception. - /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict - /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] - #[doc(alias("HDSEL"))] - pub fn new_half_duplex_on_rx( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, - mut config: Config, - half_duplex: HalfDuplexConfig, - ) -> Result { - config.swap_rx_tx = true; - config.half_duplex = true; - - Self::new_inner( - peri, - None, - None, - new_pin!(rx, half_duplex.af_type()), - None, - None, - new_dma!(tx_dma), - new_dma!(rx_dma), - config, - ) - } - - /// Perform an asynchronous write - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - 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 - } - - /// Perform an an asynchronous read with idle line detection enabled - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.read_until_idle(buffer).await - } -} +// impl<'d> Uart<'d, Async> { +// /// Create a new bidirectional UART +// pub fn new( +// peri: impl Peripheral

+ 'd, +// rx: impl Peripheral

> + 'd, +// tx: impl Peripheral

> + 'd, +// _irq: impl interrupt::typelevel::Binding> + 'd, +// tx_dma: impl Peripheral

> + 'd, +// rx_dma: impl Peripheral

> + 'd, +// config: Config, +// ) -> Result { +// Self::new_inner( +// peri, +// new_pin!(rx, config.rx_af()), +// new_pin!(tx, config.tx_af()), +// None, +// None, +// None, +// new_dma!(tx_dma), +// new_dma!(rx_dma), +// config, +// ) +// } + +// /// Create a new bidirectional UART with request-to-send and clear-to-send pins +// pub fn new_with_rtscts( +// peri: impl Peripheral

+ 'd, +// rx: impl Peripheral

> + 'd, +// tx: impl Peripheral

> + 'd, +// _irq: impl interrupt::typelevel::Binding> + 'd, +// rts: impl Peripheral

> + 'd, +// cts: impl Peripheral

> + 'd, +// tx_dma: impl Peripheral

> + 'd, +// rx_dma: impl Peripheral

> + 'd, +// config: Config, +// ) -> Result { +// Self::new_inner( +// peri, +// new_pin!(rx, config.rx_af()), +// new_pin!(tx, config.tx_af()), +// new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), +// new_pin!(cts, AfType::input(Pull::None)), +// None, +// new_dma!(tx_dma), +// new_dma!(rx_dma), +// config, +// ) +// } + +// /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. +// /// +// /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin +// /// (when it is available for your chip). There is no functional difference between these methods, as both +// /// allow bidirectional communication. +// /// +// /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard +// /// I/O in idle or in reception. It means that the I/O must be configured so that TX is +// /// configured as alternate function open-drain with an external pull-up +// /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict +// /// on the line must be managed by software (for instance by using a centralized arbiter). +// #[doc(alias("HDSEL"))] +// pub fn new_half_duplex( +// peri: impl Peripheral

+ 'd, +// tx: impl Peripheral

> + 'd, +// _irq: impl interrupt::typelevel::Binding> + 'd, +// tx_dma: impl Peripheral

> + 'd, +// rx_dma: impl Peripheral

> + 'd, +// mut config: Config, +// half_duplex: HalfDuplexConfig, +// ) -> Result { +// config.half_duplex = true; + +// Self::new_inner( +// peri, +// None, +// new_pin!(tx, half_duplex.af_type()), +// None, +// None, +// None, +// new_dma!(tx_dma), +// new_dma!(rx_dma), +// config, +// ) +// } + +// /// Perform an asynchronous write +// pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { +// 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 +// } + +// /// Perform an an asynchronous read with idle line detection enabled +// pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { +// self.rx.read_until_idle(buffer).await +// } +// } impl<'d> Uart<'d, Blocking> { /// Create a new blocking bidirectional UART. @@ -1228,8 +1094,8 @@ impl<'d> Uart<'d, Blocking> { None, None, None, - None, - None, + // None, + // None, config, ) } @@ -1250,30 +1116,8 @@ impl<'d> Uart<'d, Blocking> { new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), new_pin!(cts, AfType::input(Pull::None)), None, - None, - None, - config, - ) - } - - #[cfg(not(any(usart_v1, usart_v2)))] - /// Create a new bidirectional UART with a driver-enable pin - pub fn new_blocking_with_de( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - de: impl Peripheral

> + 'd, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_af()), - new_pin!(tx, config.tx_af()), - None, - None, - new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), - None, - None, + // None, + // None, config, ) } @@ -1295,10 +1139,6 @@ impl<'d> Uart<'d, Blocking> { mut config: Config, half_duplex: HalfDuplexConfig, ) -> Result { - #[cfg(not(any(usart_v1, usart_v2)))] - { - config.swap_rx_tx = false; - } config.half_duplex = true; Self::new_inner( @@ -1308,41 +1148,8 @@ impl<'d> Uart<'d, Blocking> { None, None, None, - None, - None, - config, - ) - } - - /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. - /// - /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. - /// There is no functional difference between these methods, as both allow bidirectional communication. - /// - /// The pin is always released when no data is transmitted. Thus, it acts as a standard - /// I/O in idle or in reception. - /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict - /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] - #[doc(alias("HDSEL"))] - pub fn new_blocking_half_duplex_on_rx( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - mut config: Config, - half_duplex: HalfDuplexConfig, - ) -> Result { - config.swap_rx_tx = true; - config.half_duplex = true; - - Self::new_inner( - peri, - None, - None, - new_pin!(rx, half_duplex.af_type()), - None, - None, - None, - None, + // None, + // None, config, ) } @@ -1356,8 +1163,8 @@ impl<'d, M: Mode> Uart<'d, M> { rts: Option>, cts: Option>, de: Option>, - tx_dma: Option>, - rx_dma: Option>, + // tx_dma: Option>, + // rx_dma: Option>, config: Config, ) -> Result { let info = T::info(); @@ -1373,7 +1180,7 @@ impl<'d, M: Mode> Uart<'d, M> { tx, cts, de, - tx_dma, + // tx_dma, }, rx: UartRx { _phantom: PhantomData, @@ -1382,10 +1189,9 @@ impl<'d, M: Mode> Uart<'d, M> { kernel_clock, rx, rts, - rx_dma, - detect_previous_overrun: config.detect_previous_overrun, - #[cfg(any(usart_v1, usart_v2))] - buffered_sr: stm32_metapac::usart::regs::Sr(0), + // rx_dma, + _detect_previous_overrun: config.detect_previous_overrun, + buffered_sr: py32_metapac::usart::regs::Sr(0), }, }; this.enable_and_configure(&config)?; @@ -1402,8 +1208,6 @@ impl<'d, M: Mode> Uart<'d, M> { info.regs.cr3().write(|w| { w.set_rtse(self.rx.rts.is_some()); w.set_ctse(self.tx.cts.is_some()); - #[cfg(not(any(usart_v1, usart_v2)))] - w.set_dem(self.tx.de.is_some()); }); configure(info, self.rx.kernel_clock, config, true, true)?; @@ -1473,31 +1277,9 @@ fn configure( return Err(ConfigError::RxOrTxNotEnabled); } - #[cfg(not(usart_v4))] static DIVS: [(u16, ()); 1] = [(1, ())]; - #[cfg(usart_v4)] - static DIVS: [(u16, vals::Presc); 12] = [ - (1, vals::Presc::DIV1), - (2, vals::Presc::DIV2), - (4, vals::Presc::DIV4), - (6, vals::Presc::DIV6), - (8, vals::Presc::DIV8), - (10, vals::Presc::DIV10), - (12, vals::Presc::DIV12), - (16, vals::Presc::DIV16), - (32, vals::Presc::DIV32), - (64, vals::Presc::DIV64), - (128, vals::Presc::DIV128), - (256, vals::Presc::DIV256), - ]; - let (mul, brr_min, brr_max) = match kind { - #[cfg(any(usart_v3, usart_v4))] - Kind::Lpuart => { - trace!("USART: Kind::Lpuart"); - (256, 0x300, 0x10_0000) - } Kind::Uart => { trace!("USART: Kind::Uart"); (1, 0x10, 0x1_0000) @@ -1524,7 +1306,6 @@ fn configure( w.set_ue(false); }); - #[cfg(not(usart_v1))] let mut over8 = false; let mut found_brr = None; for &(presc, _presc_val) in &DIVS { @@ -1538,12 +1319,9 @@ fn configure( ); if brr < brr_min { - #[cfg(not(usart_v1))] - if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + if brr * 2 >= brr_min && kind == Kind::Uart { over8 = true; r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); found_brr = Some(brr); break; } @@ -1552,19 +1330,13 @@ fn configure( if brr < brr_max { r.brr().write_value(regs::Brr(brr)); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); found_brr = Some(brr); break; } } let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; - - #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; - #[cfg(usart_v1)] - let oversampling = "default"; trace!( "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, @@ -1574,24 +1346,16 @@ fn configure( r.cr2().write(|w| { w.set_stop(match config.stop_bits { - StopBits::STOP0P5 => vals::Stop::STOP0P5, + // StopBits::STOP0P5 => vals::Stop::STOP0P5, StopBits::STOP1 => vals::Stop::STOP1, - StopBits::STOP1P5 => vals::Stop::STOP1P5, + // StopBits::STOP1P5 => vals::Stop::STOP1P5, StopBits::STOP2 => vals::Stop::STOP2, }); - - #[cfg(any(usart_v3, usart_v4))] - { - w.set_txinv(config.invert_tx); - w.set_rxinv(config.invert_rx); - w.set_swap(config.swap_rx_tx); - } }); r.cr3().modify(|w| { - #[cfg(not(usart_v1))] - w.set_onebit(config.assume_noise_free); w.set_hdsel(config.half_duplex); + w.set_over8(vals::Over8::from_bits(over8 as _)); }); r.cr1().write(|w| { @@ -1635,13 +1399,6 @@ fn configure( vals::Ps::EVEN } }); - #[cfg(not(usart_v1))] - w.set_over8(vals::Over8::from_bits(over8 as _)); - #[cfg(usart_v4)] - { - trace!("USART: set_fifoen: true (usart_v4)"); - w.set_fifoen(true); - } }); Ok(()) @@ -1773,86 +1530,56 @@ impl embedded_io::Write for UartTx<'_, M> { } } -impl embedded_io_async::Write for Uart<'_, Async> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write(buf).await?; - Ok(buf.len()) - } +// impl embedded_io_async::Write for Uart<'_, Async> { +// async fn write(&mut self, buf: &[u8]) -> Result { +// self.write(buf).await?; +// Ok(buf.len()) +// } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush().await - } -} +// async fn flush(&mut self) -> Result<(), Self::Error> { +// self.flush().await +// } +// } -impl embedded_io_async::Write for UartTx<'_, Async> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write(buf).await?; - Ok(buf.len()) - } +// impl embedded_io_async::Write for UartTx<'_, Async> { +// async fn write(&mut self, buf: &[u8]) -> Result { +// self.write(buf).await?; +// Ok(buf.len()) +// } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush().await - } -} +// async fn flush(&mut self) -> Result<(), Self::Error> { +// self.flush().await +// } +// } pub use buffered::*; pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; mod buffered; -#[cfg(not(gpdma))] -mod ringbuffered; -#[cfg(not(gpdma))] -pub use ringbuffered::RingBufferedUartRx; +// mod ringbuffered; +// pub use ringbuffered::RingBufferedUartRx; -#[cfg(any(usart_v1, usart_v2))] fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().as_ptr() as _ } -#[cfg(any(usart_v1, usart_v2))] fn rdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().as_ptr() as _ } -#[cfg(any(usart_v1, usart_v2))] fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg { r.sr() } -#[cfg(any(usart_v1, usart_v2))] #[allow(unused)] fn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) { // On v1 the flags are cleared implicitly by reads and writes to DR. } -#[cfg(any(usart_v3, usart_v4))] -fn tdr(r: Regs) -> *mut u8 { - r.tdr().as_ptr() as _ -} - -#[cfg(any(usart_v3, usart_v4))] -fn rdr(r: Regs) -> *mut u8 { - r.rdr().as_ptr() as _ -} - -#[cfg(any(usart_v3, usart_v4))] -fn sr(r: Regs) -> crate::pac::common::Reg { - r.isr() -} - -#[cfg(any(usart_v3, usart_v4))] -#[allow(unused)] -fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { - r.icr().write(|w| *w = regs::Icr(sr.0)); -} - #[derive(Clone, Copy, PartialEq, Eq)] enum Kind { Uart, - #[cfg(any(usart_v3, usart_v4))] - #[allow(unused)] - Lpuart, } struct State { From 41b9696d2ce9dc626d429eeba6c428a457c53e12 Mon Sep 17 00:00:00 2001 From: decaday Date: Thu, 14 Nov 2024 20:16:59 +0800 Subject: [PATCH 3/4] feat: buffered usart --- src/usart/buffered.rs | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/usart/buffered.rs b/src/usart/buffered.rs index 7ba2090..d6ffcdf 100644 --- a/src/usart/buffered.rs +++ b/src/usart/buffered.rs @@ -9,8 +9,6 @@ use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -#[cfg(not(any(usart_v1, usart_v2)))] -use super::DePin; use super::{ clear_interrupt_flags, configure, rdr, reconfigure, send_break, sr, tdr, Config, ConfigError, CtsPin, Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, @@ -36,7 +34,7 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { // On v1 & v2, reading DR clears the rxne, error and idle interrupt // flags. Keep this close to the SR read to reduce the chance of a // flag being set in-between. - let dr = if sr_val.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr_val.ore() || sr_val.idle()) { + let dr = if sr_val.rxne() || (sr_val.ore() || sr_val.idle()) { Some(rdr(r).read_volatile()) } else { None @@ -81,7 +79,6 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { // For other usart variants it shows that last byte from the buffer was just sent. if sr_val.tc() { // For others it is cleared above with `clear_interrupt_flags`. - #[cfg(any(usart_v1, usart_v2))] sr(r).modify(|w| w.set_tc(false)); r.cr1().modify(|w| { @@ -294,31 +291,6 @@ impl<'d> BufferedUart<'d> { ) } - /// Create a new bidirectional buffered UART driver with a driver-enable pin - #[cfg(not(any(usart_v1, usart_v2)))] - pub fn new_with_de( - peri: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - de: impl Peripheral

> + 'd, - tx_buffer: &'d mut [u8], - rx_buffer: &'d mut [u8], - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, AfType::input(config.rx_pull)), - new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), - None, - None, - new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), - tx_buffer, - rx_buffer, - config, - ) - } - fn new_inner( _peri: impl Peripheral

+ 'd, rx: Option>, @@ -375,8 +347,6 @@ impl<'d> BufferedUart<'d> { info.regs.cr3().write(|w| { w.set_rtse(self.rx.rts.is_some()); w.set_ctse(self.tx.cts.is_some()); - #[cfg(not(any(usart_v1, usart_v2)))] - w.set_dem(self.tx.de.is_some()); }); configure(info, self.rx.kernel_clock, &config, true, true)?; From 87e71e120a5de9bd3e779986f02613acb3f96eef Mon Sep 17 00:00:00 2001 From: decaday Date: Thu, 14 Nov 2024 23:12:46 +0800 Subject: [PATCH 4/4] example: add usart, usart_buffered --- README.md | 2 +- examples/py32f030/Cargo.toml | 4 +- examples/py32f030/src/bin/usart.rs | 28 ++++++++++++ examples/py32f030/src/bin/usart_buffered.rs | 49 +++++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 examples/py32f030/src/bin/usart.rs create mode 100644 examples/py32f030/src/bin/usart_buffered.rs diff --git a/README.md b/README.md index fff5134..e0fdb58 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ For a full list of chip capabilities and peripherals, check the [py32-data](http | INTERRUPT | | ✅ | | | | DMA | N/A | | | | | EXTI | | ✅+ | | | -| USART | | | | | +| USART | | ✅ | | | | I2C | | ✅ | | | | SPI | | | | | | ADC | | ✅+ | | | diff --git a/examples/py32f030/Cargo.toml b/examples/py32f030/Cargo.toml index 53dd6d9..6c6c452 100644 --- a/examples/py32f030/Cargo.toml +++ b/examples/py32f030/Cargo.toml @@ -15,8 +15,10 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } embassy-sync = { version = "0.6.0", features = ["defmt"] } embassy-executor = { version = "0.6.1", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-1_000"] } +embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embedded-io = { version = "0.6.0" } +embedded-io-async = { version = "0.6.1" } py32-hal = { path = "../../", features = [ "time-driver-tim3", "py32f030f16"]} diff --git a/examples/py32f030/src/bin/usart.rs b/examples/py32f030/src/bin/usart.rs new file mode 100644 index 0000000..c294b22 --- /dev/null +++ b/examples/py32f030/src/bin/usart.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(impl_trait_in_assoc_type)] + +use embassy_executor::Spawner; +use defmt::*; +use py32_hal::usart::{Config, Uart}; +use py32_hal::{bind_interrupts, peripherals, usart}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let p = py32_hal::init(Default::default()); + + let config = Config::default(); + let mut usart = Uart::new_blocking(p.USART1, p.PA3, p.PA2, config).unwrap(); + + unwrap!(usart.blocking_write(b"Hello Embassy World!")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} diff --git a/examples/py32f030/src/bin/usart_buffered.rs b/examples/py32f030/src/bin/usart_buffered.rs new file mode 100644 index 0000000..f3e0248 --- /dev/null +++ b/examples/py32f030/src/bin/usart_buffered.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] +#![feature(impl_trait_in_assoc_type)] + +use defmt::*; +use embassy_executor::Spawner; +use py32_hal::usart::{BufferedUart, Config}; +use py32_hal::{bind_interrupts, peripherals, usart}; +use py32_hal::time::Hertz; +use py32_hal::rcc::{Pll, PllSource, Sysclk}; +use embedded_io_async::Read; +use embedded_io_async::Write; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USART1 => usart::BufferedInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut cfg: py32_hal::Config = Default::default(); + cfg.rcc.hsi = Some(Hertz::mhz(24)); + let p = py32_hal::init(cfg); + info!("Hello World!"); + + let config = Config::default(); + let mut tx_buf = [0u8; 256]; + let mut rx_buf = [0u8; 256]; + let mut usart = BufferedUart::new(p.USART1, Irqs, p.PA3, p.PA2, &mut tx_buf, &mut rx_buf, config).unwrap(); + + usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); + info!("wrote Hello, starting echo"); + + let mut buf = [0; 5]; + loop { + // When using defmt, be cautious with the info! and other logging macros! + // If you're using a single channel (as is usually the case), defmt requires global_logger to acquire interrupts to be disabled. + // For example, defmt-rtt uses critical_section, which temporarily disables global interrupts. + //This can lead to USART Overrun error(SR.ORE), causing some data to be lost. + usart.read_exact(&mut buf[..]).await.unwrap(); + // info!("Received:{} {}", buf, buf.len()); + usart.write_all(&buf[..]).await.unwrap(); + + // use embedded_io_async::BufRead; + // let buf = usart.fill_buf().await.unwrap(); + // let n = buf.len(); + // usart.consume(n); + } +}