From 5a6394666e23555e4f329f7b1bd470d0728434a1 Mon Sep 17 00:00:00 2001 From: Bogdan Petru Chircu Mare Date: Thu, 27 Nov 2025 21:39:31 -0800 Subject: Updated per PR #52 feedback --- examples/src/bin/dma_channel_link.rs | 93 +++++++------------------- examples/src/bin/dma_interleave_transfer.rs | 31 ++------- examples/src/bin/dma_mem_to_mem.rs | 19 +----- examples/src/bin/dma_memset.rs | 31 ++------- examples/src/bin/dma_ping_pong_transfer.rs | 60 +++++++---------- examples/src/bin/dma_scatter_gather.rs | 54 ++++++--------- examples/src/bin/dma_scatter_gather_builder.rs | 18 +---- examples/src/bin/dma_wrap_transfer.rs | 31 ++------- examples/src/bin/lpuart_dma.rs | 78 +++++---------------- examples/src/bin/lpuart_ring_buffer.rs | 23 ++----- 10 files changed, 114 insertions(+), 324 deletions(-) (limited to 'examples/src') diff --git a/examples/src/bin/dma_channel_link.rs b/examples/src/bin/dma_channel_link.rs index d585f8e3a..d541dc7f4 100644 --- a/examples/src/bin/dma_channel_link.rs +++ b/examples/src/bin/dma_channel_link.rs @@ -8,20 +8,18 @@ //! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) //! //! # Embassy-style features demonstrated: -//! - `dma::edma_tcd()` accessor for simplified register access //! - `DmaChannel::new()` for channel creation //! - `DmaChannel::is_done()` and `clear_done()` helper methods //! - Channel linking with `set_minor_link()` and `set_major_link()` +//! - Standard `DmaCh*InterruptHandler` with `bind_interrupts!` macro #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{edma_tcd, DmaChannel}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -32,49 +30,12 @@ static mut DEST_BUFFER0: [u32; 4] = [0; 4]; static mut DEST_BUFFER1: [u32; 4] = [0; 4]; static mut DEST_BUFFER2: [u32; 4] = [0; 4]; -static DMA_CH2_DONE: AtomicBool = AtomicBool::new(false); - -// Custom DMA interrupt handlers for channel linking -// CH0 and CH1 just clear flags, CH2 signals completion - -pub struct Ch0Handler; -impl embassy_mcxa::interrupt::typelevel::Handler for Ch0Handler { - unsafe fn on_interrupt() { - let edma = edma_tcd(); - edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one()); - if edma.tcd(0).ch_csr().read().done().bit_is_set() { - edma.tcd(0).ch_csr().write(|w| w.done().clear_bit_by_one()); - } - } -} - -pub struct Ch1Handler; -impl embassy_mcxa::interrupt::typelevel::Handler for Ch1Handler { - unsafe fn on_interrupt() { - let edma = edma_tcd(); - edma.tcd(1).ch_int().write(|w| w.int().clear_bit_by_one()); - if edma.tcd(1).ch_csr().read().done().bit_is_set() { - edma.tcd(1).ch_csr().write(|w| w.done().clear_bit_by_one()); - } - } -} - -pub struct Ch2Handler; -impl embassy_mcxa::interrupt::typelevel::Handler for Ch2Handler { - unsafe fn on_interrupt() { - let edma = edma_tcd(); - edma.tcd(2).ch_int().write(|w| w.int().clear_bit_by_one()); - if edma.tcd(2).ch_csr().read().done().bit_is_set() { - edma.tcd(2).ch_csr().write(|w| w.done().clear_bit_by_one()); - } - DMA_CH2_DONE.store(true, Ordering::Release); - } -} - +// Bind DMA channel interrupts using Embassy-style macro +// The standard handlers call on_interrupt() which wakes wakers and clears flags bind_interrupts!(struct Irqs { - DMA_CH0 => Ch0Handler; - DMA_CH1 => Ch1Handler; - DMA_CH2 => Ch2Handler; + DMA_CH0 => DmaCh0InterruptHandler; + DMA_CH1 => DmaCh1InterruptHandler; + DMA_CH2 => DmaCh2InterruptHandler; }); /// Helper to write a u32 as decimal ASCII to UART @@ -125,21 +86,12 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA channel link example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } + // Ensure DMA is initialized (clock/reset/init handled automatically by HAL) + dma::ensure_init(); let pac_periphs = unsafe { pac::Peripherals::steal() }; - - unsafe { - dma::init(&pac_periphs); - } - - // Use edma_tcd() accessor instead of passing register block around - let edma = edma_tcd(); let dma0 = &pac_periphs.dma0; + let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; // Clear any residual state for i in 0..3 { @@ -206,7 +158,7 @@ async fn main(_spawner: Spawner) { let ch0 = DmaChannel::new(p.DMA_CH0); let ch1 = DmaChannel::new(p.DMA_CH1); - let _ch2 = DmaChannel::new(p.DMA_CH2); + let ch2 = DmaChannel::new(p.DMA_CH2); // Configure channels using direct TCD access (advanced feature demo) // This example demonstrates channel linking which requires direct TCD manipulation @@ -291,8 +243,8 @@ async fn main(_spawner: Spawner) { 2, // count (major loop = 2 iterations) false, // no interrupt ); - ch0.set_minor_link(edma, 1); // Link to CH1 after each minor loop - ch0.set_major_link(edma, 2); // Link to CH2 after major loop + ch0.set_minor_link(1); // Link to CH1 after each minor loop + ch0.set_major_link(2); // Link to CH2 after major loop // Channel 1: Transfer 16 bytes (triggered by CH0 minor link) configure_tcd( @@ -322,32 +274,35 @@ async fn main(_spawner: Spawner) { tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n").unwrap(); // Trigger first minor loop of CH0 - unsafe { ch0.trigger_start(edma); } + unsafe { ch0.trigger_start(); } // Wait for CH1 to complete (triggered by CH0 minor link) - while !ch1.is_done(edma) { + while !ch1.is_done() { cortex_m::asm::nop(); } - unsafe { ch1.clear_done(edma); } + unsafe { ch1.clear_done(); } tx.blocking_write(b"CH1 done (via minor link).\r\n").unwrap(); tx.blocking_write(b"Triggering Channel 0 (2nd minor loop)...\r\n").unwrap(); // Trigger second minor loop of CH0 - unsafe { ch0.trigger_start(edma); } + unsafe { ch0.trigger_start(); } // Wait for CH0 major loop to complete - while !ch0.is_done(edma) { + while !ch0.is_done() { cortex_m::asm::nop(); } - unsafe { ch0.clear_done(edma); } + unsafe { ch0.clear_done(); } tx.blocking_write(b"CH0 major loop done.\r\n").unwrap(); // Wait for CH2 to complete (triggered by CH0 major link) - while !DMA_CH2_DONE.load(Ordering::Acquire) { + // Using is_done() instead of AtomicBool - the standard interrupt handler + // clears the interrupt flag and wakes wakers, but DONE bit remains set + while !ch2.is_done() { cortex_m::asm::nop(); } + unsafe { ch2.clear_done(); } tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap(); diff --git a/examples/src/bin/dma_interleave_transfer.rs b/examples/src/bin/dma_interleave_transfer.rs index 710f18de3..949ea0605 100644 --- a/examples/src/bin/dma_interleave_transfer.rs +++ b/examples/src/bin/dma_interleave_transfer.rs @@ -4,7 +4,6 @@ //! to interleave data during transfer. //! //! # Embassy-style features demonstrated: -//! - `dma::edma_tcd()` accessor for simplified register access //! - `TransferOptions::default()` for configuration (used internally) //! - DMA channel with `DmaChannel::new()` @@ -13,9 +12,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh0InterruptHandler}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -80,19 +78,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA interleave transfer example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - unsafe { - dma::init(&pac_periphs); - } - - // Enable DMA interrupt + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); } @@ -130,15 +116,12 @@ async fn main(_spawner: Spawner) { // Create DMA channel using Embassy-style API let dma_ch0 = DmaChannel::new(p.DMA_CH0); - // Use edma_tcd() accessor instead of passing register block around - let edma = edma_tcd(); - // Configure interleaved transfer using direct TCD access: // - src_offset = 4: advance source by 4 bytes after each read // - dst_offset = 8: advance dest by 8 bytes after each write // This spreads source data across every other word in destination unsafe { - let t = edma.tcd(0); + let t = dma_ch0.tcd(); // Reset channel state t.ch_csr().write(|w| { @@ -182,14 +165,14 @@ async fn main(_spawner: Spawner) { cortex_m::asm::dsb(); tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for completion using channel helper method - while !dma_ch0.is_done(edma) { + while !dma_ch0.is_done() { cortex_m::asm::nop(); } - unsafe { dma_ch0.clear_done(edma); } + unsafe { dma_ch0.clear_done(); } tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") .unwrap(); diff --git a/examples/src/bin/dma_mem_to_mem.rs b/examples/src/bin/dma_mem_to_mem.rs index e193e8c6a..01e5edb1e 100644 --- a/examples/src/bin/dma_mem_to_mem.rs +++ b/examples/src/bin/dma_mem_to_mem.rs @@ -15,9 +15,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, TransferOptions}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -84,21 +83,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA memory-to-memory example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - // Get PAC peripherals for DMA init - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - // Initialize DMA - unsafe { - dma::init(&pac_periphs); - } - - // Enable DMA interrupt + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); } diff --git a/examples/src/bin/dma_memset.rs b/examples/src/bin/dma_memset.rs index b76ba988d..8a1636e57 100644 --- a/examples/src/bin/dma_memset.rs +++ b/examples/src/bin/dma_memset.rs @@ -4,7 +4,6 @@ //! The source address stays fixed while the destination increments. //! //! # Embassy-style features demonstrated: -//! - `dma::edma_tcd()` accessor for simplified register access //! - `DmaChannel::is_done()` and `clear_done()` helper methods //! - No need to pass register block around @@ -13,9 +12,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh0InterruptHandler}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -79,19 +77,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA memset example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - unsafe { - dma::init(&pac_periphs); - } - - // Enable DMA interrupt + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); } @@ -139,14 +125,11 @@ async fn main(_spawner: Spawner) { // Create DMA channel using Embassy-style API let dma_ch0 = DmaChannel::new(p.DMA_CH0); - // Use edma_tcd() accessor instead of passing register block around - let edma = edma_tcd(); - // Configure memset transfer using direct TCD access: // Source stays fixed (soff = 0, reads same pattern repeatedly) // Destination increments (doff = 4) unsafe { - let t = edma.tcd(0); + let t = dma_ch0.tcd(); // Reset channel state t.ch_csr().write(|w| { @@ -190,14 +173,14 @@ async fn main(_spawner: Spawner) { cortex_m::asm::dsb(); tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for completion using channel helper method - while !dma_ch0.is_done(edma) { + while !dma_ch0.is_done() { cortex_m::asm::nop(); } - unsafe { dma_ch0.clear_done(edma); } + unsafe { dma_ch0.clear_done(); } tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n") .unwrap(); diff --git a/examples/src/bin/dma_ping_pong_transfer.rs b/examples/src/bin/dma_ping_pong_transfer.rs index 13ad9782d..d765ea575 100644 --- a/examples/src/bin/dma_ping_pong_transfer.rs +++ b/examples/src/bin/dma_ping_pong_transfer.rs @@ -4,7 +4,9 @@ //! //! ## Approach 1: Scatter/Gather with linked TCDs (manual) //! - Two TCDs link to each other for alternating transfers -//! - Uses custom interrupt handler with AtomicBool flag +//! - Uses custom handler that delegates to on_interrupt() then signals completion +//! - Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, +//! so we need an AtomicBool to track completion //! //! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!) //! - Single continuous transfer over entire buffer @@ -12,9 +14,10 @@ //! - Application can process first half while second half is being filled //! //! # Embassy-style features demonstrated: -//! - `dma::edma_tcd()` accessor for simplified register access //! - `DmaChannel::new()` for channel creation //! - Scatter/gather with linked TCDs +//! - Custom handler that delegates to HAL's `on_interrupt()` (best practice) +//! - Standard `DmaCh1InterruptHandler` with `bind_interrupts!` macro //! - NEW: `wait_half()` for half-transfer interrupt handling #![no_std] @@ -23,9 +26,8 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh1InterruptHandler, Tcd, TransferOptions}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::dma::{self, DmaChannel, DmaCh1InterruptHandler, Tcd, TransferOptions}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -56,30 +58,31 @@ static mut TCD_POOL: TcdPool = TcdPool([Tcd { biter: 0, }; 2]); +// AtomicBool to track scatter/gather completion +// Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, +// so we need this flag to detect when each transfer completes static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); -// Custom DMA interrupt handler for ping-pong transfer -// We need a custom handler because we signal completion via TRANSFER_DONE flag -// and don't clear DONE bit when using Scatter/Gather (ESG=1) +// Custom handler for scatter/gather that delegates to HAL's on_interrupt() +// This follows the "interrupts as threads" pattern - the handler does minimal work +// (delegates to HAL + sets a flag) and the main task does the actual processing pub struct PingPongDmaHandler; impl embassy_mcxa::interrupt::typelevel::Handler for PingPongDmaHandler { unsafe fn on_interrupt() { - let edma = edma_tcd(); - - // Clear interrupt flag - edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one()); - - // Do NOT clear DONE bit when using Scatter/Gather (ESG=1), - // as the hardware loads the next TCD which resets the status. - + // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers + dma::on_interrupt(0); + // Signal completion for polling (needed because ESG clears DONE bit) TRANSFER_DONE.store(true, Ordering::Release); } } +// Bind DMA channel interrupts +// CH0: Custom handler for scatter/gather (delegates to on_interrupt + sets flag) +// CH1: Standard handler for wait_half() demo bind_interrupts!(struct Irqs { DMA_CH0 => PingPongDmaHandler; - DMA_CH1 => DmaCh1InterruptHandler; // For wait_half() demo + DMA_CH1 => DmaCh1InterruptHandler; }); /// Helper to write a u32 as decimal ASCII to UART @@ -130,22 +133,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA ping-pong transfer example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - unsafe { - dma::init(&pac_periphs); - } - - // Use edma_tcd() accessor instead of passing register block around - let edma = edma_tcd(); - - // Enable DMA interrupt + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); } @@ -228,14 +216,14 @@ async fn main(_spawner: Spawner) { }; // Load TCD0 into hardware registers - dma_ch0.load_tcd(edma, &tcds[0]); + dma_ch0.load_tcd(&tcds[0]); } tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) unsafe { - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for first half @@ -249,7 +237,7 @@ async fn main(_spawner: Spawner) { // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) unsafe { - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for second half diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs index 86dd881cd..d78605acc 100644 --- a/examples/src/bin/dma_scatter_gather.rs +++ b/examples/src/bin/dma_scatter_gather.rs @@ -5,9 +5,9 @@ //! then automatically loads the second TCD to transfer the second half. //! //! # Embassy-style features demonstrated: -//! - `dma::edma_tcd()` accessor for simplified register access //! - `DmaChannel::new()` for channel creation //! - Scatter/gather with chained TCDs +//! - Custom handler that delegates to HAL's `on_interrupt()` (best practice) #![no_std] #![no_main] @@ -15,9 +15,8 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{edma_tcd, DmaChannel, Tcd}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::dma::{self, DmaChannel, Tcd}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -44,30 +43,27 @@ static mut TCD_POOL: TcdPool = TcdPool([Tcd { biter: 0, }; 2]); +// AtomicBool to track scatter/gather completion +// Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, +// so we need this flag to detect when each transfer completes static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); -// Custom DMA interrupt handler for scatter-gather transfer -// We need a custom handler because we signal completion via TRANSFER_DONE flag -// and need to conditionally clear DONE bit based on ESG status +// Custom handler for scatter/gather that delegates to HAL's on_interrupt() +// This follows the "interrupts as threads" pattern - the handler does minimal work +// (delegates to HAL + sets a flag) and the main task does the actual processing pub struct ScatterGatherDmaHandler; impl embassy_mcxa::interrupt::typelevel::Handler for ScatterGatherDmaHandler { unsafe fn on_interrupt() { - let edma = edma_tcd(); - - // Clear interrupt flag - edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one()); - - // If ESG=1 (Scatter/Gather), the hardware loads the next TCD and clears DONE. - // If ESG=0 (Last TCD), DONE remains set and must be cleared. - if edma.tcd(0).ch_csr().read().done().bit_is_set() { - edma.tcd(0).ch_csr().write(|w| w.done().clear_bit_by_one()); - } - + // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers + dma::on_interrupt(0); + // Signal completion for polling (needed because ESG clears DONE bit) TRANSFER_DONE.store(true, Ordering::Release); } } +// Bind DMA channel interrupt +// Custom handler for scatter/gather (delegates to on_interrupt + sets flag) bind_interrupts!(struct Irqs { DMA_CH0 => ScatterGatherDmaHandler; }); @@ -120,20 +116,8 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA scatter-gather transfer example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - unsafe { - dma::init(&pac_periphs); - } - - // Use edma_tcd() accessor instead of passing register block around - let edma = edma_tcd(); + // Ensure DMA is initialized (clock/reset/init handled automatically by HAL) + dma::ensure_init(); // Enable DMA interrupt unsafe { @@ -213,7 +197,7 @@ async fn main(_spawner: Spawner) { } // Load TCD0 into hardware registers - dma_ch0.load_tcd(edma, &tcds[0]); + dma_ch0.load_tcd(&tcds[0]); } tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); @@ -221,7 +205,7 @@ async fn main(_spawner: Spawner) { // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) // TCD0 is currently loaded. unsafe { - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for first half @@ -236,7 +220,7 @@ async fn main(_spawner: Spawner) { // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) // TCD1 should have been loaded by the scatter/gather engine. unsafe { - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for second half diff --git a/examples/src/bin/dma_scatter_gather_builder.rs b/examples/src/bin/dma_scatter_gather_builder.rs index 078e26c60..51bfbeb67 100644 --- a/examples/src/bin/dma_scatter_gather_builder.rs +++ b/examples/src/bin/dma_scatter_gather_builder.rs @@ -22,9 +22,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, ScatterGatherBuilder}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -81,20 +80,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA Scatter-Gather Builder example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - // Initialize DMA - unsafe { - dma::init(&pac_periphs); - } - - // Enable DMA interrupt + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); } diff --git a/examples/src/bin/dma_wrap_transfer.rs b/examples/src/bin/dma_wrap_transfer.rs index b115a2c19..8e9aedbfb 100644 --- a/examples/src/bin/dma_wrap_transfer.rs +++ b/examples/src/bin/dma_wrap_transfer.rs @@ -4,7 +4,6 @@ //! a source buffer, effectively repeating the source data in the destination. //! //! # Embassy-style features demonstrated: -//! - `dma::edma_tcd()` accessor for simplified register access //! - `DmaChannel::is_done()` and `clear_done()` helper methods //! - No need to pass register block around @@ -13,9 +12,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh0InterruptHandler}; -use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; +use embassy_mcxa::bind_interrupts; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -80,19 +78,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA wrap transfer example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - unsafe { - dma::init(&pac_periphs); - } - - // Enable DMA interrupt + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); } @@ -130,9 +116,6 @@ async fn main(_spawner: Spawner) { // Create DMA channel using Embassy-style API let dma_ch0 = DmaChannel::new(p.DMA_CH0); - // Use edma_tcd() accessor instead of passing register block around - let edma = edma_tcd(); - // Configure wrap transfer using direct TCD access: // SRC is 16 bytes (4 * u32). We want to transfer 32 bytes (8 * u32). // SRC modulo is 16 bytes (2^4 = 16) - wraps source address. @@ -140,7 +123,7 @@ async fn main(_spawner: Spawner) { // This causes the source address to wrap around after 16 bytes, // effectively repeating the source data. unsafe { - let t = edma.tcd(0); + let t = dma_ch0.tcd(); // Reset channel state t.ch_csr().write(|w| { @@ -189,14 +172,14 @@ async fn main(_spawner: Spawner) { cortex_m::asm::dsb(); tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(edma); + dma_ch0.trigger_start(); } // Wait for completion using channel helper method - while !dma_ch0.is_done(edma) { + while !dma_ch0.is_done() { cortex_m::asm::nop(); } - unsafe { dma_ch0.clear_done(edma); } + unsafe { dma_ch0.clear_done(); } tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") .unwrap(); diff --git a/examples/src/bin/lpuart_dma.rs b/examples/src/bin/lpuart_dma.rs index 5ccf97ecc..4e321b111 100644 --- a/examples/src/bin/lpuart_dma.rs +++ b/examples/src/bin/lpuart_dma.rs @@ -3,28 +3,25 @@ //! This example demonstrates using DMA for UART TX and RX operations. //! It sends a message using DMA, then waits for 16 characters to be received //! via DMA and echoes them back. +//! +//! The DMA request sources are automatically derived from the LPUART instance type. +//! DMA clock/reset/init is handled automatically by the HAL. #![no_std] #![no_main] use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{self, DMA_REQ_LPUART2_RX, DMA_REQ_LPUART2_TX}; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; use embassy_mcxa::lpuart::{Config, LpuartDma}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -// DMA interrupt handlers -#[no_mangle] -pub extern "C" fn DMA_CH0() { - unsafe { dma::on_interrupt(0) }; -} - -#[no_mangle] -pub extern "C" fn DMA_CH1() { - unsafe { dma::on_interrupt(1) }; -} +// Bind DMA channel interrupts using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; + DMA_CH1 => DmaCh1InterruptHandler; +}); #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -35,24 +32,7 @@ async fn main(_spawner: Spawner) { defmt::info!("LPUART DMA example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - // Get PAC peripherals for DMA init - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - // Initialize DMA - unsafe { - dma::init(&pac_periphs); - } - - // Get EDMA TCD register block for transfers - let edma = &pac_periphs.edma_0_tcd0; - - // Enable DMA interrupts + // Enable DMA interrupts (per-channel, as needed) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); @@ -77,51 +57,29 @@ async fn main(_spawner: Spawner) { ) .unwrap(); - // Send a message using DMA + // Send a message using DMA (DMA request source is automatically derived from LPUART2) let tx_msg = b"Hello from LPUART2 DMA TX!\r\n"; - lpuart - .write_dma(edma, DMA_REQ_LPUART2_TX, tx_msg) - .await - .unwrap(); + lpuart.write_dma(tx_msg).await.unwrap(); defmt::info!("TX DMA complete"); // Send prompt let prompt = b"Type 16 characters to echo via DMA:\r\n"; - lpuart - .write_dma(edma, DMA_REQ_LPUART2_TX, prompt) - .await - .unwrap(); + lpuart.write_dma(prompt).await.unwrap(); // Receive 16 characters using DMA let mut rx_buf = [0u8; 16]; - lpuart - .read_dma(edma, DMA_REQ_LPUART2_RX, &mut rx_buf) - .await - .unwrap(); + lpuart.read_dma(&mut rx_buf).await.unwrap(); defmt::info!("RX DMA complete"); // Echo back the received data let echo_prefix = b"\r\nReceived: "; - lpuart - .write_dma(edma, DMA_REQ_LPUART2_TX, echo_prefix) - .await - .unwrap(); - lpuart - .write_dma(edma, DMA_REQ_LPUART2_TX, &rx_buf) - .await - .unwrap(); + lpuart.write_dma(echo_prefix).await.unwrap(); + lpuart.write_dma(&rx_buf).await.unwrap(); let done_msg = b"\r\nDone!\r\n"; - lpuart - .write_dma(edma, DMA_REQ_LPUART2_TX, done_msg) - .await - .unwrap(); + lpuart.write_dma(done_msg).await.unwrap(); defmt::info!("Example complete"); - - loop { - cortex_m::asm::wfe(); - } } diff --git a/examples/src/bin/lpuart_ring_buffer.rs b/examples/src/bin/lpuart_ring_buffer.rs index bc666560c..d71876ade 100644 --- a/examples/src/bin/lpuart_ring_buffer.rs +++ b/examples/src/bin/lpuart_ring_buffer.rs @@ -20,8 +20,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::clocks::Gate; -use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX}; +use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -56,20 +55,7 @@ async fn main(_spawner: Spawner) { defmt::info!("LPUART Ring Buffer DMA example starting..."); - // Enable DMA0 clock and release reset - unsafe { - hal::peripherals::DMA0::enable_clock(); - hal::peripherals::DMA0::release_reset(); - } - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - - // Initialize DMA - unsafe { - dma::init(&pac_periphs); - } - - // Enable DMA interrupts + // Enable DMA interrupts (DMA clock/reset/init is handled automatically by HAL) unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); @@ -99,11 +85,10 @@ async fn main(_spawner: Spawner) { // Create DMA channel for RX let dma_ch_rx = DmaChannel::new(p.DMA_CH0); - let edma = dma::edma_tcd(); // Configure the DMA mux for LPUART2 RX unsafe { - dma_ch_rx.set_request_source(edma, DMA_REQ_LPUART2_RX); + dma_ch_rx.set_request_source(DMA_REQ_LPUART2_RX); } tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap(); @@ -117,7 +102,7 @@ async fn main(_spawner: Spawner) { // Enable DMA requests to start continuous reception unsafe { - dma_ch_rx.enable_request(edma); + dma_ch_rx.enable_request(); } tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap(); -- cgit