imxrt-hal 0.5.0

Hardware abstraction layer for NXP i.MX RT microcontrollers.
Documentation
//! Low-power universal asynchronous receiver / transmitter.
//!
//! Use the LPUART peripheral to perform reads and writes with a serial
//! device. Features include
//!
//! - configurable baud rates (depends on input clock frequency)
//! - parity bits: none, even, odd
//! - inverted TX and RX lines
//! - TX and RX FIFOs with configurable watermarks
//! - DMA transfers and receives
//! - Non-blocking and blocking implementations of `embedded-hal` serial
//!   traits.
//!
//! # Example
//!
//! Demonstrates how to create and configure an LPUART peripheral. To see an example
//! of LPUART clock configuration, see the [`ccm::uart_clk`](crate::ccm::uart_clk) documentation.
//! For more information on the DMA API, see the [`dma`](crate::dma) examples.
//!
//! ```no_run
//! use imxrt_hal as hal;
//! use hal::lpuart::{Baud, Direction, Lpuart, Parity, Pins, Status, Watermark};
//! use imxrt_ral as ral;
//! # use imxrt_iomuxc::imxrt1060 as iomuxc;
//!
//! # async fn opt() -> Option<()> {
//! let (gpio_ad_b1_02, gpio_ad_b1_03) = // Handle to LPUART2 TX and RX pins...
//!     # unsafe { (iomuxc::gpio_ad_b1::GPIO_AD_B1_02::new(), iomuxc::gpio_ad_b1::GPIO_AD_B1_03::new()) };
//! # const UART_CLKC_HZ: u32 = 1;
//!
//! let registers = unsafe { ral::lpuart::LPUART2::instance() };
//! let pins = Pins { tx: gpio_ad_b1_02, rx: gpio_ad_b1_03 };
//! let mut lpuart2 = Lpuart::new(registers, pins);
//!
//! const BAUD: Baud = Baud::compute(UART_CLKC_HZ, 115200);
//! lpuart2.disable(|lpuart2| {
//!     lpuart2.set_baud(&BAUD);
//!     lpuart2.set_parity(Parity::ODD);
//!     lpuart2.enable_fifo(Watermark::tx(4));
//!     lpuart2.disable_fifo(Direction::Rx);
//!     lpuart2.set_inversion(Direction::Rx, true);
//! });
//!
//! // Fill the transmit FIFO with 0xAA...
//! while lpuart2.status().contains(Status::TRANSMIT_EMPTY) {
//!     lpuart2.write_byte(0xAA);
//! }
//!
//! // Schedule a DMA receive...
//! # let mut dma_channel = unsafe { hal::dma::DMA.channel(13) };
//! let mut buffer = [0u8; 64];
//! lpuart2.dma_read(&mut dma_channel, &mut buffer)
//!     .await.ok()?;
//!
//! // Release the peripheral instance...
//! let (lpuart2, pins) = lpuart2.release();
//!
//! // Reconstruct without the pins...
//! let mut lpuart2 = Lpuart::without_pins(lpuart2);
//! # Some(()) }
//! ```

use crate::iomuxc;
use crate::ral::{self, lpuart::Instance};

/// LPUART pins.
pub struct Pins<TX, RX>
where
    TX: iomuxc::lpuart::Pin<Direction = iomuxc::lpuart::Tx>,
    RX: iomuxc::lpuart::Pin<Module = TX::Module, Direction = iomuxc::lpuart::Rx>,
{
    /// Transfer pin.
    pub tx: TX,
    /// Receive pin.
    pub rx: RX,
}

/// LPUART peripheral.
///
/// `Lpuart` lets you configure the LPUART peripheral, and perform I/O.
/// See the [module-level documentation](crate::lpuart) for an example.
///
/// `Lpuart` implements serial traits from `embedded-hal`. It models
/// DMA transfers as futures. The type exposes a lower-level API for
/// coordinating DMA transfers. However, you may find it easier to use
/// the [`dma`](crate::dma) interface.
pub struct Lpuart<P, const N: u8> {
    pins: P,
    pub(crate) lpuart: Instance<N>,
}

/// Serial direction.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    /// Transfer direction (leaving the peripheral).
    Tx,
    /// Receiver direction (entering the peripheral).
    Rx,
}

impl<TX, RX, const N: u8> Lpuart<Pins<TX, RX>, N>
where
    TX: iomuxc::lpuart::Pin<Module = iomuxc::consts::Const<N>, Direction = iomuxc::lpuart::Tx>,
    RX: iomuxc::lpuart::Pin<Module = iomuxc::consts::Const<N>, Direction = iomuxc::lpuart::Rx>,
{
    /// Create a new LPUART peripheral from its peripheral registers
    /// and TX / RX pins.
    ///
    /// When `new` returns, the peripheral is reset, the pins are
    /// configured for their LPUART functions, and the TX and RX
    /// halves are enabled.
    pub fn new(lpuart: Instance<N>, mut pins: Pins<TX, RX>) -> Self {
        iomuxc::lpuart::prepare(&mut pins.tx);
        iomuxc::lpuart::prepare(&mut pins.rx);
        Self::init(lpuart, pins)
    }
}

impl<const N: u8> Lpuart<(), N> {
    /// Create a new LPUART peripheral from its peripheral registers
    /// without any pins.
    ///
    /// This is similar to [`new()`](Self::new), but it does not configure
    /// pins to function as inputs and outputs. You're responsible
    /// for configuring TX and RX pins and for making sure the pin state
    /// doesn't change.
    pub fn without_pins(lpuart: Instance<N>) -> Self {
        Self::init(lpuart, ())
    }
}

impl<P, const N: u8> Lpuart<P, N> {
    /// The peripheral instance.
    pub const N: u8 = N;

    fn init(lpuart: Instance<N>, pins: P) -> Self {
        ral::write_reg!(ral::lpuart, lpuart, GLOBAL, RST: 1);
        ral::write_reg!(ral::lpuart, lpuart, GLOBAL, RST: 0);
        ral::modify_reg!(ral::lpuart, lpuart, CTRL, TE: TE_1, RE: RE_1);
        Self { pins, lpuart }
    }

    /// Indicates if the transmit / receive functions are
    /// (`true`) or are not (`false`) enabled.
    pub fn is_enabled(&self, direction: Direction) -> bool {
        match direction {
            Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, CTRL, RE == RE_1),
            Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, CTRL, TE == TE_1),
        }
    }

    /// Enable (`true`) or disable (`false`) the transmit / receive
    /// functions.
    pub fn set_enable(&mut self, direction: Direction, enable: bool) {
        match direction {
            Direction::Rx => ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, RE: enable as u32),
            Direction::Tx => ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, TE: enable as u32),
        }
    }

    /// Resets all internal logic and registers.
    ///
    /// Note that this may not reset all peripheral state, like the state
    /// in the peripheral's global register.
    pub fn reset(&mut self) {
        ral::write_reg!(ral::lpuart, self.lpuart, GLOBAL, RST: 1);
        ral::write_reg!(ral::lpuart, self.lpuart, GLOBAL, RST: 0);
    }

    /// Release all components of the LPUART driver.
    ///
    /// This does not change any component state; it releases the components as-is.
    /// If you need to obtain the registers in a known, good state, consider calling
    /// methods like [`reset()`](Self::reset) before releasing the registers.
    pub fn release(self) -> (Instance<N>, P) {
        (self.lpuart, self.pins)
    }

    /// Borrow the LPUART pins.
    pub fn pins(&self) -> &P {
        &self.pins
    }

    /// Exclusively borrow the LPUART pins.
    pub fn pins_mut(&mut self) -> &mut P {
        &mut self.pins
    }

    /// Temporarily disable the LPUART peripheral.
    ///
    /// The handle to a [`Disabled`](crate::lpuart::Disabled) driver lets you modify
    /// LPUART settings that require a fully disabled peripheral. This will flush
    /// TX and RX buffers.
    pub fn disable<R>(&mut self, func: impl FnOnce(&mut Disabled<N>) -> R) -> R {
        let mut disabled = Disabled::new(&self.lpuart);
        func(&mut disabled)
    }

    /// Return the baud-specific timing values for this UART peripheral.
    pub fn baud(&self) -> Baud {
        let (osr, sbr, bothedge) =
            ral::read_reg!(ral::lpuart, self.lpuart, BAUD, OSR, SBR, BOTHEDGE);
        Baud {
            osr: osr + 1,
            sbr,
            bothedge: bothedge != 0,
        }
    }

    /// Return the parity seting for the UART peripheral.
    ///
    /// Result is `None` if there is no parity setting.
    pub fn parity(&self) -> Option<Parity> {
        let (pe, pt) = ral::read_reg!(ral::lpuart, self.lpuart, CTRL, PE, PT);
        const PARITY_ODD: u32 = Parity::Odd as u32;
        const PARITY_EVEN: u32 = Parity::Even as u32;
        match pt {
            PARITY_ODD if pe != 0 => Parity::ODD,
            PARITY_EVEN if pe != 0 => Parity::EVEN,
            _ => Parity::NONE,
        }
    }

    /// Indicates if the bits are inverted.
    #[inline]
    pub fn is_inverted(&self, direction: Direction) -> bool {
        match direction {
            Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, STAT, RXINV == 1),
            Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, CTRL, TXINV == 1),
        }
    }

    /// Indicates if the FIFO is enabled.
    #[inline]
    pub fn is_fifo_enabled(&self, direction: Direction) -> bool {
        match direction {
            Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, FIFO, RXFE == 1),
            Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, FIFO, TXFE == 1),
        }
    }

    /// Returns the FIFO watermark value.
    #[inline]
    pub fn fifo_watermark(&self, direction: Direction) -> u32 {
        match direction {
            Direction::Rx => ral::read_reg!(ral::lpuart, self.lpuart, WATER, RXWATER),
            Direction::Tx => ral::read_reg!(ral::lpuart, self.lpuart, WATER, TXWATER),
        }
    }

    /// Read the data register.
    pub fn read_data(&self) -> ReadData {
        ReadData(ral::read_reg!(ral::lpuart, self.lpuart, DATA))
    }

    /// Write a byte.
    ///
    /// This does not perform any checks for space in the transmit
    /// buffer. To check transmit buffer space, use `status`, and
    /// check for the transmit data register empty.
    pub fn write_byte(&self, byte: u8) {
        ral::write_reg!(ral::lpuart, self.lpuart, DATA, byte as u32);
    }

    /// Check the peripheral status register.
    pub fn status(&self) -> Status {
        let stat = ral::read_reg!(ral::lpuart, self.lpuart, STAT);
        let fifo = ral::read_reg!(ral::lpuart, self.lpuart, FIFO);
        Status::from_registers(stat, fifo)
    }

    /// Clear the status flags.
    ///
    /// Bits that are read-only will be cleared by the implementation, so it's
    /// safe to call with `Status::all()`.
    #[inline]
    pub fn clear_status(&mut self, status: Status) {
        let stat_flags = status & Status::W1C & Status::stat_mask();
        let fifo_flags = status & Status::W1C & Status::fifo_mask();
        ral::modify_reg!(ral::lpuart, self.lpuart, STAT, |stat| {
            let stat = stat & !Status::stat_mask().stat_bits();
            stat | stat_flags.stat_bits()
        });
        ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, |fifo| {
            let fifo = fifo & !Status::fifo_mask().fifo_bits();
            fifo | fifo_flags.fifo_bits()
        });
    }

    /// Flush data from the FIFO.
    ///
    /// This does not flush anything that's already in the transmit or receive register.
    #[inline]
    pub fn flush_fifo(&mut self, direction: Direction) {
        flush_fifo(&self.lpuart, direction);
    }

    /// Return the interrupt flags.
    ///
    /// The interrupt flags indicate the reasons that this peripheral may generate an
    /// interrupt.
    pub fn interrupts(&self) -> Interrupts {
        let ctrl = ral::read_reg!(ral::lpuart, self.lpuart, CTRL);
        let fifo = ral::read_reg!(ral::lpuart, self.lpuart, FIFO);
        Interrupts::from_bits_truncate(ctrl | fifo)
    }

    /// Let the peripheral act as a DMA destination.
    ///
    /// After this call, the peripheral will signal to the DMA engine whenever
    /// it has free space in its transfer buffer.
    pub fn enable_dma_transmit(&mut self) {
        ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, TDMAE: 1);
    }

    /// Stop the peripheral from acting as a DMA destination.
    ///
    /// See the DMA chapter in the reference manual to understand when this
    /// should be called in the DMA transfer lifecycle.
    pub fn disable_dma_transmit(&mut self) {
        while ral::read_reg!(ral::lpuart, self.lpuart, BAUD, TDMAE == 1) {
            ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, TDMAE: 0);
        }
    }

    /// Produces a pointer to the data register.
    ///
    /// You should use this pointer when coordinating a DMA transfer.
    /// You're not expected to read from this pointer in software.
    pub fn data(&self) -> *const ral::RWRegister<u32> {
        core::ptr::addr_of!(self.lpuart.DATA)
    }

    /// Let the peripheral act as a DMA source.
    ///
    /// After this call, the peripheral will signal to the DMA engine whenever
    /// it has data available to read.
    pub fn enable_dma_receive(&mut self) {
        self.clear_status(Status::W1C);
        ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, RDMAE: 1);
    }

    /// Stop the peripheral from acting as a DMA source.
    ///
    /// See the DMA chapter in the reference manual to understand when this
    /// should be called in the DMA transfer lifecycle.
    pub fn disable_dma_receive(&mut self) {
        while ral::read_reg!(ral::lpuart, self.lpuart, BAUD, RDMAE == 1) {
            ral::modify_reg!(ral::lpuart, self.lpuart, BAUD, RDMAE: 0);
        }
    }
}

fn flush_fifo<const N: u8>(lpuart: &Instance<N>, direction: Direction) {
    match direction {
        Direction::Rx => ral::modify_reg!(ral::lpuart, lpuart, FIFO, RXFLUSH: RXFLUSH_1),
        Direction::Tx => ral::modify_reg!(ral::lpuart, lpuart, FIFO, TXFLUSH: TXFLUSH_1),
    }
}

/// A temporarily-disabled LPUART peripheral.
///
/// The disabled peripheral lets you changed
/// settings that require a disabled peripheral.
pub struct Disabled<'a, const N: u8> {
    lpuart: &'a Instance<N>,
    te: bool,
    re: bool,
}

impl<const N: u8> Drop for Disabled<'_, N> {
    fn drop(&mut self) {
        ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, TE: self.te as u32, RE: self.re as u32);
    }
}

impl<'a, const N: u8> Disabled<'a, N> {
    fn new(lpuart: &'a Instance<N>) -> Self {
        let (te, re) = ral::read_reg!(ral::lpuart, lpuart, CTRL, TE, RE);
        ral::modify_reg!(ral::lpuart, lpuart, CTRL, TE: TE_0, RE: RE_0);
        for direction in [Direction::Rx, Direction::Tx] {
            flush_fifo(lpuart, direction);
        }
        Self {
            lpuart,
            te: te != 0,
            re: re != 0,
        }
    }

    /// Set baud-specific timing values for this UART peripheral.
    ///
    /// The timing values are used to set a baud rate. To compute
    /// a baud rate, see [`Baud::compute`](crate::lpuart::Baud::compute). Or,
    /// you may compute your own timing values.
    pub fn set_baud(&mut self, baud: &Baud) {
        ral::modify_reg!(ral::lpuart, self.lpuart,
            BAUD,
            OSR: baud.osr.clamp(4, 32) - 1,
            SBR: baud.sbr.min((1 << 13) - 1),
            BOTHEDGE: baud.bothedge as u32)
    }

    /// Specify parity bit settings. If there is no parity, use `None`.
    pub fn set_parity(&mut self, parity: Option<Parity>) {
        ral::modify_reg!(
            ral::lpuart,
            self.lpuart,
            CTRL,
            PE: parity.is_some() as u32,
            M: parity.is_some() as u32,
            PT: parity.map(|p| p as u32).unwrap_or(0u32)
        );
    }

    /// Reverse the polarity of data, affecting all data bits, start
    /// and stop bits, and polarity bits.
    ///
    /// The default inversion state is `false`.
    #[inline]
    pub fn set_inversion(&mut self, direction: Direction, inverted: bool) {
        match direction {
            Direction::Rx => {
                ral::modify_reg!(ral::lpuart, self.lpuart, STAT, RXINV: inverted as u32)
            }
            Direction::Tx => {
                ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, TXINV: inverted as u32)
            }
        }
    }

    /// Disable the FIFO for the given direction.
    #[inline]
    pub fn disable_fifo(&mut self, direction: Direction) {
        match direction {
            Direction::Rx => ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, RXFE: RXFE_0),
            Direction::Tx => ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, TXFE: TXFE_0),
        }
    }

    /// Enable the FIFO, and set the FIFO watermark.
    ///
    /// `watermark` describes the serial direction, and the point at which the hardware signals a full
    /// or empty FIFO. Use [`Watermark::tx`](crate::lpuart::Watermark::tx)
    /// to enable the transfer FIFO, and [`Watermark::rx`](crate::lpuart::Watermark::rx) to enable the
    /// receive FIFO.
    ///
    /// The actual watermark value is limited by the hardware. `enable_fifo` returns the
    /// actual watermark value.
    #[inline]
    pub fn enable_fifo(&mut self, watermark: Watermark) -> u32 {
        let size = match watermark.direction {
            Direction::Rx => 1 << ral::read_reg!(ral::lpuart, self.lpuart, PARAM, RXFIFO),
            Direction::Tx => 1 << ral::read_reg!(ral::lpuart, self.lpuart, PARAM, TXFIFO),
        };
        let size = watermark.size.min(size - 1);
        match watermark.direction {
            Direction::Rx => {
                ral::modify_reg!(ral::lpuart, self.lpuart, WATER, RXWATER: size);
                ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, RXFE: RXFE_1);
            }
            Direction::Tx => {
                ral::modify_reg!(ral::lpuart, self.lpuart, WATER, TXWATER: size);
                ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, TXFE: TXFE_1);
            }
        };
        size
    }

    /// Set the interrupt flags for this LPUART peripheral.
    ///
    /// Use `set_interrupts` to enable or disable interrupt generation for
    /// this peripheral.
    pub fn set_interrupts(&mut self, interrupts: Interrupts) {
        let ctrl_flags = interrupts & Interrupts::ctrl_mask();
        let fifo_flags = interrupts & Interrupts::fifo_mask();
        ral::modify_reg!(ral::lpuart, self.lpuart, CTRL, |ctrl| {
            let ctrl = ctrl & !Interrupts::ctrl_mask().bits();
            ctrl | ctrl_flags.bits()
        });
        ral::modify_reg!(ral::lpuart, self.lpuart, FIFO, |fifo| {
            let fifo = fifo & !Interrupts::fifo_mask().bits();
            fifo | fifo_flags.bits()
        });
    }
}

/// Values specific to the baud rate.
///
/// To compute the values for a given baud rate,
/// use [`compute`](Baud::compute). To understand
/// the actual baud rate, use [`value`](Baud::value).
///
/// Advanced users may choose to set the OSR, SBR, and
/// BOTHEDGE values directly.
///
/// ```no_run
/// use imxrt_hal::lpuart::Baud;
///
/// // Assume UART clock is driven from the crystal
/// // oscillator...
/// const UART_CLOCK_HZ: u32 = 24_000_000;
/// const BAUD: Baud = Baud::compute(UART_CLOCK_HZ, 115200);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Baud {
    /// Oversampling rate.
    ///
    /// This should be set between 4 and 32.
    /// The driver clamps the `osr` value within
    /// this range.
    pub osr: u32,
    /// Baud rate modulo divisor.
    ///
    /// The driver commits this value directly.
    /// A value of zero is allowed, but will disable
    /// baud rate generation in hardware. The max
    /// value is `(2^13) - 1`. The implementation
    /// limits the max value.
    pub sbr: u32,
    /// Both edge sampling.
    ///
    /// Should be set when the oversampling
    /// rate is between 4 and 7. Optional
    /// for higher sampling rates. The driver
    /// will commit this value directly.
    pub bothedge: bool,
}

impl Baud {
    /// Returns the baud value in bits per second.
    ///
    /// `source_clock_hz` is the UART clock frequency (Hz).
    ///
    /// # Panics
    ///
    /// Panics if `sbr` or `osr` is zero.
    pub const fn value(self, source_clock_hz: u32) -> u32 {
        source_clock_hz / (self.sbr * self.osr)
    }

    /// Computes a timings struct that represents a baud rate.
    ///
    /// `source_clock_hz` is the UART clock frequency (Hz). `baud`
    /// is the intended baud rate.
    pub const fn compute(source_clock_hz: u32, baud: u32) -> Baud {
        const fn max(left: u32, right: u32) -> u32 {
            if left > right {
                left
            } else {
                right
            }
        }
        const fn min(left: u32, right: u32) -> u32 {
            if left < right {
                left
            } else {
                right
            }
        }

        let mut err = u32::max_value();
        let mut best_osr = 0;
        let mut best_sbr = 0;

        let mut osr = 8;
        let mut sbr = 1;
        while osr <= 32 {
            while sbr < 8192 {
                let b = source_clock_hz / (sbr * osr);
                let e = max(baud, b) - min(baud, b);
                if e < err {
                    err = e;
                    best_osr = osr;
                    best_sbr = sbr;
                }
                sbr += 1;
            }
            osr += 1;
        }
        Baud {
            osr: best_osr,
            sbr: best_sbr,
            bothedge: 4 <= best_osr && best_osr <= 7,
        }
    }
}

/// Parity bit selection.
///
/// See [`Disabled::set_parity`](crate::lpuart::Disabled::set_parity) and
/// [`Lpuart::parity`](crate::lpuart::Lpuart::parity) for more information.
/// Consider using the associated constants to quickly specify
/// parity bits.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Parity {
    /// Even parity.
    Even = 0,
    /// Odd parity.
    Odd = 1,
}

impl Parity {
    /// No parity.
    pub const NONE: Option<Parity> = None;
    /// Even parity.
    pub const EVEN: Option<Parity> = Some(Parity::Even);
    /// Odd parity.
    pub const ODD: Option<Parity> = Some(Parity::Odd);
}

bitflags::bitflags! {
    /// Errors that may occur when reading data.
    pub struct ReadFlags : u32 {
        /// Data was received with noise.
        const NOISY = 1 << 15;
        /// Parity error when receiving data.
        const PARITY_ERROR = 1 << 14;
        /// Framing error when receiving data.
        const FRAME_ERROR = 1 << 13;
        /// Receive buffer is empty.
        ///
        /// Asserts when there is no data in the receive buffer.
        const RXEMPT = 1 << 12;
        /// Idle Line.
        ///
        /// Indicates the receiver line was idle before receiving the character.
        /// Overrun occured, and we lost data in the shift register.
        const IDLINE = 1 << 11;
    }
}

bitflags::bitflags! {
    /// Interrupt settings.
    ///
    /// A set bit indicates that the interrupt is enabled.
    pub struct Interrupts : u32 {
        /// Overrun interrupt enable.
        const OVERRUN = 1 << 27;
        /// Noise error interrupt enable.
        const NOISE_ERROR = 1 << 26;
        /// Framing error interrupt enable.
        const FRAMING_ERROR = 1 << 25;
        /// Parity error interrupt enable.
        const PARITY_ERROR = 1 << 24;
        /// Transmit empty interrupt enable.
        ///
        /// Triggers when the `TRANSMIT_EMPTY` _status_ bit is high.
        const TRANSMIT_EMPTY = 1 << 23;
        /// Transmit complete interrupt enable.
        ///
        /// Triggers an interrupt when the `TRANSMIT_COMPLETE` _status_ bit is high.
        const TRANSMIT_COMPLETE = 1 << 22;
        /// Receiver interrupt enable.
        ///
        /// Triggers when the `RECEIVE_FULL` _status_ bit is high.
        const RECEIVE_FULL = 1 << 21;

        // All of the above flags pertain to the CTRL
        // register. These flags pertain to the FIFO
        // interrupts. They can be written directly.

        /// Transmit FIFO Overflow Interrupt Enable.
        ///
        /// If set, a transmit FIFO overrun event generates an
        /// interrupt.
        const TRANSMIT_OVERFLOW = 1 << 9;
        /// Receive FIFO Underflow Interrupt Enable.
        ///
        /// If set, a receive FIFO underflow event generates an
        /// interrupt.
        const RECEIVE_UNDERFLOW = 1 << 8;
    }
}

impl Interrupts {
    /// Mask for only the FIFO bits.
    const fn fifo_mask() -> Self {
        Self::from_bits_truncate(
            Interrupts::TRANSMIT_OVERFLOW.bits() | Interrupts::RECEIVE_UNDERFLOW.bits(),
        )
    }
    /// Mask for only the CTRL bits.
    const fn ctrl_mask() -> Self {
        // Safety: bits are valid for this bitflags instance.
        Self::from_bits_truncate(Self::all().bits() & !Self::fifo_mask().bits())
    }
}

/// The result of reading from the receiver.
///
/// The data contains flags, which may indicate errors
/// in the received data. If the flags indicate value data,
/// use `u8::from` to convert the data into its raw byte.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct ReadData(u32);

impl ReadData {
    /// Access the read flags, which indicate results of the
    /// read operation.
    #[inline]
    pub fn flags(self) -> ReadFlags {
        ReadFlags::from_bits_truncate(self.0)
    }

    /// Access the raw value.
    #[inline]
    pub fn raw(self) -> u32 {
        self.0
    }
}

impl From<ReadData> for u8 {
    #[inline]
    fn from(read_data: ReadData) -> u8 {
        read_data.0 as u8
    }
}

bitflags::bitflags! {
    /// Status flags.
    pub struct Status : u32 {
        /// Receiver active flag.
        ///
        /// Set when the receiver detects a start bit. Cleared when
        /// the line is idle.
        const RECEIVE_ACTIVE = 1 << 24;
        /// Transmit data register empty.
        ///
        /// This bit is set when the transmit FIFO can accept data.
        /// - if the FIFO is enabled, this is set when the FIFO hits the watermark.
        /// - if the FIFO is disabled, this is set when there's nothing in
        ///   the transmit data register.
        const TRANSMIT_EMPTY = 1 << 23;
        /// Transmit complete.
        ///
        /// TC is cleared when there's a transmission in progress, or when a preamble /
        /// break character is loaded. It's set when the transmit buffer is empty.
        ///
        /// To clear TC, perform a write.
        const TRANSMIT_COMPLETE = 1 << 22;
        /// Receiver data register full.
        ///
        /// This bit is set when the receive FIFO is full.
        /// - if the FIFO is enabled, this is set when the FIFO hits the watermark.
        /// - if the FIFO is disabled, this is set when there's something in the
        ///   receiver register.
        const RECEIVE_FULL = 1 << 21;
        /// Idle line flag.
        ///
        /// IDLE is set when the LPUART receive line becomes idle for a full character
        /// time after a period of activity.
        const IDLE = 1 << 20;
        /// Receiver overrun.
        ///
        /// Set when software fails to prevent the receive data register
        /// from overflowing with data. The OR bit is set immediately after the
        /// stop bit has been completely received for the dataword that overflows
        /// the buffer and all the other error flags (FE, NF, and PF) are prevented
        /// from setting.
        const OVERRUN = 1 << 19;
        /// Noise flag.
        ///
        /// This is also available in the read flags. However, setting it here
        /// allows you to clear the noise flag in the status register.
        const NOISY = 1 << 18;
        /// Framing error.
        ///
        /// This is also available in the read flags. However, setting it here
        /// allows you to clear the noise flag in the status register.
        const FRAME_ERROR = 1 << 17;
        /// Parity error.
        ///
        /// This is also available in the read flags. However, setting it here
        /// allows you to clear the noise flag in the status register.
        const PARITY_ERROR = 1 << 16;

        // All flags up to and including bit 13 are marked 'reserved'
        // in the status register. We're using these for other 'status'
        // functions.

        // These two flags relate to the FIFOs. They can only be written
        // to the FIFO register after a left shift of FIFO_SHIFT.

        /// Transmitter Buffer Overflow Flag
        ///
        /// Indicates that more data has been written to the transmit buffer than it can hold.
        const TRANSMIT_OVERFLOW = 1 << 13;
        /// Receiver Buffer Underflow Flag
        ///
        /// Indicates that more data has been read from the receive buffer than was present.
        const RECEIVE_UNDERFLOW = 1 << 12;
    }
}

impl Status {
    /// The number of left shifts required to move the FIFO
    /// status bits into position for the FIFO register.
    const FIFO_SHIFT: u32 = 4;

    /// The set of status bits that are W1C.
    ///
    /// Use this to differentiate read-only bits from bits that are
    /// W1C.
    pub const W1C: Status =
        Self::from_bits_truncate(Self::all().bits() & !Self::read_only_mask().bits());

    /// Status bits that are read-only.
    ///
    /// Includes those bits in the FIFO register.
    const fn read_only_mask() -> Self {
        Self::from_bits_truncate(
            Self::RECEIVE_ACTIVE.bits()
                | Self::TRANSMIT_EMPTY.bits()
                | Self::TRANSMIT_COMPLETE.bits()
                | Self::RECEIVE_FULL.bits(),
        )
    }

    /// Return the bitflags that represent the FIFO bits.
    const fn fifo_mask() -> Self {
        Self::from_bits_truncate(Self::TRANSMIT_OVERFLOW.bits() | Self::RECEIVE_UNDERFLOW.bits())
    }
    /// Return the bitflags that represent the STAT bits.
    const fn stat_mask() -> Self {
        Self::from_bits_truncate(Self::all().bits() & !Self::fifo_mask().bits())
    }
    /// Returns the FIFO bits that may be written to the FIFO register.
    const fn fifo_bits(self) -> u32 {
        (self.bits & Self::fifo_mask().bits()) << Self::FIFO_SHIFT
    }
    /// Returns the STAT bits that may be writeen to the STAT register.
    const fn stat_bits(self) -> u32 {
        self.bits & Self::stat_mask().bits()
    }
    /// Compose status bitflags from raw STAT and FIFO register values.
    ///
    /// FIFO should only include `TXOF` and / or `RXUF` bits.
    const fn from_registers(stat: u32, fifo: u32) -> Self {
        Self::from_bits_truncate(stat | (fifo >> Self::FIFO_SHIFT))
    }
}

/// Watermark levels for TX and RX FIFOs.
///
/// See [`Lpuart::enable_fifo`](crate::lpuart::Disabled::enable_fifo) for more
/// information.
#[derive(Debug, Clone, Copy)]
pub struct Watermark {
    direction: Direction,
    size: u32,
}

impl Watermark {
    /// Specify the transmit FIFO watermark.
    ///
    /// Note that the actual watermark value will be limited by the hardware.
    #[inline]
    pub const fn tx(size: u32) -> Self {
        Watermark {
            direction: Direction::Tx,
            size,
        }
    }
    /// Specify the receive FIFO watermark.
    ///
    /// Note that the actual watermark value with be limited by the hardware.
    #[inline]
    pub const fn rx(size: core::num::NonZeroU32) -> Self {
        Watermark {
            direction: Direction::Rx,
            size: size.get(),
        }
    }
}

impl<P, const N: u8> eh02::serial::Write<u8> for Lpuart<P, N> {
    type Error = core::convert::Infallible;

    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
        self.flush()?;
        self.write_byte(word);
        Ok(())
    }

    fn flush(&mut self) -> nb::Result<(), Self::Error> {
        if !self.status().contains(Status::TRANSMIT_EMPTY) {
            Err(nb::Error::WouldBlock)
        } else {
            Ok(())
        }
    }
}

impl<P, const N: u8> eh02::serial::Read<u8> for Lpuart<P, N> {
    type Error = ReadFlags;

    fn read(&mut self) -> nb::Result<u8, Self::Error> {
        let data = self.read_data();
        self.clear_status(Status::W1C);
        if data.flags().contains(ReadFlags::RXEMPT) {
            Err(nb::Error::WouldBlock)
        } else if data
            .flags()
            .intersects(ReadFlags::PARITY_ERROR | ReadFlags::FRAME_ERROR | ReadFlags::NOISY)
        {
            Err(nb::Error::Other(data.flags()))
        } else {
            Ok(data.into())
        }
    }
}

impl<P, const N: u8> eh02::blocking::serial::Write<u8> for Lpuart<P, N> {
    type Error = core::convert::Infallible;

    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
        for word in buffer {
            nb::block!(eh02::serial::Write::write(self, *word))?;
        }

        Ok(())
    }

    fn bflush(&mut self) -> Result<(), Self::Error> {
        nb::block!(eh02::serial::Write::flush(self))?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::{Baud, ReadData, ReadFlags, Status};

    #[test]
    fn approximate_baud() {
        // Assume the 24MHz XTAL clock.
        const UART_CLOCK_HZ: u32 = 24_000_000;
        // The best baud rate we can get is
        const EXPECTED_BAUD: u32 = 115384;
        // for a target baud of
        const TARGET_BAUD: u32 = 115200;

        const BAUD: Baud = Baud::compute(UART_CLOCK_HZ, TARGET_BAUD);

        assert_eq!(BAUD.value(UART_CLOCK_HZ), EXPECTED_BAUD);

        // These values could switch, depending on the implementation...
        assert!(BAUD.sbr == 8 || BAUD.sbr == 26, "SBR: {}", BAUD.sbr);
        if BAUD.sbr == 8 {
            assert_eq!(BAUD.osr, 26);
        } else {
            assert_eq!(BAUD.osr, 8);
        }
        assert!(!BAUD.bothedge);
    }

    #[test]
    fn read_data_flags() {
        let read_data = ReadData(1 << 15 | 1 << 13);
        let flags = read_data.flags();

        assert!(flags.contains(ReadFlags::NOISY));
        assert!(!flags.contains(ReadFlags::PARITY_ERROR));
        assert!(flags.contains(ReadFlags::FRAME_ERROR));
        assert!(!flags.contains(ReadFlags::RXEMPT));
        assert!(!flags.contains(ReadFlags::IDLINE));

        assert!(flags.intersects(ReadFlags::NOISY | ReadFlags::PARITY_ERROR));
        assert!(!flags.intersects(ReadFlags::RXEMPT | ReadFlags::PARITY_ERROR));
    }

    #[test]
    fn status_flags() {
        assert_eq!(Status::fifo_mask().bits(), (1 << 13) | (1 << 12));
        assert_eq!(Status::fifo_mask().fifo_bits(), (1 << 17) | (1 << 16));
        assert_eq!(Status::stat_mask().bits(), 0x01FF_0000);
        assert_eq!(Status::stat_mask().stat_bits(), 0x01FF_0000);
        assert_eq!(Status::W1C.bits(), 0x001F_3000);

        assert!(Status::from_registers(0, (1 << 17) | (1 << 16))
            .contains(Status::TRANSMIT_OVERFLOW | Status::RECEIVE_UNDERFLOW));
        assert!(Status::from_registers(u32::MAX, 0).contains(Status::stat_mask()));

        assert!(Status::all().contains(Status::TRANSMIT_EMPTY));
    }
}