From fa54dd5849a083b286b2a3f1928428c8704d3d70 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 5 Dec 2025 15:22:38 +0100 Subject: Create separate ring buffered RX receiver to encapsulate unsafe --- embassy-mcxa/src/dma.rs | 15 ++++++++++- embassy-mcxa/src/lpuart/mod.rs | 40 +++++++++++++++++++++++++---- examples/mcxa/Cargo.toml | 1 + examples/mcxa/src/bin/lpuart_dma.rs | 2 +- examples/mcxa/src/bin/lpuart_ring_buffer.rs | 15 +++-------- 5 files changed, 55 insertions(+), 18 deletions(-) diff --git a/embassy-mcxa/src/dma.rs b/embassy-mcxa/src/dma.rs index 7d1588516..d563c2e29 100644 --- a/embassy-mcxa/src/dma.rs +++ b/embassy-mcxa/src/dma.rs @@ -2170,7 +2170,14 @@ impl<'a, W: Word> RingBuffer<'a, W> { /// Stop the DMA transfer and consume the ring buffer. /// /// Returns any remaining unread data count. - pub fn stop(self) -> usize { + pub fn stop(mut self) -> usize { + let res = self.teardown(); + drop(self); + res + } + + /// Stop the DMA transfer. Intended to be called by `stop()` or `Drop`. + fn teardown(&mut self) -> usize { let available = self.available(); // Disable the channel @@ -2187,6 +2194,12 @@ impl<'a, W: Word> RingBuffer<'a, W> { } } +impl<'a, W: Word> Drop for RingBuffer<'a, W> { + fn drop(&mut self) { + self.teardown(); + } +} + impl DmaChannel { /// Set up a circular DMA transfer for continuous peripheral-to-memory reception. /// diff --git a/embassy-mcxa/src/lpuart/mod.rs b/embassy-mcxa/src/lpuart/mod.rs index 39fecb413..a176c7032 100644 --- a/embassy-mcxa/src/lpuart/mod.rs +++ b/embassy-mcxa/src/lpuart/mod.rs @@ -1,16 +1,17 @@ +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::clocks::{enable_and_reset, ClockError, Gate, PoweredClock}; use crate::gpio::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::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits}; use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; use crate::pac::lpuart0::stat::Msbf as MsbFirst; -use crate::{AnyPin, interrupt, pac}; +use crate::{interrupt, pac, AnyPin}; pub mod buffered; @@ -720,6 +721,12 @@ pub struct LpuartDma<'a, T: Instance, TxC: DmaChannelTrait, RxC: DmaChannelTrait 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 // ============================================================================ @@ -1402,6 +1409,14 @@ impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> { 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 @@ -1441,7 +1456,7 @@ impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> { /// - 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. - pub unsafe fn setup_ring_buffer<'b>(&self, buf: &'b mut [u8]) -> RingBuffer<'b, u8> { + 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; @@ -1460,11 +1475,26 @@ impl<'a, T: Instance, C: DmaChannelTrait> LpuartRxDma<'a, T, C> { /// 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. - pub unsafe fn enable_dma_request(&self) { + 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( diff --git a/examples/mcxa/Cargo.toml b/examples/mcxa/Cargo.toml index 4d0459f41..1ac8eac53 100644 --- a/examples/mcxa/Cargo.toml +++ b/examples/mcxa/Cargo.toml @@ -20,6 +20,7 @@ embassy-time-driver = "0.2.1" embedded-io-async = "0.6.1" heapless = "0.9.2" panic-probe = { version = "1.0", features = ["print-defmt"] } +static_cell = "2.1.1" tmp108 = "0.4.0" [profile.release] diff --git a/examples/mcxa/src/bin/lpuart_dma.rs b/examples/mcxa/src/bin/lpuart_dma.rs index 1fc6595e6..34d343452 100644 --- a/examples/mcxa/src/bin/lpuart_dma.rs +++ b/examples/mcxa/src/bin/lpuart_dma.rs @@ -14,7 +14,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; use embassy_mcxa::lpuart::{Config, LpuartDma}; -use embassy_mcxa::{bind_interrupts, pac}; +use embassy_mcxa::bind_interrupts; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel interrupts using Embassy-style macro diff --git a/examples/mcxa/src/bin/lpuart_ring_buffer.rs b/examples/mcxa/src/bin/lpuart_ring_buffer.rs index 1d1a51970..b707e20f8 100644 --- a/examples/mcxa/src/bin/lpuart_ring_buffer.rs +++ b/examples/mcxa/src/bin/lpuart_ring_buffer.rs @@ -23,6 +23,7 @@ use embassy_mcxa::bind_interrupts; use embassy_mcxa::clocks::config::Div8; use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; use embassy_mcxa::lpuart::{Config, LpuartDma, LpuartTxDma}; +use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel interrupts @@ -32,7 +33,7 @@ bind_interrupts!(struct Irqs { }); // Ring buffer for RX - power of 2 is ideal for modulo efficiency -static mut RX_RING_BUFFER: [u8; 64] = [0; 64]; +static RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]); /// Helper to write a byte as hex to UART fn write_hex( @@ -75,17 +76,9 @@ async fn main(_spawner: Spawner) { tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n") .unwrap(); + let buf = RX_RING_BUFFER.take(); // Set up the ring buffer with circular DMA - // The HAL handles: DMA request source, RDMAE enable, circular transfer config, NVIC enable - let ring_buf = unsafe { - let buf = &mut *core::ptr::addr_of_mut!(RX_RING_BUFFER); - rx.setup_ring_buffer(buf) - }; - - // Enable DMA requests to start continuous reception - unsafe { - rx.enable_dma_request(); - } + let mut ring_buf = rx.into_ring_dma_rx(buf); tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n") .unwrap(); -- cgit