From d52c7ded49e4584e678a54f2870a4d855e24ee01 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 10:14:37 +0200 Subject: [PATCH] Implement remaining logic for blocking UART. async is still todo, awaiting DMA --- embassy-rp/Cargo.toml | 2 + embassy-rp/src/dma.rs | 6 + embassy-rp/src/uart.rs | 401 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 371 insertions(+), 38 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 303617ffc..b4dab7f46 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -36,6 +36,8 @@ embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["r atomic-polyfill = "0.1.5" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +nb = "1.0.0" +cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.3" critical-section = "0.2.5" diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 235e92d7c..42c4fd13e 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,5 +1,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; +use embassy_hal_common::impl_peripheral; + use crate::pac::dma::vals; use crate::{pac, peripherals}; @@ -35,6 +37,10 @@ impl Dma { } } +pub struct NoDma; + +impl_peripheral!(NoDma); + mod sealed { use super::*; diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index b19f043f8..db135d04b 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -1,42 +1,199 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; -use gpio::Pin; +use core::marker::PhantomData; -use crate::{gpio, pac, peripherals, Peripheral}; +use embassy_hal_common::{into_ref, PeripheralRef}; + +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; +use crate::{pac, peripherals, Peripheral}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum DataBits { + DataBits5, + DataBits6, + DataBits7, + DataBits8, +} + +impl DataBits { + fn bits(&self) -> u8 { + match self { + Self::DataBits5 => 0b00, + Self::DataBits6 => 0b01, + Self::DataBits7 => 0b10, + Self::DataBits8 => 0b11, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Parity { + ParityNone, + ParityEven, + ParityOdd, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum StopBits { + #[doc = "1 stop bit"] + STOP1, + #[doc = "2 stop bits"] + STOP2, +} #[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Config { pub baudrate: u32, - pub data_bits: u8, - pub stop_bits: u8, + pub data_bits: DataBits, + pub stop_bits: StopBits, + pub parity: Parity, } impl Default for Config { fn default() -> Self { Self { baudrate: 115200, - data_bits: 8, - stop_bits: 1, + data_bits: DataBits::DataBits8, + stop_bits: StopBits::STOP1, + parity: Parity::ParityNone, } } } +/// Serial error +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Triggered when the FIFO (or shift-register) is overflowed. + Overrun, + /// Triggered when a break is received + Break, + /// Triggered when there is a parity mismatch between what's received and + /// our settings. + Parity, + /// Triggered when the received character didn't have a valid stop bit. + Framing, +} + pub struct Uart<'d, T: Instance> { - inner: PeripheralRef<'d, T>, + tx: UartTx<'d, T>, + rx: UartRx<'d, T>, +} + +pub struct UartTx<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +pub struct UartRx<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> UartTx<'d, T> { + fn new() -> Self { + Self { phantom: PhantomData } + } + + pub async fn write(&mut self, _buffer: &[u8]) -> Result<(), Error> { + todo!() + } + + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let r = T::regs(); + unsafe { + for &b in buffer { + while r.uartfr().read().txff() {} + r.uartdr().write(|w| w.set_data(b)); + } + } + Ok(()) + } + + pub fn blocking_flush(&mut self) -> Result<(), Error> { + let r = T::regs(); + unsafe { while r.uartfr().read().txff() {} } + Ok(()) + } +} + +impl<'d, T: Instance> UartRx<'d, T> { + fn new() -> Self { + Self { phantom: PhantomData } + } + + pub async fn read(&mut self, _buffer: &mut [u8]) -> Result<(), Error> { + todo!(); + } + + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let r = T::regs(); + unsafe { + for b in buffer { + *b = loop { + let dr = r.uartdr().read(); + + if dr.oe() { + return Err(Error::Overrun); + } else if dr.be() { + return Err(Error::Break); + } else if dr.pe() { + return Err(Error::Parity); + } else if dr.fe() { + return Err(Error::Framing); + } else if dr.fe() { + break dr.data(); + } + }; + } + } + Ok(()) + } } impl<'d, T: Instance> Uart<'d, T> { + /// Create a new UARTE without hardware flow control pub fn new( - inner: impl Peripheral

+ 'd, + uart: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(tx, rx); + Self::new_inner(uart, rx.map_into(), tx.map_into(), None, None, config) + } + + /// Create a new UART with hardware flow control (RTS/CTS) + pub fn new_with_rtscts( + uart: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(inner, tx, rx, cts, rts); + into_ref!(tx, rx, cts, rts); + Self::new_inner( + uart, + rx.map_into(), + tx.map_into(), + Some(cts.map_into()), + Some(rts.map_into()), + config, + ) + } + + fn new_inner( + _uart: impl Peripheral

+ 'd, + tx: PeripheralRef<'d, AnyPin>, + rx: PeripheralRef<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + ) -> Self { + into_ref!(_uart); unsafe { - let p = inner.regs(); + let r = T::regs(); let clk_base = crate::clocks::clk_peri_freq(); @@ -53,49 +210,217 @@ impl<'d, T: Instance> Uart<'d, T> { } // Load PL011's baud divisor registers - p.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); - p.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); + r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); - p.uartlcr_h().write(|w| { - w.set_wlen(config.data_bits - 5); - w.set_stp2(config.stop_bits == 2); - w.set_pen(false); - w.set_eps(false); + let (pen, eps) = match config.parity { + Parity::ParityNone => (false, false), + Parity::ParityEven => (true, true), + Parity::ParityOdd => (true, false), + }; + + r.uartlcr_h().write(|w| { + w.set_wlen(config.data_bits.bits()); + w.set_stp2(config.stop_bits == StopBits::STOP2); + w.set_pen(pen); + w.set_eps(eps); w.set_fen(true); }); - p.uartcr().write(|w| { + r.uartcr().write(|w| { w.set_uarten(true); w.set_rxe(true); w.set_txe(true); + w.set_ctsen(cts.is_some()); + w.set_rtsen(rts.is_some()); }); tx.io().ctrl().write(|w| w.set_funcsel(2)); rx.io().ctrl().write(|w| w.set_funcsel(2)); - cts.io().ctrl().write(|w| w.set_funcsel(2)); - rts.io().ctrl().write(|w| w.set_funcsel(2)); + if let Some(pin) = &cts { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + } + if let Some(pin) = &rts { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + } + } + + Self { + tx: UartTx::new(), + rx: UartRx::new(), } - Self { inner } } - pub fn send(&mut self, data: &[u8]) { - unsafe { - let p = self.inner.regs(); + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.write(buffer).await + } - for &byte in data { - if !p.uartfr().read().txff() { - p.uartdr().write(|w| w.set_data(byte)); + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.blocking_write(buffer) + } + + pub fn blocking_flush(&mut self) -> Result<(), Error> { + self.tx.blocking_flush() + } + + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + 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>, UartRx<'d, T>) { + (self.tx, self.rx) + } +} + +mod eh02 { + use super::*; + + impl<'d, T: Instance> embedded_hal_02::serial::Read for UartRx<'d, T> { + type Error = Error; + fn read(&mut self) -> Result> { + let r = T::regs(); + unsafe { + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) } } } } + + impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for UartTx<'d, T> { + 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> embedded_hal_02::serial::Read for Uart<'d, T> { + type Error = Error; + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for Uart<'d, T> { + 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() + } + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl embedded_hal_1::serial::Error for Error { + fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + match *self { + Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, + Self::Break => embedded_hal_1::serial::ErrorKind::Other, + Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + } + } + } + + impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for Uart<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UartTx<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UartRx<'d, T> { + type Error = Error; + } +} + +cfg_if::cfg_if! { + if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] { + use core::future::Future; + + impl<'d, T: Instance> embedded_hal_async::serial::Write for UartTx<'d, T> + { + 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> embedded_hal_async::serial::Read for UartRx<'d, T> + { + 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> embedded_hal_async::serial::Write for Uart<'d, T> + { + 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> embedded_hal_async::serial::Read for Uart<'d, T> + { + 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) + } + } + } } mod sealed { use super::*; pub trait Instance { - fn regs(&self) -> pac::uart::Uart; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} pub trait RxPin {} @@ -106,23 +431,23 @@ mod sealed { pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident) => { - impl sealed::Instance for peripherals::$type { - fn regs(&self) -> pac::uart::Uart { - pac::$type + ($inst:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$inst { + fn regs() -> pac::uart::Uart { + pac::$inst } } - impl Instance for peripherals::$type {} + impl Instance for peripherals::$inst {} }; } impl_instance!(UART0, UART0); impl_instance!(UART1, UART1); -pub trait TxPin: sealed::TxPin + Pin {} -pub trait RxPin: sealed::RxPin + Pin {} -pub trait CtsPin: sealed::CtsPin + Pin {} -pub trait RtsPin: sealed::RtsPin + Pin {} +pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} +pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} +pub trait CtsPin: sealed::CtsPin + crate::gpio::Pin {} +pub trait RtsPin: sealed::RtsPin + crate::gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $instance:ident, $function:ident) => {