From a1867f0d742f597a25384e4a33209beeec7ec676 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Sun, 6 Jul 2025 17:35:19 -0500 Subject: mspm0: add buffered uart driver And tests for G3507. --- embassy-mspm0/Cargo.toml | 4 + embassy-mspm0/src/uart.rs | 1111 ---------------------------------- embassy-mspm0/src/uart/buffered.rs | 1060 ++++++++++++++++++++++++++++++++ embassy-mspm0/src/uart/mod.rs | 1174 ++++++++++++++++++++++++++++++++++++ 4 files changed, 2238 insertions(+), 1111 deletions(-) delete mode 100644 embassy-mspm0/src/uart.rs create mode 100644 embassy-mspm0/src/uart/buffered.rs create mode 100644 embassy-mspm0/src/uart/mod.rs (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index d2adc63d7..61afcd76d 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -36,7 +36,10 @@ embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", de embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } embedded-hal = { version = "1.0" } +embedded-hal-nb = { version = "1.0" } embedded-hal-async = { version = "1.0" } +embedded-io = "0.6.1" +embedded-io-async = "0.6.1" defmt = { version = "1.0.1", optional = true } fixed = "1.29" @@ -51,6 +54,7 @@ mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag [build-dependencies] proc-macro2 = "1.0.94" quote = "1.0.40" +cfg_aliases = "0.2.1" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f", default-features = false, features = ["metadata"] } diff --git a/embassy-mspm0/src/uart.rs b/embassy-mspm0/src/uart.rs deleted file mode 100644 index bc1d2e343..000000000 --- a/embassy-mspm0/src/uart.rs +++ /dev/null @@ -1,1111 +0,0 @@ -#![macro_use] - -use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; - -use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::PeripheralType; - -use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; -use crate::interrupt::{Interrupt, InterruptExt}; -use crate::mode::{Blocking, Mode}; -use crate::pac::uart::{vals, Uart as Regs}; -use crate::Peri; - -/// The clock source for the UART. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ClockSel { - /// Use the low frequency clock. - /// - /// The LFCLK runs at 32.768 kHz. - LfClk, - - /// Use the middle frequency clock. - /// - /// The MCLK runs at 4 MHz. - MfClk, - // BusClk, - // BusClk depends on the timer's power domain. - // This will be implemented later. -} - -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// The order of bits in byte. -pub enum BitOrder { - /// The most significant bit is first. - MsbFirst, - - /// The least significant bit is first. - LsbFirst, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Number of data bits -pub enum DataBits { - /// 5 Data Bits - DataBits5, - - /// 6 Data Bits - DataBits6, - - /// 7 Data Bits - DataBits7, - - /// 8 Data Bits - DataBits8, -} - -#[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 { - /// One stop bit - Stop1, - - /// Two stop bits - Stop2, -} - -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Config Error -pub enum ConfigError { - /// Rx or Tx not enabled - RxOrTxNotEnabled, - - /// The baud rate could not be configured with the given clocks. - InvalidBaudRate, -} - -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -/// Config -pub struct Config { - /// UART clock source. - pub clock_source: ClockSel, - - /// 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, - - /// The order of bits in a transmitted/received byte. - pub msb_order: BitOrder, - - /// If true: the `TX` is internally connected to `RX`. - pub loop_back_enable: bool, - - // TODO: Pending way to check if uart is extended - // /// If true: [manchester coding] is used. - // /// - // /// [manchester coding]: https://en.wikipedia.org/wiki/Manchester_code - // pub manchester: bool, - - // TODO: majority voting - - // TODO: fifo level select - need power domain info in metapac - - // TODO: glitch suppression - /// If true: invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_tx: bool, - - /// If true: invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_rx: bool, - - /// If true: invert RTS pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_rts: bool, - - /// If true: invert CTS pin signal values (VDD = 0/mark, Gnd = 1/idle). - pub invert_cts: bool, - - /// Set the pull configuration for the TX pin. - pub tx_pull: Pull, - - /// Set the pull configuration for the RX pin. - pub rx_pull: Pull, - - /// Set the pull configuration for the RTS pin. - pub rts_pull: Pull, - - /// Set the pull configuration for the CTS pin. - pub cts_pull: Pull, -} - -impl Default for Config { - fn default() -> Self { - Self { - clock_source: ClockSel::MfClk, - baudrate: 115200, - data_bits: DataBits::DataBits8, - stop_bits: StopBits::Stop1, - parity: Parity::ParityNone, - // hardware default - msb_order: BitOrder::LsbFirst, - loop_back_enable: false, - // manchester: false, - invert_tx: false, - invert_rx: false, - invert_rts: false, - invert_cts: false, - tx_pull: Pull::None, - rx_pull: Pull::None, - rts_pull: Pull::None, - cts_pull: Pull::None, - } - } -} - -/// 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) - } -} - -/// Serial error -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - Framing, - - Noise, - - Overrun, - - Parity, - - Break, -} - -impl core::fmt::Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let message = match self { - Self::Framing => "Framing Error", - Self::Noise => "Noise Error", - Self::Overrun => "RX Buffer Overrun", - Self::Parity => "Parity Check Error", - Self::Break => "Break Error", - }; - - write!(f, "{}", message) - } -} - -impl core::error::Error for Error {} - -/// 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. -pub struct UartRx<'d, M: Mode> { - info: &'static Info, - state: &'static State, - rx: Option>, - rts: Option>, - _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> 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 . - pub fn new_blocking( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - config: Config, - ) -> Result { - Self::new_inner(peri, new_pin!(rx, config.rx_pf()), None, config) - } - - /// Create a new rx-only UART with a request-to-send pin - pub fn new_blocking_with_rts( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - rts: Peri<'d, impl RtsPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_pf()), - new_pin!(rts, config.rts_pf()), - config, - ) - } -} - -impl<'d, M: Mode> UartRx<'d, M> { - /// Reconfigure the driver - pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - if let Some(ref rx) = self.rx { - rx.update_pf(config.rx_pf()); - } - - if let Some(ref rts) = self.rts { - rts.update_pf(config.rts_pf()); - } - - reconfigure(self.info, self.state, config) - } - - /// Perform a blocking read into `buffer` - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let r = self.info.regs; - - for b in buffer { - // Wait if nothing has arrived yet. - while r.stat().read().rxfe() {} - - // Prevent the compiler from reading from buffer too early - compiler_fence(Ordering::Acquire); - *b = read_with_error(r)?; - } - - Ok(()) - } - - /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { - set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) - } -} - -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()); - } -} - -/// 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, - tx: Option>, - cts: 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> { - reconfigure(self.info, self.state, config) - } -} - -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. - pub fn new_blocking( - peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, - config: Config, - ) -> Result { - Self::new_inner(peri, new_pin!(tx, config.tx_pf()), None, config) - } - - /// Create a new blocking tx-only UART with a clear-to-send pin - pub fn new_blocking_with_cts( - peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, - cts: Peri<'d, impl CtsPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(tx, config.tx_pf()), - new_pin!(cts, config.cts_pf()), - config, - ) - } -} - -impl<'d, M: Mode> UartTx<'d, M> { - /// Reconfigure the driver - pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - if let Some(ref tx) = self.tx { - tx.update_pf(config.tx_pf()); - } - - if let Some(ref cts) = self.cts { - cts.update_pf(config.cts_pf()); - } - - reconfigure(self.info, self.state, config) - } - - /// Perform a blocking UART write - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let r = self.info.regs; - - for &b in buffer { - // Wait if there is no space - while !r.stat().read().txfe() {} - - // Prevent the compiler from writing to buffer too early - compiler_fence(Ordering::Release); - r.txdata().write(|w| { - w.set_data(b); - }); - } - - Ok(()) - } - - /// Block until transmission complete - pub fn blocking_flush(&mut self) -> Result<(), Error> { - let r = self.info.regs; - - // Wait until TX fifo/buffer is empty - while r.stat().read().txfe() {} - Ok(()) - } - - /// Send break character - pub fn send_break(&self) { - let r = self.info.regs; - - r.lcrh().modify(|w| { - w.set_brk(true); - }); - } - - /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { - set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) - } -} - -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()); - } -} - -impl<'d> Uart<'d, Blocking> { - /// Create a new blocking bidirectional UART. - pub fn new_blocking( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_pf()), - new_pin!(tx, config.tx_pf()), - None, - None, - config, - ) - } - - /// Create a new bidirectional UART with request-to-send and clear-to-send pins - pub fn new_blocking_with_rtscts( - peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - rts: Peri<'d, impl RtsPin>, - cts: Peri<'d, impl CtsPin>, - config: Config, - ) -> Result { - Self::new_inner( - peri, - new_pin!(rx, config.rx_pf()), - new_pin!(tx, config.tx_pf()), - new_pin!(rts, config.rts_pf()), - new_pin!(cts, config.cts_pf()), - config, - ) - } -} - -impl<'d, M: Mode> Uart<'d, M> { - /// 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() - } - - /// 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) - } - - /// Split the Uart into a transmitter and receiver by mutable reference, - /// which is particularly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { - (&mut self.tx, &mut self.rx) - } - - /// Send break character - pub fn send_break(&self) { - self.tx.send_break(); - } - - /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { - set_baudrate(&self.tx.info, self.tx.state.clock.load(Ordering::Relaxed), baudrate) - } -} - -/// Peripheral instance trait. -#[allow(private_bounds)] -pub trait Instance: SealedInstance + PeripheralType { - type Interrupt: crate::interrupt::typelevel::Interrupt; -} - -/// UART `TX` pin trait -pub trait TxPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `TX`. - fn pf_num(&self) -> u8; -} - -/// UART `RX` pin trait -pub trait RxPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `RX`. - fn pf_num(&self) -> u8; -} - -/// UART `CTS` pin trait -pub trait CtsPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `CTS`. - fn pf_num(&self) -> u8; -} - -/// UART `RTS` pin trait -pub trait RtsPin: crate::gpio::Pin { - /// Get the PF number needed to use this pin as `RTS`. - fn pf_num(&self) -> u8; -} - -// ==== IMPL types ==== - -pub(crate) struct Info { - pub(crate) regs: Regs, - pub(crate) interrupt: Interrupt, -} - -pub(crate) struct State { - /// The clock rate of the UART. This might be configured. - pub(crate) clock: AtomicU32, -} - -impl<'d, M: Mode> UartRx<'d, M> { - fn new_inner( - _peri: Peri<'d, T>, - rx: Option>, - rts: Option>, - config: Config, - ) -> Result { - let mut this = Self { - info: T::info(), - state: T::state(), - rx, - rts, - _phantom: PhantomData, - }; - this.enable_and_configure(&config)?; - - Ok(this) - } - - fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { - let info = self.info; - - enable(info.regs); - configure(info, self.state, config, true, self.rts.is_some(), false, false)?; - - Ok(()) - } -} - -impl<'d, M: Mode> UartTx<'d, M> { - fn new_inner( - _peri: Peri<'d, T>, - tx: Option>, - cts: Option>, - config: Config, - ) -> Result { - let mut this = Self { - info: T::info(), - state: T::state(), - tx, - cts, - _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; - - enable(info.regs); - configure(info, state, config, false, false, true, self.cts.is_some())?; - - Ok(()) - } -} - -impl<'d, M: Mode> Uart<'d, M> { - fn new_inner( - _peri: Peri<'d, T>, - rx: Option>, - tx: Option>, - rts: Option>, - cts: Option>, - config: Config, - ) -> Result { - let info = T::info(); - let state = T::state(); - - let mut this = Self { - tx: UartTx { - info, - state, - tx, - cts, - _phantom: PhantomData, - }, - rx: UartRx { - info, - state, - rx, - rts, - _phantom: PhantomData, - }, - }; - 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; - - enable(info.regs); - configure( - info, - state, - config, - true, - self.rx.rts.is_some(), - true, - self.tx.cts.is_some(), - )?; - - info.interrupt.unpend(); - unsafe { info.interrupt.enable() }; - - Ok(()) - } -} - -impl Config { - fn tx_pf(&self) -> PfType { - PfType::output(self.tx_pull, self.invert_tx) - } - - fn rx_pf(&self) -> PfType { - PfType::input(self.rx_pull, self.invert_rx) - } - - fn rts_pf(&self) -> PfType { - PfType::output(self.rts_pull, self.invert_rts) - } - - fn cts_pf(&self) -> PfType { - PfType::input(self.rts_pull, self.invert_rts) - } -} - -fn enable(regs: Regs) { - let gprcm = regs.gprcm(0); - - gprcm.rstctl().write(|w| { - w.set_resetstkyclr(true); - w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); - }); - - gprcm.pwren().write(|w| { - w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); - }); -} - -fn configure( - info: &Info, - state: &State, - config: &Config, - enable_rx: bool, - enable_rts: bool, - enable_tx: bool, - enable_cts: bool, -) -> Result<(), ConfigError> { - let r = info.regs; - - if !enable_rx && !enable_tx { - return Err(ConfigError::RxOrTxNotEnabled); - } - - // SLAU846B says that clocks should be enabled before disabling the uart. - r.clksel().write(|w| match config.clock_source { - ClockSel::LfClk => { - w.set_lfclk_sel(true); - w.set_mfclk_sel(false); - w.set_busclk_sel(false); - } - ClockSel::MfClk => { - w.set_mfclk_sel(true); - w.set_lfclk_sel(false); - w.set_busclk_sel(false); - } - }); - - let clock = match config.clock_source { - ClockSel::LfClk => 32768, - ClockSel::MfClk => 4_000_000, - }; - - state.clock.store(clock, Ordering::Relaxed); - - info.regs.ctl0().modify(|w| { - w.set_lbe(config.loop_back_enable); - w.set_rxe(enable_rx); - w.set_txe(enable_tx); - // RXD_OUT_EN and TXD_OUT_EN? - w.set_menc(false); - w.set_mode(vals::Mode::UART); - w.set_rtsen(enable_rts); - w.set_ctsen(enable_cts); - // oversampling is set later - // TODO: config - w.set_fen(false); - // TODO: config - w.set_majvote(false); - w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); - }); - - info.regs.lcrh().modify(|w| { - let eps = if matches!(config.parity, Parity::ParityEven) { - vals::Eps::EVEN - } else { - vals::Eps::ODD - }; - - let wlen = match config.data_bits { - DataBits::DataBits5 => vals::Wlen::DATABIT5, - DataBits::DataBits6 => vals::Wlen::DATABIT6, - DataBits::DataBits7 => vals::Wlen::DATABIT7, - DataBits::DataBits8 => vals::Wlen::DATABIT8, - }; - - // Used in LIN mode only - w.set_brk(false); - w.set_pen(config.parity != Parity::ParityNone); - w.set_eps(eps); - w.set_stp2(matches!(config.stop_bits, StopBits::Stop2)); - w.set_wlen(wlen); - // appears to only be used in RS-485 mode. - w.set_sps(false); - // IDLE pattern? - w.set_sendidle(false); - // ignore extdir_setup and extdir_hold, only used in RS-485 mode. - }); - - set_baudrate_inner(info.regs, clock, config.baudrate)?; - - r.ctl0().modify(|w| { - w.set_enable(true); - }); - - Ok(()) -} - -fn reconfigure(info: &Info, state: &State, config: &Config) -> Result<(), ConfigError> { - info.interrupt.disable(); - let r = info.regs; - let ctl0 = r.ctl0().read(); - configure(info, state, config, ctl0.rxe(), ctl0.rtsen(), ctl0.txe(), ctl0.ctsen())?; - - info.interrupt.unpend(); - unsafe { info.interrupt.enable() }; - - Ok(()) -} - -/// Set the baud rate and clock settings. -/// -/// This should be done relatively late during configuration since some clock settings are invalid depending on mode. -fn set_baudrate(info: &Info, clock: u32, baudrate: u32) -> Result<(), ConfigError> { - let r = info.regs; - - info.interrupt.disable(); - - // Programming baud rate requires that the peripheral is disabled - critical_section::with(|_cs| { - r.ctl0().modify(|w| { - w.set_enable(false); - }); - }); - - // Wait for end of transmission per suggestion in SLAU 845 section 18.3.28 - while !r.stat().read().txfe() {} - - set_baudrate_inner(r, clock, baudrate)?; - - critical_section::with(|_cs| { - r.ctl0().modify(|w| { - w.set_enable(true); - }); - }); - - info.interrupt.unpend(); - unsafe { info.interrupt.enable() }; - - Ok(()) -} - -fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), ConfigError> { - // Quoting SLAU846 section 18.2.3.4: - // "When IBRD = 0, FBRD is ignored and no data gets transferred by the UART." - const MIN_IBRD: u16 = 1; - - // FBRD can be 0 - // FBRD is at most a 6-bit number. - const MAX_FBRD: u8 = 2_u8.pow(6); - - const DIVS: [(u8, vals::Clkdiv); 8] = [ - (1, vals::Clkdiv::DIV_BY_1), - (2, vals::Clkdiv::DIV_BY_2), - (3, vals::Clkdiv::DIV_BY_3), - (4, vals::Clkdiv::DIV_BY_4), - (5, vals::Clkdiv::DIV_BY_5), - (6, vals::Clkdiv::DIV_BY_6), - (7, vals::Clkdiv::DIV_BY_7), - (8, vals::Clkdiv::DIV_BY_8), - ]; - - // Quoting from SLAU 846 section 18.2.3.4: - // "Select oversampling by 3 or 8 to achieve higher speed with UARTclk/8 or UARTclk/3. In this case - // the receiver tolerance to clock deviation is reduced." - // - // "Select oversampling by 16 to increase the tolerance of the receiver to clock deviations. The - // maximum speed is limited to UARTclk/16." - // - // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock - // deviation. If no valid BRD value can be found satisifying the highest sample rate, then reduce - // sample rate until valid parameters are found. - const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)]; - - // 3x oversampling is not supported with manchester coding, DALI or IrDA. - let x3_invalid = { - let ctl0 = regs.ctl0().read(); - let irctl = regs.irctl().read(); - - ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren() - }; - let mut found = None; - - 'outer: for &(oversampling, hse_value) in &OVS { - if matches!(hse_value, vals::Hse::OVS3) && x3_invalid { - continue; - } - - // Verify that the selected oversampling does not require a clock faster than what the hardware - // is provided. - let Some(min_clock) = baudrate.checked_mul(oversampling as u32) else { - trace!( - "{}x oversampling would cause overflow for clock: {} Hz", - oversampling, - clock - ); - continue; - }; - - if min_clock > clock { - trace!("{} oversampling is too high for clock: {} Hz", oversampling, clock); - continue; - } - - for &(div, div_value) in &DIVS { - trace!( - "Trying div: {}, oversampling {} for {} baud", - div, - oversampling, - baudrate - ); - - let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else { - trace!("Calculating BRD overflowed: trying another divider"); - continue; - }; - - if ibrd < MIN_IBRD || fbrd > MAX_FBRD { - trace!("BRD was invalid: trying another divider"); - continue; - } - - found = Some((hse_value, div_value, ibrd, fbrd)); - break 'outer; - } - } - - let Some((hse, div, ibrd, fbrd)) = found else { - return Err(ConfigError::InvalidBaudRate); - }; - - regs.clkdiv().write(|w| { - w.set_ratio(div); - }); - - regs.ibrd().write(|w| { - w.set_divint(ibrd); - }); - - regs.fbrd().write(|w| { - w.set_divfrac(fbrd); - }); - - regs.ctl0().modify(|w| { - w.set_hse(hse); - }); - - Ok(()) -} - -/// Calculate the integer and fractional parts of the `BRD` value. -/// -/// Returns [`None`] if calculating this results in overflows. -/// -/// Values returned are `(ibrd, fbrd)` -fn calculate_brd(clock: u32, div: u8, baud: u32, oversampling: u8) -> Option<(u16, u8)> { - use fixed::types::U26F6; - - // Calculate BRD according to SLAU 846 section 18.2.3.4. - // - // BRD is a 22-bit value with 16 integer bits and 6 fractional bits. - // - // uart_clock = clock / div - // brd = ibrd.fbrd = uart_clock / (oversampling * baud)" - // - // It is tempting to rearrange the equation such that there is only a single division in - // order to reduce error. However this is wrong since the denominator ends up being too - // small to represent in 6 fraction bits. This means that FBRD would always be 0. - // - // Calculations are done in a U16F6 format. However the fixed crate has no such representation. - // U26F6 is used since it has the same number of fractional bits and we verify at the end that - // the integer part did not overflow. - let clock = U26F6::from_num(clock); - let div = U26F6::from_num(div); - let oversampling = U26F6::from_num(oversampling); - let baud = U26F6::from_num(baud); - - let uart_clock = clock.checked_div(div)?; - - // oversampling * baud - let denom = oversampling.checked_mul(baud)?; - // uart_clock / (oversampling * baud) - let brd = uart_clock.checked_div(denom)?; - - // Checked is used to determine overflow in the 10 most singificant bits since the - // actual representation of BRD is U16F6. - let ibrd = brd.checked_to_num::()?; - - // We need to scale FBRD's representation to an integer. - let fbrd_scale = U26F6::from_num(2_u32.checked_pow(U26F6::FRAC_NBITS)?); - - // It is suggested that 0.5 is added to ensure that any fractional parts round up to the next - // integer. If it doesn't round up then it'll get discarded which is okay. - let half = U26F6::from_num(1) / U26F6::from_num(2); - // fbrd = INT(((FRAC(BRD) * 64) + 0.5)) - let fbrd = brd - .frac() - .checked_mul(fbrd_scale)? - .checked_add(half)? - .checked_to_num::()?; - - Some((ibrd, fbrd)) -} - -fn read_with_error(r: Regs) -> Result { - let rx = r.rxdata().read(); - - if rx.frmerr() { - return Err(Error::Framing); - } else if rx.parerr() { - return Err(Error::Parity); - } else if rx.brkerr() { - return Err(Error::Break); - } else if rx.ovrerr() { - return Err(Error::Overrun); - } else if rx.nerr() { - return Err(Error::Noise); - } - - Ok(rx.data()) -} - -pub(crate) trait SealedInstance { - fn info() -> &'static Info; - fn state() -> &'static State; -} - -macro_rules! impl_uart_instance { - ($instance: ident) => { - impl crate::uart::SealedInstance for crate::peripherals::$instance { - fn info() -> &'static crate::uart::Info { - use crate::interrupt::typelevel::Interrupt; - use crate::uart::Info; - - const INFO: Info = Info { - regs: crate::pac::$instance, - interrupt: crate::interrupt::typelevel::$instance::IRQ, - }; - &INFO - } - - fn state() -> &'static crate::uart::State { - use crate::interrupt::typelevel::Interrupt; - use crate::uart::State; - - static STATE: State = State { - clock: core::sync::atomic::AtomicU32::new(0), - }; - &STATE - } - } - - impl crate::uart::Instance for crate::peripherals::$instance { - type Interrupt = crate::interrupt::typelevel::$instance; - } - }; -} - -macro_rules! impl_uart_tx_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::TxPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -macro_rules! impl_uart_rx_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::RxPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -macro_rules! impl_uart_cts_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::CtsPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -macro_rules! impl_uart_rts_pin { - ($instance: ident, $pin: ident, $pf: expr) => { - impl crate::uart::RtsPin for crate::peripherals::$pin { - fn pf_num(&self) -> u8 { - $pf - } - } - }; -} - -#[cfg(test)] -mod tests { - use super::calculate_brd; - - /// This is a smoke test based on the example in SLAU 846 section 18.2.3.4. - #[test] - fn datasheet() { - let brd = calculate_brd(40_000_000, 1, 19200, 16); - - assert!(matches!(brd, Some((130, 13)))); - } -} diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs new file mode 100644 index 000000000..cbc0b6c80 --- /dev/null +++ b/embassy-mspm0/src/uart/buffered.rs @@ -0,0 +1,1060 @@ +use core::future::{poll_fn, Future}; +use core::marker::PhantomData; +use core::slice; +use core::sync::atomic::{AtomicU8, Ordering}; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal_nb::nb; + +use crate::gpio::{AnyPin, SealedPin}; +use crate::interrupt::typelevel::Binding; +use crate::pac::uart::Uart as Regs; +use crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin}; +use crate::{interrupt, Peri}; + +/// Interrupt handler. +pub struct BufferedInterruptHandler { + _uart: PhantomData, +} + +impl interrupt::typelevel::Handler for BufferedInterruptHandler { + unsafe fn on_interrupt() { + on_interrupt(T::info().regs, T::buffered_state()) + } +} + +/// Bidirectional buffered UART which acts as a combination of [`BufferedUartTx`] and [`BufferedUartRx`]. +pub struct BufferedUart<'d> { + rx: BufferedUartRx<'d>, + tx: BufferedUartTx<'d>, +} + +impl SetConfig for BufferedUart<'_> { + 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. + pub fn new( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + rx: Peri<'d, impl RxPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + None, + None, + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART with request-to-send and clear-to-send pins + pub fn new_with_rtscts( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + rx: Peri<'d, impl RxPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, impl CtsPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + new_pin!(rts, config.rts_pf()), + new_pin!(cts, config.cts_pf()), + tx_buffer, + rx_buffer, + config, + ) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.tx.set_config(config)?; + self.rx.set_config(config) + } + + /// Set baudrate + pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { + self.rx.set_baudrate(baudrate) + } + + /// Write to UART TX buffer, blocking execution until done. + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { + self.tx.blocking_write(buffer) + } + + /// Flush UART TX buffer, blocking execution until done. + pub fn blocking_flush(&mut self) -> Result<(), Error> { + self.tx.blocking_flush() + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + self.tx.busy() + } + + /// Read from UART RX buffer, blocking execution until done. + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result { + self.rx.blocking_read(buffer) + } + + /// Send break character. + pub fn send_break(&mut self) { + self.tx.send_break() + } + + /// Split into separate RX and TX handles. + pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) { + (self.tx, self.rx) + } + + /// Split into separate RX and TX handles. + pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) { + ( + BufferedUartTx { + info: self.tx.info, + state: self.tx.state, + tx: self.tx.tx.as_mut().map(Peri::reborrow), + cts: self.tx.cts.as_mut().map(Peri::reborrow), + reborrowed: true, + }, + BufferedUartRx { + info: self.rx.info, + state: self.rx.state, + rx: self.rx.rx.as_mut().map(Peri::reborrow), + rts: self.rx.rts.as_mut().map(Peri::reborrow), + reborrowed: true, + }, + ) + } +} + +/// Rx-only buffered UART. +/// +/// Can be obtained from [`BufferedUart::split`], or can be constructed independently, +/// if you do not need the transmitting half of the driver. +pub struct BufferedUartRx<'d> { + info: &'static Info, + state: &'static BufferedState, + rx: Option>, + rts: Option>, + reborrowed: bool, +} + +impl SetConfig for BufferedUartRx<'_> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> BufferedUartRx<'d> { + /// Create a new rx-only buffered UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin. + pub fn new( + uart: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + _irq: impl Binding>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner(uart, new_pin!(rx, config.rx_pf()), None, rx_buffer, config) + } + + /// Create a new rx-only buffered UART with a request-to-send pin + pub fn new_with_rts( + uart: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + rts: Peri<'d, impl RtsPin>, + _irq: impl Binding>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(rx, config.rx_pf()), + new_pin!(rts, config.rts_pf()), + rx_buffer, + config, + ) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref rx) = self.rx { + rx.update_pf(config.rx_pf()); + } + + if let Some(ref rts) = self.rts { + rts.update_pf(config.rts_pf()); + } + + super::reconfigure(&self.info, &self.state.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { + super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) + } + + /// Read from UART RX buffer, blocking execution until done. + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result { + self.blocking_read_inner(buffer) + } +} + +impl Drop for BufferedUartRx<'_> { + fn drop(&mut self) { + if !self.reborrowed { + let state = self.state; + + // SAFETY: RX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized + // in order to meet the requirements of init. + unsafe { + state.rx_buf.deinit(); + } + + // TX is inactive if the buffer is not available. If this is true, then disable the + // interrupt handler since we are running in RX only mode. + 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()); + } + } +} + +/// Tx-only buffered UART. +/// +/// Can be obtained from [`BufferedUart::split`], or can be constructed independently, +/// if you do not need the receiving half of the driver. +pub struct BufferedUartTx<'d> { + info: &'static Info, + state: &'static BufferedState, + tx: Option>, + cts: Option>, + reborrowed: bool, +} + +impl SetConfig for BufferedUartTx<'_> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> BufferedUartTx<'d> { + /// Create a new tx-only buffered UART with no hardware flow control. + /// + /// Useful if you only want Uart Tx. It saves 1 pin. + pub fn new( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner(uart, new_pin!(tx, config.tx_pf()), None, tx_buffer, config) + } + + /// Create a new tx-only buffered UART with a clear-to-send pin + pub fn new_with_rts( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + cts: Peri<'d, impl CtsPin>, + _irq: impl Binding>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + uart, + new_pin!(tx, config.tx_pf()), + new_pin!(cts, config.cts_pf()), + tx_buffer, + config, + ) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref tx) = self.tx { + tx.update_pf(config.tx_pf()); + } + + if let Some(ref cts) = self.cts { + cts.update_pf(config.cts_pf()); + } + + super::reconfigure(self.info, &self.state.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) + } + + /// Write to UART TX buffer, blocking execution until done. + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { + self.blocking_write_inner(buffer) + } + + /// Flush UART TX buffer, blocking execution until done. + pub fn blocking_flush(&mut self) -> Result<(), Error> { + let state = self.state; + + loop { + if state.tx_buf.is_empty() { + return Ok(()); + } + } + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + super::busy(self.info.regs) + } + + /// Send break character + pub fn send_break(&mut self) { + let r = self.info.regs; + + r.lcrh().modify(|w| { + w.set_brk(true); + }); + } +} + +impl Drop for BufferedUartTx<'_> { + fn drop(&mut self) { + if !self.reborrowed { + let state = self.state; + + // SAFETY: TX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized + // in order to meet the requirements of init. + unsafe { + state.tx_buf.deinit(); + } + + // RX is inactive if the buffer is not available. If this is true, then disable the + // interrupt handler since we are running in TX only mode. + 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()); + } + } +} + +impl embedded_io_async::ErrorType for BufferedUart<'_> { + type Error = Error; +} + +impl embedded_io_async::ErrorType for BufferedUartRx<'_> { + type Error = Error; +} + +impl embedded_io_async::ErrorType for BufferedUartTx<'_> { + type Error = Error; +} + +impl embedded_io_async::Read for BufferedUart<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf).await + } +} + +impl embedded_io_async::Read for BufferedUartRx<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_inner(buf).await + } +} + +impl embedded_io_async::ReadReady for BufferedUart<'_> { + fn read_ready(&mut self) -> Result { + self.rx.read_ready() + } +} + +impl embedded_io_async::ReadReady for BufferedUartRx<'_> { + fn read_ready(&mut self) -> Result { + self.read_ready_inner() + } +} + +impl embedded_io_async::BufRead for BufferedUart<'_> { + 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 embedded_io_async::BufRead for BufferedUartRx<'_> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.fill_buf_inner().await + } + + fn consume(&mut self, amt: usize) { + self.consume_inner(amt); + } +} + +impl embedded_io_async::Write for BufferedUart<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write_inner(buf).await + } +} + +impl embedded_io_async::Write for BufferedUartTx<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_inner(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_inner().await + } +} + +impl embedded_io::Read for BufferedUart<'_> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.read(buf) + } +} + +impl embedded_io::Read for BufferedUartRx<'_> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.blocking_read_inner(buf) + } +} + +impl embedded_io::Write for BufferedUart<'_> { + fn write(&mut self, buf: &[u8]) -> Result { + self.tx.write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.flush() + } +} + +impl embedded_io::Write for BufferedUartTx<'_> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write_inner(buf) + } + + fn flush(&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 { + Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + Error::Break => embedded_hal_nb::serial::ErrorKind::Other, + } + } +} + +impl embedded_hal_nb::serial::ErrorType for BufferedUart<'_> { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BufferedUartRx<'_> { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for BufferedUartTx<'_> { + type Error = Error; +} + +impl embedded_hal_nb::serial::Read for BufferedUart<'_> { + fn read(&mut self) -> nb::Result { + self.rx.read() + } +} + +impl embedded_hal_nb::serial::Read for BufferedUartRx<'_> { + fn read(&mut self) -> nb::Result { + if self.info.regs.stat().read().rxfe() { + return Err(nb::Error::WouldBlock); + } + + super::read_with_error(self.info.regs).map_err(nb::Error::Other) + } +} + +impl embedded_hal_nb::serial::Write for BufferedUart<'_> { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.tx.write(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.flush() + } +} + +impl embedded_hal_nb::serial::Write for BufferedUartTx<'_> { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[word]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } +} + +// Impl details + +/// Buffered UART state. +pub(crate) struct BufferedState { + /// non-buffered UART state. This is inline in order to avoid [`BufferedUartRx`]/Tx + /// needing to carry around a 2nd static reference and waste another 4 bytes. + state: State, + rx_waker: AtomicWaker, + rx_buf: RingBuffer, + tx_waker: AtomicWaker, + tx_buf: RingBuffer, + rx_error: AtomicU8, +} + +// these must match bits 8..12 in RXDATA, but shifted by 8 to the right +const RXE_NOISE: u8 = 16; +const RXE_OVERRUN: u8 = 8; +const RXE_BREAK: u8 = 4; +const RXE_PARITY: u8 = 2; +const RXE_FRAMING: u8 = 1; + +impl BufferedState { + pub const fn new() -> Self { + Self { + state: State::new(), + rx_waker: AtomicWaker::new(), + rx_buf: RingBuffer::new(), + tx_waker: AtomicWaker::new(), + tx_buf: RingBuffer::new(), + rx_error: AtomicU8::new(0), + } + } +} + +impl<'d> BufferedUart<'d> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let info = T::info(); + let state = T::buffered_state(); + + let mut this = Self { + tx: BufferedUartTx { + info, + state, + tx, + cts, + reborrowed: false, + }, + rx: BufferedUartRx { + info, + state, + rx, + rts, + reborrowed: false, + }, + }; + 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; + + assert!(!tx_buffer.is_empty()); + assert!(!rx_buffer.is_empty()); + + init_buffers(info, state, Some(tx_buffer), Some(rx_buffer)); + super::enable(info.regs); + super::configure( + info, + &state.state, + config, + true, + self.rx.rts.is_some(), + true, + self.tx.cts.is_some(), + )?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } +} + +impl<'d> BufferedUartRx<'d> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + rts: Option>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::buffered_state(), + rx, + rts, + reborrowed: false, + }; + this.enable_and_configure(rx_buffer, &config)?; + + Ok(this) + } + + fn enable_and_configure(&mut self, rx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + init_buffers(info, state, None, Some(rx_buffer)); + super::enable(info.regs); + super::configure(info, &self.state.state, config, true, self.rts.is_some(), false, false)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } + + async fn read_inner(&self, buf: &mut [u8]) -> Result { + poll_fn(move |cx| { + let state = self.state; + + if let Poll::Ready(r) = self.try_read(buf) { + return Poll::Ready(r); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }) + .await + } + + fn blocking_read_inner(&self, buffer: &mut [u8]) -> Result { + loop { + match self.try_read(buffer) { + Poll::Ready(res) => return res, + Poll::Pending => continue, + } + } + } + + fn fill_buf_inner(&self) -> impl Future> { + poll_fn(move |cx| { + let mut rx_reader = unsafe { self.state.rx_buf.reader() }; + let (p, n) = rx_reader.pop_buf(); + let result = if n == 0 { + match Self::get_rx_error(self.state) { + None => { + self.state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + Some(e) => Err(e), + } + } else { + let buf = unsafe { slice::from_raw_parts(p, n) }; + Ok(buf) + }; + + Poll::Ready(result) + }) + } + + fn consume_inner(&self, amt: usize) { + let mut rx_reader = unsafe { self.state.rx_buf.reader() }; + rx_reader.pop_done(amt); + + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full or errors were detected. + self.info.regs.cpu_int(0).imask().modify(|w| { + w.set_rxint(true); + w.set_rtout(true); + }); + } + + /// we are ready to read if there is data in the buffer + fn read_ready_inner(&self) -> Result { + Ok(!self.state.rx_buf.is_empty()) + } + + fn try_read(&self, buf: &mut [u8]) -> Poll> { + let state = self.state; + + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let n = rx_reader.pop(|data| { + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + n + }); + + let result = if n == 0 { + match Self::get_rx_error(state) { + None => return Poll::Pending, + Some(e) => Err(e), + } + } else { + Ok(n) + }; + + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full or errors were detected. + self.info.regs.cpu_int(0).imask().modify(|w| { + w.set_rxint(true); + w.set_rtout(true); + }); + + Poll::Ready(result) + } + + fn get_rx_error(state: &BufferedState) -> Option { + // Cortex-M0 has does not support atomic swap, so we must do two operations. + let errs = critical_section::with(|_cs| { + let errs = state.rx_error.load(Ordering::Relaxed); + state.rx_error.store(0, Ordering::Relaxed); + + errs + }); + + if errs & RXE_NOISE != 0 { + Some(Error::Noise) + } else if errs & RXE_OVERRUN != 0 { + Some(Error::Overrun) + } else if errs & RXE_BREAK != 0 { + Some(Error::Break) + } else if errs & RXE_PARITY != 0 { + Some(Error::Parity) + } else if errs & RXE_FRAMING != 0 { + Some(Error::Framing) + } else { + None + } + } +} + +impl<'d> BufferedUartTx<'d> { + fn new_inner( + _peri: Peri<'d, T>, + tx: Option>, + cts: Option>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::buffered_state(), + tx, + cts, + reborrowed: false, + }; + + this.enable_and_configure(tx_buffer, &config)?; + + Ok(this) + } + + async fn write_inner(&self, buf: &[u8]) -> Result { + poll_fn(move |cx| { + let state = self.state; + + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let n = tx_writer.push(|data| { + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + n + }); + + if n == 0 { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. + self.info.interrupt.pend(); + Poll::Ready(Ok(n)) + }) + .await + } + + fn blocking_write_inner(&self, buffer: &[u8]) -> Result { + let state = self.state; + + loop { + let empty = state.tx_buf.is_empty(); + + // SAFETY: tx buf must be initialized if BufferedUartTx exists. + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let data = tx_writer.push_slice(); + + if !data.is_empty() { + let n = data.len().min(buffer.len()); + data[..n].copy_from_slice(&buffer[..n]); + tx_writer.push_done(n); + + if empty { + self.info.interrupt.pend(); + } + + return Ok(n); + } + } + } + + async fn flush_inner(&self) -> Result<(), Error> { + poll_fn(move |cx| { + let state = self.state; + + if !state.tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + .await + } + + fn enable_and_configure(&mut self, tx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + init_buffers(info, state, Some(tx_buffer), None); + super::enable(info.regs); + super::configure(info, &state.state, config, false, false, true, self.cts.is_some())?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } +} + +fn init_buffers<'d>( + info: &Info, + state: &BufferedState, + tx_buffer: Option<&'d mut [u8]>, + rx_buffer: Option<&'d mut [u8]>, +) { + if let Some(tx_buffer) = tx_buffer { + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + } + + if let Some(rx_buffer) = rx_buffer { + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + } + + info.regs.cpu_int(0).imask().modify(|w| { + w.set_nerr(true); + w.set_frmerr(true); + w.set_parerr(true); + w.set_brkerr(true); + w.set_ovrerr(true); + }); +} + +fn on_interrupt(r: Regs, state: &'static BufferedState) { + let int = r.cpu_int(0).mis().read(); + + // Per https://github.com/embassy-rs/embassy/pull/1458, both buffered and unbuffered handlers may be bound. + if super::dma_enabled(r) { + return; + } + + // RX + if state.rx_buf.is_available() { + // SAFETY: RX must have been initialized if RXE is set. + let mut rx_writer = unsafe { state.rx_buf.writer() }; + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + let mut error = false; + + for rx_byte in rx_buf { + let stat = r.stat().read(); + + if stat.rxfe() { + break; + } + + let data = r.rxdata().read(); + + if (data.0 >> 8) != 0 { + // Cortex-M0 does not support atomic fetch_or, must do 2 operations. + critical_section::with(|_cs| { + let mut value = state.rx_error.load(Ordering::Relaxed); + value |= (data.0 >> 8) as u8; + state.rx_error.store(value, Ordering::Relaxed); + }); + error = true; + + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + + *rx_byte = data.data(); + n_read += 1; + } + + if n_read > 0 { + rx_writer.push_done(n_read); + state.rx_waker.wake(); + } else if error { + state.rx_waker.wake(); + } + + // Disable any further RX interrupts when the buffer becomes full or + // errors have occurred. This lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if state.rx_buf.is_full() || error { + r.cpu_int(0).imask().modify(|w| { + w.set_rxint(false); + w.set_rtout(false); + }); + } + } + + if int.eot() { + r.cpu_int(0).imask().modify(|w| { + w.set_eot(false); + }); + + r.cpu_int(0).iclr().write(|w| { + w.set_eot(true); + }); + + state.tx_waker.wake(); + } + + // TX + if state.tx_buf.is_available() { + // SAFETY: TX must have been initialized if TXE is set. + let mut tx_reader = unsafe { state.tx_buf.reader() }; + let buf = tx_reader.pop_slice(); + let mut n_written = 0; + + for tx_byte in buf.iter_mut() { + let stat = r.stat().read(); + + if stat.txff() { + break; + } + + r.txdata().write(|w| { + w.set_data(*tx_byte); + }); + n_written += 1; + } + + if n_written > 0 { + // EOT will wake. + r.cpu_int(0).imask().modify(|w| { + w.set_eot(true); + }); + + tx_reader.pop_done(n_written); + } + } + + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by writing to ICLR. + let mis = r.cpu_int(0).mis().read(); + r.cpu_int(0).iclr().write(|w| { + w.set_nerr(mis.nerr()); + w.set_frmerr(mis.frmerr()); + w.set_parerr(mis.parerr()); + w.set_brkerr(mis.brkerr()); + w.set_ovrerr(mis.ovrerr()); + }); + + // Errors + if mis.nerr() { + warn!("Noise error"); + } + if mis.frmerr() { + warn!("Framing error"); + } + if mis.parerr() { + warn!("Parity error"); + } + if mis.brkerr() { + warn!("Break error"); + } + if mis.ovrerr() { + warn!("Overrun error"); + } +} diff --git a/embassy-mspm0/src/uart/mod.rs b/embassy-mspm0/src/uart/mod.rs new file mode 100644 index 000000000..6599cea06 --- /dev/null +++ b/embassy-mspm0/src/uart/mod.rs @@ -0,0 +1,1174 @@ +#![macro_use] + +mod buffered; + +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; + +pub use buffered::*; +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::PeripheralType; + +use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::mode::{Blocking, Mode}; +use crate::pac::uart::{vals, Uart as Regs}; +use crate::Peri; + +/// The clock source for the UART. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockSel { + /// Use the low frequency clock. + /// + /// The LFCLK runs at 32.768 kHz. + LfClk, + + /// Use the middle frequency clock. + /// + /// The MCLK runs at 4 MHz. + MfClk, + // BusClk, + // BusClk depends on the timer's power domain. + // This will be implemented later. +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// The order of bits in byte. +pub enum BitOrder { + /// The most significant bit is first. + MsbFirst, + + /// The least significant bit is first. + LsbFirst, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Number of data bits +pub enum DataBits { + /// 5 Data Bits + DataBits5, + + /// 6 Data Bits + DataBits6, + + /// 7 Data Bits + DataBits7, + + /// 8 Data Bits + DataBits8, +} + +#[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 { + /// One stop bit + Stop1, + + /// Two stop bits + Stop2, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Config Error +pub enum ConfigError { + /// Rx or Tx not enabled + RxOrTxNotEnabled, + + /// The baud rate could not be configured with the given clocks. + InvalidBaudRate, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Config +pub struct Config { + /// UART clock source. + pub clock_source: ClockSel, + + /// 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, + + /// The order of bits in a transmitted/received byte. + pub msb_order: BitOrder, + + /// If true: the `TX` is internally connected to `RX`. + pub loop_back_enable: bool, + + // TODO: Pending way to check if uart is extended + // /// If true: [manchester coding] is used. + // /// + // /// [manchester coding]: https://en.wikipedia.org/wiki/Manchester_code + // pub manchester: bool, + + // TODO: majority voting + /// If true: the built-in FIFO is enabled. + pub fifo_enable: bool, + + // TODO: glitch suppression + /// If true: invert TX pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_tx: bool, + + /// If true: invert RX pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_rx: bool, + + /// If true: invert RTS pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_rts: bool, + + /// If true: invert CTS pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_cts: bool, + + /// Set the pull configuration for the TX pin. + pub tx_pull: Pull, + + /// Set the pull configuration for the RX pin. + pub rx_pull: Pull, + + /// Set the pull configuration for the RTS pin. + pub rts_pull: Pull, + + /// Set the pull configuration for the CTS pin. + pub cts_pull: Pull, +} + +impl Default for Config { + fn default() -> Self { + Self { + clock_source: ClockSel::MfClk, + baudrate: 115200, + data_bits: DataBits::DataBits8, + stop_bits: StopBits::Stop1, + parity: Parity::ParityNone, + // hardware default + msb_order: BitOrder::LsbFirst, + loop_back_enable: false, + // manchester: false, + fifo_enable: false, + invert_tx: false, + invert_rx: false, + invert_rts: false, + invert_cts: false, + tx_pull: Pull::None, + rx_pull: Pull::None, + rts_pull: Pull::None, + cts_pull: Pull::None, + } + } +} + +/// 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.set_config(config) + } +} + +/// Serial error +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + Framing, + + Noise, + + Overrun, + + Parity, + + Break, +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let message = match self { + Self::Framing => "Framing Error", + Self::Noise => "Noise Error", + Self::Overrun => "RX Buffer Overrun", + Self::Parity => "Parity Check Error", + Self::Break => "Break Error", + }; + + write!(f, "{}", message) + } +} + +impl core::error::Error for Error {} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +/// 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. +pub struct UartRx<'d, M: Mode> { + info: &'static Info, + state: &'static State, + rx: Option>, + rts: Option>, + _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> 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. + pub fn new_blocking( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(rx, config.rx_pf()), None, config) + } + + /// Create a new rx-only UART with a request-to-send pin + pub fn new_blocking_with_rts( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + rts: Peri<'d, impl RtsPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_pf()), + new_pin!(rts, config.rts_pf()), + config, + ) + } +} + +impl<'d, M: Mode> UartRx<'d, M> { + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let r = self.info.regs; + + for b in buffer { + // Wait if nothing has arrived yet. + while r.stat().read().rxfe() {} + + // Prevent the compiler from reading from buffer too early + compiler_fence(Ordering::Acquire); + *b = read_with_error(r)?; + } + + Ok(()) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref rx) = self.rx { + rx.update_pf(config.rx_pf()); + } + + if let Some(ref rts) = self.rts { + rts.update_pf(config.rts_pf()); + } + + reconfigure(self.info, self.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) + } +} + +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()); + } +} + +/// 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, + tx: Option>, + cts: 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> { + reconfigure(self.info, self.state, config) + } +} + +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. + pub fn new_blocking( + peri: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(tx, config.tx_pf()), None, config) + } + + /// Create a new blocking tx-only UART with a clear-to-send pin + pub fn new_blocking_with_cts( + peri: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + cts: Peri<'d, impl CtsPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, config.tx_pf()), + new_pin!(cts, config.cts_pf()), + config, + ) + } +} + +impl<'d, M: Mode> UartTx<'d, M> { + /// Perform a blocking UART write + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let r = self.info.regs; + + for &b in buffer { + // Wait if there is no space + while !r.stat().read().txfe() {} + + // Prevent the compiler from writing to buffer too early + compiler_fence(Ordering::Release); + r.txdata().write(|w| { + w.set_data(b); + }); + } + + Ok(()) + } + + /// Block until transmission complete + pub fn blocking_flush(&mut self) -> Result<(), Error> { + let r = self.info.regs; + + // Wait until TX fifo/buffer is empty + while r.stat().read().txfe() {} + Ok(()) + } + + /// Send break character + pub fn send_break(&self) { + let r = self.info.regs; + + r.lcrh().modify(|w| { + w.set_brk(true); + }); + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + busy(self.info.regs) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if let Some(ref tx) = self.tx { + tx.update_pf(config.tx_pf()); + } + + if let Some(ref cts) = self.cts { + cts.update_pf(config.cts_pf()); + } + + reconfigure(self.info, self.state, config) + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate) + } +} + +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()); + } +} + +impl<'d> Uart<'d, Blocking> { + /// Create a new blocking bidirectional UART. + pub fn new_blocking( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + tx: Peri<'d, impl TxPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + None, + None, + config, + ) + } + + /// Create a new bidirectional UART with request-to-send and clear-to-send pins + pub fn new_blocking_with_rtscts( + peri: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + tx: Peri<'d, impl TxPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, impl CtsPin>, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_pf()), + new_pin!(tx, config.tx_pf()), + new_pin!(rts, config.rts_pf()), + new_pin!(cts, config.cts_pf()), + config, + ) + } +} + +impl<'d, M: Mode> Uart<'d, M> { + /// 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() + } + + /// Check if UART is busy. + pub fn busy(&self) -> bool { + self.tx.busy() + } + + /// 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) + } + + /// Split the Uart into a transmitter and receiver by mutable reference, + /// which is particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { + (&mut self.tx, &mut self.rx) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.tx.set_config(config)?; + self.rx.set_config(config) + } + + /// Send break character + pub fn send_break(&self) { + self.tx.send_break(); + } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(&self.tx.info, self.tx.state.clock.load(Ordering::Relaxed), baudrate) + } +} + +/// Peripheral instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType { + type Interrupt: crate::interrupt::typelevel::Interrupt; +} + +/// UART `TX` pin trait +pub trait TxPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `TX`. + fn pf_num(&self) -> u8; +} + +/// UART `RX` pin trait +pub trait RxPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `RX`. + fn pf_num(&self) -> u8; +} + +/// UART `CTS` pin trait +pub trait CtsPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `CTS`. + fn pf_num(&self) -> u8; +} + +/// UART `RTS` pin trait +pub trait RtsPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `RTS`. + fn pf_num(&self) -> u8; +} + +// ==== IMPL types ==== + +pub(crate) struct Info { + pub(crate) regs: Regs, + pub(crate) interrupt: Interrupt, +} + +pub(crate) struct State { + /// The clock rate of the UART in Hz. + clock: AtomicU32, +} + +impl State { + pub const fn new() -> Self { + Self { + clock: AtomicU32::new(0), + } + } +} + +impl<'d, M: Mode> UartRx<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + rts: Option>, + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::state(), + rx, + rts, + _phantom: PhantomData, + }; + this.enable_and_configure(&config)?; + + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + + enable(info.regs); + configure(info, self.state, config, true, self.rts.is_some(), false, false)?; + + Ok(()) + } +} + +impl<'d, M: Mode> UartTx<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + tx: Option>, + cts: Option>, + config: Config, + ) -> Result { + let mut this = Self { + info: T::info(), + state: T::state(), + tx, + cts, + _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; + + enable(info.regs); + configure(info, state, config, false, false, true, self.cts.is_some())?; + + Ok(()) + } +} + +impl<'d, M: Mode> Uart<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + config: Config, + ) -> Result { + let info = T::info(); + let state = T::state(); + + let mut this = Self { + tx: UartTx { + info, + state, + tx, + cts, + _phantom: PhantomData, + }, + rx: UartRx { + info, + state, + rx, + rts, + _phantom: PhantomData, + }, + }; + 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; + + enable(info.regs); + configure( + info, + state, + config, + true, + self.rx.rts.is_some(), + true, + self.tx.cts.is_some(), + )?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) + } +} + +impl Config { + fn tx_pf(&self) -> PfType { + PfType::output(self.tx_pull, self.invert_tx) + } + + fn rx_pf(&self) -> PfType { + PfType::input(self.rx_pull, self.invert_rx) + } + + fn rts_pf(&self) -> PfType { + PfType::output(self.rts_pull, self.invert_rts) + } + + fn cts_pf(&self) -> PfType { + PfType::input(self.rts_pull, self.invert_rts) + } +} + +fn enable(regs: Regs) { + let gprcm = regs.gprcm(0); + + gprcm.rstctl().write(|w| { + w.set_resetstkyclr(true); + w.set_resetassert(true); + w.set_key(vals::ResetKey::KEY); + }); + + gprcm.pwren().write(|w| { + w.set_enable(true); + w.set_key(vals::PwrenKey::KEY); + }); +} + +fn configure( + info: &Info, + state: &State, + config: &Config, + enable_rx: bool, + enable_rts: bool, + enable_tx: bool, + enable_cts: bool, +) -> Result<(), ConfigError> { + let r = info.regs; + + if !enable_rx && !enable_tx { + return Err(ConfigError::RxOrTxNotEnabled); + } + + // SLAU846B says that clocks should be enabled before disabling the uart. + r.clksel().write(|w| match config.clock_source { + ClockSel::LfClk => { + w.set_lfclk_sel(true); + w.set_mfclk_sel(false); + w.set_busclk_sel(false); + } + ClockSel::MfClk => { + w.set_mfclk_sel(true); + w.set_lfclk_sel(false); + w.set_busclk_sel(false); + } + }); + + let clock = match config.clock_source { + ClockSel::LfClk => 32768, + ClockSel::MfClk => 4_000_000, + }; + + state.clock.store(clock, Ordering::Relaxed); + + info.regs.ctl0().modify(|w| { + w.set_lbe(config.loop_back_enable); + // Errata UART_ERR_02, must set RXE to allow use of EOT. + w.set_rxe(enable_rx | enable_tx); + w.set_txe(enable_tx); + // RXD_OUT_EN and TXD_OUT_EN? + w.set_menc(false); + w.set_mode(vals::Mode::UART); + w.set_rtsen(enable_rts); + w.set_ctsen(enable_cts); + // oversampling is set later + w.set_fen(config.fifo_enable); + // TODO: config + w.set_majvote(false); + w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst)); + }); + + info.regs.ifls().modify(|w| { + // TODO: Need power domain info for other options. + w.set_txiflsel(vals::Iflssel::AT_LEAST_ONE); + w.set_rxiflsel(vals::Iflssel::AT_LEAST_ONE); + }); + + info.regs.lcrh().modify(|w| { + let eps = if matches!(config.parity, Parity::ParityEven) { + vals::Eps::EVEN + } else { + vals::Eps::ODD + }; + + let wlen = match config.data_bits { + DataBits::DataBits5 => vals::Wlen::DATABIT5, + DataBits::DataBits6 => vals::Wlen::DATABIT6, + DataBits::DataBits7 => vals::Wlen::DATABIT7, + DataBits::DataBits8 => vals::Wlen::DATABIT8, + }; + + // Used in LIN mode only + w.set_brk(false); + w.set_pen(config.parity != Parity::ParityNone); + w.set_eps(eps); + w.set_stp2(matches!(config.stop_bits, StopBits::Stop2)); + w.set_wlen(wlen); + // appears to only be used in RS-485 mode. + w.set_sps(false); + // IDLE pattern? + w.set_sendidle(false); + // ignore extdir_setup and extdir_hold, only used in RS-485 mode. + }); + + set_baudrate_inner(info.regs, clock, config.baudrate)?; + + r.ctl0().modify(|w| { + w.set_enable(true); + }); + + Ok(()) +} + +fn reconfigure(info: &Info, state: &State, config: &Config) -> Result<(), ConfigError> { + info.interrupt.disable(); + let r = info.regs; + let ctl0 = r.ctl0().read(); + configure(info, state, config, ctl0.rxe(), ctl0.rtsen(), ctl0.txe(), ctl0.ctsen())?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +/// Set the baud rate and clock settings. +/// +/// This should be done relatively late during configuration since some clock settings are invalid depending on mode. +fn set_baudrate(info: &Info, clock: u32, baudrate: u32) -> Result<(), ConfigError> { + let r = info.regs; + + info.interrupt.disable(); + + // Programming baud rate requires that the peripheral is disabled + critical_section::with(|_cs| { + r.ctl0().modify(|w| { + w.set_enable(false); + }); + }); + + // Wait for end of transmission per suggestion in SLAU 845 section 18.3.28 + while !r.stat().read().txfe() {} + + set_baudrate_inner(r, clock, baudrate)?; + + critical_section::with(|_cs| { + r.ctl0().modify(|w| { + w.set_enable(true); + }); + }); + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), ConfigError> { + // Quoting SLAU846 section 18.2.3.4: + // "When IBRD = 0, FBRD is ignored and no data gets transferred by the UART." + const MIN_IBRD: u16 = 1; + + // FBRD can be 0 + // FBRD is at most a 6-bit number. + const MAX_FBRD: u8 = 2_u8.pow(6); + + const DIVS: [(u8, vals::Clkdiv); 8] = [ + (1, vals::Clkdiv::DIV_BY_1), + (2, vals::Clkdiv::DIV_BY_2), + (3, vals::Clkdiv::DIV_BY_3), + (4, vals::Clkdiv::DIV_BY_4), + (5, vals::Clkdiv::DIV_BY_5), + (6, vals::Clkdiv::DIV_BY_6), + (7, vals::Clkdiv::DIV_BY_7), + (8, vals::Clkdiv::DIV_BY_8), + ]; + + // Quoting from SLAU 846 section 18.2.3.4: + // "Select oversampling by 3 or 8 to achieve higher speed with UARTclk/8 or UARTclk/3. In this case + // the receiver tolerance to clock deviation is reduced." + // + // "Select oversampling by 16 to increase the tolerance of the receiver to clock deviations. The + // maximum speed is limited to UARTclk/16." + // + // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock + // deviation. If no valid BRD value can be found satisifying the highest sample rate, then reduce + // sample rate until valid parameters are found. + const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)]; + + // 3x oversampling is not supported with manchester coding, DALI or IrDA. + let x3_invalid = { + let ctl0 = regs.ctl0().read(); + let irctl = regs.irctl().read(); + + ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren() + }; + let mut found = None; + + 'outer: for &(oversampling, hse_value) in &OVS { + if matches!(hse_value, vals::Hse::OVS3) && x3_invalid { + continue; + } + + // Verify that the selected oversampling does not require a clock faster than what the hardware + // is provided. + let Some(min_clock) = baudrate.checked_mul(oversampling as u32) else { + trace!( + "{}x oversampling would cause overflow for clock: {} Hz", + oversampling, + clock + ); + continue; + }; + + if min_clock > clock { + trace!("{} oversampling is too high for clock: {} Hz", oversampling, clock); + continue; + } + + for &(div, div_value) in &DIVS { + trace!( + "Trying div: {}, oversampling {} for {} baud", + div, + oversampling, + baudrate + ); + + let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else { + trace!("Calculating BRD overflowed: trying another divider"); + continue; + }; + + if ibrd < MIN_IBRD || fbrd > MAX_FBRD { + trace!("BRD was invalid: trying another divider"); + continue; + } + + found = Some((hse_value, div_value, ibrd, fbrd)); + break 'outer; + } + } + + let Some((hse, div, ibrd, fbrd)) = found else { + return Err(ConfigError::InvalidBaudRate); + }; + + regs.clkdiv().write(|w| { + w.set_ratio(div); + }); + + regs.ibrd().write(|w| { + w.set_divint(ibrd); + }); + + regs.fbrd().write(|w| { + w.set_divfrac(fbrd); + }); + + regs.ctl0().modify(|w| { + w.set_hse(hse); + }); + + Ok(()) +} + +/// Calculate the integer and fractional parts of the `BRD` value. +/// +/// Returns [`None`] if calculating this results in overflows. +/// +/// Values returned are `(ibrd, fbrd)` +fn calculate_brd(clock: u32, div: u8, baud: u32, oversampling: u8) -> Option<(u16, u8)> { + use fixed::types::U26F6; + + // Calculate BRD according to SLAU 846 section 18.2.3.4. + // + // BRD is a 22-bit value with 16 integer bits and 6 fractional bits. + // + // uart_clock = clock / div + // brd = ibrd.fbrd = uart_clock / (oversampling * baud)" + // + // It is tempting to rearrange the equation such that there is only a single division in + // order to reduce error. However this is wrong since the denominator ends up being too + // small to represent in 6 fraction bits. This means that FBRD would always be 0. + // + // Calculations are done in a U16F6 format. However the fixed crate has no such representation. + // U26F6 is used since it has the same number of fractional bits and we verify at the end that + // the integer part did not overflow. + let clock = U26F6::from_num(clock); + let div = U26F6::from_num(div); + let oversampling = U26F6::from_num(oversampling); + let baud = U26F6::from_num(baud); + + let uart_clock = clock.checked_div(div)?; + + // oversampling * baud + let denom = oversampling.checked_mul(baud)?; + // uart_clock / (oversampling * baud) + let brd = uart_clock.checked_div(denom)?; + + // Checked is used to determine overflow in the 10 most singificant bits since the + // actual representation of BRD is U16F6. + let ibrd = brd.checked_to_num::()?; + + // We need to scale FBRD's representation to an integer. + let fbrd_scale = U26F6::from_num(2_u32.checked_pow(U26F6::FRAC_NBITS)?); + + // It is suggested that 0.5 is added to ensure that any fractional parts round up to the next + // integer. If it doesn't round up then it'll get discarded which is okay. + let half = U26F6::from_num(1) / U26F6::from_num(2); + // fbrd = INT(((FRAC(BRD) * 64) + 0.5)) + let fbrd = brd + .frac() + .checked_mul(fbrd_scale)? + .checked_add(half)? + .checked_to_num::()?; + + Some((ibrd, fbrd)) +} + +fn read_with_error(r: Regs) -> Result { + let rx = r.rxdata().read(); + + if rx.frmerr() { + return Err(Error::Framing); + } else if rx.parerr() { + return Err(Error::Parity); + } else if rx.brkerr() { + return Err(Error::Break); + } else if rx.ovrerr() { + return Err(Error::Overrun); + } else if rx.nerr() { + return Err(Error::Noise); + } + + Ok(rx.data()) +} + +/// This function assumes CTL0.ENABLE is set (for errata cases). +fn busy(r: Regs) -> bool { + // Errata UART_ERR_08 + if cfg!(any( + mspm0g151x, mspm0g351x, mspm0l110x, mspm0l130x, mspm0l134x, mspm0c110x, + )) { + let stat = r.stat().read(); + // "Poll TXFIFO status and the CTL0.ENABLE register bit to identify BUSY status." + !stat.txfe() + } else { + r.stat().read().busy() + } +} + +// TODO: Implement when dma uart is implemented. +fn dma_enabled(_r: Regs) -> bool { + false +} + +pub(crate) trait SealedInstance { + fn info() -> &'static Info; + fn state() -> &'static State; + fn buffered_state() -> &'static BufferedState; +} + +macro_rules! impl_uart_instance { + ($instance: ident) => { + impl crate::uart::SealedInstance for crate::peripherals::$instance { + fn info() -> &'static crate::uart::Info { + use crate::interrupt::typelevel::Interrupt; + use crate::uart::Info; + + const INFO: Info = Info { + regs: crate::pac::$instance, + interrupt: crate::interrupt::typelevel::$instance::IRQ, + }; + &INFO + } + + fn state() -> &'static crate::uart::State { + use crate::uart::State; + + static STATE: State = State::new(); + &STATE + } + + fn buffered_state() -> &'static crate::uart::BufferedState { + use crate::uart::BufferedState; + + static STATE: BufferedState = BufferedState::new(); + &STATE + } + } + + impl crate::uart::Instance for crate::peripherals::$instance { + type Interrupt = crate::interrupt::typelevel::$instance; + } + }; +} + +macro_rules! impl_uart_tx_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::TxPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_uart_rx_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::RxPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_uart_cts_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::CtsPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_uart_rts_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::uart::RtsPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +#[cfg(test)] +mod tests { + use super::calculate_brd; + + /// This is a smoke test based on the example in SLAU 846 section 18.2.3.4. + #[test] + fn datasheet() { + let brd = calculate_brd(40_000_000, 1, 19200, 16); + + assert!(matches!(brd, Some((130, 13)))); + } +} -- cgit