//! # API for the UART peripheral //! //! ## Examples //! //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart.rs) //! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/uart-irq-rtic.rs) use core::{convert::Infallible, ptr}; use core::{marker::PhantomData, ops::Deref}; use libm::floorf; pub use crate::utility::IrqCfg; use crate::{ clock::{self, enable_peripheral_clock, PeripheralClocks}, gpio::pins::{ AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9, }, pac::{uarta as uart_base, IRQSEL, SYSCONFIG, UARTA, UARTB}, prelude::*, time::{Bps, Hertz}, utility::unmask_irq, }; use embedded_hal::{blocking, serial}; //================================================================================================== // Type-Level support //================================================================================================== pub trait Pins {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} impl Pins for (Pin, Pin) {} //================================================================================================== // Regular Definitions //================================================================================================== #[derive(Debug)] pub enum Error { Overrun, FramingError, ParityError, BreakCondition, TransferPending, BufferTooShort, /// Can be a combination of overrun, framing or parity error IrqError, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Event { // Receiver FIFO interrupt enable. Generates interrupt // when FIFO is at least half full. Half full is defined as FIFO // count >= RXFIFOIRQTRG RxFifoHalfFull, // Framing error, Overrun error, Parity Error and Break error RxError, // Event for timeout condition: Data in the FIFO and no receiver // FIFO activity for 4 character times RxTimeout, // Transmitter FIFO interrupt enable. Generates interrupt // when FIFO is at least half full. Half full is defined as FIFO // count >= TXFIFOIRQTRG TxFifoHalfFull, // FIFO overflow error TxError, // Generate interrupt when transmit FIFO is empty and TXBUSY is 0 TxEmpty, // Interrupt when CTSn changes value TxCts, } #[derive(Copy, Clone, PartialEq, Eq)] pub enum Parity { None, Odd, Even, } #[derive(Copy, Clone, PartialEq, Eq)] pub enum StopBits { One = 0, Two = 1, } #[derive(Copy, Clone, PartialEq, Eq)] pub enum WordSize { Five = 0, Six = 1, Seven = 2, Eight = 3, } pub struct Config { pub baudrate: Bps, pub parity: Parity, pub stopbits: StopBits, // When false, use standard 16x baud clock, other 8x baud clock pub baud8: bool, pub wordsize: WordSize, pub enable_tx: bool, pub enable_rx: bool, } impl Config { pub fn baudrate(mut self, baudrate: Bps) -> Self { self.baudrate = baudrate; self } pub fn parity_none(mut self) -> Self { self.parity = Parity::None; self } pub fn parity_even(mut self) -> Self { self.parity = Parity::Even; self } pub fn parity_odd(mut self) -> Self { self.parity = Parity::Odd; self } pub fn stopbits(mut self, stopbits: StopBits) -> Self { self.stopbits = stopbits; self } pub fn wordsize(mut self, wordsize: WordSize) -> Self { self.wordsize = wordsize; self } pub fn baud8(mut self, baud: bool) -> Self { self.baud8 = baud; self } } impl Default for Config { fn default() -> Config { let baudrate = 115_200_u32.bps(); Config { baudrate, parity: Parity::None, stopbits: StopBits::One, baud8: false, wordsize: WordSize::Eight, enable_tx: true, enable_rx: true, } } } impl From for Config { fn from(baud: Bps) -> Self { Config::default().baudrate(baud) } } //================================================================================================== // IRQ Definitions //================================================================================================== struct IrqInfo { rx_len: usize, rx_idx: usize, irq_cfg: IrqCfg, mode: IrqReceptionMode, } pub enum IrqResultMask { Complete = 0, Overflow = 1, FramingError = 2, ParityError = 3, Break = 4, Timeout = 5, Addr9 = 6, /// Should not happen Unknown = 7, } /// This struct is used to return the default IRQ handler result to the user #[derive(Debug, Default)] pub struct IrqResult { raw_res: u32, pub bytes_read: usize, } impl IrqResult { pub const fn new() -> Self { IrqResult { raw_res: 0, bytes_read: 0, } } } impl IrqResult { #[inline] pub fn raw_result(&self) -> u32 { self.raw_res } #[inline] pub(crate) fn clear_result(&mut self) { self.raw_res = 0; } #[inline] pub(crate) fn set_result(&mut self, flag: IrqResultMask) { self.raw_res |= 1 << flag as u32; } #[inline] pub fn complete(&self) -> bool { if ((self.raw_res >> IrqResultMask::Complete as u32) & 0x01) == 0x01 { return true; } false } #[inline] pub fn error(&self) -> bool { if self.overflow_error() || self.framing_error() || self.parity_error() { return true; } false } #[inline] pub fn overflow_error(&self) -> bool { if ((self.raw_res >> IrqResultMask::Overflow as u32) & 0x01) == 0x01 { return true; } false } #[inline] pub fn framing_error(&self) -> bool { if ((self.raw_res >> IrqResultMask::FramingError as u32) & 0x01) == 0x01 { return true; } false } #[inline] pub fn parity_error(&self) -> bool { if ((self.raw_res >> IrqResultMask::ParityError as u32) & 0x01) == 0x01 { return true; } false } #[inline] pub fn timeout(&self) -> bool { if ((self.raw_res >> IrqResultMask::Timeout as u32) & 0x01) == 0x01 { return true; } false } } #[derive(Debug, PartialEq)] enum IrqReceptionMode { Idle, Pending, } //================================================================================================== // UART implementation //================================================================================================== /// Type erased variant of a UART. Can be created with the [`Uart::downgrade`] function. pub struct UartBase { uart: UART, tx: Tx, rx: Rx, } /// Serial abstraction. Entry point to create a new UART pub struct Uart { uart_base: UartBase, pins: PINS, } /// UART using the IRQ capabilities of the peripheral. Can be created with the /// [`Uart::into_uart_with_irq`] function. pub struct UartWithIrq { irq_base: UartWithIrqBase, pins: PINS, } /// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the /// [`UartWithIrq::downgrade`] function. pub struct UartWithIrqBase { pub uart: UartBase, irq_info: IrqInfo, } /// Serial receiver pub struct Rx { _usart: PhantomData, } /// Serial transmitter pub struct Tx { _usart: PhantomData, } impl Rx { fn new() -> Self { Self { _usart: PhantomData, } } } impl Tx { fn new() -> Self { Self { _usart: PhantomData, } } } pub trait Instance: Deref { fn ptr() -> *const uart_base::RegisterBlock; const IDX: u8; } impl UartBase { /// This function assumes that the peripheral clock was alredy enabled /// in the SYSCONFIG register fn init(self, config: Config, sys_clk: Hertz) -> Self { let baud_multiplier = match config.baud8 { false => 16, true => 8, }; let x = sys_clk.0 as f32 / (config.baudrate.0 * baud_multiplier) as f32; let integer_part = floorf(x) as u32; let frac = floorf(64.0 * (x - integer_part as f32) + 0.5) as u32; self.uart .clkscale .write(|w| unsafe { w.bits(integer_part * 64 + frac) }); let (paren, pareven) = match config.parity { Parity::None => (false, false), Parity::Odd => (true, false), Parity::Even => (true, true), }; let stopbits = match config.stopbits { StopBits::One => false, StopBits::Two => true, }; let wordsize = config.wordsize as u8; let baud8 = config.baud8; self.uart.ctrl.write(|w| { w.paren().bit(paren); w.pareven().bit(pareven); w.stopbits().bit(stopbits); w.baud8().bit(baud8); unsafe { w.wordsize().bits(wordsize) } }); let (txenb, rxenb) = (config.enable_tx, config.enable_rx); // Clear the FIFO self.uart.fifo_clr.write(|w| { w.rxfifo().set_bit(); w.txfifo().set_bit() }); self.uart.enable.write(|w| { w.rxenable().bit(rxenb); w.txenable().bit(txenb) }); self } #[inline] pub fn enable_rx(&mut self) { self.uart.enable.modify(|_, w| w.rxenable().set_bit()); } #[inline] pub fn disable_rx(&mut self) { self.uart.enable.modify(|_, w| w.rxenable().clear_bit()); } #[inline] pub fn enable_tx(&mut self) { self.uart.enable.modify(|_, w| w.txenable().set_bit()); } #[inline] pub fn disable_tx(&mut self) { self.uart.enable.modify(|_, w| w.txenable().clear_bit()); } #[inline] pub fn clear_rx_fifo(&mut self) { self.uart.fifo_clr.write(|w| w.rxfifo().set_bit()); } #[inline] pub fn clear_tx_fifo(&mut self) { self.uart.fifo_clr.write(|w| w.txfifo().set_bit()); } #[inline] pub fn clear_rx_status(&mut self) { self.uart.fifo_clr.write(|w| w.rxsts().set_bit()); } #[inline] pub fn clear_tx_status(&mut self) { self.uart.fifo_clr.write(|w| w.txsts().set_bit()); } pub fn listen(&self, event: Event) { self.uart.irq_enb.modify(|_, w| match event { Event::RxError => w.irq_rx_status().set_bit(), Event::RxFifoHalfFull => w.irq_rx().set_bit(), Event::RxTimeout => w.irq_rx_to().set_bit(), Event::TxEmpty => w.irq_tx_empty().set_bit(), Event::TxError => w.irq_tx_status().set_bit(), Event::TxFifoHalfFull => w.irq_tx().set_bit(), Event::TxCts => w.irq_tx_cts().set_bit(), }); } pub fn unlisten(&self, event: Event) { self.uart.irq_enb.modify(|_, w| match event { Event::RxError => w.irq_rx_status().clear_bit(), Event::RxFifoHalfFull => w.irq_rx().clear_bit(), Event::RxTimeout => w.irq_rx_to().clear_bit(), Event::TxEmpty => w.irq_tx_empty().clear_bit(), Event::TxError => w.irq_tx_status().clear_bit(), Event::TxFifoHalfFull => w.irq_tx().clear_bit(), Event::TxCts => w.irq_tx_cts().clear_bit(), }); } pub fn release(self) -> UART { // Clear the FIFO self.uart.fifo_clr.write(|w| { w.rxfifo().set_bit(); w.txfifo().set_bit() }); self.uart.enable.write(|w| { w.rxenable().clear_bit(); w.txenable().clear_bit() }); self.uart } pub fn split(self) -> (Tx, Rx) { (self.tx, self.rx) } } impl Uart where UART: Instance, { /// This function assumes that the peripheral clock was alredy enabled /// in the SYSCONFIG register fn init(mut self, config: Config, sys_clk: Hertz) -> Self { self.uart_base = self.uart_base.init(config, sys_clk); self } /// If the IRQ capabilities of the peripheral are used, the UART needs to be converted /// with this function pub fn into_uart_with_irq( self, irq_cfg: IrqCfg, sys_cfg: Option<&mut SYSCONFIG>, irq_sel: Option<&mut IRQSEL>, ) -> UartWithIrq { let (uart, pins) = self.downgrade_internal(); UartWithIrq { pins, irq_base: UartWithIrqBase { uart, irq_info: IrqInfo { rx_len: 0, rx_idx: 0, irq_cfg, mode: IrqReceptionMode::Idle, }, } .init(sys_cfg, irq_sel), } } #[inline] pub fn enable_rx(&mut self) { self.uart_base.enable_rx(); } #[inline] pub fn disable_rx(&mut self) { self.uart_base.enable_rx(); } #[inline] pub fn enable_tx(&mut self) { self.uart_base.enable_tx(); } #[inline] pub fn disable_tx(&mut self) { self.uart_base.disable_tx(); } #[inline] pub fn clear_rx_fifo(&mut self) { self.uart_base.clear_rx_fifo(); } #[inline] pub fn clear_tx_fifo(&mut self) { self.uart_base.clear_tx_fifo(); } #[inline] pub fn clear_rx_status(&mut self) { self.uart_base.clear_rx_status(); } #[inline] pub fn clear_tx_status(&mut self) { self.uart_base.clear_tx_status(); } pub fn listen(&self, event: Event) { self.uart_base.listen(event); } pub fn unlisten(&self, event: Event) { self.uart_base.unlisten(event); } pub fn release(self) -> (UART, PINS) { (self.uart_base.release(), self.pins) } fn downgrade_internal(self) -> (UartBase, PINS) { let base = UartBase { uart: self.uart_base.uart, tx: self.uart_base.tx, rx: self.uart_base.rx, }; (base, self.pins) } pub fn downgrade(self) -> UartBase { UartBase { uart: self.uart_base.uart, tx: self.uart_base.tx, rx: self.uart_base.rx, } } pub fn split(self) -> (Tx, Rx) { self.uart_base.split() } } impl Instance for UARTA { fn ptr() -> *const uart_base::RegisterBlock { UARTA::ptr() as *const _ } const IDX: u8 = 0; } impl Instance for UARTB { fn ptr() -> *const uart_base::RegisterBlock { UARTB::ptr() as *const _ } const IDX: u8 = 1; } macro_rules! uart_impl { ($($UARTX:ident: ($uartx:ident, $clk_enb_enum:path),)+) => { $( impl> Uart<$UARTX, PINS> { pub fn $uartx( uart: $UARTX, pins: PINS, config: impl Into, syscfg: &mut SYSCONFIG, sys_clk: impl Into ) -> Self { enable_peripheral_clock(syscfg, $clk_enb_enum); Uart { uart_base: UartBase { uart, tx: Tx::new(), rx: Rx::new(), }, pins, } .init(config.into(), sys_clk.into()) } } )+ } } impl UartWithIrqBase { fn init(self, sys_cfg: Option<&mut SYSCONFIG>, irq_sel: Option<&mut IRQSEL>) -> Self { if let Some(sys_cfg) = sys_cfg { enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel) } if let Some(irq_sel) = irq_sel { if self.irq_info.irq_cfg.route { irq_sel.uart[UART::IDX as usize] .write(|w| unsafe { w.bits(self.irq_info.irq_cfg.irq as u32) }); } } self } /// This initializes a non-blocking read transfer using the IRQ capabilities of the UART /// peripheral. /// /// The only required information is the maximum length for variable sized reception /// or the expected length for fixed length reception. If variable sized packets are expected, /// the timeout functionality of the IRQ should be enabled as well. After calling this function, /// the [`irq_handler`](Self::irq_handler) function should be called in the user interrupt /// handler to read the received packets and reinitiate another transfer if desired. pub fn read_fixed_len_using_irq( &mut self, max_len: usize, enb_timeout_irq: bool, ) -> Result<(), Error> { if self.irq_info.mode != IrqReceptionMode::Idle { return Err(Error::TransferPending); } self.irq_info.mode = IrqReceptionMode::Pending; self.irq_info.rx_idx = 0; self.irq_info.rx_len = max_len; self.uart.enable_rx(); self.uart.enable_tx(); self.enable_rx_irq_sources(enb_timeout_irq); if self.irq_info.irq_cfg.enable { unmask_irq(self.irq_info.irq_cfg.irq); } Ok(()) } #[inline] fn enable_rx_irq_sources(&mut self, timeout: bool) { self.uart.uart.irq_enb.modify(|_, w| { if timeout { w.irq_rx_to().set_bit(); } w.irq_rx_status().set_bit(); w.irq_rx().set_bit() }); } #[inline] fn disable_rx_irq_sources(&mut self) { self.uart.uart.irq_enb.modify(|_, w| { w.irq_rx_to().clear_bit(); w.irq_rx_status().clear_bit(); w.irq_rx().clear_bit() }); } #[inline] pub fn enable_tx(&mut self) { self.uart.enable_tx() } #[inline] pub fn disable_tx(&mut self) { self.uart.disable_tx() } pub fn cancel_transfer(&mut self) { // Disable IRQ cortex_m::peripheral::NVIC::mask(self.irq_info.irq_cfg.irq); self.disable_rx_irq_sources(); self.uart.clear_tx_fifo(); self.irq_info.rx_idx = 0; self.irq_info.rx_len = 0; } /// Default IRQ handler which can be used to read the packets arriving on the UART peripheral. /// /// If passed buffer is equal to or larger than the specified maximum length, an /// [`Error::BufferTooShort`] will be returned pub fn irq_handler(&mut self, res: &mut IrqResult, buf: &mut [u8]) -> Result<(), Error> { if buf.len() < self.irq_info.rx_len { return Err(Error::BufferTooShort); } let irq_end = self.uart.uart.irq_end.read(); let enb_status = self.uart.uart.enable.read(); let rx_enabled = enb_status.rxenable().bit_is_set(); let _tx_enabled = enb_status.txenable().bit_is_set(); let read_handler = |res: &mut IrqResult, read_res: nb::Result| -> Result, Error> { match read_res { Ok(byte) => Ok(Some(byte)), Err(nb::Error::WouldBlock) => Ok(None), Err(nb::Error::Other(e)) => match e { Error::Overrun => { res.set_result(IrqResultMask::Overflow); Err(Error::IrqError) } Error::FramingError => { res.set_result(IrqResultMask::FramingError); Err(Error::IrqError) } Error::ParityError => { res.set_result(IrqResultMask::ParityError); Err(Error::IrqError) } _ => { res.set_result(IrqResultMask::Unknown); Err(Error::IrqError) } }, } }; if irq_end.irq_rx().bit_is_set() { // If this interrupt bit is set, the trigger level is available at the very least. // Read everything as fast as possible for _ in 0..core::cmp::min( self.uart.uart.rxfifoirqtrg.read().bits() as usize, self.irq_info.rx_len, ) { buf[self.irq_info.rx_idx] = (self.uart.uart.data.read().bits() & 0xff) as u8; self.irq_info.rx_idx += 1; } // While there is data in the FIFO, write it into the reception buffer loop { if self.irq_info.rx_idx == self.irq_info.rx_len { self.irq_completion_handler(res); return Ok(()); } if let Some(byte) = read_handler(res, self.uart.read())? { buf[self.irq_info.rx_idx] = byte; self.irq_info.rx_idx += 1; } else { break; } } } // RX transfer not complete, check for RX errors if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled { // Read status register again, might have changed since reading received data let rx_status = self.uart.uart.rxstatus.read(); res.clear_result(); if rx_status.rxovr().bit_is_set() { res.set_result(IrqResultMask::Overflow); } if rx_status.rxfrm().bit_is_set() { res.set_result(IrqResultMask::FramingError); } if rx_status.rxpar().bit_is_set() { res.set_result(IrqResultMask::ParityError); } if rx_status.rxbrk().bit_is_set() { res.set_result(IrqResultMask::Break); } if rx_status.rxto().bit_is_set() { // A timeout has occured but there might be some leftover data in the FIFO, // so read that data as well while let Some(byte) = read_handler(res, self.uart.read())? { buf[self.irq_info.rx_idx] = byte; self.irq_info.rx_idx += 1; } self.irq_completion_handler(res); res.set_result(IrqResultMask::Timeout); return Ok(()); } // If it is not a timeout, it's an error if res.raw_res != 0 { self.disable_rx_irq_sources(); return Err(Error::IrqError); } } // Clear the interrupt status bits self.uart .uart .irq_clr .write(|w| unsafe { w.bits(irq_end.bits()) }); Ok(()) } fn irq_completion_handler(&mut self, res: &mut IrqResult) { self.disable_rx_irq_sources(); self.uart.disable_rx(); res.bytes_read = self.irq_info.rx_idx; res.clear_result(); res.set_result(IrqResultMask::Complete); self.irq_info.mode = IrqReceptionMode::Idle; self.irq_info.rx_idx = 0; self.irq_info.rx_len = 0; } pub fn release(self) -> UART { self.uart.release() } } impl UartWithIrq { /// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc pub fn read_fixed_len_using_irq( &mut self, max_len: usize, enb_timeout_irq: bool, ) -> Result<(), Error> { self.irq_base .read_fixed_len_using_irq(max_len, enb_timeout_irq) } pub fn cancel_transfer(&mut self) { self.irq_base.cancel_transfer() } /// See [`UartWithIrqBase::irq_handler`] doc pub fn irq_handler(&mut self, res: &mut IrqResult, buf: &mut [u8]) -> Result<(), Error> { self.irq_base.irq_handler(res, buf) } pub fn release(self) -> (UART, PINS) { (self.irq_base.release(), self.pins) } pub fn downgrade(self) -> (UartWithIrqBase, PINS) { (self.irq_base, self.pins) } } uart_impl! { UARTA: (uarta, clock::PeripheralClocks::Uart0), UARTB: (uartb, clock::PeripheralClocks::Uart1), } impl Tx where UART: Instance {} impl serial::Write for UartBase { type Error = Infallible; 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 serial::Write for Uart where UART: Instance, { type Error = Infallible; fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { self.uart_base.write(word) } fn flush(&mut self) -> nb::Result<(), Self::Error> { self.uart_base.flush() } } impl blocking::serial::write::Default for Uart {} impl serial::Write for Tx { type Error = Infallible; fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { let reader = unsafe { &(*UART::ptr()) }.txstatus.read(); if reader.wrrdy().bit_is_clear() { return Err(nb::Error::WouldBlock); } else { // DPARITY bit not supported yet unsafe { // NOTE(unsafe) atomic write to data register // NOTE(write_volatile) 8-bit write that's not // possible through the svd2rust API ptr::write_volatile(&(*UART::ptr()).data as *const _ as *mut _, word); } } Ok(()) } fn flush(&mut self) -> nb::Result<(), Self::Error> { let reader = unsafe { &(*UART::ptr()) }.txstatus.read(); if reader.wrbusy().bit_is_clear() { Ok(()) } else { Err(nb::Error::WouldBlock) } } } impl serial::Read for UartBase { type Error = Error; fn read(&mut self) -> nb::Result { self.rx.read() } } impl serial::Read for Uart { type Error = Error; fn read(&mut self) -> nb::Result { self.uart_base.read() } } impl serial::Read for Rx { type Error = Error; fn read(&mut self) -> nb::Result { let uart = unsafe { &(*UART::ptr()) }; let status_reader = uart.rxstatus.read(); let err = if status_reader.rxovr().bit_is_set() { Some(Error::Overrun) } else if status_reader.rxfrm().bit_is_set() { Some(Error::FramingError) } else if status_reader.rxpar().bit_is_set() { Some(Error::ParityError) } else { None }; if let Some(err) = err { // The status code is always related to the next bit for the framing // and parity status bits. We have to read the DATA register // so that the next status reflects the next DATA word // For overrun error, we read as well to clear the peripheral uart.data.read().bits(); Err(err.into()) } else if status_reader.rdavl().bit_is_set() { let data = uart.data.read().bits(); Ok((data & 0xff) as u8) } else { Err(nb::Error::WouldBlock) } } } impl core::fmt::Write for Tx where Tx: embedded_hal::serial::Write, { fn write_str(&mut self, s: &str) -> core::fmt::Result { s.as_bytes() .iter() .try_for_each(|c| nb::block!(self.write(*c))) .map_err(|_| core::fmt::Error) } } impl core::fmt::Write for UartBase where UartBase: embedded_hal::serial::Write, { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.tx.write_str(s) } }