From d64ae225b30bc3a0f7a9b1277188b6e60fc97484 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 10 Apr 2025 10:39:54 -0700 Subject: [PATCH] Add UART and DMA drivers Both blocking and async versions are supported. Add separate examples for each mode. --- docs/pages/imxrt.adoc | 2 + embassy-imxrt/src/dma.rs | 418 ++++++++ embassy-imxrt/src/flexcomm/mod.rs | 252 +++++ embassy-imxrt/src/flexcomm/uart.rs | 1230 ++++++++++++++++++++++++ embassy-imxrt/src/gpio.rs | 20 +- embassy-imxrt/src/lib.rs | 26 +- examples/mimxrt6/src/bin/uart-async.rs | 87 ++ examples/mimxrt6/src/bin/uart.rs | 55 ++ 8 files changed, 2069 insertions(+), 21 deletions(-) create mode 100644 embassy-imxrt/src/dma.rs create mode 100644 embassy-imxrt/src/flexcomm/mod.rs create mode 100644 embassy-imxrt/src/flexcomm/uart.rs create mode 100644 examples/mimxrt6/src/bin/uart-async.rs create mode 100644 examples/mimxrt6/src/bin/uart.rs diff --git a/docs/pages/imxrt.adoc b/docs/pages/imxrt.adoc index bbd65e494..87867e1e0 100644 --- a/docs/pages/imxrt.adoc +++ b/docs/pages/imxrt.adoc @@ -10,5 +10,7 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb The following peripherals have a HAL implementation at present * CRC +* DMA * GPIO * RNG +* UART diff --git a/embassy-imxrt/src/dma.rs b/embassy-imxrt/src/dma.rs new file mode 100644 index 000000000..e141447f3 --- /dev/null +++ b/embassy-imxrt/src/dma.rs @@ -0,0 +1,418 @@ +//! DMA driver. + +use core::future::Future; +use core::pin::Pin; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; + +use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +use pac::dma0::channel::cfg::Periphreqen; +use pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width}; + +use crate::clocks::enable_and_reset; +use crate::interrupt::InterruptExt; +use crate::peripherals::DMA0; +use crate::sealed::Sealed; +use crate::{interrupt, pac, peripherals, BitIter}; + +#[cfg(feature = "rt")] +#[interrupt] +fn DMA0() { + let reg = unsafe { crate::pac::Dma0::steal() }; + + if reg.intstat().read().activeerrint().bit() { + let err = reg.errint0().read().bits(); + + for channel in BitIter(err) { + error!("DMA error interrupt on channel {}!", channel); + reg.errint0().write(|w| unsafe { w.err().bits(1 << channel) }); + CHANNEL_WAKERS[channel as usize].wake(); + } + } + + if reg.intstat().read().activeint().bit() { + let ia = reg.inta0().read().bits(); + + for channel in BitIter(ia) { + reg.inta0().write(|w| unsafe { w.ia().bits(1 << channel) }); + CHANNEL_WAKERS[channel as usize].wake(); + } + } +} + +/// Initialize DMA controllers (DMA0 only, for now) +pub(crate) unsafe fn init() { + let sysctl0 = crate::pac::Sysctl0::steal(); + let dmactl0 = crate::pac::Dma0::steal(); + + enable_and_reset::(); + + interrupt::DMA0.disable(); + interrupt::DMA0.set_priority(interrupt::Priority::P3); + + dmactl0.ctrl().modify(|_, w| w.enable().set_bit()); + + // Set channel descriptor SRAM base address + // Descriptor base must be 1K aligned + let descriptor_base = core::ptr::addr_of!(DESCRIPTORS.descs) as u32; + dmactl0.srambase().write(|w| w.bits(descriptor_base)); + + // Ensure AHB priority it highest (M4 == DMAC0) + sysctl0.ahbmatrixprior().modify(|_, w| w.m4().bits(0)); + + interrupt::DMA0.unpend(); + interrupt::DMA0.enable(); +} + +/// DMA read. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. +pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> { + let count = ((to.len() / W::size() as usize) - 1) as isize; + + copy_inner( + ch, + from as *const u32, + (to as *mut u32).byte_offset(count * W::size()), + W::width(), + count, + false, + true, + true, + ) +} + +/// DMA write. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. +pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> { + let count = ((from.len() / W::size() as usize) - 1) as isize; + + copy_inner( + ch, + (from as *const u32).byte_offset(count * W::size()), + to as *mut u32, + W::width(), + count, + true, + false, + true, + ) +} + +/// DMA copy between slices. +/// +/// SAFETY: Slices must point to locations reachable by DMA. +pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> { + let from_len = from.len(); + let to_len = to.len(); + assert_eq!(from_len, to_len); + + let count = ((from_len / W::size() as usize) - 1) as isize; + + copy_inner( + ch, + from.as_ptr().byte_offset(count * W::size()) as *const u32, + to.as_mut_ptr().byte_offset(count * W::size()) as *mut u32, + W::width(), + count, + true, + true, + false, + ) +} + +fn copy_inner<'a, C: Channel>( + ch: Peri<'a, C>, + from: *const u32, + to: *mut u32, + width: Width, + count: isize, + incr_read: bool, + incr_write: bool, + periph: bool, +) -> Transfer<'a, C> { + let p = ch.regs(); + + unsafe { + DESCRIPTORS.descs[ch.number() as usize].src = from as u32; + DESCRIPTORS.descs[ch.number() as usize].dest = to as u32; + } + + compiler_fence(Ordering::SeqCst); + + p.errint0().write(|w| unsafe { w.err().bits(1 << ch.number()) }); + p.inta0().write(|w| unsafe { w.ia().bits(1 << ch.number()) }); + + p.channel(ch.number().into()).cfg().write(|w| { + unsafe { w.chpriority().bits(0) } + .periphreqen() + .variant(match periph { + false => Periphreqen::Disabled, + true => Periphreqen::Enabled, + }) + .hwtrigen() + .clear_bit() + }); + + p.intenset0().write(|w| unsafe { w.inten().bits(1 << ch.number()) }); + + p.channel(ch.number().into()).xfercfg().write(|w| { + unsafe { w.xfercount().bits(count as u16) } + .cfgvalid() + .set_bit() + .clrtrig() + .set_bit() + .reload() + .clear_bit() + .setinta() + .set_bit() + .width() + .variant(width) + .srcinc() + .variant(match incr_read { + false => Srcinc::NoIncrement, + true => Srcinc::WidthX1, + // REVISIT: what about WidthX2 and WidthX4? + }) + .dstinc() + .variant(match incr_write { + false => Dstinc::NoIncrement, + true => Dstinc::WidthX1, + // REVISIT: what about WidthX2 and WidthX4? + }) + }); + + p.enableset0().write(|w| unsafe { w.ena().bits(1 << ch.number()) }); + + p.channel(ch.number().into()) + .xfercfg() + .modify(|_, w| w.swtrig().set_bit()); + + compiler_fence(Ordering::SeqCst); + + Transfer::new(ch) +} + +/// DMA transfer driver. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: Peri<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub(crate) fn new(channel: Peri<'a, C>) -> Self { + Self { channel } + } + + pub(crate) fn abort(&mut self) -> usize { + let p = self.channel.regs(); + + p.abort0().write(|w| w.channel(self.channel.number()).set_bit()); + while p.busy0().read().bsy().bits() & (1 << self.channel.number()) != 0 {} + + p.enableclr0() + .write(|w| unsafe { w.clr().bits(1 << self.channel.number()) }); + + let width: u8 = p + .channel(self.channel.number().into()) + .xfercfg() + .read() + .width() + .variant() + .unwrap() + .into(); + + let count = p + .channel(self.channel.number().into()) + .xfercfg() + .read() + .xfercount() + .bits() + + 1; + + usize::from(count) * usize::from(width) + } +} + +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.abort(); + } +} + +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // Re-register the waker on each call to poll() because any calls to + // wake will deregister the waker. + CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); + + if self.channel.regs().active0().read().act().bits() & (1 << self.channel.number()) == 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + } +} + +/// DMA channel descriptor +#[derive(Copy, Clone)] +#[repr(C)] +struct Descriptor { + reserved: u32, + src: u32, + dest: u32, + link: u32, +} + +impl Descriptor { + const fn new() -> Self { + Self { + reserved: 0, + src: 0, + dest: 0, + link: 0, + } + } +} + +#[repr(align(1024))] +struct Descriptors { + descs: [Descriptor; CHANNEL_COUNT], +} + +impl Descriptors { + const fn new() -> Self { + Self { + descs: [const { Descriptor::new() }; CHANNEL_COUNT], + } + } +} + +static mut DESCRIPTORS: Descriptors = Descriptors::new(); +static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; +pub(crate) const CHANNEL_COUNT: usize = 33; + +/// DMA channel interface. +#[allow(private_bounds)] +pub trait Channel: PeripheralType + Sealed + Into + Sized + 'static { + /// Channel number. + fn number(&self) -> u8; + + /// Channel registry block. + fn regs(&self) -> &'static pac::dma0::RegisterBlock { + unsafe { &*crate::pac::Dma0::ptr() } + } +} + +/// DMA word. +#[allow(private_bounds)] +pub trait Word: Sealed { + /// Transfer width. + fn width() -> Width; + + /// Size in bytes for the width. + fn size() -> isize; +} + +impl Sealed for u8 {} +impl Word for u8 { + fn width() -> Width { + Width::Bit8 + } + + fn size() -> isize { + 1 + } +} + +impl Sealed for u16 {} +impl Word for u16 { + fn width() -> Width { + Width::Bit16 + } + + fn size() -> isize { + 2 + } +} + +impl Sealed for u32 {} +impl Word for u32 { + fn width() -> Width { + Width::Bit32 + } + + fn size() -> isize { + 4 + } +} + +/// Type erased DMA channel. +pub struct AnyChannel { + number: u8, +} + +impl_peripheral!(AnyChannel); + +impl Sealed for AnyChannel {} +impl Channel for AnyChannel { + fn number(&self) -> u8 { + self.number + } +} + +macro_rules! channel { + ($name:ident, $num:expr) => { + impl Sealed for peripherals::$name {} + impl Channel for peripherals::$name { + fn number(&self) -> u8 { + $num + } + } + + impl From for crate::dma::AnyChannel { + fn from(val: peripherals::$name) -> Self { + Self { number: val.number() } + } + } + }; +} + +channel!(DMA0_CH0, 0); +channel!(DMA0_CH1, 1); +channel!(DMA0_CH2, 2); +channel!(DMA0_CH3, 3); +channel!(DMA0_CH4, 4); +channel!(DMA0_CH5, 5); +channel!(DMA0_CH6, 6); +channel!(DMA0_CH7, 7); +channel!(DMA0_CH8, 8); +channel!(DMA0_CH9, 9); +channel!(DMA0_CH10, 10); +channel!(DMA0_CH11, 11); +channel!(DMA0_CH12, 12); +channel!(DMA0_CH13, 13); +channel!(DMA0_CH14, 14); +channel!(DMA0_CH15, 15); +channel!(DMA0_CH16, 16); +channel!(DMA0_CH17, 17); +channel!(DMA0_CH18, 18); +channel!(DMA0_CH19, 19); +channel!(DMA0_CH20, 20); +channel!(DMA0_CH21, 21); +channel!(DMA0_CH22, 22); +channel!(DMA0_CH23, 23); +channel!(DMA0_CH24, 24); +channel!(DMA0_CH25, 25); +channel!(DMA0_CH26, 26); +channel!(DMA0_CH27, 27); +channel!(DMA0_CH28, 28); +channel!(DMA0_CH29, 29); +channel!(DMA0_CH30, 30); +channel!(DMA0_CH31, 31); +channel!(DMA0_CH32, 32); diff --git a/embassy-imxrt/src/flexcomm/mod.rs b/embassy-imxrt/src/flexcomm/mod.rs new file mode 100644 index 000000000..4473c9a77 --- /dev/null +++ b/embassy-imxrt/src/flexcomm/mod.rs @@ -0,0 +1,252 @@ +//! Implements Flexcomm interface wrapper for easier usage across modules + +pub mod uart; + +use paste::paste; + +use crate::clocks::{enable_and_reset, SysconPeripheral}; +use crate::peripherals::{ + FLEXCOMM0, FLEXCOMM1, FLEXCOMM14, FLEXCOMM15, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, +}; +use crate::{pac, PeripheralType}; + +/// clock selection option +#[derive(Copy, Clone, Debug)] +pub enum Clock { + /// SFRO + Sfro, + + /// FFRO + Ffro, + + /// `AUDIO_PLL` + AudioPll, + + /// MASTER + Master, + + /// FCn_FRG with Main clock source + FcnFrgMain, + + /// FCn_FRG with Pll clock source + FcnFrgPll, + + /// FCn_FRG with Sfro clock source + FcnFrgSfro, + + /// FCn_FRG with Ffro clock source + FcnFrgFfro, + + /// disabled + None, +} + +/// do not allow implementation of trait outside this mod +mod sealed { + /// trait does not get re-exported outside flexcomm mod, allowing us to safely expose only desired APIs + pub trait Sealed {} +} + +/// primary low-level flexcomm interface +pub(crate) trait FlexcommLowLevel: sealed::Sealed + PeripheralType + SysconPeripheral + 'static + Send { + // fetch the flexcomm register block for direct manipulation + fn reg() -> &'static pac::flexcomm0::RegisterBlock; + + // set the clock select for this flexcomm instance and remove from reset + fn enable(clk: Clock); +} + +macro_rules! impl_flexcomm { + ($($idx:expr),*) => { + $( + paste!{ + impl sealed::Sealed for crate::peripherals::[] {} + + impl FlexcommLowLevel for crate::peripherals::[] { + fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock { + // SAFETY: safe from single executor, enforce + // via peripheral reference lifetime tracking + unsafe { + &*crate::pac::[]::ptr() + } + } + + fn enable(clk: Clock) { + // SAFETY: safe from single executor + let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; + + clkctl1.flexcomm($idx).fcfclksel().write(|w| match clk { + Clock::Sfro => w.sel().sfro_clk(), + Clock::Ffro => w.sel().ffro_clk(), + Clock::AudioPll => w.sel().audio_pll_clk(), + Clock::Master => w.sel().master_clk(), + Clock::FcnFrgMain => w.sel().fcn_frg_clk(), + Clock::FcnFrgPll => w.sel().fcn_frg_clk(), + Clock::FcnFrgSfro => w.sel().fcn_frg_clk(), + Clock::FcnFrgFfro => w.sel().fcn_frg_clk(), + Clock::None => w.sel().none(), // no clock? throw an error? + }); + + clkctl1.flexcomm($idx).frgclksel().write(|w| match clk { + Clock::FcnFrgMain => w.sel().main_clk(), + Clock::FcnFrgPll => w.sel().frg_pll_clk(), + Clock::FcnFrgSfro => w.sel().sfro_clk(), + Clock::FcnFrgFfro => w.sel().ffro_clk(), + _ => w.sel().none(), // not using frg ... + }); + + // todo: add support for frg div/mult + clkctl1 + .flexcomm($idx) + .frgctl() + .write(|w| + // SAFETY: unsafe only used for .bits() call + unsafe { w.mult().bits(0) }); + + enable_and_reset::<[]>(); + } + } + } + )* + } +} + +impl_flexcomm!(0, 1, 2, 3, 4, 5, 6, 7); + +// TODO: FLEXCOMM 14 is untested. Enable SPI support on FLEXCOMM14 +// Add special case FLEXCOMM14 +impl sealed::Sealed for crate::peripherals::FLEXCOMM14 {} + +impl FlexcommLowLevel for crate::peripherals::FLEXCOMM14 { + fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock { + // SAFETY: safe from single executor, enforce + // via peripheral reference lifetime tracking + unsafe { &*crate::pac::Flexcomm14::ptr() } + } + + fn enable(clk: Clock) { + // SAFETY: safe from single executor + let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; + + clkctl1.fc14fclksel().write(|w| match clk { + Clock::Sfro => w.sel().sfro_clk(), + Clock::Ffro => w.sel().ffro_clk(), + Clock::AudioPll => w.sel().audio_pll_clk(), + Clock::Master => w.sel().master_clk(), + Clock::FcnFrgMain => w.sel().fcn_frg_clk(), + Clock::FcnFrgPll => w.sel().fcn_frg_clk(), + Clock::FcnFrgSfro => w.sel().fcn_frg_clk(), + Clock::FcnFrgFfro => w.sel().fcn_frg_clk(), + Clock::None => w.sel().none(), // no clock? throw an error? + }); + + clkctl1.frg14clksel().write(|w| match clk { + Clock::FcnFrgMain => w.sel().main_clk(), + Clock::FcnFrgPll => w.sel().frg_pll_clk(), + Clock::FcnFrgSfro => w.sel().sfro_clk(), + Clock::FcnFrgFfro => w.sel().ffro_clk(), + _ => w.sel().none(), // not using frg ... + }); + + // todo: add support for frg div/mult + clkctl1.frg14ctl().write(|w| + // SAFETY: unsafe only used for .bits() call + unsafe { w.mult().bits(0) }); + + enable_and_reset::(); + } +} + +// Add special case FLEXCOMM15 +impl sealed::Sealed for crate::peripherals::FLEXCOMM15 {} + +impl FlexcommLowLevel for crate::peripherals::FLEXCOMM15 { + fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock { + // SAFETY: safe from single executor, enforce + // via peripheral reference lifetime tracking + unsafe { &*crate::pac::Flexcomm15::ptr() } + } + + fn enable(clk: Clock) { + // SAFETY: safe from single executor + let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; + + clkctl1.fc15fclksel().write(|w| match clk { + Clock::Sfro => w.sel().sfro_clk(), + Clock::Ffro => w.sel().ffro_clk(), + Clock::AudioPll => w.sel().audio_pll_clk(), + Clock::Master => w.sel().master_clk(), + Clock::FcnFrgMain => w.sel().fcn_frg_clk(), + Clock::FcnFrgPll => w.sel().fcn_frg_clk(), + Clock::FcnFrgSfro => w.sel().fcn_frg_clk(), + Clock::FcnFrgFfro => w.sel().fcn_frg_clk(), + Clock::None => w.sel().none(), // no clock? throw an error? + }); + clkctl1.frg15clksel().write(|w| match clk { + Clock::FcnFrgMain => w.sel().main_clk(), + Clock::FcnFrgPll => w.sel().frg_pll_clk(), + Clock::FcnFrgSfro => w.sel().sfro_clk(), + Clock::FcnFrgFfro => w.sel().ffro_clk(), + _ => w.sel().none(), // not using frg ... + }); + // todo: add support for frg div/mult + clkctl1.frg15ctl().write(|w| + // SAFETY: unsafe only used for .bits() call + unsafe { w.mult().bits(0) }); + + enable_and_reset::(); + } +} + +macro_rules! into_mode { + ($mode:ident, $($fc:ident),*) => { + paste! { + /// Sealed Mode trait + trait []: FlexcommLowLevel {} + + /// Select mode of operation + #[allow(private_bounds)] + pub trait []: [] { + /// Set mode of operation + fn []() { + Self::reg().pselid().write(|w| w.persel().[<$mode>]()); + } + } + } + + $( + paste!{ + impl [] for crate::peripherals::$fc {} + impl [] for crate::peripherals::$fc {} + } + )* + } +} + +into_mode!(usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7); +into_mode!(spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14); +into_mode!(i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15); + +into_mode!( + i2s_transmit, + FLEXCOMM0, + FLEXCOMM1, + FLEXCOMM2, + FLEXCOMM3, + FLEXCOMM4, + FLEXCOMM5, + FLEXCOMM6, + FLEXCOMM7 +); + +into_mode!( + i2s_receive, + FLEXCOMM0, + FLEXCOMM1, + FLEXCOMM2, + FLEXCOMM3, + FLEXCOMM4, + FLEXCOMM5, + FLEXCOMM6, + FLEXCOMM7 +); diff --git a/embassy-imxrt/src/flexcomm/uart.rs b/embassy-imxrt/src/flexcomm/uart.rs new file mode 100644 index 000000000..230b30d43 --- /dev/null +++ b/embassy-imxrt/src/flexcomm/uart.rs @@ -0,0 +1,1230 @@ +//! Universal Asynchronous Receiver Transmitter (UART) driver. + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; +use core::task::Poll; + +use embassy_futures::select::{select, Either}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +use paste::paste; + +use crate::dma::AnyChannel; +use crate::flexcomm::Clock; +use crate::gpio::{AnyPin, GpioPin as Pin}; +use crate::interrupt::typelevel::Interrupt; +use crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate}; +use crate::pac::usart0::cfg::{Clkpol, Datalen, Loop, Paritysel as Parity, Stoplen, Syncen, Syncmst}; +use crate::pac::usart0::ctl::Cc; +use crate::sealed::Sealed; +use crate::{dma, interrupt}; + +/// Driver move trait. +#[allow(private_bounds)] +pub trait Mode: Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl Sealed for Blocking {} +impl Mode for Blocking {} + +/// Async mode. +pub struct Async; +impl Sealed for Async {} +impl Mode for Async {} + +/// Uart driver. +pub struct Uart<'a, M: Mode> { + tx: UartTx<'a, M>, + rx: UartRx<'a, M>, +} + +/// Uart TX driver. +pub struct UartTx<'a, M: Mode> { + info: Info, + tx_dma: Option>, + _phantom: PhantomData<(&'a (), M)>, +} + +/// Uart RX driver. +pub struct UartRx<'a, M: Mode> { + info: Info, + rx_dma: Option>, + _phantom: PhantomData<(&'a (), M)>, +} + +/// UART config +#[derive(Clone, Copy)] +pub struct Config { + /// Baudrate of the Uart + pub baudrate: u32, + /// data length + pub data_bits: Datalen, + /// Parity + pub parity: Parity, + /// Stop bits + pub stop_bits: Stoplen, + /// Polarity of the clock + pub clock_polarity: Clkpol, + /// Sync/ Async operation selection + pub operation: Syncen, + /// Sync master/slave mode selection (only applicable in sync mode) + pub sync_mode_master_select: Syncmst, + /// USART continuous Clock generation enable in synchronous master mode. + pub continuous_clock: Cc, + /// Normal/ loopback mode + pub loopback_mode: Loop, + /// Clock type + pub clock: Clock, +} + +impl Default for Config { + /// Default configuration for single channel sampling. + fn default() -> Self { + Self { + baudrate: 115_200, + data_bits: Datalen::Bit8, + parity: Parity::NoParity, + stop_bits: Stoplen::Bit1, + clock_polarity: Clkpol::FallingEdge, + operation: Syncen::AsynchronousMode, + sync_mode_master_select: Syncmst::Slave, + continuous_clock: Cc::ClockOnCharacter, + loopback_mode: Loop::Normal, + clock: crate::flexcomm::Clock::Sfro, + } + } +} + +/// Uart Errors +#[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, + + /// Uart baud rate cannot be supported with the given clock + UnsupportedBaudrate, + + /// RX FIFO Empty + RxFifoEmpty, + + /// TX FIFO Full + TxFifoFull, + + /// TX Busy + TxBusy, +} +/// shorthand for -> `Result` +pub type Result = core::result::Result; + +impl<'a, M: Mode> UartTx<'a, M> { + fn new_inner(tx_dma: Option>) -> Self { + let uarttx = Self { + info: T::info(), + tx_dma, + _phantom: PhantomData, + }; + uarttx.info.refcnt.fetch_add(1, Ordering::Relaxed); + uarttx + } +} + +impl<'a, M: Mode> Drop for UartTx<'a, M> { + fn drop(&mut self) { + if self.info.refcnt.fetch_sub(1, Ordering::Relaxed) == 1 { + while self.info.regs.stat().read().txidle().bit_is_clear() {} + + self.info.regs.fifointenclr().modify(|_, w| { + w.txerr() + .set_bit() + .rxerr() + .set_bit() + .txlvl() + .set_bit() + .rxlvl() + .set_bit() + }); + + self.info + .regs + .fifocfg() + .modify(|_, w| w.dmatx().clear_bit().dmarx().clear_bit()); + + self.info.regs.cfg().modify(|_, w| w.enable().disabled()); + } + } +} + +impl<'a> UartTx<'a, Blocking> { + /// Create a new UART which can only send data + /// Unidirectional Uart - Tx only + pub fn new_blocking(_inner: Peri<'a, T>, tx: Peri<'a, impl TxPin>, config: Config) -> Result { + tx.as_tx(); + + let _tx = tx.into(); + Uart::::init::(Some(_tx), None, None, None, config)?; + + Ok(Self::new_inner::(None)) + } + + fn write_byte_internal(&mut self, byte: u8) -> Result<()> { + // SAFETY: unsafe only used for .bits() + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(u16::from(byte)) }); + + Ok(()) + } + + fn blocking_write_byte(&mut self, byte: u8) -> Result<()> { + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + + // Prevent the compiler from reordering write_byte_internal() + // before the loop above. + compiler_fence(Ordering::Release); + + self.write_byte_internal(byte) + } + + fn write_byte(&mut self, byte: u8) -> Result<()> { + if self.info.regs.fifostat().read().txnotfull().bit_is_clear() { + Err(Error::TxFifoFull) + } else { + self.write_byte_internal(byte) + } + } + + /// Transmit the provided buffer blocking execution until done. + pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { + for x in buf { + self.blocking_write_byte(*x)?; + } + + Ok(()) + } + + /// Transmit the provided buffer. Non-blocking version, bails out + /// if it would block. + pub fn write(&mut self, buf: &[u8]) -> Result<()> { + for x in buf { + self.write_byte(*x)?; + } + + Ok(()) + } + + /// Flush UART TX blocking execution until done. + pub fn blocking_flush(&mut self) -> Result<()> { + while self.info.regs.stat().read().txidle().bit_is_clear() {} + Ok(()) + } + + /// Flush UART TX. + pub fn flush(&mut self) -> Result<()> { + if self.info.regs.stat().read().txidle().bit_is_clear() { + Err(Error::TxBusy) + } else { + Ok(()) + } + } +} + +impl<'a, M: Mode> UartRx<'a, M> { + fn new_inner(rx_dma: Option>) -> Self { + let uartrx = Self { + info: T::info(), + rx_dma, + _phantom: PhantomData, + }; + uartrx.info.refcnt.fetch_add(1, Ordering::Relaxed); + uartrx + } +} + +impl<'a, M: Mode> Drop for UartRx<'a, M> { + fn drop(&mut self) { + if self.info.refcnt.fetch_sub(1, Ordering::Relaxed) == 1 { + while self.info.regs.stat().read().rxidle().bit_is_clear() {} + + self.info.regs.fifointenclr().modify(|_, w| { + w.txerr() + .set_bit() + .rxerr() + .set_bit() + .txlvl() + .set_bit() + .rxlvl() + .set_bit() + }); + + self.info + .regs + .fifocfg() + .modify(|_, w| w.dmatx().clear_bit().dmarx().clear_bit()); + + self.info.regs.cfg().modify(|_, w| w.enable().disabled()); + } + } +} + +impl<'a> UartRx<'a, Blocking> { + /// Create a new blocking UART which can only receive data + pub fn new_blocking(_inner: Peri<'a, T>, rx: Peri<'a, impl RxPin>, config: Config) -> Result { + rx.as_rx(); + + let _rx = rx.into(); + Uart::::init::(None, Some(_rx), None, None, config)?; + + Ok(Self::new_inner::(None)) + } +} + +impl UartRx<'_, Blocking> { + fn read_byte_internal(&mut self) -> Result { + if self.info.regs.fifostat().read().rxerr().bit_is_set() { + self.info.regs.fifocfg().modify(|_, w| w.emptyrx().set_bit()); + self.info.regs.fifostat().modify(|_, w| w.rxerr().set_bit()); + Err(Error::Read) + } else if self.info.regs.stat().read().parityerrint().bit_is_set() { + self.info.regs.stat().modify(|_, w| w.parityerrint().clear_bit_by_one()); + Err(Error::Parity) + } else if self.info.regs.stat().read().framerrint().bit_is_set() { + self.info.regs.stat().modify(|_, w| w.framerrint().clear_bit_by_one()); + Err(Error::Framing) + } else if self.info.regs.stat().read().rxnoiseint().bit_is_set() { + self.info.regs.stat().modify(|_, w| w.rxnoiseint().clear_bit_by_one()); + Err(Error::Noise) + } else { + let byte = self.info.regs.fiford().read().rxdata().bits() as u8; + Ok(byte) + } + } + + fn read_byte(&mut self) -> Result { + if self.info.regs.fifostat().read().rxnotempty().bit_is_clear() { + Err(Error::RxFifoEmpty) + } else { + self.read_byte_internal() + } + } + + fn blocking_read_byte(&mut self) -> Result { + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + + // Prevent the compiler from reordering read_byte_internal() + // before the loop above. + compiler_fence(Ordering::Acquire); + + self.read_byte_internal() + } + + /// Read from UART RX. Non-blocking version, bails out if it would + /// block. + pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { + for b in buf.iter_mut() { + *b = self.read_byte()?; + } + + Ok(()) + } + + /// Read from UART RX blocking execution until done. + pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { + for b in buf.iter_mut() { + *b = self.blocking_read_byte()?; + } + + Ok(()) + } +} + +impl<'a, M: Mode> Uart<'a, M> { + fn init( + tx: Option>, + rx: Option>, + rts: Option>, + cts: Option>, + config: Config, + ) -> Result<()> { + T::enable(config.clock); + T::into_usart(); + + let regs = T::info().regs; + + if tx.is_some() { + regs.fifocfg().modify(|_, w| w.emptytx().set_bit().enabletx().enabled()); + + // clear FIFO error + regs.fifostat().write(|w| w.txerr().set_bit()); + } + + if rx.is_some() { + regs.fifocfg().modify(|_, w| w.emptyrx().set_bit().enablerx().enabled()); + + // clear FIFO error + regs.fifostat().write(|w| w.rxerr().set_bit()); + } + + if rts.is_some() && cts.is_some() { + regs.cfg().modify(|_, w| w.ctsen().enabled()); + } + + Self::set_baudrate_inner::(config.baudrate, config.clock)?; + Self::set_uart_config::(config); + + Ok(()) + } + + fn get_fc_freq(clock: Clock) -> Result { + match clock { + Clock::Sfro => Ok(16_000_000), + Clock::Ffro => Ok(48_000_000), + // We only support Sfro and Ffro now. + _ => Err(Error::InvalidArgument), + } + } + + fn set_baudrate_inner(baudrate: u32, clock: Clock) -> Result<()> { + // Get source clock frequency according to clock type. + let source_clock_hz = Self::get_fc_freq(clock)?; + + if baudrate == 0 { + return Err(Error::InvalidArgument); + } + + let regs = T::info().regs; + + // If synchronous master mode is enabled, only configure the BRG value. + if regs.cfg().read().syncen().is_synchronous_mode() { + // Master + if regs.cfg().read().syncmst().is_master() { + // Calculate the BRG value + let brgval = (source_clock_hz / baudrate) - 1; + + // SAFETY: unsafe only used for .bits() + regs.brg().write(|w| unsafe { w.brgval().bits(brgval as u16) }); + } + } else { + // Smaller values of OSR can make the sampling position within a + // data bit less accurate and may potentially cause more noise + // errors or incorrect data. + let (_, osr, brg) = (8..16).rev().fold( + (u32::MAX, u32::MAX, u32::MAX), + |(best_diff, best_osr, best_brg), osrval| { + // Compare source_clock_hz agaist with ((osrval + 1) * baudrate) to make sure + // (source_clock_hz / ((osrval + 1) * baudrate)) is not less than 0. + if source_clock_hz < ((osrval + 1) * baudrate) { + (best_diff, best_osr, best_brg) + } else { + let brgval = (source_clock_hz / ((osrval + 1) * baudrate)) - 1; + // We know brgval will not be less than 0 now, it should have already been a valid u32 value, + // then compare it agaist with 65535. + if brgval > 65535 { + (best_diff, best_osr, best_brg) + } else { + // Calculate the baud rate based on the BRG value + let candidate = source_clock_hz / ((osrval + 1) * (brgval + 1)); + + // Calculate the difference between the + // current baud rate and the desired baud rate + let diff = (candidate as i32 - baudrate as i32).unsigned_abs(); + + // Check if the current calculated difference is the best so far + if diff < best_diff { + (diff, osrval, brgval) + } else { + (best_diff, best_osr, best_brg) + } + } + } + }, + ); + + // Value over range + if brg > 65535 { + return Err(Error::UnsupportedBaudrate); + } + + // SAFETY: unsafe only used for .bits() + regs.osr().write(|w| unsafe { w.osrval().bits(osr as u8) }); + + // SAFETY: unsafe only used for .bits() + regs.brg().write(|w| unsafe { w.brgval().bits(brg as u16) }); + } + + Ok(()) + } + + fn set_uart_config(config: Config) { + let regs = T::info().regs; + + regs.cfg().modify(|_, w| w.enable().disabled()); + + regs.cfg().modify(|_, w| { + w.datalen() + .variant(config.data_bits) + .stoplen() + .variant(config.stop_bits) + .paritysel() + .variant(config.parity) + .loop_() + .variant(config.loopback_mode) + .syncen() + .variant(config.operation) + .clkpol() + .variant(config.clock_polarity) + }); + + regs.cfg().modify(|_, w| w.enable().enabled()); + } + + /// Split the Uart into a transmitter and receiver, which is particularly + /// useful when having two tasks correlating to transmitting and receiving. + pub fn split(self) -> (UartTx<'a, M>, UartRx<'a, M>) { + (self.tx, self.rx) + } + + /// Split the Uart into a transmitter and receiver by mutable reference, + /// which is particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split_ref(&mut self) -> (&mut UartTx<'a, M>, &mut UartRx<'a, M>) { + (&mut self.tx, &mut self.rx) + } +} + +impl<'a> Uart<'a, Blocking> { + /// Create a new blocking UART + pub fn new_blocking( + _inner: Peri<'a, T>, + tx: Peri<'a, impl TxPin>, + rx: Peri<'a, impl RxPin>, + config: Config, + ) -> Result { + tx.as_tx(); + rx.as_rx(); + + let tx = tx.into(); + let rx = rx.into(); + + Self::init::(Some(tx), Some(rx), None, None, config)?; + + Ok(Self { + tx: UartTx::new_inner::(None), + rx: UartRx::new_inner::(None), + }) + } + + /// Read from UART RX blocking execution until done. + pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> { + self.rx.blocking_read(buf) + } + + /// Read from UART RX. Non-blocking version, bails out if it would + /// block. + pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { + self.rx.read(buf) + } + + /// Transmit the provided buffer blocking execution until done. + pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> { + self.tx.blocking_write(buf) + } + + /// Transmit the provided buffer. Non-blocking version, bails out + /// if it would block. + pub fn write(&mut self, buf: &[u8]) -> Result<()> { + self.tx.write(buf) + } + + /// Flush UART TX blocking execution until done. + pub fn blocking_flush(&mut self) -> Result<()> { + self.tx.blocking_flush() + } + + /// Flush UART TX. + pub fn flush(&mut self) -> Result<()> { + self.tx.flush() + } +} + +impl<'a> UartTx<'a, Async> { + /// Create a new DMA enabled UART which can only send data + pub fn new_async( + _inner: Peri<'a, T>, + tx: Peri<'a, impl TxPin>, + _irq: impl interrupt::typelevel::Binding> + 'a, + tx_dma: Peri<'a, impl TxDma>, + config: Config, + ) -> Result { + tx.as_tx(); + + let _tx = tx.into(); + Uart::::init::(Some(_tx), None, None, None, config)?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Ok(Self::new_inner::(Some(tx_dma.into()))) + } + + /// Transmit the provided buffer asynchronously. + pub async fn write(&mut self, buf: &[u8]) -> Result<()> { + let regs = self.info.regs; + + // Disable DMA on completion/cancellation + let _dma_guard = OnDrop::new(|| { + regs.fifocfg().modify(|_, w| w.dmatx().disabled()); + }); + + for chunk in buf.chunks(1024) { + regs.fifocfg().modify(|_, w| w.dmatx().enabled()); + + let ch = self.tx_dma.as_mut().unwrap().reborrow(); + let transfer = unsafe { dma::write(ch, chunk, regs.fifowr().as_ptr() as *mut u8) }; + + let res = select( + transfer, + poll_fn(|cx| { + UART_WAKERS[self.info.index].register(cx.waker()); + + self.info.regs.intenset().write(|w| { + w.framerren() + .set_bit() + .parityerren() + .set_bit() + .rxnoiseen() + .set_bit() + .aberren() + .set_bit() + }); + + let stat = self.info.regs.stat().read(); + + self.info.regs.stat().write(|w| { + w.framerrint() + .clear_bit_by_one() + .parityerrint() + .clear_bit_by_one() + .rxnoiseint() + .clear_bit_by_one() + .aberr() + .clear_bit_by_one() + }); + + if stat.framerrint().bit_is_set() { + Poll::Ready(Err(Error::Framing)) + } else if stat.parityerrint().bit_is_set() { + Poll::Ready(Err(Error::Parity)) + } else if stat.rxnoiseint().bit_is_set() { + Poll::Ready(Err(Error::Noise)) + } else if stat.aberr().bit_is_set() { + Poll::Ready(Err(Error::Fail)) + } else { + Poll::Pending + } + }), + ) + .await; + + match res { + Either::First(()) | Either::Second(Ok(())) => (), + Either::Second(e) => return e, + } + } + + Ok(()) + } + + /// Flush UART TX asynchronously. + pub async fn flush(&mut self) -> Result<()> { + self.wait_on( + |me| { + if me.info.regs.stat().read().txidle().bit_is_set() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| w.txidleen().set_bit()); + }, + ) + .await + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + async fn wait_on(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + poll_fn(|cx| { + // Register waker before checking condition, to ensure that wakes/interrupts + // aren't lost between f() and g() + UART_WAKERS[self.info.index].register(cx.waker()); + let r = f(self); + + if r.is_pending() { + g(self); + } + + r + }) + .await + } +} + +impl<'a> UartRx<'a, Async> { + /// Create a new DMA enabled UART which can only receive data + pub fn new_async( + _inner: Peri<'a, T>, + rx: Peri<'a, impl RxPin>, + _irq: impl interrupt::typelevel::Binding> + 'a, + rx_dma: Peri<'a, impl RxDma>, + config: Config, + ) -> Result { + rx.as_rx(); + + let _rx = rx.into(); + Uart::::init::(None, Some(_rx), None, None, config)?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Ok(Self::new_inner::(Some(rx_dma.into()))) + } + + /// Read from UART RX asynchronously. + pub async fn read(&mut self, buf: &mut [u8]) -> Result<()> { + let regs = self.info.regs; + + // Disable DMA on completion/cancellation + let _dma_guard = OnDrop::new(|| { + regs.fifocfg().modify(|_, w| w.dmarx().disabled()); + }); + + for chunk in buf.chunks_mut(1024) { + regs.fifocfg().modify(|_, w| w.dmarx().enabled()); + + let ch = self.rx_dma.as_mut().unwrap().reborrow(); + let transfer = unsafe { dma::read(ch, regs.fiford().as_ptr() as *const u8, chunk) }; + + let res = select( + transfer, + poll_fn(|cx| { + UART_WAKERS[self.info.index].register(cx.waker()); + + self.info.regs.intenset().write(|w| { + w.framerren() + .set_bit() + .parityerren() + .set_bit() + .rxnoiseen() + .set_bit() + .aberren() + .set_bit() + }); + + let stat = self.info.regs.stat().read(); + + self.info.regs.stat().write(|w| { + w.framerrint() + .clear_bit_by_one() + .parityerrint() + .clear_bit_by_one() + .rxnoiseint() + .clear_bit_by_one() + .aberr() + .clear_bit_by_one() + }); + + if stat.framerrint().bit_is_set() { + Poll::Ready(Err(Error::Framing)) + } else if stat.parityerrint().bit_is_set() { + Poll::Ready(Err(Error::Parity)) + } else if stat.rxnoiseint().bit_is_set() { + Poll::Ready(Err(Error::Noise)) + } else if stat.aberr().bit_is_set() { + Poll::Ready(Err(Error::Fail)) + } else { + Poll::Pending + } + }), + ) + .await; + + match res { + Either::First(()) | Either::Second(Ok(())) => (), + Either::Second(e) => return e, + } + } + + Ok(()) + } +} + +impl<'a> Uart<'a, Async> { + /// Create a new DMA enabled UART + pub fn new_async( + _inner: Peri<'a, T>, + tx: Peri<'a, impl TxPin>, + rx: Peri<'a, impl RxPin>, + _irq: impl interrupt::typelevel::Binding> + 'a, + tx_dma: Peri<'a, impl TxDma>, + rx_dma: Peri<'a, impl RxDma>, + config: Config, + ) -> Result { + tx.as_tx(); + rx.as_rx(); + + let tx = tx.into(); + let rx = rx.into(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::init::(Some(tx), Some(rx), None, None, config)?; + + Ok(Self { + tx: UartTx::new_inner::(Some(tx_dma.into())), + rx: UartRx::new_inner::(Some(rx_dma.into())), + }) + } + + /// Create a new DMA enabled UART with hardware flow control (RTS/CTS) + pub fn new_with_rtscts( + _inner: Peri<'a, T>, + tx: Peri<'a, impl TxPin>, + rx: Peri<'a, impl RxPin>, + rts: Peri<'a, impl RtsPin>, + cts: Peri<'a, impl CtsPin>, + _irq: impl interrupt::typelevel::Binding> + 'a, + tx_dma: Peri<'a, impl TxDma>, + rx_dma: Peri<'a, impl RxDma>, + config: Config, + ) -> Result { + tx.as_tx(); + rx.as_rx(); + rts.as_rts(); + cts.as_cts(); + + let tx = tx.into(); + let rx = rx.into(); + let rts = rts.into(); + let cts = cts.into(); + + Self::init::(Some(tx), Some(rx), Some(rts), Some(cts), config)?; + + Ok(Self { + tx: UartTx::new_inner::(Some(tx_dma.into())), + rx: UartRx::new_inner::(Some(rx_dma.into())), + }) + } + + /// Read from UART RX. + pub async fn read(&mut self, buf: &mut [u8]) -> Result<()> { + self.rx.read(buf).await + } + + /// Transmit the provided buffer. + pub async fn write(&mut self, buf: &[u8]) -> Result<()> { + self.tx.write(buf).await + } + + /// Flush UART TX. + pub async fn flush(&mut self) -> Result<()> { + self.tx.flush().await + } +} + +impl embedded_hal_02::serial::Read for UartRx<'_, 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 UartTx<'_, 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 UartTx<'_, 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 Uart<'_, 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 Uart<'_, 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 Uart<'_, 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_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 UartRx<'_, Blocking> { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for UartTx<'_, Blocking> { + type Error = Error; +} + +impl embedded_hal_nb::serial::ErrorType for Uart<'_, Blocking> { + type Error = Error; +} + +impl embedded_hal_nb::serial::Read for UartRx<'_, 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 UartTx<'_, 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 embedded_hal_nb::serial::Read for Uart<'_, Blocking> { + fn read(&mut self) -> core::result::Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } +} + +impl embedded_hal_nb::serial::Write for Uart<'_, Blocking> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } +} + +struct Info { + regs: &'static crate::pac::usart0::RegisterBlock, + index: usize, + refcnt: AtomicU8, +} + +trait SealedInstance { + fn info() -> Info; + fn index() -> usize; +} + +/// UART interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +const UART_COUNT: usize = 8; +static UART_WAKERS: [AtomicWaker; UART_COUNT] = [const { AtomicWaker::new() }; UART_COUNT]; + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let waker = &UART_WAKERS[T::index()]; + let regs = T::info().regs; + let stat = regs.intstat().read(); + + if stat.txidle().bit_is_set() + || stat.framerrint().bit_is_set() + || stat.parityerrint().bit_is_set() + || stat.rxnoiseint().bit_is_set() + || stat.aberrint().bit_is_set() + { + regs.intenclr().write(|w| { + w.txidleclr() + .set_bit() + .framerrclr() + .set_bit() + .parityerrclr() + .set_bit() + .rxnoiseclr() + .set_bit() + .aberrclr() + .set_bit() + }); + } + + waker.wake(); + } +} + +/// UART instance trait. +#[allow(private_bounds)] +pub trait Instance: crate::flexcomm::IntoUsart + SealedInstance + PeripheralType + 'static + Send { + /// Interrupt for this UART instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_instance { + ($($n:expr),*) => { + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + fn info() -> Info { + Info { + regs: unsafe { &*crate::pac::[]::ptr() }, + index: $n, + refcnt: AtomicU8::new(0), + } + } + + #[inline] + fn index() -> usize { + $n + } + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + } + } + )* + }; +} + +impl_instance!(0, 1, 2, 3, 4, 5, 6, 7); + +impl Sealed for T {} + +/// io configuration trait for Uart Tx configuration +pub trait TxPin: Pin + Sealed + PeripheralType { + /// convert the pin to appropriate function for Uart Tx usage + fn as_tx(&self); +} + +/// io configuration trait for Uart Rx configuration +pub trait RxPin: Pin + Sealed + PeripheralType { + /// convert the pin to appropriate function for Uart Rx usage + fn as_rx(&self); +} + +/// io configuration trait for Uart Cts +pub trait CtsPin: Pin + Sealed + PeripheralType { + /// convert the pin to appropriate function for Uart Cts usage + fn as_cts(&self); +} + +/// io configuration trait for Uart Rts +pub trait RtsPin: Pin + Sealed + PeripheralType { + /// convert the pin to appropriate function for Uart Rts usage + fn as_rts(&self); +} + +macro_rules! impl_pin_trait { + ($fcn:ident, $mode:ident, $($pin:ident, $fn:ident),*) => { + paste! { + $( + impl [<$mode:camel Pin>] for crate::peripherals::$pin { + fn [](&self) { + // UM11147 table 507 pg 495 + self.set_function(crate::iopctl::Function::$fn) + .set_pull(Pull::None) + .enable_input_buffer() + .set_slew_rate(SlewRate::Standard) + .set_drive_strength(DriveStrength::Normal) + .disable_analog_multiplex() + .set_drive_mode(DriveMode::PushPull) + .set_input_inverter(Inverter::Disabled); + } + } + )* + } + }; +} + +// FLEXCOMM0 +impl_pin_trait!(FLEXCOMM0, tx, PIO0_1, F1, PIO3_1, F5); +impl_pin_trait!(FLEXCOMM0, rx, PIO0_2, F1, PIO3_2, F5); +impl_pin_trait!(FLEXCOMM0, cts, PIO0_3, F1, PIO3_3, F5); +impl_pin_trait!(FLEXCOMM0, rts, PIO0_4, F1, PIO3_4, F5); + +// FLEXCOMM1 +impl_pin_trait!(FLEXCOMM1, tx, PIO0_8, F1, PIO7_26, F1); +impl_pin_trait!(FLEXCOMM1, rx, PIO0_9, F1, PIO7_27, F1); +impl_pin_trait!(FLEXCOMM1, cts, PIO0_10, F1, PIO7_28, F1); +impl_pin_trait!(FLEXCOMM1, rts, PIO0_11, F1, PIO7_29, F1); + +// FLEXCOMM2 +impl_pin_trait!(FLEXCOMM2, tx, PIO0_15, F1, PIO7_30, F5); +impl_pin_trait!(FLEXCOMM2, rx, PIO0_16, F1, PIO7_31, F5); +impl_pin_trait!(FLEXCOMM2, cts, PIO0_17, F1, PIO4_8, F5); +impl_pin_trait!(FLEXCOMM2, rts, PIO0_18, F1); + +// FLEXCOMM3 +impl_pin_trait!(FLEXCOMM3, tx, PIO0_22, F1); +impl_pin_trait!(FLEXCOMM3, rx, PIO0_23, F1); +impl_pin_trait!(FLEXCOMM3, cts, PIO0_24, F1); +impl_pin_trait!(FLEXCOMM3, rts, PIO0_25, F1); + +// FLEXCOMM4 +impl_pin_trait!(FLEXCOMM4, tx, PIO0_29, F1); +impl_pin_trait!(FLEXCOMM4, rx, PIO0_30, F1); +impl_pin_trait!(FLEXCOMM4, cts, PIO0_31, F1); +impl_pin_trait!(FLEXCOMM4, rts, PIO1_0, F1); + +// FLEXCOMM5 +impl_pin_trait!(FLEXCOMM5, tx, PIO1_4, F1, PIO3_16, F5); +impl_pin_trait!(FLEXCOMM5, rx, PIO1_5, F1, PIO3_17, F5); +impl_pin_trait!(FLEXCOMM5, cts, PIO1_6, F1, PIO3_18, F5); +impl_pin_trait!(FLEXCOMM5, rts, PIO1_7, F1, PIO3_23, F5); + +// FLEXCOMM6 +impl_pin_trait!(FLEXCOMM6, tx, PIO3_26, F1); +impl_pin_trait!(FLEXCOMM6, rx, PIO3_27, F1); +impl_pin_trait!(FLEXCOMM6, cts, PIO3_28, F1); +impl_pin_trait!(FLEXCOMM6, rts, PIO3_29, F1); + +// FLEXCOMM7 +impl_pin_trait!(FLEXCOMM7, tx, PIO4_1, F1); +impl_pin_trait!(FLEXCOMM7, rx, PIO4_2, F1); +impl_pin_trait!(FLEXCOMM7, cts, PIO4_3, F1); +impl_pin_trait!(FLEXCOMM7, rts, PIO4_4, F1); + +/// UART Tx DMA trait. +#[allow(private_bounds)] +pub trait TxDma: crate::dma::Channel {} + +/// UART Rx DMA trait. +#[allow(private_bounds)] +pub trait RxDma: crate::dma::Channel {} + +macro_rules! impl_dma { + ($fcn:ident, $mode:ident, $dma:ident) => { + paste! { + impl [<$mode Dma>] for crate::peripherals::$dma {} + } + }; +} + +impl_dma!(FLEXCOMM0, Rx, DMA0_CH0); +impl_dma!(FLEXCOMM0, Tx, DMA0_CH1); + +impl_dma!(FLEXCOMM1, Rx, DMA0_CH2); +impl_dma!(FLEXCOMM1, Tx, DMA0_CH3); + +impl_dma!(FLEXCOMM2, Rx, DMA0_CH4); +impl_dma!(FLEXCOMM2, Tx, DMA0_CH5); + +impl_dma!(FLEXCOMM3, Rx, DMA0_CH6); +impl_dma!(FLEXCOMM3, Tx, DMA0_CH7); + +impl_dma!(FLEXCOMM4, Rx, DMA0_CH8); +impl_dma!(FLEXCOMM4, Tx, DMA0_CH9); + +impl_dma!(FLEXCOMM5, Rx, DMA0_CH10); +impl_dma!(FLEXCOMM5, Tx, DMA0_CH11); + +impl_dma!(FLEXCOMM6, Rx, DMA0_CH12); +impl_dma!(FLEXCOMM6, Tx, DMA0_CH13); + +impl_dma!(FLEXCOMM7, Rx, DMA0_CH14); +impl_dma!(FLEXCOMM7, Tx, DMA0_CH15); diff --git a/embassy-imxrt/src/gpio.rs b/embassy-imxrt/src/gpio.rs index 6883c4ee0..e62fde8b1 100644 --- a/embassy-imxrt/src/gpio.rs +++ b/embassy-imxrt/src/gpio.rs @@ -13,7 +13,7 @@ use crate::clocks::enable_and_reset; use crate::iopctl::IopctlPin; pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate}; use crate::sealed::Sealed; -use crate::{interrupt, peripherals, Peri, PeripheralType}; +use crate::{interrupt, peripherals, BitIter, Peri, PeripheralType}; // This should be unique per IMXRT package const PORT_COUNT: usize = 8; @@ -63,24 +63,6 @@ fn GPIO_INTA() { irq_handler(&GPIO_WAKERS); } -#[cfg(feature = "rt")] -struct BitIter(u32); - -#[cfg(feature = "rt")] -impl Iterator for BitIter { - type Item = u32; - - fn next(&mut self) -> Option { - match self.0.trailing_zeros() { - 32 => None, - b => { - self.0 &= !(1 << b); - Some(b) - } - } - } -} - #[cfg(feature = "rt")] fn irq_handler(port_wakers: &[Option<&PortWaker>]) { let reg = unsafe { crate::pac::Gpio::steal() }; diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index b1183d8fc..ad0d9e21c 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -19,6 +19,8 @@ pub(crate) mod fmt; pub mod clocks; pub mod crc; +pub mod dma; +pub mod flexcomm; pub mod gpio; pub mod iopctl; pub mod rng; @@ -129,14 +131,16 @@ pub fn init(config: config::Config) -> Peripherals { // before doing anything important. let peripherals = Peripherals::take(); + #[cfg(feature = "_time-driver")] + time_driver::init(config.time_interrupt_priority); + unsafe { if let Err(e) = clocks::init(config.clocks) { error!("unable to initialize Clocks for reason: {:?}", e); // Panic here? } + dma::init(); } - #[cfg(feature = "_time-driver")] - time_driver::init(config.time_interrupt_priority); gpio::init(); peripherals @@ -145,3 +149,21 @@ pub fn init(config: config::Config) -> Peripherals { pub(crate) mod sealed { pub trait Sealed {} } + +#[cfg(feature = "rt")] +struct BitIter(u32); + +#[cfg(feature = "rt")] +impl Iterator for BitIter { + type Item = u32; + + fn next(&mut self) -> Option { + match self.0.trailing_zeros() { + 32 => None, + b => { + self.0 &= !(1 << b); + Some(b) + } + } + } +} diff --git a/examples/mimxrt6/src/bin/uart-async.rs b/examples/mimxrt6/src/bin/uart-async.rs new file mode 100644 index 000000000..58e31f379 --- /dev/null +++ b/examples/mimxrt6/src/bin/uart-async.rs @@ -0,0 +1,87 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_imxrt::flexcomm::uart::{self, Async, Uart}; +use embassy_imxrt::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FLEXCOMM2 => uart::InterruptHandler; + FLEXCOMM4 => uart::InterruptHandler; +}); + +const BUFLEN: usize = 16; + +#[embassy_executor::task] +async fn usart4_task(mut uart: Uart<'static, Async>) { + info!("RX Task"); + + loop { + let mut rx_buf = [0; BUFLEN]; + uart.read(&mut rx_buf).await.unwrap(); + info!("usart4: rx_buf {:02x}", rx_buf); + + Timer::after_millis(10).await; + + let tx_buf = [0xaa; BUFLEN]; + uart.write(&tx_buf).await.unwrap(); + info!("usart4: tx_buf {:02x}", tx_buf); + } +} + +#[embassy_executor::task] +async fn usart2_task(mut uart: Uart<'static, Async>) { + info!("TX Task"); + + loop { + let tx_buf = [0x55; BUFLEN]; + uart.write(&tx_buf).await.unwrap(); + info!("usart2: tx_buf {:02x}", tx_buf); + + Timer::after_millis(10).await; + + let mut rx_buf = [0x00; BUFLEN]; + uart.read(&mut rx_buf).await.unwrap(); + info!("usart2: rx_buf {:02x}", rx_buf); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("UART test start"); + + let usart4 = Uart::new_with_rtscts( + p.FLEXCOMM4, + p.PIO0_29, + p.PIO0_30, + p.PIO1_0, + p.PIO0_31, + Irqs, + p.DMA0_CH9, + p.DMA0_CH8, + Default::default(), + ) + .unwrap(); + spawner.must_spawn(usart4_task(usart4)); + + let usart2 = Uart::new_with_rtscts( + p.FLEXCOMM2, + p.PIO0_15, + p.PIO0_16, + p.PIO0_18, + p.PIO0_17, + Irqs, + p.DMA0_CH5, + p.DMA0_CH4, + Default::default(), + ) + .unwrap(); + spawner.must_spawn(usart2_task(usart2)); +} diff --git a/examples/mimxrt6/src/bin/uart.rs b/examples/mimxrt6/src/bin/uart.rs new file mode 100644 index 000000000..d6a75f85d --- /dev/null +++ b/examples/mimxrt6/src/bin/uart.rs @@ -0,0 +1,55 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_imxrt::flexcomm::uart::{Blocking, Uart, UartRx, UartTx}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn usart4_task(mut uart: UartRx<'static, Blocking>) { + info!("RX Task"); + + loop { + let mut buf = [0; 8]; + + Timer::after_millis(10).await; + + uart.blocking_read(&mut buf).unwrap(); + + let s = core::str::from_utf8(&buf).unwrap(); + + info!("Received '{}'", s); + } +} + +#[embassy_executor::task] +async fn usart2_task(mut uart: UartTx<'static, Blocking>) { + info!("TX Task"); + + loop { + let buf = "Testing\0".as_bytes(); + + uart.blocking_write(buf).unwrap(); + + Timer::after_millis(10).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_imxrt::init(Default::default()); + + info!("UART test start"); + + let usart4 = Uart::new_blocking(p.FLEXCOMM4, p.PIO0_29, p.PIO0_30, Default::default()).unwrap(); + + let (_, usart4) = usart4.split(); + spawner.must_spawn(usart4_task(usart4)); + + let usart2 = UartTx::new_blocking(p.FLEXCOMM2, p.PIO0_15, Default::default()).unwrap(); + spawner.must_spawn(usart2_task(usart2)); +}