From 0e395d37479e0a1fa5f8a94d1c734dc9cd03b89f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 13 Jun 2024 16:45:56 +0200 Subject: [PATCH] completed UART HAL, added first example --- va416xx-hal/Cargo.toml | 6 + va416xx-hal/examples/uart.rs | 48 ++ va416xx-hal/src/lib.rs | 1 + va416xx-hal/src/time.rs | 160 +----- va416xx-hal/src/uart.rs | 967 +++++++++++++++++++++++++++++++++++ 5 files changed, 1037 insertions(+), 145 deletions(-) create mode 100644 va416xx-hal/examples/uart.rs create mode 100644 va416xx-hal/src/uart.rs diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index ae093dd..e192432 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -15,8 +15,13 @@ cortex-m = "0.7" cortex-m-rt = "0.7" nb = "1" paste = "1" +embedded-hal-nb = "1" embedded-hal = "1" +embedded-io = "0.6" typenum = "1.12.0" +defmt = { version = "0.3", optional = true } +fugit = "0.3" +delegate = "0.12" [dependencies.va416xx] path = "../va416xx" @@ -24,6 +29,7 @@ version = "0.1.0" [features] rt = ["va416xx/rt"] +defmt = ["dep:defmt"] [dev-dependencies] panic-rtt-target = { version = "0.1.3" } diff --git a/va416xx-hal/examples/uart.rs b/va416xx-hal/examples/uart.rs new file mode 100644 index 0000000..672e3b4 --- /dev/null +++ b/va416xx-hal/examples/uart.rs @@ -0,0 +1,48 @@ +//! UART example application. Sends a test string over a UART and then enters +//! echo mode +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use embedded_io::{Read, Write}; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +use va416xx_hal::time::{Hertz, MegaHertz}; +use va416xx_hal::{gpio::PinsG, pac, uart}; + +#[entry] +fn main() -> ! { + rtt_init_print!(); + rprintln!("-- VA416xx UART example application--"); + + // SAFETY: Peripherals are only stolen once here. + let mut dp = unsafe { pac::Peripherals::steal() }; + + let gpiob = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg); + let tx = gpiob.pg0.into_funsel_1(); + let rx = gpiob.pg1.into_funsel_1(); + + let uartb = uart::Uart::uart0( + dp.uart0, + (tx, rx), + Hertz::from_raw(115200), + &mut dp.sysconfig, + Hertz::from_raw(MegaHertz::from_raw(20).to_Hz()), + ); + let (mut tx, mut rx) = uartb.split(); + let mut recv_buf: [u8; 32] = [0; 32]; + writeln!(tx, "Hello World").unwrap(); + loop { + // Echo what is received on the serial link. + match rx.read(&mut recv_buf) { + Ok(recvd) => { + if let Err(e) = tx.write(&recv_buf[0..recvd]) { + rprintln!("UART TX error: {:?}", e); + } + } + Err(e) => { + rprintln!("UART RX error {:?}", e); + } + } + } +} diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index 6aad93a..0f54b47 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -8,6 +8,7 @@ pub mod clock; pub mod gpio; pub mod time; pub mod typelevel; +pub mod uart; #[derive(Debug, Eq, Copy, Clone, PartialEq)] pub enum FunSel { diff --git a/va416xx-hal/src/time.rs b/va416xx-hal/src/time.rs index 6b0200e..9808028 100644 --- a/va416xx-hal/src/time.rs +++ b/va416xx-hal/src/time.rs @@ -1,156 +1,26 @@ //! Time units -//! -//! See [`Hertz`], [`KiloHertz`] and [`MegaHertz`] for creating increasingly higher frequencies. -//! -//! The [`U32Ext`] trait adds various methods like `.hz()`, `.mhz()`, etc to the `u32` primitive type, -//! allowing it to be converted into frequencies. -/// Bits per second -#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] -pub struct Bps(pub u32); +// Frequency based /// Hertz -/// -/// Create a frequency specified in [Hertz](https://en.wikipedia.org/wiki/Hertz). -/// -/// See also [`KiloHertz`] and [`MegaHertz`] for semantically correct ways of creating higher -/// frequencies. -/// -/// # Examples -/// -/// ## Create an 60 Hz frequency -/// -/// ```rust -/// use stm32f1xx_hal::time::Hertz; -/// -/// let freq = 60.hz(); -/// ``` -#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] -pub struct Hertz(pub u32); +pub type Hertz = fugit::HertzU32; -/// Kilohertz -/// -/// Create a frequency specified in kilohertz. -/// -/// See also [`Hertz`] and [`MegaHertz`] for semantically correct ways of creating lower or higher -/// frequencies. -/// -/// # Examples -/// -/// ## Create a 100 Khz frequency -/// -/// This example creates a 100 KHz frequency. This could be used to set an I2C data rate or PWM -/// frequency, etc. -/// -/// ```rust -/// use stm32f1xx_hal::time::Hertz; -/// -/// let freq = 100.khz(); -/// ``` -#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] -pub struct KiloHertz(pub u32); +/// KiloHertz +pub type KiloHertz = fugit::KilohertzU32; -/// Megahertz -/// -/// Create a frequency specified in megahertz. -/// -/// See also [`Hertz`] and [`KiloHertz`] for semantically correct ways of creating lower -/// frequencies. -/// -/// # Examples -/// -/// ## Create a an 8 MHz frequency -/// -/// This example creates an 8 MHz frequency that could be used to configure an SPI peripheral, etc. -/// -/// ```rust -/// use stm32f1xx_hal::time::Hertz; -/// -/// let freq = 8.mhz(); -/// ``` -#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] -pub struct MegaHertz(pub u32); +/// MegaHertz +pub type MegaHertz = fugit::MegahertzU32; -/// Time unit -#[derive(PartialEq, PartialOrd, Clone, Copy)] -pub struct MilliSeconds(pub u32); +// Period based -#[derive(PartialEq, PartialOrd, Clone, Copy)] -pub struct MicroSeconds(pub u32); +/// Seconds +pub type Seconds = fugit::SecsDurationU32; -/// Extension trait that adds convenience methods to the `u32` type -pub trait U32Ext { - /// Wrap in `Bps` - fn bps(self) -> Bps; +/// Milliseconds +pub type Milliseconds = fugit::MillisDurationU32; - /// Wrap in `Hertz` - fn hz(self) -> Hertz; +/// Microseconds +pub type Microseconds = fugit::MicrosDurationU32; - /// Wrap in `KiloHertz` - fn khz(self) -> KiloHertz; - - /// Wrap in `MegaHertz` - fn mhz(self) -> MegaHertz; - - /// Wrap in `MilliSeconds` - fn ms(self) -> MilliSeconds; - - /// Wrap in `MicroSeconds` - fn us(self) -> MicroSeconds; -} - -impl U32Ext for u32 { - fn bps(self) -> Bps { - Bps(self) - } - - fn hz(self) -> Hertz { - Hertz(self) - } - - fn khz(self) -> KiloHertz { - KiloHertz(self) - } - - fn mhz(self) -> MegaHertz { - MegaHertz(self) - } - - fn ms(self) -> MilliSeconds { - MilliSeconds(self) - } - - fn us(self) -> MicroSeconds { - MicroSeconds(self) - } -} - -impl From for Hertz { - fn from(val: KiloHertz) -> Self { - Self(val.0 * 1_000) - } -} - -impl From for Hertz { - fn from(val: MegaHertz) -> Self { - Self(val.0 * 1_000_000) - } -} - -impl From for KiloHertz { - fn from(val: MegaHertz) -> Self { - Self(val.0 * 1_000) - } -} - -impl From for Hertz { - fn from(val: MilliSeconds) -> Self { - Self(1_000 / val.0) - } -} - -impl From for Hertz { - fn from(val: MicroSeconds) -> Self { - Self(1_000_000 / val.0) - } -} +/// Nanoseconds +pub type Nanoseconds = fugit::NanosDurationU32; diff --git a/va416xx-hal/src/uart.rs b/va416xx-hal/src/uart.rs new file mode 100644 index 0000000..0beb7b0 --- /dev/null +++ b/va416xx-hal/src/uart.rs @@ -0,0 +1,967 @@ +use core::marker::PhantomData; +use core::ops::Deref; + +use embedded_hal_nb::serial::Read; +use fugit::RateExtU32; + +use crate::clock::{self}; +use crate::gpio::{AltFunc1, Pin, PD11, PD12, PE2, PE3, PF11, PF12, PF8, PF9, PG0, PG1}; +use crate::time::Hertz; +use crate::{ + gpio::{AltFunc2, AltFunc3, PA2, PA3, PB14, PB15, PC14, PC15, PC4, PC5}, + pac::{uart0 as uart_base, Uart0, Uart1, Uart2}, +}; + +//================================================================================================== +// Type-Level support +//================================================================================================== + +pub trait TxRxPins {} + +impl TxRxPins for (Pin, Pin) {} +impl TxRxPins for (Pin, Pin) {} +impl TxRxPins for (Pin, Pin) {} +impl TxRxPins for (Pin, Pin) {} + +impl TxRxPins for (Pin, Pin) {} +impl TxRxPins for (Pin, Pin) {} +impl TxRxPins for (Pin, Pin) {} + +impl TxRxPins for (Pin, Pin) {} +impl TxRxPins for (Pin, Pin) {} + +//================================================================================================== +// Regular Definitions +//================================================================================================== + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + Overrun, + FramingError, + ParityError, + BreakCondition, + TransferPending, + BufferTooShort, +} + +#[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 enum StopBits { + One = 0, + Two = 1, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WordSize { + Five = 0, + Six = 1, + Seven = 2, + Eight = 3, +} + +#[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 { + Config { + baudrate: 115200_u32.Hz(), + parity: Parity::None, + stopbits: StopBits::One, + baud8: false, + wordsize: WordSize::Eight, + enable_tx: true, + enable_rx: true, + } + } +} + +impl From for Config { + fn from(value: Hertz) -> Self { + Config::default().baudrate(value) + } +} + +//================================================================================================== +// IRQ Definitions +//================================================================================================== + +struct IrqInfo { + rx_len: usize, + rx_idx: usize, + 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 { + inner: UartBase, + pins: Pins, +} + +/// UART using the IRQ capabilities of the peripheral. Can be created with the +/// [`Uart::into_uart_with_irq`] function. Currently, only the RX side for IRQ based reception +/// is implemented. +pub struct UartWithIrq { + base: UartWithIrqBase, + pins: Pins, +} + +/// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the +/// [`UartWithIrq::downgrade`] function. Currently, only the RX side for IRQ based reception +/// is implemented. +pub struct UartWithIrqBase { + pub inner: 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 { + const IDX: u8; + + fn ptr() -> *const uart_base::RegisterBlock; +} + +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, + }; + // Calculations here are derived from chapter 10.4.4 (p.74) of the datasheet. + let x = sys_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32; + let integer_part = x as u32; + let frac = (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()); + } + + 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 + UartInstance: Instance, +{ + /// This function assumes that the peripheral clock was already enabled + /// in the SYSCONFIG register + fn init(mut self, config: Config, sys_clk: Hertz) -> Self { + self.inner = self.inner.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) -> UartWithIrq { + let (inner, pins) = self.downgrade_internal(); + UartWithIrq { + pins, + base: UartWithIrqBase { + inner, + irq_info: IrqInfo { + rx_len: 0, + rx_idx: 0, + mode: IrqReceptionMode::Idle, + }, + }, + } + } + + delegate::delegate! { + to self.inner { + #[inline] + pub fn enable_rx(&mut self); + #[inline] + pub fn disable_rx(&mut self); + + #[inline] + pub fn enable_tx(&mut self); + #[inline] + pub fn disable_tx(&mut self); + + #[inline] + pub fn clear_rx_fifo(&mut self); + #[inline] + pub fn clear_tx_fifo(&mut self); + + #[inline] + pub fn listen(&self, event: Event); + #[inline] + pub fn unlisten(&self, event: Event); + #[inline] + pub fn split(self) -> (Tx, Rx); + } + } + + fn downgrade_internal(self) -> (UartBase, Pins) { + let base = UartBase { + uart: self.inner.uart, + tx: self.inner.tx, + rx: self.inner.rx, + }; + (base, self.pins) + } + + pub fn release(self) -> (UartInstance, Pins) { + (self.inner.release(), self.pins) + } +} + +#[derive(Default, Debug)] +pub struct IrqUartError { + overflow: bool, + framing: bool, + parity: bool, +} + +impl IrqUartError { + pub fn error(&self) -> bool { + self.overflow || self.framing || self.parity + } +} + +#[derive(Debug)] +pub enum IrqError { + BufferTooShort { found: usize, expected: usize }, + Uart(IrqUartError), +} + +impl UartWithIrqBase { + /// 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.inner.enable_rx(); + self.inner.enable_tx(); + self.enable_rx_irq_sources(enb_timeout_irq); + Ok(()) + } + + #[inline] + fn enable_rx_irq_sources(&mut self, timeout: bool) { + self.inner.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.inner.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.inner.enable_tx() + } + + #[inline] + pub fn disable_tx(&mut self) { + self.inner.disable_tx() + } + + pub fn cancel_transfer(&mut self) { + self.disable_rx_irq_sources(); + self.inner.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, buf: &mut [u8]) -> Result { + if buf.len() < self.irq_info.rx_len { + return Err(IrqError::BufferTooShort { + found: buf.len(), + expected: self.irq_info.rx_len, + }); + } + let mut res = IrqResult::default(); + let mut possible_error = IrqUartError::default(); + + let rx_status = self.inner.uart.rxstatus().read(); + res.raw_res = rx_status.bits(); + let irq_end = self.inner.uart.irq_end().read(); + let enb_status = self.inner.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, + possible_error: &mut IrqUartError, + read_res: nb::Result| + -> Option { + match read_res { + Ok(byte) => Some(byte), + Err(nb::Error::WouldBlock) => None, + Err(nb::Error::Other(e)) => { + match e { + Error::Overrun => { + possible_error.overflow = true; + } + Error::FramingError => { + possible_error.framing = true; + } + Error::ParityError => { + possible_error.parity = true; + } + _ => { + res.set_result(IrqResultMask::Unknown); + } + } + None + } + } + }; + 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.inner.uart.rxfifoirqtrg().read().bits() as usize, + self.irq_info.rx_len, + ) { + buf[self.irq_info.rx_idx] = (self.inner.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(&mut res); + return Ok(res); + } + if let Some(byte) = read_handler(&mut res, &mut possible_error, self.inner.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.inner.uart.rxstatus().read(); + res.raw_res = rx_status.bits(); + if rx_status.rxovr().bit_is_set() { + possible_error.overflow = true; + } + if rx_status.rxfrm().bit_is_set() { + possible_error.framing = true; + } + if rx_status.rxpar().bit_is_set() { + possible_error.parity = true; + } + 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(&mut res, &mut possible_error, self.inner.read()) + { + buf[self.irq_info.rx_idx] = byte; + self.irq_info.rx_idx += 1; + } + self.irq_completion_handler(&mut res); + res.set_result(IrqResultMask::Timeout); + return Ok(res); + } + + // If it is not a timeout, it's an error + if possible_error.error() { + self.disable_rx_irq_sources(); + return Err(IrqError::Uart(possible_error)); + } + } + + // Clear the interrupt status bits + self.inner + .uart + .irq_clr() + .write(|w| unsafe { w.bits(irq_end.bits()) }); + Ok(res) + } + + fn irq_completion_handler(&mut self, res: &mut IrqResult) { + self.disable_rx_irq_sources(); + self.inner.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.inner.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.base.read_fixed_len_using_irq(max_len, enb_timeout_irq) + } + + pub fn cancel_transfer(&mut self) { + self.base.cancel_transfer() + } + + /// See [`UartWithIrqBase::irq_handler`] doc + pub fn irq_handler(&mut self, buf: &mut [u8]) -> Result { + self.base.irq_handler(buf) + } + + pub fn release(self) -> (Uart, Pins) { + (self.base.release(), self.pins) + } + + pub fn downgrade(self) -> (UartWithIrqBase, Pins) { + (self.base, self.pins) + } +} + +impl Instance for Uart0 { + const IDX: u8 = 0; + + fn ptr() -> *const uart_base::RegisterBlock { + Uart0::ptr() as *const _ + } +} + +impl Instance for Uart1 { + const IDX: u8 = 1; + + fn ptr() -> *const uart_base::RegisterBlock { + Uart1::ptr() as *const _ + } +} + +impl Instance for Uart2 { + const IDX: u8 = 2; + + fn ptr() -> *const uart_base::RegisterBlock { + Uart2::ptr() as *const _ + } +} + +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 va416xx::Sysconfig, + sys_clk: impl Into + ) -> Self + { + crate::clock::enable_peripheral_clock(syscfg, $clk_enb_enum); + Uart { + inner: UartBase { + uart, + tx: Tx::new(), + rx: Rx::new(), + }, + pins, + }.init(config.into(), sys_clk.into()) + } + } + + )+ + } +} + +uart_impl! { + Uart0: (uart0, clock::PeripheralClocks::Uart0), + Uart1: (uart1, clock::PeripheralClocks::Uart1), + Uart2: (uart2, clock::PeripheralClocks::Uart2), +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { + embedded_hal_nb::serial::ErrorKind::Other + } +} + +impl embedded_io::ErrorType for Rx { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for Rx { + type Error = Error; +} + +impl embedded_hal_nb::serial::Read for Rx { + 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 embedded_io::Read for Rx { + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + + for byte in buf.iter_mut() { + let w = nb::block!(>::read(self))?; + *byte = w; + } + + Ok(buf.len()) + } +} + +impl embedded_io::ErrorType for Tx { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for Tx { + type Error = Error; +} + +impl embedded_hal_nb::serial::Write for Tx { + 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 + (*Uart::ptr()).data().write(|w| w.bits(word as u32)); + } + } + Ok(()) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + // SAFETY: Only TX related registers are used. + let reader = unsafe { &(*Uart::ptr()) }.txstatus().read(); + if reader.wrbusy().bit_is_set() { + 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); + } + + for byte in buf.iter() { + nb::block!(>::write( + self, *byte + ))?; + } + + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + nb::block!(>::flush(self)) + } +} + +impl embedded_io::ErrorType for UartBase { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for UartBase { + type Error = Error; +} + +impl embedded_hal_nb::serial::Read for UartBase { + fn read(&mut self) -> nb::Result { + self.rx.read() + } +} + +impl embedded_hal_nb::serial::Write for UartBase { + 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() + } +}