use core::future::Future; use core::marker::PhantomData; use embassy_hal_internal::{Peri, PeripheralType}; use paste::paste; use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; use crate::clocks::{ClockError, Gate, PoweredClock, enable_and_reset}; use crate::gpio::{AnyPin, SealedPin}; use crate::pac::lpuart0::baud::Sbns as StopBits; use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Pt as Parity}; use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; use crate::pac::lpuart0::stat::Msbf as MsbFirst; use crate::{interrupt, pac}; pub mod buffered; // ============================================================================ // DMA INTEGRATION // ============================================================================ use crate::dma::{ Channel as DmaChannelTrait, DMA_MAX_TRANSFER_SIZE, DmaChannel, DmaRequest, EnableInterrupt, RingBuffer, }; // ============================================================================ // MISC // ============================================================================ mod sealed { /// Simply seal a trait to prevent external implementations pub trait Sealed {} } // ============================================================================ // INSTANCE TRAIT // ============================================================================ pub type Regs = &'static crate::pac::lpuart0::RegisterBlock; pub trait SealedInstance { fn info() -> Info; fn index() -> usize; fn buffered_state() -> &'static buffered::State; } pub struct Info { pub regs: Regs, } /// Trait for LPUART peripheral instances #[allow(private_bounds)] pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate { const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance; type Interrupt: interrupt::typelevel::Interrupt; /// Type-safe DMA request source for TX type TxDmaRequest: DmaRequest; /// Type-safe DMA request source for RX type RxDmaRequest: DmaRequest; } macro_rules! impl_instance { ($($n:expr);* $(;)?) => { $( paste!{ impl SealedInstance for crate::peripherals::[] { fn info() -> Info { Info { regs: unsafe { &*pac::[]::ptr() }, } } #[inline] fn index() -> usize { $n } fn buffered_state() -> &'static buffered::State { static BUFFERED_STATE: buffered::State = buffered::State::new(); &BUFFERED_STATE } } impl Instance for crate::peripherals::[] { const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance = crate::clocks::periph_helpers::LpuartInstance::[]; type Interrupt = crate::interrupt::typelevel::[]; type TxDmaRequest = crate::dma::[]; type RxDmaRequest = crate::dma::[]; } } )* }; } // DMA request sources are now type-safe via associated types. // The request source numbers are defined in src/dma.rs: // LPUART0: RX=21, TX=22 -> Lpuart0RxRequest, Lpuart0TxRequest // LPUART1: RX=23, TX=24 -> Lpuart1RxRequest, Lpuart1TxRequest // LPUART2: RX=25, TX=26 -> Lpuart2RxRequest, Lpuart2TxRequest // LPUART3: RX=27, TX=28 -> Lpuart3RxRequest, Lpuart3TxRequest // LPUART4: RX=29, TX=30 -> Lpuart4RxRequest, Lpuart4TxRequest // LPUART5: RX=31, TX=32 -> Lpuart5RxRequest, Lpuart5TxRequest impl_instance!(0; 1; 2; 3; 4; 5); // ============================================================================ // INSTANCE HELPER FUNCTIONS // ============================================================================ /// Perform software reset on the LPUART peripheral pub fn perform_software_reset(regs: Regs) { // Software reset - set and clear RST bit (Global register) regs.global().write(|w| w.rst().reset()); regs.global().write(|w| w.rst().no_effect()); } /// Disable both transmitter and receiver pub fn disable_transceiver(regs: Regs) { regs.ctrl().modify(|_, w| w.te().disabled().re().disabled()); } /// Calculate and configure baudrate settings pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> { let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?; // Configure BAUD register regs.baud().modify(|_, w| unsafe { // Clear and set OSR w.osr().bits(osr - 1); // Clear and set SBR w.sbr().bits(sbr); // Set BOTHEDGE if OSR is between 4 and 7 if osr > 3 && osr < 8 { w.bothedge().enabled() } else { w.bothedge().disabled() } }); Ok(()) } /// Configure frame format (stop bits, data bits) pub fn configure_frame_format(regs: Regs, config: &Config) { // Configure stop bits regs.baud().modify(|_, w| w.sbns().variant(config.stop_bits_count)); // Clear M10 for now (10-bit mode) regs.baud().modify(|_, w| w.m10().disabled()); } /// Configure control settings (parity, data bits, idle config, pin swap) pub fn configure_control_settings(regs: Regs, config: &Config) { regs.ctrl().modify(|_, w| { // Parity configuration let mut w = if let Some(parity) = config.parity_mode { w.pe().enabled().pt().variant(parity) } else { w.pe().disabled() }; // Data bits configuration w = match config.data_bits_count { DataBits::Data8 => { if config.parity_mode.is_some() { w.m().data9() // 8 data + 1 parity = 9 bits } else { w.m().data8() // 8 data bits only } } DataBits::Data9 => w.m().data9(), }; // Idle configuration w = w.idlecfg().variant(config.rx_idle_config); w = w.ilt().variant(config.rx_idle_type); // Swap TXD/RXD if configured if config.swap_txd_rxd { w.swap().swap() } else { w.swap().standard() } }); } /// Configure FIFO settings and watermarks pub fn configure_fifo(regs: Regs, config: &Config) { // Configure WATER register for FIFO watermarks regs.water().write(|w| unsafe { w.rxwater() .bits(config.rx_fifo_watermark) .txwater() .bits(config.tx_fifo_watermark) }); // Enable TX/RX FIFOs regs.fifo().modify(|_, w| w.txfe().enabled().rxfe().enabled()); // Flush FIFOs regs.fifo() .modify(|_, w| w.txflush().txfifo_rst().rxflush().rxfifo_rst()); } /// Clear all status flags pub fn clear_all_status_flags(regs: Regs) { regs.stat().reset(); } /// Configure hardware flow control if enabled pub fn configure_flow_control(regs: Regs, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) { if enable_rx_rts || enable_tx_cts { regs.modir().modify(|_, w| { let mut w = w; // Configure TX CTS w = w.txctsc().variant(config.tx_cts_config); w = w.txctssrc().variant(config.tx_cts_source); if enable_rx_rts { w = w.rxrtse().enabled(); } else { w = w.rxrtse().disabled(); } if enable_tx_cts { w = w.txctse().enabled(); } else { w = w.txctse().disabled(); } w }); } } /// Configure bit order (MSB first or LSB first) pub fn configure_bit_order(regs: Regs, msb_first: MsbFirst) { regs.stat().modify(|_, w| w.msbf().variant(msb_first)); } /// Enable transmitter and/or receiver based on configuration pub fn enable_transceiver(regs: Regs, enable_tx: bool, enable_rx: bool) { regs.ctrl().modify(|_, w| { let mut w = w; if enable_tx { w = w.te().enabled(); } if enable_rx { w = w.re().enabled(); } w }); } pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> { let mut baud_diff = baudrate; let mut osr = 0u8; let mut sbr = 0u16; // Try OSR values from 4 to 32 for osr_temp in 4u8..=32u8 { // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2 let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2); let sbr_temp = if sbr_calc == 0 { 1 } else if sbr_calc > 0x1FFF { 0x1FFF } else { sbr_calc as u16 }; // Calculate actual baud rate let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32); let temp_diff = calculated_baud.abs_diff(baudrate); if temp_diff <= baud_diff { baud_diff = temp_diff; osr = osr_temp; sbr = sbr_temp; } } // Check if baud rate difference is within 3% if baud_diff > (baudrate / 100) * 3 { return Err(Error::UnsupportedBaudrate); } Ok((osr, sbr)) } /// Wait for all transmit operations to complete pub fn wait_for_tx_complete(regs: Regs) { // Wait for TX FIFO to empty while regs.water().read().txcount().bits() != 0 { // Wait for TX FIFO to drain } // Wait for last character to shift out (TC = Transmission Complete) while regs.stat().read().tc().is_active() { // Wait for transmission to complete } } pub fn check_and_clear_rx_errors(regs: Regs) -> Result<()> { let stat = regs.stat().read(); let mut status = Ok(()); // Check for overrun first - other error flags are prevented when OR is set if stat.or().is_overrun() { regs.stat().write(|w| w.or().clear_bit_by_one()); return Err(Error::Overrun); } if stat.pf().is_parity() { regs.stat().write(|w| w.pf().clear_bit_by_one()); status = Err(Error::Parity); } if stat.fe().is_error() { regs.stat().write(|w| w.fe().clear_bit_by_one()); status = Err(Error::Framing); } if stat.nf().is_noise() { regs.stat().write(|w| w.nf().clear_bit_by_one()); status = Err(Error::Noise); } status } pub fn has_data(regs: Regs) -> bool { if regs.param().read().rxfifo().bits() > 0 { // FIFO is available - check RXCOUNT in WATER register regs.water().read().rxcount().bits() > 0 } else { // No FIFO - check RDRF flag in STAT register regs.stat().read().rdrf().is_rxdata() } } // ============================================================================ // PIN TRAITS FOR LPUART FUNCTIONALITY // ============================================================================ impl sealed::Sealed for T {} /// io configuration trait for Lpuart Tx configuration pub trait TxPin: Into + sealed::Sealed + PeripheralType { /// convert the pin to appropriate function for Lpuart Tx usage fn as_tx(&self); } /// io configuration trait for Lpuart Rx configuration pub trait RxPin: Into + sealed::Sealed + PeripheralType { /// convert the pin to appropriate function for Lpuart Rx usage fn as_rx(&self); } /// io configuration trait for Lpuart Cts pub trait CtsPin: Into + sealed::Sealed + PeripheralType { /// convert the pin to appropriate function for Lpuart Cts usage fn as_cts(&self); } /// io configuration trait for Lpuart Rts pub trait RtsPin: Into + sealed::Sealed + PeripheralType { /// convert the pin to appropriate function for Lpuart Rts usage fn as_rts(&self); } macro_rules! impl_tx_pin { ($inst:ident, $pin:ident, $alt:ident) => { impl TxPin for crate::peripherals::$pin { fn as_tx(&self) { // TODO: Check these are right self.set_pull(crate::gpio::Pull::Up); self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); self.set_drive_strength(crate::gpio::DriveStrength::Double.into()); self.set_function(crate::pac::port0::pcr0::Mux::$alt); self.set_enable_input_buffer(); } } }; } macro_rules! impl_rx_pin { ($inst:ident, $pin:ident, $alt:ident) => { impl RxPin for crate::peripherals::$pin { fn as_rx(&self) { // TODO: Check these are right self.set_pull(crate::gpio::Pull::Up); self.set_slew_rate(crate::gpio::SlewRate::Fast.into()); self.set_drive_strength(crate::gpio::DriveStrength::Double.into()); self.set_function(crate::pac::port0::pcr0::Mux::$alt); self.set_enable_input_buffer(); } } }; } // TODO: Macro and impls for CTS/RTS pins macro_rules! impl_cts_pin { ($inst:ident, $pin:ident, $alt:ident) => { impl CtsPin for crate::peripherals::$pin { fn as_cts(&self) { todo!() } } }; } macro_rules! impl_rts_pin { ($inst:ident, $pin:ident, $alt:ident) => { impl RtsPin for crate::peripherals::$pin { fn as_rts(&self) { todo!() } } }; } // LPUART 0 impl_tx_pin!(LPUART0, P0_3, Mux2); impl_tx_pin!(LPUART0, P0_21, Mux3); impl_tx_pin!(LPUART0, P2_1, Mux2); impl_rx_pin!(LPUART0, P0_2, Mux2); impl_rx_pin!(LPUART0, P0_20, Mux3); impl_rx_pin!(LPUART0, P2_0, Mux2); impl_cts_pin!(LPUART0, P0_1, Mux2); impl_cts_pin!(LPUART0, P0_23, Mux3); impl_cts_pin!(LPUART0, P2_3, Mux2); impl_rts_pin!(LPUART0, P0_0, Mux2); impl_rts_pin!(LPUART0, P0_22, Mux3); impl_rts_pin!(LPUART0, P2_2, Mux2); // LPUART 1 impl_tx_pin!(LPUART1, P1_9, Mux2); impl_tx_pin!(LPUART1, P2_13, Mux3); impl_tx_pin!(LPUART1, P3_9, Mux3); impl_tx_pin!(LPUART1, P3_21, Mux3); impl_rx_pin!(LPUART1, P1_8, Mux2); impl_rx_pin!(LPUART1, P2_12, Mux3); impl_rx_pin!(LPUART1, P3_8, Mux3); impl_rx_pin!(LPUART1, P3_20, Mux3); impl_cts_pin!(LPUART1, P1_11, Mux2); impl_cts_pin!(LPUART1, P2_17, Mux3); impl_cts_pin!(LPUART1, P3_11, Mux3); impl_cts_pin!(LPUART1, P3_23, Mux3); impl_rts_pin!(LPUART1, P1_10, Mux2); impl_rts_pin!(LPUART1, P2_15, Mux3); impl_rts_pin!(LPUART1, P2_16, Mux3); impl_rts_pin!(LPUART1, P3_10, Mux3); // LPUART 2 impl_tx_pin!(LPUART2, P1_5, Mux3); impl_tx_pin!(LPUART2, P1_13, Mux3); impl_tx_pin!(LPUART2, P2_2, Mux3); impl_tx_pin!(LPUART2, P2_10, Mux3); impl_tx_pin!(LPUART2, P3_15, Mux2); impl_rx_pin!(LPUART2, P1_4, Mux3); impl_rx_pin!(LPUART2, P1_12, Mux3); impl_rx_pin!(LPUART2, P2_3, Mux3); impl_rx_pin!(LPUART2, P2_11, Mux3); impl_rx_pin!(LPUART2, P3_14, Mux2); impl_cts_pin!(LPUART2, P1_7, Mux3); impl_cts_pin!(LPUART2, P1_15, Mux3); impl_cts_pin!(LPUART2, P2_4, Mux3); impl_cts_pin!(LPUART2, P3_13, Mux2); impl_rts_pin!(LPUART2, P1_6, Mux3); impl_rts_pin!(LPUART2, P1_14, Mux3); impl_rts_pin!(LPUART2, P2_5, Mux3); impl_rts_pin!(LPUART2, P3_12, Mux2); // LPUART 3 impl_tx_pin!(LPUART3, P3_1, Mux3); impl_tx_pin!(LPUART3, P3_12, Mux3); impl_tx_pin!(LPUART3, P4_5, Mux3); impl_rx_pin!(LPUART3, P3_0, Mux3); impl_rx_pin!(LPUART3, P3_13, Mux3); impl_rx_pin!(LPUART3, P4_2, Mux3); impl_cts_pin!(LPUART3, P3_7, Mux3); impl_cts_pin!(LPUART3, P3_14, Mux3); impl_cts_pin!(LPUART3, P4_6, Mux3); impl_rts_pin!(LPUART3, P3_6, Mux3); impl_rts_pin!(LPUART3, P3_15, Mux3); impl_rts_pin!(LPUART3, P4_7, Mux3); // LPUART 4 impl_tx_pin!(LPUART4, P2_7, Mux3); impl_tx_pin!(LPUART4, P3_19, Mux2); impl_tx_pin!(LPUART4, P3_27, Mux3); impl_tx_pin!(LPUART4, P4_3, Mux3); impl_rx_pin!(LPUART4, P2_6, Mux3); impl_rx_pin!(LPUART4, P3_18, Mux2); impl_rx_pin!(LPUART4, P3_28, Mux3); impl_rx_pin!(LPUART4, P4_4, Mux3); impl_cts_pin!(LPUART4, P2_0, Mux3); impl_cts_pin!(LPUART4, P3_17, Mux2); impl_cts_pin!(LPUART4, P3_31, Mux3); impl_rts_pin!(LPUART4, P2_1, Mux3); impl_rts_pin!(LPUART4, P3_16, Mux2); impl_rts_pin!(LPUART4, P3_30, Mux3); // LPUART 5 impl_tx_pin!(LPUART5, P1_10, Mux8); impl_tx_pin!(LPUART5, P1_17, Mux8); impl_rx_pin!(LPUART5, P1_11, Mux8); impl_rx_pin!(LPUART5, P1_16, Mux8); impl_cts_pin!(LPUART5, P1_12, Mux8); impl_cts_pin!(LPUART5, P1_19, Mux8); impl_rts_pin!(LPUART5, P1_13, Mux8); impl_rts_pin!(LPUART5, P1_18, Mux8); // ============================================================================ // ERROR TYPES AND RESULTS // ============================================================================ /// LPUART error types #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// Read error Read, /// Buffer overflow Overrun, /// Noise error Noise, /// Framing error Framing, /// Parity error Parity, /// Failure Fail, /// Invalid argument InvalidArgument, /// Lpuart baud rate cannot be supported with the given clock UnsupportedBaudrate, /// RX FIFO Empty RxFifoEmpty, /// TX FIFO Full TxFifoFull, /// TX Busy TxBusy, /// Clock Error ClockSetup(ClockError), } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Error::Read => write!(f, "Read error"), Error::Overrun => write!(f, "Buffer overflow"), Error::Noise => write!(f, "Noise error"), Error::Framing => write!(f, "Framing error"), Error::Parity => write!(f, "Parity error"), Error::Fail => write!(f, "Failure"), Error::InvalidArgument => write!(f, "Invalid argument"), Error::UnsupportedBaudrate => write!(f, "Unsupported baud rate"), Error::RxFifoEmpty => write!(f, "RX FIFO empty"), Error::TxFifoFull => write!(f, "TX FIFO full"), Error::TxBusy => write!(f, "TX busy"), Error::ClockSetup(e) => write!(f, "Clock setup error: {:?}", e), } } } impl core::error::Error for Error {} /// A specialized Result type for LPUART operations pub type Result = core::result::Result; // ============================================================================ // CONFIGURATION STRUCTURES // ============================================================================ /// Lpuart config #[derive(Debug, Clone, Copy)] pub struct Config { /// Power state required for this peripheral pub power: PoweredClock, /// Clock source pub source: LpuartClockSel, /// Clock divisor pub div: Div4, /// Baud rate in bits per second pub baudrate_bps: u32, /// Parity configuration pub parity_mode: Option, /// Number of data bits pub data_bits_count: DataBits, /// MSB First or LSB First configuration pub msb_first: MsbFirst, /// Number of stop bits pub stop_bits_count: StopBits, /// TX FIFO watermark pub tx_fifo_watermark: u8, /// RX FIFO watermark pub rx_fifo_watermark: u8, /// TX CTS source pub tx_cts_source: TxCtsSource, /// TX CTS configure pub tx_cts_config: TxCtsConfig, /// RX IDLE type pub rx_idle_type: IdleType, /// RX IDLE configuration pub rx_idle_config: IdleConfig, /// Swap TXD and RXD pins pub swap_txd_rxd: bool, } impl Default for Config { fn default() -> Self { Self { baudrate_bps: 115_200u32, parity_mode: None, data_bits_count: DataBits::Data8, msb_first: MsbFirst::LsbFirst, stop_bits_count: StopBits::One, tx_fifo_watermark: 0, rx_fifo_watermark: 1, tx_cts_source: TxCtsSource::Cts, tx_cts_config: TxCtsConfig::Start, rx_idle_type: IdleType::FromStart, rx_idle_config: IdleConfig::Idle1, swap_txd_rxd: false, power: PoweredClock::NormalEnabledDeepSleepDisabled, source: LpuartClockSel::FroLfDiv, div: Div4::no_div(), } } } /// LPUART status flags #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Status { /// Transmit data register empty pub tx_empty: bool, /// Transmission complete pub tx_complete: bool, /// Receive data register full pub rx_full: bool, /// Idle line detected pub idle: bool, /// Receiver overrun pub overrun: bool, /// Noise error pub noise: bool, /// Framing error pub framing: bool, /// Parity error pub parity: bool, } // ============================================================================ // MODE TRAITS (BLOCKING/ASYNC) // ============================================================================ /// Driver move trait. #[allow(private_bounds)] pub trait Mode: sealed::Sealed {} /// Blocking mode. pub struct Blocking; impl sealed::Sealed for Blocking {} impl Mode for Blocking {} /// Async mode. pub struct Async; impl sealed::Sealed for Async {} impl Mode for Async {} // ============================================================================ // CORE DRIVER STRUCTURES // ============================================================================ /// Lpuart driver. pub struct Lpuart<'a, M: Mode> { info: Info, tx: LpuartTx<'a, M>, rx: LpuartRx<'a, M>, } /// Lpuart TX driver. pub struct LpuartTx<'a, M: Mode> { info: Info, _tx_pin: Peri<'a, AnyPin>, _cts_pin: Option>, mode: PhantomData<(&'a (), M)>, } /// Lpuart Rx driver. pub struct LpuartRx<'a, M: Mode> { info: Info, _rx_pin: Peri<'a, AnyPin>, _rts_pin: Option>, mode: PhantomData<(&'a (), M)>, } /// Lpuart TX driver with DMA support. pub struct LpuartTxDma<'a, T: Instance, C: DmaChannelTrait> { info: Info, _tx_pin: Peri<'a, AnyPin>, tx_dma: DmaChannel, _instance: core::marker::PhantomData, } /// Lpuart RX driver with DMA support. pub struct LpuartRxDma<'a, T: Instance, C: DmaChannelTrait> { info: Info, _rx_pin: Peri<'a, AnyPin>, rx_dma: DmaChannel, _instance: core::marker::PhantomData, } /// Lpuart driver with DMA support for both TX and RX. pub struct LpuartDma<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> { tx: LpuartTxDma<'a, T, TxC>, rx: LpuartRxDma<'a, T, RxC>, } /// Lpuart RX driver with ring-buffered DMA support. pub struct LpuartRxRingDma<'peri, 'ring, T: Instance, C: DmaChannelTrait> { _inner: LpuartRxDma<'peri, T, C>, ring: RingBuffer<'ring, u8>, } // ============================================================================ // LPUART CORE IMPLEMENTATION // ============================================================================ impl<'a, M: Mode> Lpuart<'a, M> { fn init( enable_tx: bool, enable_rx: bool, enable_tx_cts: bool, enable_rx_rts: bool, config: Config, ) -> Result<()> { let regs = T::info().regs; // Enable clocks let conf = LpuartConfig { power: config.power, source: config.source, div: config.div, instance: T::CLOCK_INSTANCE, }; let clock_freq = unsafe { enable_and_reset::(&conf).map_err(Error::ClockSetup)? }; // Perform initialization sequence perform_software_reset(regs); disable_transceiver(regs); configure_baudrate(regs, config.baudrate_bps, clock_freq)?; configure_frame_format(regs, &config); configure_control_settings(regs, &config); configure_fifo(regs, &config); clear_all_status_flags(regs); configure_flow_control(regs, enable_tx_cts, enable_rx_rts, &config); configure_bit_order(regs, config.msb_first); enable_transceiver(regs, enable_rx, enable_tx); Ok(()) } /// Deinitialize the LPUART peripheral pub fn deinit(&self) -> Result<()> { let regs = self.info.regs; // Wait for TX operations to complete wait_for_tx_complete(regs); // Clear all status flags clear_all_status_flags(regs); // Disable the module - clear all CTRL register bits regs.ctrl().reset(); Ok(()) } /// Split the Lpuart into a transmitter and receiver pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) { (self.tx, self.rx) } /// Split the Lpuart into a transmitter and receiver by mutable reference pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) { (&mut self.tx, &mut self.rx) } } // ============================================================================ // BLOCKING MODE IMPLEMENTATIONS // ============================================================================ impl<'a> Lpuart<'a, Blocking> { /// Create a new blocking LPUART instance with RX/TX pins. pub fn new_blocking( _inner: Peri<'a, T>, tx_pin: Peri<'a, impl TxPin>, rx_pin: Peri<'a, impl RxPin>, config: Config, ) -> Result { // Configure the pins for LPUART usage tx_pin.as_tx(); rx_pin.as_rx(); // Initialize the peripheral Self::init::(true, true, false, false, config)?; Ok(Self { info: T::info(), tx: LpuartTx::new_inner(T::info(), tx_pin.into(), None), rx: LpuartRx::new_inner(T::info(), rx_pin.into(), None), }) } /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins pub fn new_blocking_with_rtscts( _inner: Peri<'a, T>, tx_pin: Peri<'a, impl TxPin>, rx_pin: Peri<'a, impl RxPin>, cts_pin: Peri<'a, impl CtsPin>, rts_pin: Peri<'a, impl RtsPin>, config: Config, ) -> Result { // Configure the pins for LPUART usage rx_pin.as_rx(); tx_pin.as_tx(); rts_pin.as_rts(); cts_pin.as_cts(); // Initialize the peripheral with flow control Self::init::(true, true, true, true, config)?; Ok(Self { info: T::info(), rx: LpuartRx::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into())), tx: LpuartTx::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into())), }) } } // ---------------------------------------------------------------------------- // Blocking TX Implementation // ---------------------------------------------------------------------------- impl<'a, M: Mode> LpuartTx<'a, M> { fn new_inner(info: Info, tx_pin: Peri<'a, AnyPin>, cts_pin: Option>) -> Self { Self { info, _tx_pin: tx_pin, _cts_pin: cts_pin, mode: PhantomData, } } } impl<'a> LpuartTx<'a, Blocking> { /// Create a new blocking LPUART transmitter instance pub fn new_blocking( _inner: Peri<'a, T>, tx_pin: Peri<'a, impl TxPin>, config: Config, ) -> Result { // Configure the pins for LPUART usage tx_pin.as_tx(); // Initialize the peripheral Lpuart::::init::(true, false, false, false, config)?; Ok(Self::new_inner(T::info(), tx_pin.into(), None)) } /// Create a new blocking LPUART transmitter instance with CTS flow control pub fn new_blocking_with_cts( _inner: Peri<'a, T>, tx_pin: Peri<'a, impl TxPin>, cts_pin: Peri<'a, impl CtsPin>, config: Config, ) -> Result { tx_pin.as_tx(); cts_pin.as_cts(); Lpuart::::init::(true, false, true, false, config)?; Ok(Self::new_inner(T::info(), tx_pin.into(), Some(cts_pin.into()))) } fn write_byte_internal(&mut self, byte: u8) -> Result<()> { self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) }); Ok(()) } fn blocking_write_byte(&mut self, byte: u8) -> Result<()> { while self.info.regs.stat().read().tdre().is_txdata() {} self.write_byte_internal(byte) } fn write_byte(&mut self, byte: u8) -> Result<()> { if self.info.regs.stat().read().tdre().is_txdata() { Err(Error::TxFifoFull) } else { self.write_byte_internal(byte) } } /// Write data to LPUART TX blocking execution until all data is sent. pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { for x in buf { self.blocking_write_byte(*x)?; } Ok(()) } pub fn write_str_blocking(&mut self, buf: &str) { let _ = self.blocking_write(buf.as_bytes()); } /// Write data to LPUART TX without blocking. pub fn write(&mut self, buf: &[u8]) -> Result<()> { for x in buf { self.write_byte(*x)?; } Ok(()) } /// Flush LPUART TX blocking execution until all data has been transmitted. pub fn blocking_flush(&mut self) -> Result<()> { while self.info.regs.water().read().txcount().bits() != 0 { // Wait for TX FIFO to drain } // Wait for last character to shift out while self.info.regs.stat().read().tc().is_active() { // Wait for transmission to complete } Ok(()) } /// Flush LPUART TX. pub fn flush(&mut self) -> Result<()> { // Check if TX FIFO is empty if self.info.regs.water().read().txcount().bits() != 0 { return Err(Error::TxBusy); } // Check if transmission is complete if self.info.regs.stat().read().tc().is_active() { return Err(Error::TxBusy); } Ok(()) } } // ---------------------------------------------------------------------------- // Blocking RX Implementation // ---------------------------------------------------------------------------- impl<'a, M: Mode> LpuartRx<'a, M> { fn new_inner(info: Info, rx_pin: Peri<'a, AnyPin>, rts_pin: Option>) -> Self { Self { info, _rx_pin: rx_pin, _rts_pin: rts_pin, mode: PhantomData, } } } impl<'a> LpuartRx<'a, Blocking> { /// Create a new blocking LPUART Receiver instance pub fn new_blocking( _inner: Peri<'a, T>, rx_pin: Peri<'a, impl RxPin>, config: Config, ) -> Result { rx_pin.as_rx(); Lpuart::::init::(false, true, false, false, config)?; Ok(Self::new_inner(T::info(), rx_pin.into(), None)) } /// Create a new blocking LPUART Receiver instance with RTS flow control pub fn new_blocking_with_rts( _inner: Peri<'a, T>, rx_pin: Peri<'a, impl RxPin>, rts_pin: Peri<'a, impl RtsPin>, config: Config, ) -> Result { rx_pin.as_rx(); rts_pin.as_rts(); Lpuart::::init::(false, true, false, true, config)?; Ok(Self::new_inner(T::info(), rx_pin.into(), Some(rts_pin.into()))) } fn read_byte_internal(&mut self) -> Result { let data = self.info.regs.data().read(); Ok((data.bits() & 0xFF) as u8) } fn read_byte(&mut self) -> Result { check_and_clear_rx_errors(self.info.regs)?; if !has_data(self.info.regs) { return Err(Error::RxFifoEmpty); } self.read_byte_internal() } fn blocking_read_byte(&mut self) -> Result { loop { if has_data(self.info.regs) { return self.read_byte_internal(); } check_and_clear_rx_errors(self.info.regs)?; } } /// Read data from LPUART RX without blocking. pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { for byte in buf.iter_mut() { *byte = self.read_byte()?; } Ok(()) } /// Read data from LPUART RX blocking execution until the buffer is filled. pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { for byte in buf.iter_mut() { *byte = self.blocking_read_byte()?; } Ok(()) } } impl<'a> Lpuart<'a, Blocking> { /// Read data from LPUART RX blocking execution until the buffer is filled pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { self.rx.blocking_read(buf) } /// Read data from LPUART RX without blocking pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { self.rx.read(buf) } /// Write data to LPUART TX blocking execution until all data is sent pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { self.tx.blocking_write(buf) } pub fn write_byte(&mut self, byte: u8) -> Result<()> { self.tx.write_byte(byte) } pub fn read_byte_blocking(&mut self) -> u8 { loop { if let Ok(b) = self.rx.read_byte() { return b; } } } pub fn write_str_blocking(&mut self, buf: &str) { self.tx.write_str_blocking(buf); } /// Write data to LPUART TX without blocking pub fn write(&mut self, buf: &[u8]) -> Result<()> { self.tx.write(buf) } /// Flush LPUART TX blocking execution until all data has been transmitted pub fn blocking_flush(&mut self) -> Result<()> { self.tx.blocking_flush() } /// Flush LPUART TX without blocking pub fn flush(&mut self) -> Result<()> { self.tx.flush() } } // ============================================================================ // ASYNC MODE IMPLEMENTATIONS (DMA-based) // ============================================================================ /// Guard struct that ensures DMA is stopped if the async future is cancelled. /// /// This implements the RAII pattern: if the future is dropped before completion /// (e.g., due to a timeout), the DMA transfer is automatically aborted to prevent /// use-after-free when the buffer goes out of scope. struct TxDmaGuard<'a, C: DmaChannelTrait> { dma: &'a DmaChannel, regs: Regs, } impl<'a, C: DmaChannelTrait> TxDmaGuard<'a, C> { fn new(dma: &'a DmaChannel, regs: Regs) -> Self { Self { dma, regs } } /// Complete the transfer normally (don't abort on drop). fn complete(self) { // Cleanup self.regs.baud().modify(|_, w| w.tdmae().disabled()); unsafe { self.dma.disable_request(); self.dma.clear_done(); } // Don't run drop since we've cleaned up core::mem::forget(self); } } impl Drop for TxDmaGuard<'_, C> { fn drop(&mut self) { // Abort the DMA transfer if still running unsafe { self.dma.disable_request(); self.dma.clear_done(); self.dma.clear_interrupt(); } // Disable UART TX DMA request self.regs.baud().modify(|_, w| w.tdmae().disabled()); } } /// Guard struct for RX DMA transfers. struct RxDmaGuard<'a, C: DmaChannelTrait> { dma: &'a DmaChannel, regs: Regs, } impl<'a, C: DmaChannelTrait> RxDmaGuard<'a, C> { fn new(dma: &'a DmaChannel, regs: Regs) -> Self { Self { dma, regs } } /// Complete the transfer normally (don't abort on drop). fn complete(self) { // Ensure DMA writes are visible to CPU cortex_m::asm::dsb(); // Cleanup self.regs.baud().modify(|_, w| w.rdmae().disabled()); unsafe { self.dma.disable_request(); self.dma.clear_done(); } // Don't run drop since we've cleaned up core::mem::forget(self); } } impl Drop for RxDmaGuard<'_, C> { fn drop(&mut self) { // Abort the DMA transfer if still running unsafe { self.dma.disable_request(); self.dma.clear_done(); self.dma.clear_interrupt(); } // Disable UART RX DMA request self.regs.baud().modify(|_, w| w.rdmae().disabled()); } } impl<'a, T: Instance, C: DmaChannelTrait> LpuartTxDma<'a, T, C> { /// Create a new LPUART TX driver with DMA support. pub fn new( _inner: Peri<'a, T>, tx_pin: Peri<'a, impl TxPin>, tx_dma_ch: Peri<'a, C>, config: Config, ) -> Result { tx_pin.as_tx(); let tx_pin: Peri<'a, AnyPin> = tx_pin.into(); // Initialize LPUART with TX enabled, RX disabled, no flow control Lpuart::::init::(true, false, false, false, config)?; // Enable interrupt let tx_dma = DmaChannel::new(tx_dma_ch); tx_dma.enable_interrupt(); Ok(Self { info: T::info(), _tx_pin: tx_pin, tx_dma, _instance: core::marker::PhantomData, }) } /// Write data using DMA. /// /// This configures the DMA channel for a memory-to-peripheral transfer /// and waits for completion asynchronously. Large buffers are automatically /// split into chunks that fit within the DMA transfer limit. /// /// The DMA request source is automatically derived from the LPUART instance type. /// /// # Safety /// /// If the returned future is dropped before completion (e.g., due to a timeout), /// the DMA transfer is automatically aborted to prevent use-after-free. /// /// # Arguments /// * `buf` - Data buffer to transmit pub async fn write_dma(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { return Ok(0); } let mut total = 0; for chunk in buf.chunks(DMA_MAX_TRANSFER_SIZE) { total += self.write_dma_inner(chunk).await?; } Ok(total) } /// Internal helper to write a single chunk (max 0x7FFF bytes) using DMA. async fn write_dma_inner(&mut self, buf: &[u8]) -> Result { let len = buf.len(); let peri_addr = self.info.regs.data().as_ptr() as *mut u8; unsafe { // Clean up channel state self.tx_dma.disable_request(); self.tx_dma.clear_done(); self.tx_dma.clear_interrupt(); // Set DMA request source from instance type (type-safe) self.tx_dma.set_request_source::(); // Configure TCD for memory-to-peripheral transfer self.tx_dma .setup_write_to_peripheral(buf, peri_addr, EnableInterrupt::Yes); // Enable UART TX DMA request self.info.regs.baud().modify(|_, w| w.tdmae().enabled()); // Enable DMA channel request self.tx_dma.enable_request(); } // Create guard that will abort DMA if this future is dropped let guard = TxDmaGuard::new(&self.tx_dma, self.info.regs); // Wait for completion asynchronously core::future::poll_fn(|cx| { self.tx_dma.waker().register(cx.waker()); if self.tx_dma.is_done() { core::task::Poll::Ready(()) } else { core::task::Poll::Pending } }) .await; // Transfer completed successfully - clean up without aborting guard.complete(); Ok(len) } /// Blocking write (fallback when DMA is not needed) pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { for &byte in buf { while self.info.regs.stat().read().tdre().is_txdata() {} self.info.regs.data().modify(|_, w| unsafe { w.bits(u32::from(byte)) }); } Ok(()) } /// Flush TX blocking pub fn blocking_flush(&mut self) -> Result<()> { while self.info.regs.water().read().txcount().bits() != 0 {} while self.info.regs.stat().read().tc().is_active() {} Ok(()) } } impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> { /// Create a new LPUART RX driver with DMA support. pub fn new( _inner: Peri<'a, T>, rx_pin: Peri<'a, impl RxPin>, rx_dma_ch: Peri<'a, C>, config: Config, ) -> Result { rx_pin.as_rx(); let rx_pin: Peri<'a, AnyPin> = rx_pin.into(); // Initialize LPUART with TX disabled, RX enabled, no flow control Lpuart::::init::(false, true, false, false, config)?; // Enable dma interrupt let rx_dma = DmaChannel::new(rx_dma_ch); rx_dma.enable_interrupt(); Ok(Self { info: T::info(), _rx_pin: rx_pin, rx_dma, _instance: core::marker::PhantomData, }) } /// Read data using DMA. /// /// This configures the DMA channel for a peripheral-to-memory transfer /// and waits for completion asynchronously. Large buffers are automatically /// split into chunks that fit within the DMA transfer limit. /// /// The DMA request source is automatically derived from the LPUART instance type. /// /// # Safety /// /// If the returned future is dropped before completion (e.g., due to a timeout), /// the DMA transfer is automatically aborted to prevent use-after-free. /// /// # Arguments /// * `buf` - Buffer to receive data into pub async fn read_dma(&mut self, buf: &mut [u8]) -> Result { if buf.is_empty() { return Ok(0); } let mut total = 0; for chunk in buf.chunks_mut(DMA_MAX_TRANSFER_SIZE) { total += self.read_dma_inner(chunk).await?; } Ok(total) } /// Internal helper to read a single chunk (max 0x7FFF bytes) using DMA. async fn read_dma_inner(&mut self, buf: &mut [u8]) -> Result { let len = buf.len(); let peri_addr = self.info.regs.data().as_ptr() as *const u8; unsafe { // Clean up channel state self.rx_dma.disable_request(); self.rx_dma.clear_done(); self.rx_dma.clear_interrupt(); // Set DMA request source from instance type (type-safe) self.rx_dma.set_request_source::(); // Configure TCD for peripheral-to-memory transfer self.rx_dma .setup_read_from_peripheral(peri_addr, buf, EnableInterrupt::Yes); // Enable UART RX DMA request self.info.regs.baud().modify(|_, w| w.rdmae().enabled()); // Enable DMA channel request self.rx_dma.enable_request(); } // Create guard that will abort DMA if this future is dropped let guard = RxDmaGuard::new(&self.rx_dma, self.info.regs); // Wait for completion asynchronously core::future::poll_fn(|cx| { self.rx_dma.waker().register(cx.waker()); if self.rx_dma.is_done() { core::task::Poll::Ready(()) } else { core::task::Poll::Pending } }) .await; // Transfer completed successfully - clean up without aborting guard.complete(); Ok(len) } /// Blocking read (fallback when DMA is not needed) pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { for byte in buf.iter_mut() { loop { if has_data(self.info.regs) { *byte = (self.info.regs.data().read().bits() & 0xFF) as u8; break; } check_and_clear_rx_errors(self.info.regs)?; } } Ok(()) } pub fn into_ring_dma_rx<'buf>(self, buf: &'buf mut [u8]) -> LpuartRxRingDma<'a, 'buf, T, C> { unsafe { let ring = self.setup_ring_buffer(buf); self.enable_dma_request(); LpuartRxRingDma { _inner: self, ring } } } /// Set up a ring buffer for continuous DMA reception. /// /// This configures the DMA channel for circular operation, enabling continuous /// reception of data without gaps. The DMA will continuously write received /// bytes into the buffer, wrapping around when it reaches the end. /// /// This method encapsulates all the low-level setup: /// - Configures the DMA request source for this LPUART instance /// - Enables the RX DMA request in the LPUART peripheral /// - Sets up the circular DMA transfer /// - Enables the NVIC interrupt for async wakeups /// /// # Arguments /// /// * `buf` - Destination buffer for received data (power-of-2 size is ideal for efficiency) /// /// # Returns /// /// A [`RingBuffer`] that can be used to asynchronously read received data. /// /// # Example /// /// ```no_run /// static mut RX_BUF: [u8; 64] = [0; 64]; /// /// let rx = LpuartRxDma::new(p.LPUART2, p.P2_3, p.DMA_CH0, config).unwrap(); /// let ring_buf = unsafe { rx.setup_ring_buffer(&mut RX_BUF) }; /// /// // Read data as it arrives /// let mut buf = [0u8; 16]; /// let n = ring_buf.read(&mut buf).await.unwrap(); /// ``` /// /// # Safety /// /// - The buffer must remain valid for the lifetime of the returned RingBuffer. /// - Only one RingBuffer should exist per LPUART RX channel at a time. /// - The caller must ensure the static buffer is not accessed elsewhere while /// the ring buffer is active. unsafe fn setup_ring_buffer<'b>(&self, buf: &'b mut [u8]) -> RingBuffer<'b, u8> { // Get the peripheral data register address let peri_addr = self.info.regs.data().as_ptr() as *const u8; // Configure DMA request source for this LPUART instance (type-safe) self.rx_dma.set_request_source::(); // Enable RX DMA request in the LPUART peripheral self.info.regs.baud().modify(|_, w| w.rdmae().enabled()); // Set up circular DMA transfer (this also enables NVIC interrupt) self.rx_dma.setup_circular_read(peri_addr, buf) } /// Enable the DMA channel request. /// /// Call this after `setup_ring_buffer()` to start continuous reception. /// This is separated from setup to allow for any additional configuration /// before starting the transfer. unsafe fn enable_dma_request(&self) { self.rx_dma.enable_request(); } } impl<'peri, 'buf, T: Instance, C: DmaChannelTrait> LpuartRxRingDma<'peri, 'buf, T, C> { /// Read from the ring buffer pub fn read<'d>( &mut self, dst: &'d mut [u8], ) -> impl Future> + use<'_, 'buf, 'd, T, C> { self.ring.read(dst) } /// Clear the current contents of the ring buffer pub fn clear(&mut self) { self.ring.clear(); } } impl<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait> LpuartDma<'a, T, TxC, RxC> { /// Create a new LPUART driver with DMA support for both TX and RX. pub fn new( _inner: Peri<'a, T>, tx_pin: Peri<'a, impl TxPin>, rx_pin: Peri<'a, impl RxPin>, tx_dma_ch: Peri<'a, TxC>, rx_dma_ch: Peri<'a, RxC>, config: Config, ) -> Result { tx_pin.as_tx(); rx_pin.as_rx(); let tx_pin: Peri<'a, AnyPin> = tx_pin.into(); let rx_pin: Peri<'a, AnyPin> = rx_pin.into(); // Initialize LPUART with both TX and RX enabled, no flow control Lpuart::::init::(true, true, false, false, config)?; // Enable DMA interrupts let tx_dma = DmaChannel::new(tx_dma_ch); let rx_dma = DmaChannel::new(rx_dma_ch); tx_dma.enable_interrupt(); rx_dma.enable_interrupt(); Ok(Self { tx: LpuartTxDma { info: T::info(), _tx_pin: tx_pin, tx_dma, _instance: core::marker::PhantomData, }, rx: LpuartRxDma { info: T::info(), _rx_pin: rx_pin, rx_dma, _instance: core::marker::PhantomData, }, }) } /// Split into separate TX and RX drivers pub fn split(self) -> (LpuartTxDma<'a, T, TxC>, LpuartRxDma<'a, T, RxC>) { (self.tx, self.rx) } /// Write data using DMA pub async fn write_dma(&mut self, buf: &[u8]) -> Result { self.tx.write_dma(buf).await } /// Read data using DMA pub async fn read_dma(&mut self, buf: &mut [u8]) -> Result { self.rx.read_dma(buf).await } } // ============================================================================ // EMBEDDED-IO-ASYNC TRAIT IMPLEMENTATIONS // ============================================================================ impl embedded_io::ErrorType for LpuartTxDma<'_, T, C> { type Error = Error; } impl embedded_io::ErrorType for LpuartRxDma<'_, T, C> { type Error = Error; } impl embedded_io::ErrorType for LpuartDma<'_, T, TxC, RxC> { type Error = Error; } // ============================================================================ // EMBEDDED-HAL 0.2 TRAIT IMPLEMENTATIONS // ============================================================================ impl embedded_hal_02::serial::Read for LpuartRx<'_, Blocking> { type Error = Error; fn read(&mut self) -> core::result::Result> { let mut buf = [0; 1]; match self.read(&mut buf) { Ok(_) => Ok(buf[0]), Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(e)), } } } impl embedded_hal_02::serial::Write for LpuartTx<'_, Blocking> { type Error = Error; fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error> { match self.write(&[word]) { Ok(_) => Ok(()), Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(e)), } } fn flush(&mut self) -> core::result::Result<(), nb::Error> { match self.flush() { Ok(_) => Ok(()), Err(Error::TxBusy) => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(e)), } } } impl embedded_hal_02::blocking::serial::Write for LpuartTx<'_, Blocking> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> { self.blocking_write(buffer) } fn bflush(&mut self) -> core::result::Result<(), Self::Error> { self.blocking_flush() } } impl embedded_hal_02::serial::Read for Lpuart<'_, Blocking> { type Error = Error; fn read(&mut self) -> core::result::Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } impl embedded_hal_02::serial::Write for Lpuart<'_, Blocking> { type Error = Error; fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error> { embedded_hal_02::serial::Write::write(&mut self.tx, word) } fn flush(&mut self) -> core::result::Result<(), nb::Error> { embedded_hal_02::serial::Write::flush(&mut self.tx) } } impl embedded_hal_02::blocking::serial::Write for Lpuart<'_, Blocking> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> { self.blocking_write(buffer) } fn bflush(&mut self) -> core::result::Result<(), Self::Error> { self.blocking_flush() } } // ============================================================================ // EMBEDDED-HAL-NB TRAIT IMPLEMENTATIONS // ============================================================================ impl embedded_hal_nb::serial::Error for Error { fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise, _ => embedded_hal_nb::serial::ErrorKind::Other, } } } impl embedded_hal_nb::serial::ErrorType for LpuartRx<'_, Blocking> { type Error = Error; } impl embedded_hal_nb::serial::ErrorType for LpuartTx<'_, Blocking> { type Error = Error; } impl embedded_hal_nb::serial::ErrorType for Lpuart<'_, Blocking> { type Error = Error; } impl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> { fn read(&mut self) -> nb::Result { let mut buf = [0; 1]; match self.read(&mut buf) { Ok(_) => Ok(buf[0]), Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(e)), } } } impl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> { fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { match self.write(&[word]) { Ok(_) => Ok(()), Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(e)), } } fn flush(&mut self) -> nb::Result<(), Self::Error> { match self.flush() { Ok(_) => Ok(()), Err(Error::TxBusy) => Err(nb::Error::WouldBlock), Err(e) => Err(nb::Error::Other(e)), } } } impl core::fmt::Write for LpuartTx<'_, Blocking> { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.blocking_write(s.as_bytes()).map_err(|_| core::fmt::Error) } } impl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> { fn read(&mut self) -> nb::Result { embedded_hal_nb::serial::Read::read(&mut self.rx) } } impl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { embedded_hal_nb::serial::Write::write(&mut self.tx, char) } fn flush(&mut self) -> nb::Result<(), Self::Error> { embedded_hal_nb::serial::Write::flush(&mut self.tx) } } // ============================================================================ // EMBEDDED-IO TRAIT IMPLEMENTATIONS // ============================================================================ impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other } } impl embedded_io::ErrorType for LpuartRx<'_, Blocking> { type Error = Error; } impl embedded_io::ErrorType for LpuartTx<'_, Blocking> { type Error = Error; } impl embedded_io::ErrorType for Lpuart<'_, Blocking> { type Error = Error; } impl embedded_io::Read for LpuartRx<'_, Blocking> { fn read(&mut self, buf: &mut [u8]) -> core::result::Result { self.blocking_read(buf).map(|_| buf.len()) } } impl embedded_io::Write for LpuartTx<'_, Blocking> { fn write(&mut self, buf: &[u8]) -> core::result::Result { self.blocking_write(buf).map(|_| buf.len()) } fn flush(&mut self) -> core::result::Result<(), Self::Error> { self.blocking_flush() } } impl embedded_io::Read for Lpuart<'_, Blocking> { fn read(&mut self, buf: &mut [u8]) -> core::result::Result { embedded_io::Read::read(&mut self.rx, buf) } } impl embedded_io::Write for Lpuart<'_, Blocking> { fn write(&mut self, buf: &[u8]) -> core::result::Result { embedded_io::Write::write(&mut self.tx, buf) } fn flush(&mut self) -> core::result::Result<(), Self::Error> { embedded_io::Write::flush(&mut self.tx) } }