//! # API for the UART peripheral //! //! The core of this API are the [Uart], [Rx] and [Tx] structures. //! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver //! using interrupts. //! //! The [rx_asynch] and [tx_asynch] modules provide an asynchronous non-blocking API for the UART //! peripheral. //! //! ## Examples //! //! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/uart.rs) //! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/rtic/src/bin/uart-echo-rtic.rs) //! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader) use core::{convert::Infallible, ops::Deref}; pub mod regs; use crate::{FunSel, InterruptConfig, gpio::IoPeriphPin, pins::PinMarker}; use arbitrary_int::{Number, u6, u18}; use fugit::RateExtU32; use regs::{ClkScale, Control, Data, Enable, FifoClear, InterruptClear, MmioUart}; use crate::{PeripheralSelect, enable_nvic_interrupt, enable_peripheral_clock, time::Hertz}; use embedded_hal_nb::serial::Read; pub use regs::{Bank, Stopbits, WordSize}; #[cfg(feature = "vor1x")] mod pins_vor1x; //================================================================================================== // Type-Level support //================================================================================================== pub trait TxPin: PinMarker { const BANK: Bank; const FUN_SEL: FunSel; } pub trait RxPin: PinMarker { const BANK: Bank; const FUN_SEL: FunSel; } //================================================================================================== // Regular Definitions //================================================================================================== #[derive(Debug, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("no interrupt ID was set")] pub struct NoInterruptIdWasSet; #[derive(Debug, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("transer is pending")] pub struct TransferPendingError; #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] 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(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Parity { None, Odd, Even, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { pub baudrate: Hertz, 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: Hertz) -> 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.Hz(); 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: Hertz) -> Self { Config::default().baudrate(baud) } } //================================================================================================== // IRQ Definitions //================================================================================================== #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IrqContextTimeoutOrMaxSize { rx_idx: usize, mode: IrqReceptionMode, pub max_len: usize, } impl IrqContextTimeoutOrMaxSize { pub fn new(max_len: usize) -> Self { IrqContextTimeoutOrMaxSize { rx_idx: 0, max_len, mode: IrqReceptionMode::Idle, } } } impl IrqContextTimeoutOrMaxSize { pub fn reset(&mut self) { self.rx_idx = 0; self.mode = IrqReceptionMode::Idle; } } /// This struct is used to return the default IRQ handler result to the user #[derive(Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IrqResult { pub bytes_read: usize, pub errors: Option, } /// This struct is used to return the default IRQ handler result to the user #[derive(Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IrqResultMaxSizeOrTimeout { complete: bool, timeout: bool, pub errors: Option, pub bytes_read: usize, } impl IrqResultMaxSizeOrTimeout { pub fn new() -> Self { IrqResultMaxSizeOrTimeout { complete: false, timeout: false, errors: None, bytes_read: 0, } } } impl IrqResultMaxSizeOrTimeout { #[inline] pub fn has_errors(&self) -> bool { self.errors.is_some() } #[inline] pub fn overflow_error(&self) -> bool { self.errors.is_some_and(|e| e.overflow) } #[inline] pub fn framing_error(&self) -> bool { self.errors.is_some_and(|e| e.framing) } #[inline] pub fn parity_error(&self) -> bool { self.errors.is_some_and(|e| e.parity) } #[inline] pub fn timeout(&self) -> bool { self.timeout } #[inline] pub fn complete(&self) -> bool { self.complete } } #[derive(Debug, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] enum IrqReceptionMode { Idle, Pending, } #[derive(Default, Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UartErrors { overflow: bool, framing: bool, parity: bool, other: bool, } impl UartErrors { #[inline(always)] pub fn overflow(&self) -> bool { self.overflow } #[inline(always)] pub fn framing(&self) -> bool { self.framing } #[inline(always)] pub fn parity(&self) -> bool { self.parity } #[inline(always)] pub fn other(&self) -> bool { self.other } } impl UartErrors { #[inline(always)] pub fn error(&self) -> bool { self.overflow || self.framing || self.parity || self.other } } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BufferTooShortError { found: usize, expected: usize, } //================================================================================================== // UART peripheral wrapper //================================================================================================== use va108xx::uarta as uart_base; pub trait UartPeripheralMarker: Deref { const ID: Bank; const PERIPH_SEL: PeripheralSelect; const PTR: *const uart_base::RegisterBlock; /* /// Retrieve the peripheral structure. /// /// # Safety /// /// This circumvents the safety guarantees of the HAL. unsafe fn steal() -> Self; #[inline(always)] fn ptr() -> *const uart_base::RegisterBlock { Self::PTR } /// Retrieve the type erased peripheral register block. /// /// # Safety /// /// This circumvents the safety guarantees of the HAL. #[inline(always)] unsafe fn reg_block() -> &'static uart_base::RegisterBlock { unsafe { &(*Self::ptr()) } } */ } impl UartPeripheralMarker for va108xx::Uarta { const ID: Bank = Bank::Uart0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; const PTR: *const uart_base::RegisterBlock = Self::PTR; /* #[inline(always)] unsafe fn steal() -> Self { Self::steal() } */ } impl UartPeripheralMarker for va108xx::Uartb { const ID: Bank = Bank::Uart1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; const PTR: *const uart_base::RegisterBlock = Self::PTR; /* #[inline(always)] unsafe fn steal() -> Self { Self::steal() } */ } #[derive(Debug, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("UART ID missmatch between peripheral and pins.")] pub struct UartIdMissmatchError; //================================================================================================== // UART implementation //================================================================================================== /// UART driver structure. pub struct Uart { tx: Tx, rx: Rx, } impl Uart { /// Calls [Self::new] with the interrupt configuration to some valid value. pub fn new_with_interrupt( sys_clk: Hertz, uart: UartI, pins: (Tx, Rx), config: Config, irq_cfg: InterruptConfig, ) -> Result { Self::new(sys_clk, uart, pins, config, Some(irq_cfg)) } /// Calls [Self::new] with the interrupt configuration to [None]. pub fn new_without_interrupt( sys_clk: Hertz, uart: UartI, pins: (Tx, Rx), config: Config, ) -> Result { Self::new(sys_clk, uart, pins, config, None) } /// Create a new UART peripheral with an interrupt configuration. /// /// # Arguments /// /// - `syscfg`: The system configuration register block /// - `sys_clk`: The system clock frequency /// - `uart`: The concrete UART peripheral instance. /// - `pins`: UART TX and RX pin tuple. /// - `config`: UART specific configuration parameters like baudrate. /// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan /// is to use TX or RX functionality relying on interrupts. If only the blocking API without /// any interrupt support is used, this can be [None]. pub fn new( sys_clk: Hertz, _uart: UartI, _pins: (TxPinI, RxPinI), config: Config, opt_irq_cfg: Option, ) -> Result { if UartI::ID != TxPinI::BANK || UartI::ID != RxPinI::BANK { return Err(UartIdMissmatchError); } IoPeriphPin::new(TxPinI::ID, TxPinI::FUN_SEL, None); IoPeriphPin::new(RxPinI::ID, TxPinI::FUN_SEL, None); enable_peripheral_clock(UartI::PERIPH_SEL); let mut reg_block = regs::Uart::new_mmio(UartI::ID); let baud_multiplier = match config.baud8 { false => 16, true => 8, }; // This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating // point calculations. let frac = ((sys_clk.raw() % (config.baudrate.raw() * 16)) * 64 + (config.baudrate.raw() * 8)) / (config.baudrate.raw() * 16); // Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet. let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; let integer_part = x as u32; reg_block.write_clkscale( ClkScale::builder() .with_int(u18::new(integer_part)) .with_frac(u6::new(frac as u8)) .build(), ); let (paren, pareven) = match config.parity { Parity::None => (false, false), Parity::Odd => (true, false), Parity::Even => (true, true), }; reg_block.write_ctrl( Control::builder() .with_baud8(config.baud8) .with_auto_rts(false) .with_def_rts(false) .with_auto_cts(false) .with_loopback_block(false) .with_loopback(false) .with_wordsize(config.wordsize) .with_stopbits(config.stopbits) .with_parity_manual(false) .with_parity_even(pareven) .with_parity_enable(paren) .build(), ); // Clear the FIFO reg_block.write_fifo_clr(FifoClear::builder().with_tx(true).with_rx(true).build()); reg_block.write_enable( Enable::builder() .with_tx(config.enable_tx) .with_rx(config.enable_rx) .build(), ); // TODO: VA108xx specific if let Some(irq_cfg) = opt_irq_cfg { if irq_cfg.route { enable_peripheral_clock(PeripheralSelect::Irqsel); unsafe { va108xx::Irqsel::steal() } .uart0(UartI::ID as usize) .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); } if irq_cfg.enable_in_nvic { // Safety: User has specifically configured this. unsafe { enable_nvic_interrupt(irq_cfg.id) }; } } Ok(Uart { tx: Tx::new(UartI::ID), rx: Rx::new(UartI::ID), }) } #[inline] pub fn enable_rx(&mut self) { self.rx.enable(); } #[inline] pub fn disable_rx(&mut self) { self.rx.disable(); } #[inline] pub fn enable_tx(&mut self) { self.tx.enable(); } #[inline] pub fn disable_tx(&mut self) { self.tx.disable(); } /// This also clears status conditons for the RX FIFO. #[inline] pub fn clear_rx_fifo(&mut self) { self.rx.clear_fifo(); } /// This also clears status conditons for the TX FIFO. #[inline] pub fn clear_tx_fifo(&mut self) { self.tx.clear_fifo(); } pub fn listen(&mut self, event: Event) { self.tx.regs.modify_irq_enabled(|mut value| { match event { Event::RxError => value.set_rx_status(true), Event::RxFifoHalfFull => value.set_rx(true), Event::RxTimeout => value.set_rx_timeout(true), Event::TxEmpty => value.set_tx_empty(true), Event::TxError => value.set_tx_status(true), Event::TxFifoHalfFull => value.set_tx(true), Event::TxCts => value.set_tx_cts(true), } value }); } pub fn unlisten(&mut self, event: Event) { self.tx.regs.modify_irq_enabled(|mut value| { match event { Event::RxError => value.set_rx_status(false), Event::RxFifoHalfFull => value.set_rx(false), Event::RxTimeout => value.set_rx_timeout(false), Event::TxEmpty => value.set_tx_empty(false), Event::TxError => value.set_tx_status(false), Event::TxFifoHalfFull => value.set_tx(false), Event::TxCts => value.set_tx_cts(false), } value }); } /// Poll receiver errors. pub fn poll_rx_errors(&self) -> Option { self.rx.poll_errors() } pub fn split(self) -> (Tx, Rx) { (self.tx, self.rx) } } impl embedded_io::ErrorType for Uart { type Error = Infallible; } impl embedded_hal_nb::serial::ErrorType for Uart { type Error = Infallible; } impl embedded_hal_nb::serial::Read for Uart { fn read(&mut self) -> nb::Result { self.rx.read() } } impl embedded_hal_nb::serial::Write for Uart { fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { self.tx.write(word).map_err(|e| { if let nb::Error::Other(_) = e { unreachable!() } nb::Error::WouldBlock }) } fn flush(&mut self) -> nb::Result<(), Self::Error> { self.tx.flush().map_err(|e| { if let nb::Error::Other(_) = e { unreachable!() } nb::Error::WouldBlock }) } } #[inline(always)] pub fn enable_rx(uart: &mut MmioUart<'static>) { uart.modify_enable(|mut value| { value.set_rx(true); value }); } #[inline(always)] pub fn disable_rx(uart: &mut MmioUart<'static>) { uart.modify_enable(|mut value| { value.set_rx(false); value }); } #[inline(always)] pub fn enable_rx_interrupts(uart: &mut MmioUart<'static>, timeout: bool) { uart.modify_irq_enabled(|mut value| { value.set_rx_status(true); value.set_rx(true); if timeout { value.set_rx_timeout(true); } value }); } #[inline(always)] pub fn disable_rx_interrupts(uart: &mut MmioUart<'static>) { uart.modify_irq_enabled(|mut value| { value.set_rx_status(false); value.set_rx(false); value.set_rx_timeout(false); value }); } /// Serial receiver. /// /// Can be created by using the [Uart::split] API. pub struct Rx { id: Bank, regs: regs::MmioUart<'static>, } impl Rx { /// Retrieve a TX pin without expecting an explicit UART structure /// /// # Safety /// /// Circumvents the HAL safety guarantees. #[inline(always)] pub unsafe fn steal(id: Bank) -> Self { Self::new(id) } #[inline(always)] fn new(id: Bank) -> Self { Self { id, regs: regs::Uart::new_mmio(id), } } /* /// Direct access to the peripheral structure. /// /// # Safety /// /// You must ensure that only registers related to the operation of the RX side are used. #[inline(always)] pub const unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { self.regs_priv() } #[inline(always)] const fn regs_priv(&self) -> &'static uart_base::RegisterBlock { unsafe { self.0.reg_block() } } */ pub fn poll_errors(&self) -> Option { let mut errors = UartErrors::default(); let status = self.regs.read_rx_status(); if status.overrun_error() { errors.overflow = true; } else if status.framing_error() { errors.framing = true; } else if status.parity_error() { errors.parity = true; } else { return None; }; Some(errors) } #[inline] pub fn clear_fifo(&mut self) { self.regs .write_fifo_clr(FifoClear::builder().with_tx(false).with_rx(true).build()); } #[inline] pub fn disable_interrupts(&mut self) { disable_rx_interrupts(&mut self.regs); } #[inline] pub fn enable_interrupts(&mut self, timeout: bool) { enable_rx_interrupts(&mut self.regs, timeout); } #[inline] pub fn enable(&mut self) { enable_rx(&mut self.regs); } #[inline] pub fn disable(&mut self) { disable_rx(&mut self.regs); } /// Low level function to read a word from the UART FIFO. /// /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. /// /// Please note that you might have to mask the returned value with 0xff to retrieve the actual /// value if you use the manual parity mode. See chapter 4.6.2 for more information. #[inline(always)] pub fn read_fifo(&mut self) -> nb::Result { if !self.regs.read_rx_status().data_available() { return Err(nb::Error::WouldBlock); } Ok(self.read_fifo_unchecked()) } /// Low level function to read a word from from the UART FIFO. /// /// This does not necesarily mean there is a word in the FIFO available. /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb] /// API. /// /// Please note that you might have to mask the returned value with 0xff to retrieve the actual /// value if you use the manual parity mode. See chapter 4.6.2 for more information. #[inline(always)] pub fn read_fifo_unchecked(&mut self) -> u32 { self.regs.read_data().raw_value() } pub fn into_rx_with_irq(self) -> RxWithInterrupt { RxWithInterrupt::new(self) } } impl embedded_io::ErrorType for Rx { type Error = Infallible; } impl embedded_hal_nb::serial::ErrorType for Rx { type Error = Infallible; } impl embedded_hal_nb::serial::Read for Rx { fn read(&mut self) -> nb::Result { self.read_fifo().map(|val| (val & 0xff) as u8).map_err(|e| { if let nb::Error::Other(_) = e { unreachable!() } nb::Error::WouldBlock }) } } impl embedded_io::Read for Rx { fn read(&mut self, buf: &mut [u8]) -> Result { if buf.is_empty() { return Ok(0); } let mut read = 0; loop { if self.regs.read_rx_status().data_available() { break; } } for byte in buf.iter_mut() { match >::read(self) { Ok(w) => { *byte = w; read += 1; } Err(nb::Error::WouldBlock) => break, } } Ok(read) } } #[inline(always)] pub fn enable_tx(uart: &mut MmioUart<'static>) { uart.modify_enable(|mut value| { value.set_tx(true); value }); } #[inline(always)] pub fn disable_tx(uart: &mut MmioUart<'static>) { uart.modify_enable(|mut value| { value.set_tx(false); value }); } #[inline(always)] pub fn enable_tx_interrupts(uart: &mut MmioUart<'static>) { uart.modify_irq_enabled(|mut value| { value.set_tx(true); value.set_tx_empty(true); value.set_tx_status(true); value }); } #[inline(always)] pub fn disable_tx_interrupts(uart: &mut MmioUart<'static>) { uart.modify_irq_enabled(|mut value| { value.set_tx(false); value.set_tx_empty(false); value.set_tx_status(false); value }); } /// Serial transmitter /// /// Can be created by using the [Uart::split] API. pub struct Tx { id: Bank, regs: regs::MmioUart<'static>, } impl Tx { /// Retrieve a TX pin without expecting an explicit UART structure /// /// # Safety /// /// Circumvents the HAL safety guarantees. #[inline(always)] pub unsafe fn steal(id: Bank) -> Self { Self::new(id) } #[inline(always)] fn new(id: Bank) -> Self { Self { id, regs: regs::Uart::new_mmio(id), } } /* /// Direct access to the peripheral structure. /// /// # Safety /// /// You must ensure that only registers related to the operation of the TX side are used. #[inline(always)] pub unsafe fn regs(&self) -> &'static uart_base::RegisterBlock { self.0.reg_block() } #[inline(always)] fn regs_priv(&self) -> &'static uart_base::RegisterBlock { unsafe { self.regs() } } */ #[inline] pub fn clear_fifo(&mut self) { self.regs .write_fifo_clr(FifoClear::builder().with_tx(true).with_rx(false).build()); } #[inline] pub fn enable(&mut self) { self.regs.modify_enable(|mut value| { value.set_tx(true); value }); } #[inline] pub fn disable(&mut self) { self.regs.modify_enable(|mut value| { value.set_tx(false); value }); } /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. /// /// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty. /// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow /// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal /// is 0 #[inline] pub fn enable_interrupts(&mut self) { // Safety: We own the UART structure enable_tx_interrupts(&mut self.regs); } /// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. /// /// [Self::enable_interrupts] documents the interrupts. #[inline] pub fn disable_interrupts(&mut self) { // Safety: We own the UART structure disable_tx_interrupts(&mut self.regs); } /// Low level function to write a word to the UART FIFO. /// /// Uses the [nb] API to allow usage in blocking and non-blocking contexts. /// /// Please note that you might have to mask the returned value with 0xff to retrieve the actual /// value if you use the manual parity mode. See chapter 11.4.1 for more information. #[inline(always)] pub fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> { if !self.regs.read_tx_status().ready() { return Err(nb::Error::WouldBlock); } self.write_fifo_unchecked(data); Ok(()) } /// Low level function to write a word to the UART FIFO. /// /// This does not necesarily mean that the FIFO can process another word because it might be /// full. /// Use the [Self::write_fifo] function to write a word to the FIFO reliably using the [nb] /// API. #[inline(always)] pub fn write_fifo_unchecked(&mut self, data: u32) { self.regs.write_data(Data::new_with_raw_value(data)); } pub fn into_async(self) -> TxAsync { TxAsync::new(self) } } impl embedded_io::ErrorType for Tx { type Error = Infallible; } impl embedded_hal_nb::serial::ErrorType for Tx { type Error = Infallible; } impl embedded_hal_nb::serial::Write for Tx { fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { self.write_fifo(word as u32) } fn flush(&mut self) -> nb::Result<(), Self::Error> { // SAFETY: Only TX related registers are used. if self.regs.read_tx_status().write_busy() { return Err(nb::Error::WouldBlock); } Ok(()) } } impl embedded_io::Write for Tx { fn write(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { return Ok(0); } loop { if self.regs.read_tx_status().ready() { break; } } let mut written = 0; for byte in buf.iter() { match >::write(self, *byte) { Ok(_) => written += 1, Err(nb::Error::WouldBlock) => return Ok(written), } } Ok(written) } fn flush(&mut self) -> Result<(), Self::Error> { nb::block!(>::flush(self)) } } /// Serial receiver, using interrupts to offload reading to the hardware. /// /// You can use [Rx::into_rx_with_irq] to convert a normal [Rx] structure into this structure. /// This structure provides two distinct ways to read the UART RX using interrupts. It should /// be noted that the interrupt service routine (ISR) still has to be provided by the user. However, /// this structure provides API calls which can be used inside the ISRs to simplify the reading /// of the UART. /// /// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You /// can simply use [Self::start] to prepare the peripheral and then call the /// [Self::on_interrupt] in the interrupt service routine. /// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You /// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and /// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service /// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to /// start reading the next packet. pub struct RxWithInterrupt(Rx); impl RxWithInterrupt { pub fn new(rx: Rx) -> Self { Self(rx) } /// This function should be called once at initialization time if the regular /// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver. pub fn start(&mut self) { self.0.enable(); self.enable_interrupts(true); } #[inline(always)] pub fn rx(&self) -> &Rx { &self.0 } /// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based] /// function to read packets with a maximum size or variable sized packets by using the /// receive timeout of the hardware. /// /// This function should be called once at initialization to initiate the context state /// and to [Self::start] the receiver. After that, it should be called after each /// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception /// of a packet. pub fn read_fixed_len_or_timeout_based_using_irq( &mut self, context: &mut IrqContextTimeoutOrMaxSize, ) -> Result<(), TransferPendingError> { if context.mode != IrqReceptionMode::Idle { return Err(TransferPendingError); } context.mode = IrqReceptionMode::Pending; context.rx_idx = 0; self.start(); Ok(()) } #[inline] fn enable_interrupts(&mut self, timeout: bool) { self.0.enable_interrupts(timeout); } #[inline] fn disable_interrupts(&mut self) { self.0.disable_interrupts(); } pub fn cancel_transfer(&mut self) { self.disable_interrupts(); self.0.clear_fifo(); } /// This function should be called in the user provided UART interrupt handler. /// /// It simply empties any bytes in the FIFO into the user provided buffer and returns the /// result of the operation. /// /// This function will not disable the RX interrupts, so you don't need to call any other /// API after calling this function to continue emptying the FIFO. RX errors are handled /// as partial errors and are returned as part of the [IrqResult]. pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult { let mut result = IrqResult::default(); let irq_status = self.0.regs.read_irq_status(); let irq_enabled = self.0.regs.read_irq_enabled(); let rx_enabled = irq_enabled.rx(); // Half-Full interrupt. We have a guaranteed amount of data we can read. if irq_status.rx() { let available_bytes = self.0.regs.read_rx_fifo_trigger().level().as_usize(); // If this interrupt bit is set, the trigger level is available at the very least. // Read everything as fast as possible for _ in 0..available_bytes { buf[result.bytes_read] = (self.0.read_fifo_unchecked() & 0xff) as u8; result.bytes_read += 1; } } // Timeout, empty the FIFO completely. if irq_status.rx_timeout() { // While there is data in the FIFO, write it into the reception buffer while let Ok(byte) = self.0.read_fifo() { buf[result.bytes_read] = byte as u8; result.bytes_read += 1; } } // RX transfer not complete, check for RX errors if rx_enabled { self.check_for_errors(&mut result.errors); } // Clear the interrupt status bits self.0.regs.write_irq_clr( InterruptClear::builder() .with_rx_overrun(true) .with_tx_overrun(false) .build(), ); result } /// This function should be called in the user provided UART interrupt handler. /// /// This function is used to read packets which either have a maximum size or variable sized /// packet which are bounded by sufficient delays between them, triggering a hardware timeout. /// /// If either the maximum number of packets have been read or a timeout occured, the transfer /// will be deemed completed. The state information of the transfer is tracked in the /// [IrqContextTimeoutOrMaxSize] structure. /// /// If passed buffer is equal to or larger than the specified maximum length, an /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors /// and returned inside the [IrqResultMaxSizeOrTimeout] structure. pub fn on_interrupt_max_size_or_timeout_based( &mut self, context: &mut IrqContextTimeoutOrMaxSize, buf: &mut [u8], ) -> Result { if buf.len() < context.max_len { return Err(BufferTooShortError { found: buf.len(), expected: context.max_len, }); } let mut result = IrqResultMaxSizeOrTimeout::default(); let irq_status = self.0.regs.read_irq_status(); let rx_enabled = self.0.regs.read_enable().rx(); // Half-Full interrupt. We have a guaranteed amount of data we can read. if irq_status.rx() { // Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO. // We use this trick/hack because the timeout feature of the peripheral relies on data // being in the RX FIFO. If data continues arriving, another half-full IRQ will fire. // If not, the last byte(s) is/are emptied by the timeout interrupt. let available_bytes = self.0.regs.read_rx_fifo_trigger().level().as_usize(); let bytes_to_read = core::cmp::min( available_bytes.saturating_sub(1), context.max_len - context.rx_idx, ); // If this interrupt bit is set, the trigger level is available at the very least. // Read everything as fast as possible for _ in 0..bytes_to_read { buf[context.rx_idx] = (self.0.read_fifo_unchecked() & 0xff) as u8; context.rx_idx += 1; } // On high-baudrates, data might be available immediately, and we possible have to // read continuosly? Then again, the CPU should always be faster than that. I'd rather // rely on the hardware firing another IRQ. I have not tried baudrates higher than // 115200 so far. } // Timeout, empty the FIFO completely. if irq_status.rx_timeout() { // While there is data in the FIFO, write it into the reception buffer loop { if context.rx_idx == context.max_len { break; } // While there is data in the FIFO, write it into the reception buffer match self.0.read() { Ok(byte) => { buf[context.rx_idx] = byte; context.rx_idx += 1; } Err(_) => break, } } self.irq_completion_handler_max_size_timeout(&mut result, context); return Ok(result); } // RX transfer not complete, check for RX errors if (context.rx_idx < context.max_len) && rx_enabled { self.check_for_errors(&mut result.errors); } // Clear the interrupt status bits self.0.regs.write_irq_clr( InterruptClear::builder() .with_rx_overrun(true) .with_tx_overrun(false) .build(), ); Ok(result) } fn check_for_errors(&self, errors: &mut Option) { let rx_status = self.0.regs.read_rx_status(); if rx_status.overrun_error() || rx_status.framing_error() || rx_status.parity_error() { let err = errors.get_or_insert(UartErrors::default()); if rx_status.overrun_error() { err.overflow = true; } if rx_status.framing_error() { err.framing = true; } if rx_status.parity_error() { err.parity = true; } } } fn irq_completion_handler_max_size_timeout( &mut self, res: &mut IrqResultMaxSizeOrTimeout, context: &mut IrqContextTimeoutOrMaxSize, ) { self.disable_interrupts(); self.0.disable(); res.bytes_read = context.rx_idx; res.complete = true; context.mode = IrqReceptionMode::Idle; context.rx_idx = 0; } /// # Safety /// /// This API allows creating multiple UART instances when releasing the TX structure as well. /// The user must ensure that these instances are not used to create multiple overlapping /// UART drivers. pub unsafe fn release(mut self) -> Rx { self.disable_interrupts(); self.0 } } pub mod tx_asynch; pub use tx_asynch::*; pub mod rx_asynch; pub use rx_asynch::*;