From e09bde93457737a043497dab41cd18a937790618 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 14 Mar 2022 22:03:35 +0100 Subject: [PATCH] Add support for splitting stm32 usart into TX and RX * Keeps existing API for usart, but wraps it in Tx and Rx sub-types * Adds split() method similar to nRF for getting indepdendent TX and RX parts * Implements e-h traits for TX and RX types * Add stm32h7 example --- embassy-stm32/src/usart/mod.rs | 259 +++++++++++++++++------- examples/stm32h7/src/bin/usart_split.rs | 67 ++++++ 2 files changed, 251 insertions(+), 75 deletions(-) create mode 100644 examples/stm32h7/src/bin/usart_split.rs diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index a62c49c7..80d92878 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -72,57 +72,26 @@ pub enum Error { } pub struct Uart<'d, T: Instance, TxDma = NoDma, RxDma = NoDma> { - inner: T, + phantom: PhantomData<&'d mut T>, + tx: UartTx<'d, T, TxDma>, + rx: UartRx<'d, T, RxDma>, +} + +pub struct UartTx<'d, T: Instance, TxDma = NoDma> { phantom: PhantomData<&'d mut T>, tx_dma: TxDma, +} + +pub struct UartRx<'d, T: Instance, RxDma = NoDma> { + phantom: PhantomData<&'d mut T>, rx_dma: RxDma, } -impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { - pub fn new( - inner: impl Unborrow + 'd, - rx: impl Unborrow> + 'd, - tx: impl Unborrow> + 'd, - tx_dma: impl Unborrow + 'd, - rx_dma: impl Unborrow + 'd, - config: Config, - ) -> Self { - unborrow!(inner, rx, tx, tx_dma, rx_dma); - - T::enable(); - let pclk_freq = T::frequency(); - - // TODO: better calculation, including error checking and OVER8 if possible. - let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate; - - let r = inner.regs(); - - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - r.cr2().write(|_w| {}); - r.cr3().write(|_w| {}); - r.brr().write_value(regs::Brr(div)); - r.cr1().write(|w| { - w.set_ue(true); - w.set_te(true); - w.set_re(true); - w.set_m0(vals::M0::BIT8); - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); - } - +impl<'d, T: Instance, TxDma> UartTx<'d, T, TxDma> { + fn new(tx_dma: TxDma) -> Self { Self { - inner, - phantom: PhantomData, tx_dma, - rx_dma, + phantom: PhantomData, } } @@ -133,16 +102,43 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let ch = &mut self.tx_dma; let request = ch.request(); unsafe { - self.inner.regs().cr3().modify(|reg| { + T::regs().cr3().modify(|reg| { reg.set_dmat(true); }); } - let r = self.inner.regs(); - let dst = tdr(r); + let dst = tdr(T::regs()); crate::dma::write(ch, request, buffer, dst).await; Ok(()) } + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + unsafe { + let r = T::regs(); + for &b in buffer { + while !sr(r).read().txe() {} + tdr(r).write_volatile(b); + } + } + Ok(()) + } + + pub fn blocking_flush(&mut self) -> Result<(), Error> { + unsafe { + let r = T::regs(); + while !sr(r).read().tc() {} + } + Ok(()) + } +} + +impl<'d, T: Instance, RxDma> UartRx<'d, T, RxDma> { + fn new(rx_dma: RxDma) -> Self { + Self { + rx_dma, + phantom: PhantomData, + } + } + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> where RxDma: crate::usart::RxDma, @@ -150,11 +146,11 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let ch = &mut self.rx_dma; let request = ch.request(); unsafe { - self.inner.regs().cr3().modify(|reg| { + T::regs().cr3().modify(|reg| { reg.set_dmar(true); }); } - let r = self.inner.regs(); + let r = T::regs(); let src = rdr(r); crate::dma::read(ch, request, src, buffer).await; Ok(()) @@ -162,7 +158,7 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { unsafe { - let r = self.inner.regs(); + let r = T::regs(); for b in buffer { loop { let sr = sr(r).read(); @@ -187,36 +183,96 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } Ok(()) } +} + +impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { + pub fn new( + _inner: impl Unborrow + 'd, + rx: impl Unborrow> + 'd, + tx: impl Unborrow> + 'd, + tx_dma: impl Unborrow + 'd, + rx_dma: impl Unborrow + 'd, + config: Config, + ) -> Self { + unborrow!(_inner, rx, tx, tx_dma, rx_dma); + + T::enable(); + let pclk_freq = T::frequency(); + + // TODO: better calculation, including error checking and OVER8 if possible. + let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate; + + let r = T::regs(); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + r.cr2().write(|_w| {}); + r.cr3().write(|_w| {}); + r.brr().write_value(regs::Brr(div)); + r.cr1().write(|w| { + w.set_ue(true); + w.set_te(true); + w.set_re(true); + w.set_m0(vals::M0::BIT8); + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + Self { + phantom: PhantomData, + tx: UartTx::new(tx_dma), + rx: UartRx::new(rx_dma), + } + } + + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> + where + TxDma: crate::usart::TxDma, + { + self.tx.write(buffer).await + } pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - unsafe { - let r = self.inner.regs(); - for &b in buffer { - while !sr(r).read().txe() {} - tdr(r).write_volatile(b); - } - } - Ok(()) + self.tx.blocking_write(buffer) } pub fn blocking_flush(&mut self) -> Result<(), Error> { - unsafe { - let r = self.inner.regs(); - while !sr(r).read().tc() {} - } - Ok(()) + self.tx.blocking_flush() + } + + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> + where + RxDma: crate::usart::RxDma, + { + self.rx.read(buffer).await + } + + 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 + /// particuarly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split(self) -> (UartTx<'d, T, TxDma>, UartRx<'d, T, RxDma>) { + (self.tx, self.rx) } } mod eh02 { use super::*; - impl<'d, T: Instance, TxDma, RxDma> embedded_hal_02::serial::Read - for Uart<'d, T, TxDma, RxDma> - { + impl<'d, T: Instance, RxDma> embedded_hal_02::serial::Read for UartRx<'d, T, RxDma> { type Error = Error; fn read(&mut self) -> Result> { - let r = self.inner.regs(); + let r = T::regs(); unsafe { let sr = sr(r).read(); if sr.pe() { @@ -240,6 +296,25 @@ mod eh02 { } } + impl<'d, T: Instance, TxDma> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, TxDma> { + 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, T: Instance, TxDma, RxDma> embedded_hal_02::serial::Read + for Uart<'d, T, TxDma, RxDma> + { + type Error = Error; + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + impl<'d, T: Instance, TxDma, RxDma> embedded_hal_02::blocking::serial::Write for Uart<'d, T, TxDma, RxDma> { @@ -273,12 +348,48 @@ mod eh1 { { type Error = Error; } + + impl<'d, T: Instance, TxDma> embedded_hal_1::serial::ErrorType for UartTx<'d, T, TxDma> { + type Error = Error; + } + + impl<'d, T: Instance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { + type Error = Error; + } } cfg_if::cfg_if! { if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { use core::future::Future; + impl<'d, T: Instance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma> + where + TxDma: crate::usart::TxDma, + { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buf) + } + + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } + } + } + + impl<'d, T: Instance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma> + where + RxDma: crate::usart::RxDma, + { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buf) + } + } + impl<'d, T: Instance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma> where TxDma: crate::usart::TxDma, @@ -329,7 +440,6 @@ mod buffered { } struct StateInner<'d, T: Instance> { - uart: Uart<'d, T, NoDma, NoDma>, phantom: PhantomData<&'d mut T>, rx_waker: WakerRegistration, @@ -351,14 +461,14 @@ mod buffered { impl<'d, T: Instance> BufferedUart<'d, T> { pub unsafe fn new( state: &'d mut State<'d, T>, - uart: Uart<'d, T, NoDma, NoDma>, + _uart: Uart<'d, T, NoDma, NoDma>, irq: impl Unborrow + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) -> BufferedUart<'d, T> { unborrow!(irq); - let r = uart.inner.regs(); + let r = T::regs(); r.cr1().modify(|w| { w.set_rxneie(true); w.set_idleie(true); @@ -366,7 +476,6 @@ mod buffered { Self { inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner { - uart, phantom: PhantomData, tx: RingBuffer::new(tx_buffer), tx_waker: WakerRegistration::new(), @@ -383,7 +492,7 @@ mod buffered { Self: 'd, { fn on_rx(&mut self) { - let r = self.uart.inner.regs(); + let r = T::regs(); unsafe { let sr = sr(r).read(); clear_interrupt_flags(r, sr); @@ -425,7 +534,7 @@ mod buffered { } fn on_tx(&mut self) { - let r = self.uart.inner.regs(); + let r = T::regs(); unsafe { if sr(r).read().txe() { let buf = self.tx.pop_buf(); @@ -575,7 +684,7 @@ unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) { pub(crate) mod sealed { pub trait Instance { - fn regs(&self) -> crate::pac::usart::Usart; + fn regs() -> crate::pac::usart::Usart; } } @@ -595,7 +704,7 @@ dma_trait!(RxDma, Instance); foreach_interrupt!( ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { impl sealed::Instance for peripherals::$inst { - fn regs(&self) -> crate::pac::usart::Usart { + fn regs() -> crate::pac::usart::Usart { crate::pac::$inst } } diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs new file mode 100644 index 00000000..b112290f --- /dev/null +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use embassy::blocking_mutex::raw::NoopRawMutex; +use embassy::channel::mpsc::{self, Channel, Sender}; +use embassy::executor::Spawner; +use embassy::util::Forever; +use embassy_stm32::dma::NoDma; +use embassy_stm32::{ + peripherals::{DMA1_CH1, UART7}, + usart::{Config, Uart, UartRx}, + Peripherals, +}; +use example_common::*; + +#[embassy::task] +async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} + +static CHANNEL: Forever> = Forever::new(); + +#[embassy::main] +async fn main(spawner: Spawner, p: Peripherals) -> ! { + info!("Hello World!"); + + let config = Config::default(); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, config); + unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); + + let (mut tx, rx) = usart.split(); + + let c = CHANNEL.put(Channel::new()); + let (s, mut r) = mpsc::split(c); + + unwrap!(spawner.spawn(reader(rx, s))); + + loop { + if let Some(buf) = r.recv().await { + info!("writing..."); + unwrap!(tx.write(&buf).await); + } + } +} + +#[embassy::task] +async fn reader( + mut rx: UartRx<'static, UART7, DMA1_CH1>, + s: Sender<'static, NoopRawMutex, [u8; 8], 1>, +) { + let mut buf = [0; 8]; + loop { + info!("reading..."); + unwrap!(rx.read(&mut buf).await); + unwrap!(s.send(buf).await); + } +}