From 03356a261801d7ee234490809eef3eac3c27cc52 Mon Sep 17 00:00:00 2001 From: Bogdan Petru Chircu Mare Date: Tue, 25 Nov 2025 22:09:01 -0800 Subject: feat(dma): add DMA driver with 10 verified examples Initial DMA driver implementation for MCXA276 with: Core DMA Features: - DmaChannel type with ownership tracking via Channel trait - Transfer, RingBuffer, and ScatterGatherBuilder abstractions - Support for mem-to-mem, mem-to-peripheral, peripheral-to-mem transfers - Interrupt-driven completion with embassy async/await integration - Word size abstraction (u8, u16, u32) via Word trait LPUART DMA Integration: - LpuartTxDma and LpuartRxDma drivers for async UART with DMA - LpuartDma combined TX/RX driver - Automatic chunking for buffers > 0x7FFF bytes - DMA guards with Drop impl for safe cancellation 10 Verified Examples: - dma_mem2mem: Basic memory-to-memory copy - dma_memset: Memory fill with pattern - dma_uart_tx: UART transmit via DMA - dma_uart_rx: UART receive via DMA - dma_uart_loopback: Combined TX/RX loopback test - dma_scatter_gather: Linked descriptor chains - dma_channel_linking: Major/minor loop channel linking - dma_ring_buffer: Circular buffer for continuous streaming - dma_ping_pong: Double-buffering pattern - dma_software_trigger: Manual transfer triggering PR Feedback Addressed: - Use PAC accessor for LPUART DATA register instead of manual offset - Add EnableInterrupt enum to replace boolean parameter for readability - Add DMA guards with Drop impl for safe async cancellation - Automatic chunking for large buffers instead of returning error - Use NonNull<[W]> + PhantomData for RingBuffer (DMA acts like separate thread) - Remove edma parameter from all methods (single eDMA instance steals ptr internally) - Make edma_tcd() non-public (HAL should not expose PAC items) --- examples/src/bin/dma_channel_link.rs | 396 +++++++++++++++++++++++++ examples/src/bin/dma_interleave_transfer.rs | 226 ++++++++++++++ examples/src/bin/dma_mem_to_mem.rs | 248 ++++++++++++++++ examples/src/bin/dma_memset.rs | 232 +++++++++++++++ examples/src/bin/dma_ping_pong_transfer.rs | 384 ++++++++++++++++++++++++ examples/src/bin/dma_scatter_gather.rs | 281 ++++++++++++++++++ examples/src/bin/dma_scatter_gather_builder.rs | 244 +++++++++++++++ examples/src/bin/dma_wrap_transfer.rs | 231 +++++++++++++++ examples/src/bin/lpuart_dma.rs | 127 ++++++++ examples/src/bin/lpuart_ring_buffer.rs | 162 ++++++++++ 10 files changed, 2531 insertions(+) create mode 100644 examples/src/bin/dma_channel_link.rs create mode 100644 examples/src/bin/dma_interleave_transfer.rs create mode 100644 examples/src/bin/dma_mem_to_mem.rs create mode 100644 examples/src/bin/dma_memset.rs create mode 100644 examples/src/bin/dma_ping_pong_transfer.rs create mode 100644 examples/src/bin/dma_scatter_gather.rs create mode 100644 examples/src/bin/dma_scatter_gather_builder.rs create mode 100644 examples/src/bin/dma_wrap_transfer.rs create mode 100644 examples/src/bin/lpuart_dma.rs create mode 100644 examples/src/bin/lpuart_ring_buffer.rs (limited to 'examples') diff --git a/examples/src/bin/dma_channel_link.rs b/examples/src/bin/dma_channel_link.rs new file mode 100644 index 000000000..d585f8e3a --- /dev/null +++ b/examples/src/bin/dma_channel_link.rs @@ -0,0 +1,396 @@ +//! DMA channel linking example for MCXA276. +//! +//! This example demonstrates DMA channel linking (minor and major loop linking): +//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: +//! - Minor Link to Channel 1 (triggers CH1 after each minor loop) +//! - Major Link to Channel 2 (triggers CH2 after major loop completes) +//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) +//! - 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()` + +#![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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Buffers +static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; +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_interrupts!(struct Irqs { + DMA_CH0 => Ch0Handler; + DMA_CH1 => Ch1Handler; + DMA_CH2 => Ch2Handler; +}); + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA channel link 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(); + let dma0 = &pac_periphs.dma0; + + // Clear any residual state + for i in 0..3 { + let t = edma.tcd(i); + t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + t.ch_es().write(|w| w.err().clear_bit_by_one()); + t.ch_mux().write(|w| unsafe { w.bits(0) }); + } + + // Clear Global Halt/Error state + dma0.mp_csr().modify(|_, w| { + w.halt().normal_operation() + .hae().normal_operation() + .ecx().normal_operation() + .cx().normal_operation() + }); + + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH2); + } + + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC_BUFFER = [1, 2, 3, 4]; + DEST_BUFFER0 = [0; 4]; + DEST_BUFFER1 = [0; 4]; + DEST_BUFFER2 = [0; 4]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST0 (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST1 (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST2 (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA channels with Embassy-style API...\r\n") + .unwrap(); + + let ch0 = DmaChannel::new(p.DMA_CH0); + let ch1 = DmaChannel::new(p.DMA_CH1); + 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 + + // Helper to configure TCD for memory-to-memory transfer + // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt + #[allow(clippy::too_many_arguments)] + unsafe fn configure_tcd( + edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, + ch: usize, + src: u32, + dst: u32, + width: u8, + nbytes: u32, + count: u16, + enable_int: bool, + ) { + let t = edma.tcd(ch); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq().disable() + .earq().disable() + .eei().no_error() + .ebw().disable() + .done().clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr().write(|w| w.saddr().bits(src)); + t.tcd_daddr().write(|w| w.daddr().bits(dst)); + + // Offsets: increment by width + t.tcd_soff().write(|w| w.soff().bits(width as u16)); + t.tcd_doff().write(|w| w.doff().bits(width as u16)); + + // Attributes: size = log2(width) + let size = match width { + 1 => 0, + 2 => 1, + 4 => 2, + _ => 0, + }; + t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); + + // Number of bytes per minor loop + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Major loop: reset source address after major loop + let total_bytes = nbytes * count as u32; + t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); + + // Major loop count + t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); + + // Control/status: enable interrupt if requested + if enable_int { + t.tcd_csr().write(|w| w.intmajor().set_bit()); + } else { + t.tcd_csr().write(|w| w.intmajor().clear_bit()); + } + + cortex_m::asm::dsb(); + } + + unsafe { + + // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) + // Minor Link -> Channel 1 + // Major Link -> Channel 2 + configure_tcd( + edma, + 0, + core::ptr::addr_of!(SRC_BUFFER) as u32, + core::ptr::addr_of_mut!(DEST_BUFFER0) as u32, + 4, // src width + 8, // nbytes (minor loop = 2 words) + 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 + + // Channel 1: Transfer 16 bytes (triggered by CH0 minor link) + configure_tcd( + edma, + 1, + core::ptr::addr_of!(SRC_BUFFER) as u32, + core::ptr::addr_of_mut!(DEST_BUFFER1) as u32, + 4, + 16, // full buffer in one minor loop + 1, // 1 major iteration + false, + ); + + // Channel 2: Transfer 16 bytes (triggered by CH0 major link) + configure_tcd( + edma, + 2, + core::ptr::addr_of!(SRC_BUFFER) as u32, + core::ptr::addr_of_mut!(DEST_BUFFER2) as u32, + 4, + 16, // full buffer in one minor loop + 1, // 1 major iteration + true, // enable interrupt + ); + } + + tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n").unwrap(); + + // Trigger first minor loop of CH0 + unsafe { ch0.trigger_start(edma); } + + // Wait for CH1 to complete (triggered by CH0 minor link) + while !ch1.is_done(edma) { + cortex_m::asm::nop(); + } + unsafe { ch1.clear_done(edma); } + + 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); } + + // Wait for CH0 major loop to complete + while !ch0.is_done(edma) { + cortex_m::asm::nop(); + } + unsafe { ch0.clear_done(edma); } + + 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) { + cortex_m::asm::nop(); + } + + tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap(); + + tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n") + .unwrap(); + + tx.blocking_write(b"DEST0 (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST1 (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST2 (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify all buffers match source + let mut success = true; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC_BUFFER) as *const u32; + let dst0_ptr = core::ptr::addr_of!(DEST_BUFFER0) as *const u32; + let dst1_ptr = core::ptr::addr_of!(DEST_BUFFER1) as *const u32; + let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32; + + for i in 0..4 { + if *dst0_ptr.add(i) != *src_ptr.add(i) { success = false; } + if *dst1_ptr.add(i) != *src_ptr.add(i) { success = false; } + if *dst2_ptr.add(i) != *src_ptr.add(i) { success = false; } + } + } + + if success { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } else { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/dma_interleave_transfer.rs b/examples/src/bin/dma_interleave_transfer.rs new file mode 100644 index 000000000..710f18de3 --- /dev/null +++ b/examples/src/bin/dma_interleave_transfer.rs @@ -0,0 +1,226 @@ +//! DMA interleaved transfer example for MCXA276. +//! +//! This example demonstrates using DMA with custom source/destination offsets +//! 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()` + +#![no_std] +#![no_main] + +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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +const BUFFER_LENGTH: usize = 16; +const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; + +// Buffers in RAM +static mut SRC_BUFFER: [u32; HALF_BUFF_LENGTH] = [0; HALF_BUFF_LENGTH]; +static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA interleave transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC_BUFFER = [1, 2, 3, 4, 5, 6, 7, 8]; + DEST_BUFFER = [0; BUFFER_LENGTH]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, HALF_BUFF_LENGTH); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // 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); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq().disable() + .earq().disable() + .eei().no_error() + .ebw().disable() + .done().clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32)); + t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + + // Custom offsets for interleaving + t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read + t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write + + // Attributes: 32-bit transfers (size = 2) + t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); + + // Transfer entire source buffer in one minor loop + let nbytes = (HALF_BUFF_LENGTH * 4) as u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Reset source address after major loop + t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); + // Destination uses 2x offset, so adjust accordingly + let dst_total = (HALF_BUFF_LENGTH * 8) as u32; + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + dma_ch0.trigger_start(edma); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done(edma) { + cortex_m::asm::nop(); + } + unsafe { dma_ch0.clear_done(edma); } + + tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 + let mut mismatch = false; + unsafe { + for i in 0..BUFFER_LENGTH { + if i % 2 == 0 { + if DEST_BUFFER[i] != SRC_BUFFER[i / 2] { + mismatch = true; + } + } else if DEST_BUFFER[i] != 0 { + mismatch = true; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/dma_mem_to_mem.rs b/examples/src/bin/dma_mem_to_mem.rs new file mode 100644 index 000000000..e193e8c6a --- /dev/null +++ b/examples/src/bin/dma_mem_to_mem.rs @@ -0,0 +1,248 @@ +//! DMA memory-to-memory transfer example for MCXA276. +//! +//! This example demonstrates using DMA to copy data between memory buffers +//! using the Embassy-style async API with type-safe transfers. +//! +//! # Embassy-style features demonstrated: +//! - `TransferOptions` for configuration +//! - Type-safe `mem_to_mem()` method with async `.await` +//! - `Transfer` Future that can be `.await`ed +//! - `Word` trait for automatic transfer width detection +//! - `memset()` method for filling memory with a pattern + +#![no_std] +#![no_main] + +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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +const BUFFER_LENGTH: usize = 4; + +// Buffers in RAM (static mut is automatically placed in .bss/.data) +static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; +static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; +static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; // u32 max is 4294967295 (10 digits) + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer as [v1, v2, v3, v4] to UART +/// Takes a raw pointer to avoid warnings about shared references to mutable statics +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) { + tx.blocking_write(b"[").ok(); + unsafe { + let buf = &*buf_ptr; + for (i, val) in buf.iter().enumerate() { + write_u32(tx, *val); + if i < buf.len() - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + // Create UART for debug output + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC_BUFFER = [1, 2, 3, 4]; + DEST_BUFFER = [0; BUFFER_LENGTH]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, &raw const SRC_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, &raw const DEST_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // Create DMA channel + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure transfer options (Embassy-style) + // TransferOptions defaults to: complete_transfer_interrupt = true + let options = TransferOptions::default(); + + // ========================================================================= + // Part 1: Embassy-style async API demonstration (mem_to_mem) + // ========================================================================= + // + // Use the new type-safe `mem_to_mem()` method: + // - Automatically determines transfer width from buffer element type (u32) + // - Returns a `Transfer` future that can be `.await`ed + // - Uses TransferOptions for consistent configuration + // + // Using async `.await` - the executor can run other tasks while waiting! + + // Perform type-safe memory-to-memory transfer using Embassy-style async API + unsafe { + let src = &*core::ptr::addr_of!(SRC_BUFFER); + let dst = &mut *core::ptr::addr_of_mut!(DEST_BUFFER); + + // Using async `.await` - the executor can run other tasks while waiting! + let transfer = dma_ch0.mem_to_mem(src, dst, options); + transfer.await; + } + + tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, &raw const DEST_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + // Verify data + let mut mismatch = false; + unsafe { + for i in 0..BUFFER_LENGTH { + if SRC_BUFFER[i] != DEST_BUFFER[i] { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap(); + defmt::error!("FAIL: mem_to_mem mismatch!"); + } else { + tx.blocking_write(b"PASS: mem_to_mem verified.\r\n\r\n").unwrap(); + defmt::info!("PASS: mem_to_mem verified."); + } + + // ========================================================================= + // Part 2: memset() demonstration + // ========================================================================= + // + // The `memset()` method fills a buffer with a pattern value: + // - Fixed source address (pattern is read repeatedly) + // - Incrementing destination address + // - Uses the same Transfer future pattern + + tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n").unwrap(); + + tx.blocking_write(b"Memset Buffer (before): ").unwrap(); + print_buffer(&mut tx, &raw const MEMSET_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + // Fill buffer with a pattern value using DMA memset + let pattern: u32 = 0xDEADBEEF; + tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap(); + + unsafe { + let dst = &mut *core::ptr::addr_of_mut!(MEMSET_BUFFER); + + // Using blocking_wait() for demonstration - also shows non-async usage + let transfer = dma_ch0.memset(&pattern, dst, options); + transfer.blocking_wait(); + } + + tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap(); + tx.blocking_write(b"Memset Buffer (after): ").unwrap(); + print_buffer(&mut tx, &raw const MEMSET_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + // Verify memset result + let mut memset_ok = true; + unsafe { + #[allow(clippy::needless_range_loop)] + for i in 0..BUFFER_LENGTH { + if MEMSET_BUFFER[i] != pattern { + memset_ok = false; + break; + } + } + } + + if !memset_ok { + tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap(); + defmt::error!("FAIL: memset mismatch!"); + } else { + tx.blocking_write(b"PASS: memset verified.\r\n\r\n").unwrap(); + defmt::info!("PASS: memset verified."); + } + + tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap(); + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/dma_memset.rs b/examples/src/bin/dma_memset.rs new file mode 100644 index 000000000..b76ba988d --- /dev/null +++ b/examples/src/bin/dma_memset.rs @@ -0,0 +1,232 @@ +//! DMA memset example for MCXA276. +//! +//! This example demonstrates using DMA to fill a buffer with a repeated pattern. +//! 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 + +#![no_std] +#![no_main] + +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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +const BUFFER_LENGTH: usize = 4; + +// Buffers in RAM +static mut PATTERN: u32 = 0; +static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA memset example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + PATTERN = 0xDEADBEEF; + DEST_BUFFER = [0; BUFFER_LENGTH]; + } + + tx.blocking_write(b"Pattern value: 0x").unwrap(); + // Print pattern in hex + unsafe { + let hex_chars = b"0123456789ABCDEF"; + let mut hex_buf = [0u8; 8]; + let mut val = PATTERN; + for i in (0..8).rev() { + hex_buf[i] = hex_chars[(val & 0xF) as usize]; + val >>= 4; + } + tx.blocking_write(&hex_buf).ok(); + } + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // 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); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq().disable() + .earq().disable() + .eei().no_error() + .ebw().disable() + .done().clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source address (pattern) - fixed + t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32)); + // Destination address - increments + t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + + // Source offset = 0 (stays fixed), Dest offset = 4 (increments) + t.tcd_soff().write(|w| w.soff().bits(0)); + t.tcd_doff().write(|w| w.doff().bits(4)); + + // Attributes: 32-bit transfers (size = 2) + t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); + + // Transfer entire buffer in one minor loop + let nbytes = (BUFFER_LENGTH * 4) as u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Source doesn't need adjustment (stays fixed) + t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); + // Reset dest address after major loop + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + dma_ch0.trigger_start(edma); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done(edma) { + cortex_m::asm::nop(); + } + unsafe { dma_ch0.clear_done(edma); } + + tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: All elements should equal PATTERN + let mut mismatch = false; + unsafe { + #[allow(clippy::needless_range_loop)] + for i in 0..BUFFER_LENGTH { + if DEST_BUFFER[i] != PATTERN { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/dma_ping_pong_transfer.rs b/examples/src/bin/dma_ping_pong_transfer.rs new file mode 100644 index 000000000..13ad9782d --- /dev/null +++ b/examples/src/bin/dma_ping_pong_transfer.rs @@ -0,0 +1,384 @@ +//! DMA ping-pong/double-buffer transfer example for MCXA276. +//! +//! This example demonstrates two approaches for ping-pong/double-buffering: +//! +//! ## Approach 1: Scatter/Gather with linked TCDs (manual) +//! - Two TCDs link to each other for alternating transfers +//! - Uses custom interrupt handler with AtomicBool flag +//! +//! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!) +//! - Single continuous transfer over entire buffer +//! - Uses half-transfer interrupt to know when first half is ready +//! - 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 +//! - NEW: `wait_half()` for half-transfer interrupt handling + +#![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, DmaCh1InterruptHandler, Tcd, TransferOptions}; +use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Source and destination buffers for Approach 1 (scatter/gather) +static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; +static mut DST: [u32; 8] = [0; 8]; + +// Source and destination buffers for Approach 2 (wait_half) +static mut SRC2: [u32; 8] = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; +static mut DST2: [u32; 8] = [0; 8]; + +// TCD pool for scatter/gather - must be 32-byte aligned +#[repr(C, align(32))] +struct TcdPool([Tcd; 2]); + +static mut TCD_POOL: TcdPool = TcdPool([Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + biter: 0, +}; 2]); + +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) +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. + + TRANSFER_DONE.store(true, Ordering::Release); + } +} + +bind_interrupts!(struct Irqs { + DMA_CH0 => PingPongDmaHandler; + DMA_CH1 => DmaCh1InterruptHandler; // For wait_half() demo +}); + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA ping-pong transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC = [1, 2, 3, 4, 5, 6, 7, 8]; + DST = [0; 8]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring ping-pong DMA with Embassy-style API...\r\n") + .unwrap(); + + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure ping-pong transfer using direct TCD access: + // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. + // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. + // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. + unsafe { + let tcds = &mut *core::ptr::addr_of_mut!(TCD_POOL.0); + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; + + let half_len = 4usize; + let half_bytes = (half_len * 4) as u32; + + let tcd0_addr = &tcds[0] as *const _ as u32; + let tcd1_addr = &tcds[1] as *const _ as u32; + + // TCD0: First half -> Links to TCD1 + tcds[0] = Tcd { + saddr: src_ptr as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: half_bytes, + slast: 0, + daddr: dst_ptr as u32, + doff: 4, + citer: 1, + dlast_sga: tcd1_addr as i32, + csr: 0x0012, // ESG | INTMAJOR + biter: 1, + }; + + // TCD1: Second half -> Links to TCD0 + tcds[1] = Tcd { + saddr: src_ptr.add(half_len) as u32, + soff: 4, + attr: 0x0202, + nbytes: half_bytes, + slast: 0, + daddr: dst_ptr.add(half_len) as u32, + doff: 4, + citer: 1, + dlast_sga: tcd0_addr as i32, + csr: 0x0012, + biter: 1, + }; + + // Load TCD0 into hardware registers + dma_ch0.load_tcd(edma, &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); + } + + // Wait for first half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"First half transferred.\r\n").unwrap(); + tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); + + // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) + unsafe { + dma_ch0.trigger_start(edma); + } + + // Wait for second half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); + + tx.blocking_write(b"EDMA ping-pong transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: DST should match SRC + let mut mismatch = false; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of!(DST) as *const u32; + for i in 0..8 { + if *src_ptr.add(i) != *dst_ptr.add(i) { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Approach 1 mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Approach 1 mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Approach 1 data verified.\r\n\r\n").unwrap(); + defmt::info!("PASS: Approach 1 data verified."); + } + + // ========================================================================= + // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!) + // ========================================================================= + // + // This approach uses a single continuous DMA transfer with half-transfer + // interrupt enabled. The wait_half() method allows you to be notified + // when the first half of the buffer is complete, so you can process it + // while the second half is still being filled. + // + // Benefits: + // - Simpler setup (no TCD pool needed) + // - True async/await support + // - Good for streaming data processing + + tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n").unwrap(); + + // Enable DMA CH1 interrupt + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + } + + // Initialize approach 2 buffers + unsafe { + SRC2 = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; + DST2 = [0; 8]; + } + + tx.blocking_write(b"SRC2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + let dma_ch1 = DmaChannel::new(p.DMA_CH1); + + // Configure transfer with half-transfer interrupt enabled + let mut options = TransferOptions::default(); + options.half_transfer_interrupt = true; // Enable half-transfer interrupt + options.complete_transfer_interrupt = true; + + tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n").unwrap(); + + unsafe { + let src = &*core::ptr::addr_of!(SRC2); + let dst = &mut *core::ptr::addr_of_mut!(DST2); + + // Create the transfer + let mut transfer = dma_ch1.mem_to_mem(src, dst, options); + + // Wait for half-transfer (first 4 elements) + tx.blocking_write(b"Waiting for first half...\r\n").unwrap(); + let half_ok = transfer.wait_half().await; + + if half_ok { + tx.blocking_write(b"Half-transfer complete! First half of DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b"(Processing first half while second half transfers...)\r\n").unwrap(); + } + + // Wait for complete transfer + tx.blocking_write(b"Waiting for second half...\r\n").unwrap(); + transfer.await; + } + + tx.blocking_write(b"Transfer complete! Full DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify approach 2 + let mut mismatch2 = false; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC2) as *const u32; + let dst_ptr = core::ptr::addr_of!(DST2) as *const u32; + for i in 0..8 { + if *src_ptr.add(i) != *dst_ptr.add(i) { + mismatch2 = true; + break; + } + } + } + + if mismatch2 { + tx.blocking_write(b"FAIL: Approach 2 mismatch!\r\n").unwrap(); + defmt::error!("FAIL: Approach 2 mismatch!"); + } else { + tx.blocking_write(b"PASS: Approach 2 verified.\r\n").unwrap(); + defmt::info!("PASS: Approach 2 verified."); + } + + tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n").unwrap(); + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs new file mode 100644 index 000000000..86dd881cd --- /dev/null +++ b/examples/src/bin/dma_scatter_gather.rs @@ -0,0 +1,281 @@ +//! DMA scatter-gather transfer example for MCXA276. +//! +//! This example demonstrates using DMA with scatter/gather to chain multiple +//! transfer descriptors. The first TCD transfers the first half of the buffer, +//! 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 + +#![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, Tcd}; +use embassy_mcxa::{bind_interrupts, dma}; +use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Source and destination buffers +static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; +static mut DST: [u32; 8] = [0; 8]; + +// TCD pool for scatter/gather - must be 32-byte aligned +#[repr(C, align(32))] +struct TcdPool([Tcd; 2]); + +static mut TCD_POOL: TcdPool = TcdPool([Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + biter: 0, +}; 2]); + +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 +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()); + } + + TRANSFER_DONE.store(true, Ordering::Release); + } +} + +bind_interrupts!(struct Irqs { + DMA_CH0 => ScatterGatherDmaHandler; +}); + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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(); + + // Enable DMA interrupt + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA scatter-gather transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC = [1, 2, 3, 4, 5, 6, 7, 8]; + DST = [0; 8]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n") + .unwrap(); + + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure scatter-gather transfer using direct TCD access: + // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. + // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. + // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. + unsafe { + let tcds = core::slice::from_raw_parts_mut( + core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, + 2, + ); + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; + + let num_tcds = 2usize; + let chunk_len = 4usize; // 8 / 2 + let chunk_bytes = (chunk_len * 4) as u32; + + for i in 0..num_tcds { + let is_last = i == num_tcds - 1; + let next_tcd_addr = if is_last { + 0 // No next TCD + } else { + &tcds[i + 1] as *const _ as u32 + }; + + tcds[i] = Tcd { + saddr: src_ptr.add(i * chunk_len) as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: chunk_bytes, + slast: 0, + daddr: dst_ptr.add(i * chunk_len) as u32, + doff: 4, + citer: 1, + dlast_sga: next_tcd_addr as i32, + // ESG (scatter/gather) for non-last, INTMAJOR for all + csr: if is_last { 0x0002 } else { 0x0012 }, + biter: 1, + }; + } + + // Load TCD0 into hardware registers + dma_ch0.load_tcd(edma, &tcds[0]); + } + + tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); + + // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) + // TCD0 is currently loaded. + unsafe { + dma_ch0.trigger_start(edma); + } + + // Wait for first half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"First half transferred.\r\n").unwrap(); + tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); + + // 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); + } + + // Wait for second half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); + + tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: DST should match SRC + let mut mismatch = false; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of!(DST) as *const u32; + for i in 0..8 { + if *src_ptr.add(i) != *dst_ptr.add(i) { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/dma_scatter_gather_builder.rs b/examples/src/bin/dma_scatter_gather_builder.rs new file mode 100644 index 000000000..078e26c60 --- /dev/null +++ b/examples/src/bin/dma_scatter_gather_builder.rs @@ -0,0 +1,244 @@ +//! DMA Scatter-Gather Builder example for MCXA276. +//! +//! This example demonstrates using the new `ScatterGatherBuilder` API for +//! chaining multiple DMA transfers with a type-safe builder pattern. +//! +//! # Features demonstrated: +//! - `ScatterGatherBuilder::new()` for creating a builder +//! - `add_transfer()` for adding memory-to-memory segments +//! - `build()` to start the chained transfer +//! - Automatic TCD linking and ESG bit management +//! +//! # Comparison with manual scatter-gather: +//! The manual approach (see `dma_scatter_gather.rs`) requires: +//! - Manual TCD pool allocation and alignment +//! - Manual CSR/ESG/INTMAJOR bit manipulation +//! - Manual dlast_sga address calculations +//! +//! The builder approach handles all of this automatically! + +#![no_std] +#![no_main] + +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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel 0 interrupt +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +// Source buffers (multiple segments) +static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444]; +static mut SRC2: [u32; 4] = [0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]; +static mut SRC3: [u32; 4] = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]; + +// Destination buffers (one per segment) +static mut DST1: [u32; 4] = [0; 4]; +static mut DST2: [u32; 4] = [0; 4]; +static mut DST3: [u32; 4] = [0; 4]; + +/// Helper to write a u32 as hex to UART +fn write_hex(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + const HEX: &[u8; 16] = b"0123456789ABCDEF"; + for i in (0..8).rev() { + let nibble = ((val >> (i * 4)) & 0xF) as usize; + tx.blocking_write(&[HEX[nibble]]).ok(); + } +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_hex(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + // Create UART for debug output + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap(); + tx.blocking_write(b"===================================\r\n\r\n").unwrap(); + + // Show source buffers + tx.blocking_write(b"Source buffers:\r\n").unwrap(); + tx.blocking_write(b" SRC1: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" SRC2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" SRC3: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC3) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + tx.blocking_write(b"Destination buffers (before):\r\n").unwrap(); + tx.blocking_write(b" DST1: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST3: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Create DMA channel + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n").unwrap(); + + // ========================================================================= + // ScatterGatherBuilder API demonstration + // ========================================================================= + // + // The builder pattern makes scatter-gather transfers much easier: + // 1. Create a builder + // 2. Add transfer segments with add_transfer() + // 3. Call build() to start the entire chain + // No manual TCD manipulation required! + + let mut builder = ScatterGatherBuilder::::new(); + + // Add three transfer segments - the builder handles TCD linking automatically + unsafe { + let src1 = &*core::ptr::addr_of!(SRC1); + let dst1 = &mut *core::ptr::addr_of_mut!(DST1); + builder.add_transfer(src1, dst1); + } + + unsafe { + let src2 = &*core::ptr::addr_of!(SRC2); + let dst2 = &mut *core::ptr::addr_of_mut!(DST2); + builder.add_transfer(src2, dst2); + } + + unsafe { + let src3 = &*core::ptr::addr_of!(SRC3); + let dst3 = &mut *core::ptr::addr_of_mut!(DST3); + builder.add_transfer(src3, dst3); + } + + tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap(); + tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n").unwrap(); + + // Build and execute the scatter-gather chain + // The build() method: + // - Links all TCDs together with ESG bit + // - Sets INTMAJOR on all TCDs + // - Loads the first TCD into hardware + // - Returns a Transfer future + unsafe { + let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); + transfer.blocking_wait(); + } + + tx.blocking_write(b"Scatter-gather transfer complete!\r\n\r\n").unwrap(); + + // Show results + tx.blocking_write(b"Destination buffers (after):\r\n").unwrap(); + tx.blocking_write(b" DST1: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST3: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify all three segments + let mut all_ok = true; + unsafe { + let src1 = core::ptr::addr_of!(SRC1) as *const u32; + let dst1 = core::ptr::addr_of!(DST1) as *const u32; + for i in 0..4 { + if *src1.add(i) != *dst1.add(i) { + all_ok = false; + } + } + + let src2 = core::ptr::addr_of!(SRC2) as *const u32; + let dst2 = core::ptr::addr_of!(DST2) as *const u32; + for i in 0..4 { + if *src2.add(i) != *dst2.add(i) { + all_ok = false; + } + } + + let src3 = core::ptr::addr_of!(SRC3) as *const u32; + let dst3 = core::ptr::addr_of!(DST3) as *const u32; + for i in 0..4 { + if *src3.add(i) != *dst3.add(i) { + all_ok = false; + } + } + } + + if all_ok { + tx.blocking_write(b"PASS: All segments verified!\r\n").unwrap(); + defmt::info!("PASS: All segments verified!"); + } else { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } + + tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n").unwrap(); + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/src/bin/dma_wrap_transfer.rs b/examples/src/bin/dma_wrap_transfer.rs new file mode 100644 index 000000000..b115a2c19 --- /dev/null +++ b/examples/src/bin/dma_wrap_transfer.rs @@ -0,0 +1,231 @@ +//! DMA wrap transfer example for MCXA276. +//! +//! This example demonstrates using DMA with modulo addressing to wrap around +//! 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 + +#![no_std] +#![no_main] + +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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::pac; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +// Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo +#[repr(align(16))] +struct AlignedSrc([u32; 4]); + +static mut SRC: AlignedSrc = AlignedSrc([0; 4]); +static mut DST: [u32; 8] = [0; 8]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: false, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC.0 = [1, 2, 3, 4]; + DST = [0; 8]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // 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. + // DST modulo is 0 (disabled). + // This causes the source address to wrap around after 16 bytes, + // effectively repeating the source data. + unsafe { + let t = edma.tcd(0); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq().disable() + .earq().disable() + .eei().no_error() + .ebw().disable() + .done().clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32)); + t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32)); + + // Offsets: both increment by 4 bytes + t.tcd_soff().write(|w| w.soff().bits(4)); + t.tcd_doff().write(|w| w.doff().bits(4)); + + // Attributes: 32-bit transfers (size = 2) + // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled) + t.tcd_attr().write(|w| { + w.ssize().bits(2) + .dsize().bits(2) + .smod().bits(4) // Source modulo: 2^4 = 16 bytes + .dmod().bits(0) // Dest modulo: disabled + }); + + // Transfer 32 bytes total in one minor loop + let nbytes = 32u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Source wraps via modulo, no adjustment needed + t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); + // Reset dest address after major loop + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + dma_ch0.trigger_start(edma); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done(edma) { + cortex_m::asm::nop(); + } + unsafe { dma_ch0.clear_done(edma); } + + tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4] + let expected = [1u32, 2, 3, 4, 1, 2, 3, 4]; + let mut mismatch = false; + unsafe { + for i in 0..8 { + if DST[i] != expected[i] { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} + diff --git a/examples/src/bin/lpuart_dma.rs b/examples/src/bin/lpuart_dma.rs new file mode 100644 index 000000000..5ccf97ecc --- /dev/null +++ b/examples/src/bin/lpuart_dma.rs @@ -0,0 +1,127 @@ +//! LPUART DMA example for MCXA276. +//! +//! 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. + +#![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::lpuart::{Config, LpuartDma}; +use embassy_mcxa::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) }; +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + } + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: true, + ..Default::default() + }; + + // Create UART instance with DMA channels + let mut lpuart = LpuartDma::new( + p.LPUART2, + p.P2_2, // TX pin + p.P2_3, // RX pin + p.DMA_CH0, // TX DMA channel + p.DMA_CH1, // RX DMA channel + config, + ) + .unwrap(); + + // Send a message using DMA + let tx_msg = b"Hello from LPUART2 DMA TX!\r\n"; + lpuart + .write_dma(edma, DMA_REQ_LPUART2_TX, 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(); + + // Receive 16 characters using DMA + let mut rx_buf = [0u8; 16]; + lpuart + .read_dma(edma, DMA_REQ_LPUART2_RX, &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(); + let done_msg = b"\r\nDone!\r\n"; + lpuart + .write_dma(edma, DMA_REQ_LPUART2_TX, 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 new file mode 100644 index 000000000..bc666560c --- /dev/null +++ b/examples/src/bin/lpuart_ring_buffer.rs @@ -0,0 +1,162 @@ +//! LPUART Ring Buffer DMA example for MCXA276. +//! +//! This example demonstrates using the new `RingBuffer` API for continuous +//! circular DMA reception from a UART peripheral. +//! +//! # Features demonstrated: +//! - `setup_circular_read()` for continuous peripheral-to-memory DMA +//! - `RingBuffer` for async reading of received data +//! - Handling of potential overrun conditions +//! - Half-transfer and complete-transfer interrupts for timely wakeups +//! +//! # How it works: +//! 1. Set up a circular DMA transfer from LPUART RX to a ring buffer +//! 2. DMA continuously writes received bytes into the buffer, wrapping around +//! 3. Application asynchronously reads data as it arrives +//! 4. Both half-transfer and complete-transfer interrupts wake the reader + +#![no_std] +#![no_main] + +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::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use embassy_mcxa::{bind_interrupts, pac}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel interrupts +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; + DMA_CH1 => DmaCh1InterruptHandler; +}); + +// Ring buffer for RX - power of 2 is ideal for modulo efficiency +static mut RX_RING_BUFFER: [u8; 64] = [0; 64]; + +/// Helper to write a byte as hex to UART +fn write_hex(tx: &mut LpuartTx<'_, Blocking>, byte: u8) { + const HEX: &[u8; 16] = b"0123456789ABCDEF"; + let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]]; + tx.blocking_write(&buf).ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + 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 + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + } + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: true, + ..Default::default() + }; + + // Create blocking UART for TX (we'll use DMA for RX only) + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap(); + tx.blocking_write(b"==============================\r\n\r\n").unwrap(); + + // Get LPUART2 RX data register address for DMA + let lpuart2 = unsafe { &*pac::Lpuart2::ptr() }; + let rx_data_addr = lpuart2.data().as_ptr() as *const u8; + + // Enable RX DMA request in LPUART + lpuart2.baud().modify(|_, w| w.rdmae().enabled()); + + // 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); + } + + tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap(); + + // Set up the ring buffer with circular DMA + // This configures the DMA for continuous reception + let ring_buf = unsafe { + let buf = &mut *core::ptr::addr_of_mut!(RX_RING_BUFFER); + dma_ch_rx.setup_circular_read(rx_data_addr, buf) + }; + + // Enable DMA requests to start continuous reception + unsafe { + dma_ch_rx.enable_request(edma); + } + + tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap(); + tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n").unwrap(); + + // Main loop: read from ring buffer and echo back + let mut read_buf = [0u8; 16]; + let mut total_received: usize = 0; + + loop { + // Async read - waits until data is available + match ring_buf.read(&mut read_buf).await { + Ok(n) if n > 0 => { + total_received += n; + + // Echo back what we received + tx.blocking_write(b"RX[").unwrap(); + for (i, &byte) in read_buf.iter().enumerate().take(n) { + write_hex(&mut tx, byte); + if i < n - 1 { + tx.blocking_write(b" ").unwrap(); + } + } + tx.blocking_write(b"]: ").unwrap(); + tx.blocking_write(&read_buf[..n]).unwrap(); + tx.blocking_write(b"\r\n").unwrap(); + + defmt::info!("Received {} bytes, total: {}", n, total_received); + } + Ok(_) => { + // No data, shouldn't happen with async read + } + Err(_) => { + // Overrun detected + tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap(); + defmt::error!("Ring buffer overrun!"); + ring_buf.clear(); + } + } + } +} + -- cgit 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') 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 From 230237b73cbe6f31780ea407ed13bee1adf8eaa2 Mon Sep 17 00:00:00 2001 From: Bogdan Petru Chircu Mare Date: Mon, 1 Dec 2025 09:08:03 -0800 Subject: Apply rustfmt formatting fixes --- examples/src/bin/dma_channel_link.rs | 88 ++++++++++++++++---------- examples/src/bin/dma_interleave_transfer.rs | 38 ++++++----- examples/src/bin/dma_mem_to_mem.rs | 12 ++-- examples/src/bin/dma_memset.rs | 37 ++++++----- examples/src/bin/dma_ping_pong_transfer.rs | 52 ++++++++------- examples/src/bin/dma_scatter_gather.rs | 42 ++++++------ examples/src/bin/dma_scatter_gather_builder.rs | 17 +++-- examples/src/bin/dma_wrap_transfer.rs | 46 ++++++++------ examples/src/bin/lpuart_dma.rs | 4 +- examples/src/bin/lpuart_ring_buffer.rs | 12 ++-- 10 files changed, 201 insertions(+), 147 deletions(-) (limited to 'examples') diff --git a/examples/src/bin/dma_channel_link.rs b/examples/src/bin/dma_channel_link.rs index d541dc7f4..34162d931 100644 --- a/examples/src/bin/dma_channel_link.rs +++ b/examples/src/bin/dma_channel_link.rs @@ -18,10 +18,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{self, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler, DmaChannel}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Buffers @@ -104,10 +103,14 @@ async fn main(_spawner: Spawner) { // Clear Global Halt/Error state dma0.mp_csr().modify(|_, w| { - w.halt().normal_operation() - .hae().normal_operation() - .ecx().normal_operation() - .cx().normal_operation() + w.halt() + .normal_operation() + .hae() + .normal_operation() + .ecx() + .normal_operation() + .cx() + .normal_operation() }); unsafe { @@ -126,8 +129,7 @@ async fn main(_spawner: Spawner) { let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); let (mut tx, _rx) = lpuart.split(); - tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n") - .unwrap(); + tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n").unwrap(); // Initialize buffers unsafe { @@ -180,11 +182,16 @@ async fn main(_spawner: Spawner) { // Reset channel state t.ch_csr().write(|w| { - w.erq().disable() - .earq().disable() - .eei().no_error() - .ebw().disable() - .done().clear_bit_by_one() + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() }); t.ch_es().write(|w| w.bits(0)); t.ch_int().write(|w| w.int().clear_bit_by_one()); @@ -211,8 +218,10 @@ async fn main(_spawner: Spawner) { // Major loop: reset source address after major loop let total_bytes = nbytes * count as u32; - t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); - t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); + t.tcd_slast_sda() + .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); + t.tcd_dlast_sga() + .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); // Major loop count t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); @@ -229,7 +238,6 @@ async fn main(_spawner: Spawner) { } unsafe { - // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) // Minor Link -> Channel 1 // Major Link -> Channel 2 @@ -265,34 +273,44 @@ async fn main(_spawner: Spawner) { core::ptr::addr_of!(SRC_BUFFER) as u32, core::ptr::addr_of_mut!(DEST_BUFFER2) as u32, 4, - 16, // full buffer in one minor loop - 1, // 1 major iteration + 16, // full buffer in one minor loop + 1, // 1 major iteration true, // enable interrupt ); } - tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n").unwrap(); + tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n") + .unwrap(); // Trigger first minor loop of CH0 - unsafe { ch0.trigger_start(); } + unsafe { + ch0.trigger_start(); + } // Wait for CH1 to complete (triggered by CH0 minor link) while !ch1.is_done() { cortex_m::asm::nop(); } - unsafe { ch1.clear_done(); } + 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(); + tx.blocking_write(b"Triggering Channel 0 (2nd minor loop)...\r\n") + .unwrap(); // Trigger second minor loop of CH0 - unsafe { ch0.trigger_start(); } + unsafe { + ch0.trigger_start(); + } // Wait for CH0 major loop to complete while !ch0.is_done() { cortex_m::asm::nop(); } - unsafe { ch0.clear_done(); } + unsafe { + ch0.clear_done(); + } tx.blocking_write(b"CH0 major loop done.\r\n").unwrap(); @@ -302,12 +320,13 @@ async fn main(_spawner: Spawner) { while !ch2.is_done() { cortex_m::asm::nop(); } - unsafe { ch2.clear_done(); } + unsafe { + ch2.clear_done(); + } tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap(); - tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n") - .unwrap(); + tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n").unwrap(); tx.blocking_write(b"DEST0 (after): ").unwrap(); print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); @@ -330,9 +349,15 @@ async fn main(_spawner: Spawner) { let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32; for i in 0..4 { - if *dst0_ptr.add(i) != *src_ptr.add(i) { success = false; } - if *dst1_ptr.add(i) != *src_ptr.add(i) { success = false; } - if *dst2_ptr.add(i) != *src_ptr.add(i) { success = false; } + if *dst0_ptr.add(i) != *src_ptr.add(i) { + success = false; + } + if *dst1_ptr.add(i) != *src_ptr.add(i) { + success = false; + } + if *dst2_ptr.add(i) != *src_ptr.add(i) { + success = false; + } } } @@ -348,4 +373,3 @@ async fn main(_spawner: Spawner) { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/dma_interleave_transfer.rs b/examples/src/bin/dma_interleave_transfer.rs index 949ea0605..c0ebb0a46 100644 --- a/examples/src/bin/dma_interleave_transfer.rs +++ b/examples/src/bin/dma_interleave_transfer.rs @@ -12,10 +12,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel 0 interrupt using Embassy-style macro @@ -125,22 +124,29 @@ async fn main(_spawner: Spawner) { // Reset channel state t.ch_csr().write(|w| { - w.erq().disable() - .earq().disable() - .eei().no_error() - .ebw().disable() - .done().clear_bit_by_one() + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() }); t.ch_es().write(|w| w.bits(0)); t.ch_int().write(|w| w.int().clear_bit_by_one()); // Source/destination addresses - t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32)); - t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + t.tcd_saddr() + .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32)); + t.tcd_daddr() + .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); // Custom offsets for interleaving - t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read - t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write + t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read + t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write // Attributes: 32-bit transfers (size = 2) t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); @@ -153,7 +159,8 @@ async fn main(_spawner: Spawner) { t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); // Destination uses 2x offset, so adjust accordingly let dst_total = (HALF_BUFF_LENGTH * 8) as u32; - t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); + t.tcd_dlast_sga() + .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); // Major loop count = 1 t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); @@ -172,7 +179,9 @@ async fn main(_spawner: Spawner) { while !dma_ch0.is_done() { cortex_m::asm::nop(); } - unsafe { dma_ch0.clear_done(); } + unsafe { + dma_ch0.clear_done(); + } tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") .unwrap(); @@ -206,4 +215,3 @@ async fn main(_spawner: Spawner) { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/dma_mem_to_mem.rs b/examples/src/bin/dma_mem_to_mem.rs index 01e5edb1e..72916384f 100644 --- a/examples/src/bin/dma_mem_to_mem.rs +++ b/examples/src/bin/dma_mem_to_mem.rs @@ -15,10 +15,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, TransferOptions}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel 0 interrupt using Embassy-style macro @@ -147,8 +146,7 @@ async fn main(_spawner: Spawner) { transfer.await; } - tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n") - .unwrap(); + tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap(); tx.blocking_write(b"Destination Buffer (after): ").unwrap(); print_buffer(&mut tx, &raw const DEST_BUFFER); tx.blocking_write(b"\r\n").unwrap(); @@ -181,7 +179,8 @@ async fn main(_spawner: Spawner) { // - Incrementing destination address // - Uses the same Transfer future pattern - tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n").unwrap(); + tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n") + .unwrap(); tx.blocking_write(b"Memset Buffer (before): ").unwrap(); print_buffer(&mut tx, &raw const MEMSET_BUFFER); @@ -230,4 +229,3 @@ async fn main(_spawner: Spawner) { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/dma_memset.rs b/examples/src/bin/dma_memset.rs index 8a1636e57..9fbba85e9 100644 --- a/examples/src/bin/dma_memset.rs +++ b/examples/src/bin/dma_memset.rs @@ -12,10 +12,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel 0 interrupt using Embassy-style macro @@ -92,8 +91,7 @@ async fn main(_spawner: Spawner) { let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); let (mut tx, _rx) = lpuart.split(); - tx.blocking_write(b"EDMA memset example begin.\r\n\r\n") - .unwrap(); + tx.blocking_write(b"EDMA memset example begin.\r\n\r\n").unwrap(); // Initialize buffers unsafe { @@ -133,19 +131,26 @@ async fn main(_spawner: Spawner) { // Reset channel state t.ch_csr().write(|w| { - w.erq().disable() - .earq().disable() - .eei().no_error() - .ebw().disable() - .done().clear_bit_by_one() + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() }); t.ch_es().write(|w| w.bits(0)); t.ch_int().write(|w| w.int().clear_bit_by_one()); // Source address (pattern) - fixed - t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32)); + t.tcd_saddr() + .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32)); // Destination address - increments - t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + t.tcd_daddr() + .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); // Source offset = 0 (stays fixed), Dest offset = 4 (increments) t.tcd_soff().write(|w| w.soff().bits(0)); @@ -180,10 +185,11 @@ async fn main(_spawner: Spawner) { while !dma_ch0.is_done() { cortex_m::asm::nop(); } - unsafe { dma_ch0.clear_done(); } + unsafe { + dma_ch0.clear_done(); + } - tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n") - .unwrap(); + tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n").unwrap(); tx.blocking_write(b"Destination Buffer (after): ").unwrap(); print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); tx.blocking_write(b"\r\n\r\n").unwrap(); @@ -212,4 +218,3 @@ async fn main(_spawner: Spawner) { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/dma_ping_pong_transfer.rs b/examples/src/bin/dma_ping_pong_transfer.rs index d765ea575..692515441 100644 --- a/examples/src/bin/dma_ping_pong_transfer.rs +++ b/examples/src/bin/dma_ping_pong_transfer.rs @@ -24,12 +24,12 @@ #![no_main] use core::sync::atomic::{AtomicBool, Ordering}; + use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, DmaCh1InterruptHandler, Tcd, TransferOptions}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{self, DmaCh1InterruptHandler, DmaChannel, Tcd, TransferOptions}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Source and destination buffers for Approach 1 (scatter/gather) @@ -44,19 +44,21 @@ static mut DST2: [u32; 8] = [0; 8]; #[repr(C, align(32))] struct TcdPool([Tcd; 2]); -static mut TCD_POOL: TcdPool = TcdPool([Tcd { - saddr: 0, - soff: 0, - attr: 0, - nbytes: 0, - slast: 0, - daddr: 0, - doff: 0, - citer: 0, - dlast_sga: 0, - csr: 0, - biter: 0, -}; 2]); +static mut TCD_POOL: TcdPool = TcdPool( + [Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + biter: 0, + }; 2], +); // AtomicBool to track scatter/gather completion // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, @@ -289,7 +291,8 @@ async fn main(_spawner: Spawner) { // - True async/await support // - Good for streaming data processing - tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n").unwrap(); + tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n") + .unwrap(); // Enable DMA CH1 interrupt unsafe { @@ -310,10 +313,11 @@ async fn main(_spawner: Spawner) { // Configure transfer with half-transfer interrupt enabled let mut options = TransferOptions::default(); - options.half_transfer_interrupt = true; // Enable half-transfer interrupt + options.half_transfer_interrupt = true; // Enable half-transfer interrupt options.complete_transfer_interrupt = true; - tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n").unwrap(); + tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n") + .unwrap(); unsafe { let src = &*core::ptr::addr_of!(SRC2); @@ -327,10 +331,12 @@ async fn main(_spawner: Spawner) { let half_ok = transfer.wait_half().await; if half_ok { - tx.blocking_write(b"Half-transfer complete! First half of DST2: ").unwrap(); + tx.blocking_write(b"Half-transfer complete! First half of DST2: ") + .unwrap(); print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b"(Processing first half while second half transfers...)\r\n").unwrap(); + tx.blocking_write(b"(Processing first half while second half transfers...)\r\n") + .unwrap(); } // Wait for complete transfer @@ -363,10 +369,10 @@ async fn main(_spawner: Spawner) { defmt::info!("PASS: Approach 2 verified."); } - tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n").unwrap(); + tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n") + .unwrap(); loop { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs index d78605acc..b5ae00057 100644 --- a/examples/src/bin/dma_scatter_gather.rs +++ b/examples/src/bin/dma_scatter_gather.rs @@ -13,12 +13,12 @@ #![no_main] use core::sync::atomic::{AtomicBool, Ordering}; + use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; 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 embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Source and destination buffers @@ -29,19 +29,21 @@ static mut DST: [u32; 8] = [0; 8]; #[repr(C, align(32))] struct TcdPool([Tcd; 2]); -static mut TCD_POOL: TcdPool = TcdPool([Tcd { - saddr: 0, - soff: 0, - attr: 0, - nbytes: 0, - slast: 0, - daddr: 0, - doff: 0, - citer: 0, - dlast_sga: 0, - csr: 0, - biter: 0, -}; 2]); +static mut TCD_POOL: TcdPool = TcdPool( + [Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + biter: 0, + }; 2], +); // AtomicBool to track scatter/gather completion // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, @@ -53,7 +55,9 @@ static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); // (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 { +impl embassy_mcxa::interrupt::typelevel::Handler + for ScatterGatherDmaHandler +{ unsafe fn on_interrupt() { // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers dma::on_interrupt(0); @@ -161,10 +165,7 @@ async fn main(_spawner: Spawner) { // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. unsafe { - let tcds = core::slice::from_raw_parts_mut( - core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, - 2, - ); + let tcds = core::slice::from_raw_parts_mut(core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, 2); let src_ptr = core::ptr::addr_of!(SRC) as *const u32; let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; @@ -262,4 +263,3 @@ async fn main(_spawner: Spawner) { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/dma_scatter_gather_builder.rs b/examples/src/bin/dma_scatter_gather_builder.rs index 51bfbeb67..d42ff841e 100644 --- a/examples/src/bin/dma_scatter_gather_builder.rs +++ b/examples/src/bin/dma_scatter_gather_builder.rs @@ -22,10 +22,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, ScatterGatherBuilder}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, ScatterGatherBuilder}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel 0 interrupt @@ -97,7 +96,8 @@ async fn main(_spawner: Spawner) { let (mut tx, _rx) = lpuart.split(); tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap(); - tx.blocking_write(b"===================================\r\n\r\n").unwrap(); + tx.blocking_write(b"===================================\r\n\r\n") + .unwrap(); // Show source buffers tx.blocking_write(b"Source buffers:\r\n").unwrap(); @@ -125,7 +125,8 @@ async fn main(_spawner: Spawner) { // Create DMA channel let dma_ch0 = DmaChannel::new(p.DMA_CH0); - tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n").unwrap(); + tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n") + .unwrap(); // ========================================================================= // ScatterGatherBuilder API demonstration @@ -159,7 +160,8 @@ async fn main(_spawner: Spawner) { } tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap(); - tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n").unwrap(); + tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n") + .unwrap(); // Build and execute the scatter-gather chain // The build() method: @@ -222,7 +224,8 @@ async fn main(_spawner: Spawner) { defmt::error!("FAIL: Mismatch detected!"); } - tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n").unwrap(); + tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n") + .unwrap(); loop { cortex_m::asm::wfe(); diff --git a/examples/src/bin/dma_wrap_transfer.rs b/examples/src/bin/dma_wrap_transfer.rs index 8e9aedbfb..0babf4c20 100644 --- a/examples/src/bin/dma_wrap_transfer.rs +++ b/examples/src/bin/dma_wrap_transfer.rs @@ -12,10 +12,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; -use embassy_mcxa::bind_interrupts; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; +use embassy_mcxa::{bind_interrupts, pac}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel 0 interrupt using Embassy-style macro @@ -93,8 +92,7 @@ async fn main(_spawner: Spawner) { let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); let (mut tx, _rx) = lpuart.split(); - tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n") - .unwrap(); + tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n").unwrap(); // Initialize buffers unsafe { @@ -127,18 +125,25 @@ async fn main(_spawner: Spawner) { // Reset channel state t.ch_csr().write(|w| { - w.erq().disable() - .earq().disable() - .eei().no_error() - .ebw().disable() - .done().clear_bit_by_one() + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() }); t.ch_es().write(|w| w.bits(0)); t.ch_int().write(|w| w.int().clear_bit_by_one()); // Source/destination addresses - t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32)); - t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32)); + t.tcd_saddr() + .write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32)); + t.tcd_daddr() + .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32)); // Offsets: both increment by 4 bytes t.tcd_soff().write(|w| w.soff().bits(4)); @@ -147,10 +152,14 @@ async fn main(_spawner: Spawner) { // Attributes: 32-bit transfers (size = 2) // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled) t.tcd_attr().write(|w| { - w.ssize().bits(2) - .dsize().bits(2) - .smod().bits(4) // Source modulo: 2^4 = 16 bytes - .dmod().bits(0) // Dest modulo: disabled + w.ssize() + .bits(2) + .dsize() + .bits(2) + .smod() + .bits(4) // Source modulo: 2^4 = 16 bytes + .dmod() + .bits(0) // Dest modulo: disabled }); // Transfer 32 bytes total in one minor loop @@ -179,7 +188,9 @@ async fn main(_spawner: Spawner) { while !dma_ch0.is_done() { cortex_m::asm::nop(); } - unsafe { dma_ch0.clear_done(); } + unsafe { + dma_ch0.clear_done(); + } tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") .unwrap(); @@ -211,4 +222,3 @@ async fn main(_spawner: Spawner) { cortex_m::asm::wfe(); } } - diff --git a/examples/src/bin/lpuart_dma.rs b/examples/src/bin/lpuart_dma.rs index 4e321b111..f4dfbcf39 100644 --- a/examples/src/bin/lpuart_dma.rs +++ b/examples/src/bin/lpuart_dma.rs @@ -48,8 +48,7 @@ async fn main(_spawner: Spawner) { // Create UART instance with DMA channels let mut lpuart = LpuartDma::new( - p.LPUART2, - p.P2_2, // TX pin + p.LPUART2, p.P2_2, // TX pin p.P2_3, // RX pin p.DMA_CH0, // TX DMA channel p.DMA_CH1, // RX DMA channel @@ -82,4 +81,3 @@ async fn main(_spawner: Spawner) { defmt::info!("Example complete"); } - diff --git a/examples/src/bin/lpuart_ring_buffer.rs b/examples/src/bin/lpuart_ring_buffer.rs index d71876ade..6cc14f1c7 100644 --- a/examples/src/bin/lpuart_ring_buffer.rs +++ b/examples/src/bin/lpuart_ring_buffer.rs @@ -20,7 +20,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX}; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaChannel, 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 _}; @@ -91,7 +91,8 @@ async fn main(_spawner: Spawner) { dma_ch_rx.set_request_source(DMA_REQ_LPUART2_RX); } - tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap(); + tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n") + .unwrap(); // Set up the ring buffer with circular DMA // This configures the DMA for continuous reception @@ -105,8 +106,10 @@ async fn main(_spawner: Spawner) { dma_ch_rx.enable_request(); } - tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap(); - tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n").unwrap(); + tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n") + .unwrap(); + tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n") + .unwrap(); // Main loop: read from ring buffer and echo back let mut read_buf = [0u8; 16]; @@ -144,4 +147,3 @@ async fn main(_spawner: Spawner) { } } } - -- cgit From bc21f9b35e09fe9ef2556adf53f1d600af909d03 Mon Sep 17 00:00:00 2001 From: Bogdan Petru Chircu Mare Date: Mon, 1 Dec 2025 17:07:58 -0800 Subject: refactor(dma): move DMA_MAX_TRANSFER_SIZE to dma module and init during HAL startup Address felipebalbi's review comments on PR #52: - Move DMA_MAX_TRANSFER_SIZE constant from lpuart/mod.rs to dma.rs where it logically belongs (describes eDMA4 hardware limitation) - Add public dma::init() function called during hal::init() instead of lazy initialization via ensure_init() - Remove ensure_init() entirely since it's no longer needed - Remove ensure_init() calls from DmaChannel::new() and from_token() - Remove ensure_init() calls from examples (dma_channel_link, dma_scatter_gather) - Refactor lpuart_ring_buffer example to use LpuartDma::new() + split() pattern instead of separate TX/RX drivers - Add [lints.rust] section to suppress unexpected_cfgs warning for 'rt' feature used by embassy_hal_internal::interrupt_mod! macro This makes DMA initialization explicit during HAL startup (like GPIO) and keeps DMA-specific constants in the DMA module. --- examples/src/bin/dma_channel_link.rs | 5 ++- examples/src/bin/dma_scatter_gather.rs | 3 +- examples/src/bin/lpuart_ring_buffer.rs | 57 ++++++++++++---------------------- 3 files changed, 23 insertions(+), 42 deletions(-) (limited to 'examples') diff --git a/examples/src/bin/dma_channel_link.rs b/examples/src/bin/dma_channel_link.rs index 34162d931..361c9ebc7 100644 --- a/examples/src/bin/dma_channel_link.rs +++ b/examples/src/bin/dma_channel_link.rs @@ -18,7 +18,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler, DmaChannel}; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler, DmaChannel}; 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 _}; @@ -85,8 +85,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA channel link example starting..."); - // Ensure DMA is initialized (clock/reset/init handled automatically by HAL) - dma::ensure_init(); + // DMA is initialized during hal::init() - no need to call ensure_init() let pac_periphs = unsafe { pac::Peripherals::steal() }; let dma0 = &pac_periphs.dma0; diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs index b5ae00057..9844071b7 100644 --- a/examples/src/bin/dma_scatter_gather.rs +++ b/examples/src/bin/dma_scatter_gather.rs @@ -120,8 +120,7 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA scatter-gather transfer example starting..."); - // Ensure DMA is initialized (clock/reset/init handled automatically by HAL) - dma::ensure_init(); + // DMA is initialized during hal::init() - no need to call ensure_init() // Enable DMA interrupt unsafe { diff --git a/examples/src/bin/lpuart_ring_buffer.rs b/examples/src/bin/lpuart_ring_buffer.rs index 6cc14f1c7..0946bad03 100644 --- a/examples/src/bin/lpuart_ring_buffer.rs +++ b/examples/src/bin/lpuart_ring_buffer.rs @@ -1,18 +1,18 @@ //! LPUART Ring Buffer DMA example for MCXA276. //! -//! This example demonstrates using the new `RingBuffer` API for continuous -//! circular DMA reception from a UART peripheral. +//! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()` +//! API for continuous circular DMA reception from a UART peripheral. //! //! # Features demonstrated: -//! - `setup_circular_read()` for continuous peripheral-to-memory DMA +//! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA //! - `RingBuffer` for async reading of received data //! - Handling of potential overrun conditions //! - Half-transfer and complete-transfer interrupts for timely wakeups //! //! # How it works: -//! 1. Set up a circular DMA transfer from LPUART RX to a ring buffer -//! 2. DMA continuously writes received bytes into the buffer, wrapping around -//! 3. Application asynchronously reads data as it arrives +//! 1. Create an `LpuartRxDma` driver with a DMA channel +//! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration +//! 3. Application asynchronously reads data as it arrives via `ring_buf.read()` //! 4. Both half-transfer and complete-transfer interrupts wake the reader #![no_std] @@ -20,9 +20,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaChannel, DMA_REQ_LPUART2_RX}; -use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::{bind_interrupts, pac}; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; +use embassy_mcxa::lpuart::{Config, LpuartDma, LpuartTxDma}; +use embassy_mcxa::bind_interrupts; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Bind DMA channel interrupts @@ -35,7 +35,10 @@ bind_interrupts!(struct Irqs { static mut RX_RING_BUFFER: [u8; 64] = [0; 64]; /// Helper to write a byte as hex to UART -fn write_hex(tx: &mut LpuartTx<'_, Blocking>, byte: u8) { +fn write_hex( + tx: &mut LpuartTxDma<'_, T, C>, + byte: u8, +) { const HEX: &[u8; 16] = b"0123456789ABCDEF"; let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]]; tx.blocking_write(&buf).ok(); @@ -55,12 +58,6 @@ async fn main(_spawner: Spawner) { defmt::info!("LPUART Ring Buffer DMA example starting..."); - // 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); - } - // Create UART configuration let config = Config { baudrate_bps: 115_200, @@ -69,41 +66,27 @@ async fn main(_spawner: Spawner) { ..Default::default() }; - // Create blocking UART for TX (we'll use DMA for RX only) - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); + // Create LPUART with DMA support for both TX and RX, then split + // This is the proper Embassy pattern - create once, split into TX and RX + let lpuart = LpuartDma::new(p.LPUART2, p.P2_2, p.P2_3, p.DMA_CH1, p.DMA_CH0, config).unwrap(); + let (mut tx, rx) = lpuart.split(); tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap(); tx.blocking_write(b"==============================\r\n\r\n").unwrap(); - // Get LPUART2 RX data register address for DMA - let lpuart2 = unsafe { &*pac::Lpuart2::ptr() }; - let rx_data_addr = lpuart2.data().as_ptr() as *const u8; - - // Enable RX DMA request in LPUART - lpuart2.baud().modify(|_, w| w.rdmae().enabled()); - - // Create DMA channel for RX - let dma_ch_rx = DmaChannel::new(p.DMA_CH0); - - // Configure the DMA mux for LPUART2 RX - unsafe { - dma_ch_rx.set_request_source(DMA_REQ_LPUART2_RX); - } - tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n") .unwrap(); // Set up the ring buffer with circular DMA - // This configures the DMA for continuous reception + // 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); - dma_ch_rx.setup_circular_read(rx_data_addr, buf) + rx.setup_ring_buffer(buf) }; // Enable DMA requests to start continuous reception unsafe { - dma_ch_rx.enable_request(); + rx.enable_dma_request(); } tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n") -- cgit From b252db845e19603faf528cf93fe0c44757a27430 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 5 Dec 2025 14:28:47 +0100 Subject: Move --- examples/.cargo/config.toml | 17 - examples/.gitignore | 1 - examples/Cargo.lock | 1555 -------------------- examples/Cargo.toml | 26 - examples/build.rs | 20 - examples/mcxa/.cargo/config.toml | 17 + examples/mcxa/.gitignore | 1 + examples/mcxa/Cargo.lock | 1555 ++++++++++++++++++++ examples/mcxa/Cargo.toml | 26 + examples/mcxa/build.rs | 20 + examples/mcxa/memory.x | 5 + examples/mcxa/src/bin/adc_interrupt.rs | 84 ++ examples/mcxa/src/bin/adc_polling.rs | 68 + examples/mcxa/src/bin/blinky.rs | 36 + examples/mcxa/src/bin/button.rs | 23 + examples/mcxa/src/bin/button_async.rs | 29 + examples/mcxa/src/bin/clkout.rs | 69 + examples/mcxa/src/bin/dma_channel_link.rs | 372 +++++ examples/mcxa/src/bin/dma_interleave_transfer.rs | 215 +++ examples/mcxa/src/bin/dma_mem_to_mem.rs | 229 +++ examples/mcxa/src/bin/dma_memset.rs | 218 +++ examples/mcxa/src/bin/dma_ping_pong_transfer.rs | 376 +++++ examples/mcxa/src/bin/dma_scatter_gather.rs | 262 ++++ .../mcxa/src/bin/dma_scatter_gather_builder.rs | 231 +++ examples/mcxa/src/bin/dma_wrap_transfer.rs | 222 +++ examples/mcxa/src/bin/hello.rs | 119 ++ examples/mcxa/src/bin/i2c-blocking.rs | 31 + examples/mcxa/src/bin/i2c-scan-blocking.rs | 41 + examples/mcxa/src/bin/lpuart_buffered.rs | 62 + examples/mcxa/src/bin/lpuart_dma.rs | 81 + examples/mcxa/src/bin/lpuart_polling.rs | 47 + examples/mcxa/src/bin/lpuart_ring_buffer.rs | 130 ++ examples/mcxa/src/bin/rtc_alarm.rs | 74 + examples/mcxa/src/lib.rs | 16 + examples/memory.x | 5 - examples/src/bin/adc_interrupt.rs | 84 -- examples/src/bin/adc_polling.rs | 68 - examples/src/bin/blinky.rs | 36 - examples/src/bin/button.rs | 23 - examples/src/bin/button_async.rs | 29 - examples/src/bin/clkout.rs | 69 - examples/src/bin/dma_channel_link.rs | 372 ----- examples/src/bin/dma_interleave_transfer.rs | 215 --- examples/src/bin/dma_mem_to_mem.rs | 229 --- examples/src/bin/dma_memset.rs | 218 --- examples/src/bin/dma_ping_pong_transfer.rs | 376 ----- examples/src/bin/dma_scatter_gather.rs | 262 ---- examples/src/bin/dma_scatter_gather_builder.rs | 231 --- examples/src/bin/dma_wrap_transfer.rs | 222 --- examples/src/bin/hello.rs | 119 -- examples/src/bin/i2c-blocking.rs | 31 - examples/src/bin/i2c-scan-blocking.rs | 41 - examples/src/bin/lpuart_buffered.rs | 62 - examples/src/bin/lpuart_dma.rs | 81 - examples/src/bin/lpuart_polling.rs | 47 - examples/src/bin/lpuart_ring_buffer.rs | 130 -- examples/src/bin/rtc_alarm.rs | 74 - examples/src/lib.rs | 16 - 58 files changed, 4659 insertions(+), 4659 deletions(-) delete mode 100644 examples/.cargo/config.toml delete mode 100644 examples/.gitignore delete mode 100644 examples/Cargo.lock delete mode 100644 examples/Cargo.toml delete mode 100644 examples/build.rs create mode 100644 examples/mcxa/.cargo/config.toml create mode 100644 examples/mcxa/.gitignore create mode 100644 examples/mcxa/Cargo.lock create mode 100644 examples/mcxa/Cargo.toml create mode 100644 examples/mcxa/build.rs create mode 100644 examples/mcxa/memory.x create mode 100644 examples/mcxa/src/bin/adc_interrupt.rs create mode 100644 examples/mcxa/src/bin/adc_polling.rs create mode 100644 examples/mcxa/src/bin/blinky.rs create mode 100644 examples/mcxa/src/bin/button.rs create mode 100644 examples/mcxa/src/bin/button_async.rs create mode 100644 examples/mcxa/src/bin/clkout.rs create mode 100644 examples/mcxa/src/bin/dma_channel_link.rs create mode 100644 examples/mcxa/src/bin/dma_interleave_transfer.rs create mode 100644 examples/mcxa/src/bin/dma_mem_to_mem.rs create mode 100644 examples/mcxa/src/bin/dma_memset.rs create mode 100644 examples/mcxa/src/bin/dma_ping_pong_transfer.rs create mode 100644 examples/mcxa/src/bin/dma_scatter_gather.rs create mode 100644 examples/mcxa/src/bin/dma_scatter_gather_builder.rs create mode 100644 examples/mcxa/src/bin/dma_wrap_transfer.rs create mode 100644 examples/mcxa/src/bin/hello.rs create mode 100644 examples/mcxa/src/bin/i2c-blocking.rs create mode 100644 examples/mcxa/src/bin/i2c-scan-blocking.rs create mode 100644 examples/mcxa/src/bin/lpuart_buffered.rs create mode 100644 examples/mcxa/src/bin/lpuart_dma.rs create mode 100644 examples/mcxa/src/bin/lpuart_polling.rs create mode 100644 examples/mcxa/src/bin/lpuart_ring_buffer.rs create mode 100644 examples/mcxa/src/bin/rtc_alarm.rs create mode 100644 examples/mcxa/src/lib.rs delete mode 100644 examples/memory.x delete mode 100644 examples/src/bin/adc_interrupt.rs delete mode 100644 examples/src/bin/adc_polling.rs delete mode 100644 examples/src/bin/blinky.rs delete mode 100644 examples/src/bin/button.rs delete mode 100644 examples/src/bin/button_async.rs delete mode 100644 examples/src/bin/clkout.rs delete mode 100644 examples/src/bin/dma_channel_link.rs delete mode 100644 examples/src/bin/dma_interleave_transfer.rs delete mode 100644 examples/src/bin/dma_mem_to_mem.rs delete mode 100644 examples/src/bin/dma_memset.rs delete mode 100644 examples/src/bin/dma_ping_pong_transfer.rs delete mode 100644 examples/src/bin/dma_scatter_gather.rs delete mode 100644 examples/src/bin/dma_scatter_gather_builder.rs delete mode 100644 examples/src/bin/dma_wrap_transfer.rs delete mode 100644 examples/src/bin/hello.rs delete mode 100644 examples/src/bin/i2c-blocking.rs delete mode 100644 examples/src/bin/i2c-scan-blocking.rs delete mode 100644 examples/src/bin/lpuart_buffered.rs delete mode 100644 examples/src/bin/lpuart_dma.rs delete mode 100644 examples/src/bin/lpuart_polling.rs delete mode 100644 examples/src/bin/lpuart_ring_buffer.rs delete mode 100644 examples/src/bin/rtc_alarm.rs delete mode 100644 examples/src/lib.rs (limited to 'examples') diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml deleted file mode 100644 index aedc55b06..000000000 --- a/examples/.cargo/config.toml +++ /dev/null @@ -1,17 +0,0 @@ -[target.thumbv8m.main-none-eabihf] -runner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000' - -rustflags = [ - "-C", "linker=flip-link", - "-C", "link-arg=-Tlink.x", - "-C", "link-arg=-Tdefmt.x", - # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x - # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 - "-C", "link-arg=--nmagic", -] - -[build] -target = "thumbv8m.main-none-eabihf" # Cortex-M33 - -[env] -DEFMT_LOG = "trace" diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index 2f7896d1d..000000000 --- a/examples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target/ diff --git a/examples/Cargo.lock b/examples/Cargo.lock deleted file mode 100644 index c6e864df2..000000000 --- a/examples/Cargo.lock +++ /dev/null @@ -1,1555 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "askama" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" -dependencies = [ - "askama_derive", - "itoa", - "percent-encoding", - "serde", - "serde_json", -] - -[[package]] -name = "askama_derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" -dependencies = [ - "askama_parser", - "memchr", - "proc-macro2", - "quote", - "rustc-hash", - "syn 2.0.110", -] - -[[package]] -name = "askama_parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" -dependencies = [ - "memchr", - "winnow 0.7.13", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "bare-metal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "bitfield" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cc" -version = "1.2.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cordyceps" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" -dependencies = [ - "loom", - "tracing", -] - -[[package]] -name = "cortex-m" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" -dependencies = [ - "bare-metal", - "bitfield", - "critical-section", - "embedded-hal 0.2.7", - "volatile-register", -] - -[[package]] -name = "cortex-m-rt" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" -dependencies = [ - "cortex-m-rt-macros", -] - -[[package]] -name = "cortex-m-rt-macros" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.110", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "dd-manifest-tree" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5793572036e0a6638977c7370c6afc423eac848ee8495f079b8fd3964de7b9f9" -dependencies = [ - "toml", -] - -[[package]] -name = "defmt" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" -dependencies = [ - "defmt-parser", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror", -] - -[[package]] -name = "defmt-rtt" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" -dependencies = [ - "critical-section", - "defmt", -] - -[[package]] -name = "device-driver" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0e43acfcbb0bb3b7435cc1b1dbb33596cacfec1eb243336b74a398e0bd6cbf" -dependencies = [ - "device-driver-macros", - "embedded-io", - "embedded-io-async", -] - -[[package]] -name = "device-driver-generation" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3935aec9cf5bb2ab927f59ca69faecf976190390b0ce34c6023889e9041040c0" -dependencies = [ - "anyhow", - "askama", - "bitvec", - "convert_case", - "dd-manifest-tree", - "itertools", - "kdl", - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "device-driver-macros" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fdc68ed515c4eddff2e95371185b4becba066085bf36d50f07f09782af98e17" -dependencies = [ - "device-driver-generation", - "proc-macro2", - "syn 2.0.110", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embassy-embedded-hal" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" -dependencies = [ - "embassy-futures", - "embassy-hal-internal", - "embassy-sync", - "embedded-hal 0.2.7", - "embedded-hal 1.0.0", - "embedded-hal-async", - "embedded-storage", - "embedded-storage-async", - "nb 1.1.0", -] - -[[package]] -name = "embassy-executor" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b" -dependencies = [ - "cortex-m", - "critical-section", - "document-features", - "embassy-executor-macros", - "embassy-executor-timer-queue", -] - -[[package]] -name = "embassy-executor-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfdddc3a04226828316bf31393b6903ee162238576b1584ee2669af215d55472" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "embassy-executor-timer-queue" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" - -[[package]] -name = "embassy-futures" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" - -[[package]] -name = "embassy-hal-internal" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" -dependencies = [ - "cortex-m", - "critical-section", - "num-traits", -] - -[[package]] -name = "embassy-mcxa" -version = "0.1.0" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "critical-section", - "defmt", - "embassy-embedded-hal", - "embassy-hal-internal", - "embassy-sync", - "embassy-time", - "embassy-time-driver", - "embedded-hal 0.2.7", - "embedded-hal 1.0.0", - "embedded-hal-async", - "embedded-hal-nb", - "embedded-io", - "embedded-io-async", - "heapless 0.8.0", - "maitake-sync", - "mcxa-pac", - "nb 1.1.0", - "paste", -] - -[[package]] -name = "embassy-mcxa-examples" -version = "0.1.0" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "critical-section", - "defmt", - "defmt-rtt", - "embassy-embedded-hal", - "embassy-executor", - "embassy-mcxa", - "embassy-sync", - "embassy-time", - "embassy-time-driver", - "embedded-io-async", - "heapless 0.9.2", - "panic-probe", - "tmp108", -] - -[[package]] -name = "embassy-sync" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" -dependencies = [ - "cfg-if", - "critical-section", - "embedded-io-async", - "futures-core", - "futures-sink", - "heapless 0.8.0", -] - -[[package]] -name = "embassy-time" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" -dependencies = [ - "cfg-if", - "critical-section", - "document-features", - "embassy-time-driver", - "embedded-hal 0.2.7", - "embedded-hal 1.0.0", - "embedded-hal-async", - "futures-core", -] - -[[package]] -name = "embassy-time-driver" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" -dependencies = [ - "document-features", -] - -[[package]] -name = "embedded-hal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" -dependencies = [ - "nb 0.1.3", - "void", -] - -[[package]] -name = "embedded-hal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" - -[[package]] -name = "embedded-hal-async" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" -dependencies = [ - "embedded-hal 1.0.0", -] - -[[package]] -name = "embedded-hal-nb" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" -dependencies = [ - "embedded-hal 1.0.0", - "nb 1.1.0", -] - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - -[[package]] -name = "embedded-io-async" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" -dependencies = [ - "embedded-io", -] - -[[package]] -name = "embedded-storage" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" - -[[package]] -name = "embedded-storage-async" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" -dependencies = [ - "embedded-storage", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "find-msvc-tools" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "generator" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" -dependencies = [ - "cc", - "cfg-if", - "libc", - "log", - "rustversion", - "windows", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "kdl" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a29e7b50079ff44549f68c0becb1c73d7f6de2a4ea952da77966daf3d4761e" -dependencies = [ - "miette", - "num", - "winnow 0.6.24", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "maitake-sync" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "748f86d9befd480b602c3bebc9ef30dbf2f3dfc8acc4a73d07b90f0117e6de3f" -dependencies = [ - "cordyceps", - "critical-section", - "loom", - "mutex-traits", - "mycelium-bitfield", - "pin-project", - "portable-atomic", - "tracing", -] - -[[package]] -name = "manyhow" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" -dependencies = [ - "manyhow-macros", - "proc-macro2", - "quote", - "syn 1.0.109", - "syn 2.0.110", -] - -[[package]] -name = "manyhow-macros" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" -dependencies = [ - "proc-macro-utils", - "proc-macro2", - "quote", -] - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "maybe-async-cfg" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dbfaa67a76e2623580df07d6bb5e7956c0a4bae4b418314083a9c619bd66627" -dependencies = [ - "manyhow", - "proc-macro2", - "pulldown-cmark", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "mcxa-pac" -version = "0.1.0" -source = "git+https://github.com/OpenDevicePartnership/mcxa-pac#e18dfb52500ca77b8d8326662b966a80251182ca" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "critical-section", - "defmt", - "vcell", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "miette" -version = "7.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" -dependencies = [ - "cfg-if", - "unicode-width", -] - -[[package]] -name = "mutex-traits" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3929f2b5633d29cf7b6624992e5f3c1e9334f1193423e12d17be4faf678cde3f" - -[[package]] -name = "mycelium-bitfield" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc" - -[[package]] -name = "nb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" -dependencies = [ - "nb 1.1.0", -] - -[[package]] -name = "nb" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "panic-probe" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" -dependencies = [ - "cortex-m", - "defmt", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" -dependencies = [ - "critical-section", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "proc-macro-utils" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" -dependencies = [ - "proc-macro2", - "quote", - "smallvec", -] - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pulldown-cmark" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" -dependencies = [ - "bitflags 2.10.0", - "memchr", - "unicase", -] - -[[package]] -name = "quote" -version = "1.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "serde_json" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", - "serde_core", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tmp108" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d644cc97d3cee96793f454b834881f78b5d4e89c90ecf26b3690f42004d111" -dependencies = [ - "device-driver", - "embedded-hal 1.0.0", - "maybe-async-cfg", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow 0.7.13", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "tracing" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb41cbdb933e23b7929f47bb577710643157d7602ef3a2ebd3902b13ac5eda6" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "tracing-core" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vcell" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "volatile-register" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" -dependencies = [ - "vcell", -] - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core", - "windows-link 0.1.3", - "windows-threading", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "winnow" -version = "0.6.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] diff --git a/examples/Cargo.toml b/examples/Cargo.toml deleted file mode 100644 index a1092c416..000000000 --- a/examples/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "embassy-mcxa-examples" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - -[dependencies] -cortex-m = { version = "0.7", features = ["critical-section-single-core"] } -cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] } -critical-section = "1.2.0" -defmt = "1.0" -defmt-rtt = "1.0" -embassy-embedded-hal = "0.5.0" -embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false } -embassy-mcxa = { path = "../", features = ["defmt", "unstable-pac", "time"] } -embassy-sync = "0.7.2" -embassy-time = "0.5.0" -embassy-time-driver = "0.2.1" -embedded-io-async = "0.6.1" -heapless = "0.9.2" -panic-probe = { version = "1.0", features = ["print-defmt"] } -tmp108 = "0.4.0" - -[profile.release] -lto = true # better optimizations -debug = 2 # enough information for defmt/rtt locations diff --git a/examples/build.rs b/examples/build.rs deleted file mode 100644 index f076bba9f..000000000 --- a/examples/build.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - -fn main() { - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH" - // cortex-m-rt expects FLASH for code, RAM for data/bss/stack - // Both are in RAM, but separated to satisfy cortex-m-rt's expectations - // MCXA256 has 128KB RAM total - File::create(out.join("memory.x")) - .unwrap() - .write_all(include_bytes!("memory.x")) - .unwrap(); - - println!("cargo:rustc-link-search={}", out.display()); - println!("cargo:rerun-if-changed=memory.x"); -} diff --git a/examples/mcxa/.cargo/config.toml b/examples/mcxa/.cargo/config.toml new file mode 100644 index 000000000..aedc55b06 --- /dev/null +++ b/examples/mcxa/.cargo/config.toml @@ -0,0 +1,17 @@ +[target.thumbv8m.main-none-eabihf] +runner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000' + +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] + +[build] +target = "thumbv8m.main-none-eabihf" # Cortex-M33 + +[env] +DEFMT_LOG = "trace" diff --git a/examples/mcxa/.gitignore b/examples/mcxa/.gitignore new file mode 100644 index 000000000..2f7896d1d --- /dev/null +++ b/examples/mcxa/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/examples/mcxa/Cargo.lock b/examples/mcxa/Cargo.lock new file mode 100644 index 000000000..c6e864df2 --- /dev/null +++ b/examples/mcxa/Cargo.lock @@ -0,0 +1,1555 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "askama" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" +dependencies = [ + "askama_parser", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "syn 2.0.110", +] + +[[package]] +name = "askama_parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" +dependencies = [ + "memchr", + "winnow 0.7.13", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cordyceps" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +dependencies = [ + "loom", + "tracing", +] + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield", + "critical-section", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.110", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "dd-manifest-tree" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5793572036e0a6638977c7370c6afc423eac848ee8495f079b8fd3964de7b9f9" +dependencies = [ + "toml", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "device-driver" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0e43acfcbb0bb3b7435cc1b1dbb33596cacfec1eb243336b74a398e0bd6cbf" +dependencies = [ + "device-driver-macros", + "embedded-io", + "embedded-io-async", +] + +[[package]] +name = "device-driver-generation" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3935aec9cf5bb2ab927f59ca69faecf976190390b0ce34c6023889e9041040c0" +dependencies = [ + "anyhow", + "askama", + "bitvec", + "convert_case", + "dd-manifest-tree", + "itertools", + "kdl", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "device-driver-macros" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fdc68ed515c4eddff2e95371185b4becba066085bf36d50f07f09782af98e17" +dependencies = [ + "device-driver-generation", + "proc-macro2", + "syn 2.0.110", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embassy-embedded-hal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" +dependencies = [ + "embassy-futures", + "embassy-hal-internal", + "embassy-sync", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + +[[package]] +name = "embassy-executor" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b" +dependencies = [ + "cortex-m", + "critical-section", + "document-features", + "embassy-executor-macros", + "embassy-executor-timer-queue", +] + +[[package]] +name = "embassy-executor-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfdddc3a04226828316bf31393b6903ee162238576b1584ee2669af215d55472" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "embassy-executor-timer-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" + +[[package]] +name = "embassy-futures" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" + +[[package]] +name = "embassy-hal-internal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" +dependencies = [ + "cortex-m", + "critical-section", + "num-traits", +] + +[[package]] +name = "embassy-mcxa" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt", + "embassy-embedded-hal", + "embassy-hal-internal", + "embassy-sync", + "embassy-time", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "heapless 0.8.0", + "maitake-sync", + "mcxa-pac", + "nb 1.1.0", + "paste", +] + +[[package]] +name = "embassy-mcxa-examples" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt", + "defmt-rtt", + "embassy-embedded-hal", + "embassy-executor", + "embassy-mcxa", + "embassy-sync", + "embassy-time", + "embassy-time-driver", + "embedded-io-async", + "heapless 0.9.2", + "panic-probe", + "tmp108", +] + +[[package]] +name = "embassy-sync" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-core", + "futures-sink", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" +dependencies = [ + "document-features", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-storage" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" + +[[package]] +name = "embedded-storage-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" +dependencies = [ + "embedded-storage", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "generator" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "kdl" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a29e7b50079ff44549f68c0becb1c73d7f6de2a4ea952da77966daf3d4761e" +dependencies = [ + "miette", + "num", + "winnow 0.6.24", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "maitake-sync" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "748f86d9befd480b602c3bebc9ef30dbf2f3dfc8acc4a73d07b90f0117e6de3f" +dependencies = [ + "cordyceps", + "critical-section", + "loom", + "mutex-traits", + "mycelium-bitfield", + "pin-project", + "portable-atomic", + "tracing", +] + +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 1.0.109", + "syn 2.0.110", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "maybe-async-cfg" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dbfaa67a76e2623580df07d6bb5e7956c0a4bae4b418314083a9c619bd66627" +dependencies = [ + "manyhow", + "proc-macro2", + "pulldown-cmark", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "mcxa-pac" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/mcxa-pac#e18dfb52500ca77b8d8326662b966a80251182ca" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt", + "vcell", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "cfg-if", + "unicode-width", +] + +[[package]] +name = "mutex-traits" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3929f2b5633d29cf7b6624992e5f3c1e9334f1193423e12d17be4faf678cde3f" + +[[package]] +name = "mycelium-bitfield" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +dependencies = [ + "critical-section", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" +dependencies = [ + "bitflags 2.10.0", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tmp108" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d644cc97d3cee96793f454b834881f78b5d4e89c90ecf26b3690f42004d111" +dependencies = [ + "device-driver", + "embedded-hal 1.0.0", + "maybe-async-cfg", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow 0.7.13", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tracing" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb41cbdb933e23b7929f47bb577710643157d7602ef3a2ebd3902b13ac5eda6" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "tracing-core" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/examples/mcxa/Cargo.toml b/examples/mcxa/Cargo.toml new file mode 100644 index 000000000..a1092c416 --- /dev/null +++ b/examples/mcxa/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "embassy-mcxa-examples" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] } +critical-section = "1.2.0" +defmt = "1.0" +defmt-rtt = "1.0" +embassy-embedded-hal = "0.5.0" +embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false } +embassy-mcxa = { path = "../", features = ["defmt", "unstable-pac", "time"] } +embassy-sync = "0.7.2" +embassy-time = "0.5.0" +embassy-time-driver = "0.2.1" +embedded-io-async = "0.6.1" +heapless = "0.9.2" +panic-probe = { version = "1.0", features = ["print-defmt"] } +tmp108 = "0.4.0" + +[profile.release] +lto = true # better optimizations +debug = 2 # enough information for defmt/rtt locations diff --git a/examples/mcxa/build.rs b/examples/mcxa/build.rs new file mode 100644 index 000000000..f076bba9f --- /dev/null +++ b/examples/mcxa/build.rs @@ -0,0 +1,20 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH" + // cortex-m-rt expects FLASH for code, RAM for data/bss/stack + // Both are in RAM, but separated to satisfy cortex-m-rt's expectations + // MCXA256 has 128KB RAM total + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/examples/mcxa/memory.x b/examples/mcxa/memory.x new file mode 100644 index 000000000..315ced58a --- /dev/null +++ b/examples/mcxa/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1M + RAM : ORIGIN = 0x20000000, LENGTH = 128K +} diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs new file mode 100644 index 000000000..83d8046b3 --- /dev/null +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::init_adc_pins; +use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; +use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::clocks::PoweredClock; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::pac::adc1::tctrl::Tcmd; +use hal::{bind_interrupts, InterruptExt}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1 => hal::adc::AdcHandler; +}); + +#[used] +#[no_mangle] +static KEEP_ADC: unsafe extern "C" fn() = ADC1; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("ADC interrupt Example"); + + unsafe { + init_adc_pins(); + } + + let adc_config = LpadcConfig { + enable_in_doze_mode: true, + conversion_average_mode: CalAvgs::Average128, + enable_analog_preliminary: true, + power_up_delay: 0x80, + reference_voltage_source: Refsel::Option3, + power_level_mode: Pwrsel::Lowest, + trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, + enable_conv_pause: false, + conv_pause_delay: 0, + fifo_watermark: 0, + power: PoweredClock::NormalEnabledDeepSleepDisabled, + source: AdcClockSel::FroLfDiv, + div: Div4::no_div(), + }; + let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + + let mut conv_command_config = adc.get_default_conv_command_config(); + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + adc.set_conv_command_config(1, &conv_command_config); + + let mut conv_trigger_config = adc.get_default_conv_trigger_config(); + conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; + conv_trigger_config.enable_hardware_trigger = false; + adc.set_conv_trigger_config(0, &conv_trigger_config); + + defmt::info!("ADC configuration done..."); + + adc.enable_interrupt(0x1); + + unsafe { + hal::interrupt::ADC1.enable(); + } + + unsafe { + cortex_m::interrupt::enable(); + } + + loop { + adc.do_software_trigger(1); + while !adc.is_interrupt_triggered() { + // Wait until the interrupt is triggered + } + defmt::info!("*** ADC interrupt TRIGGERED! ***"); + //TBD need to print the value + } +} diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs new file mode 100644 index 000000000..ddf3f586b --- /dev/null +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -0,0 +1,68 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::init_adc_pins; +use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; +use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::clocks::PoweredClock; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::pac::adc1::tctrl::Tcmd; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const G_LPADC_RESULT_SHIFT: u32 = 0; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_adc_pins(); + } + + defmt::info!("=== ADC polling Example ==="); + + let adc_config = LpadcConfig { + enable_in_doze_mode: true, + conversion_average_mode: CalAvgs::Average128, + enable_analog_preliminary: true, + power_up_delay: 0x80, + reference_voltage_source: Refsel::Option3, + power_level_mode: Pwrsel::Lowest, + trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, + enable_conv_pause: false, + conv_pause_delay: 0, + fifo_watermark: 0, + power: PoweredClock::NormalEnabledDeepSleepDisabled, + source: AdcClockSel::FroLfDiv, + div: Div4::no_div(), + }; + let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + + let mut conv_command_config = adc.get_default_conv_command_config(); + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + adc.set_conv_command_config(1, &conv_command_config); + + let mut conv_trigger_config = adc.get_default_conv_trigger_config(); + conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; + conv_trigger_config.enable_hardware_trigger = false; + adc.set_conv_trigger_config(0, &conv_trigger_config); + + defmt::info!("=== ADC configuration done... ==="); + + loop { + adc.do_software_trigger(1); + let mut result: Option = None; + while result.is_none() { + result = hal::adc::get_conv_result(); + } + let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; + defmt::info!("value: {=u16}", value); + } +} diff --git a/examples/mcxa/src/bin/blinky.rs b/examples/mcxa/src/bin/blinky.rs new file mode 100644 index 000000000..dd08ec0d9 --- /dev/null +++ b/examples/mcxa/src/bin/blinky.rs @@ -0,0 +1,36 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use hal::gpio::{DriveStrength, Level, Output, SlewRate}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("Blink example"); + + let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast); + let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast); + let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast); + + loop { + defmt::info!("Toggle LEDs"); + + red.toggle(); + Timer::after_millis(250).await; + + red.toggle(); + green.toggle(); + Timer::after_millis(250).await; + + green.toggle(); + blue.toggle(); + Timer::after_millis(250).await; + blue.toggle(); + + Timer::after_millis(250).await; + } +} diff --git a/examples/mcxa/src/bin/button.rs b/examples/mcxa/src/bin/button.rs new file mode 100644 index 000000000..943edbb15 --- /dev/null +++ b/examples/mcxa/src/bin/button.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use hal::gpio::{Input, Pull}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("Button example"); + + // This button is labeled "WAKEUP" on the FRDM-MCXA276 + // The board already has a 10K pullup + let monitor = Input::new(p.P1_7, Pull::Disabled); + + loop { + defmt::info!("Pin level is {:?}", monitor.get_level()); + Timer::after_millis(1000).await; + } +} diff --git a/examples/mcxa/src/bin/button_async.rs b/examples/mcxa/src/bin/button_async.rs new file mode 100644 index 000000000..6cc7b62cd --- /dev/null +++ b/examples/mcxa/src/bin/button_async.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use hal::gpio::{Input, Pull}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("GPIO interrupt example"); + + // This button is labeled "WAKEUP" on the FRDM-MCXA276 + // The board already has a 10K pullup + let mut pin = Input::new(p.P1_7, Pull::Disabled); + + let mut press_count = 0u32; + + loop { + pin.wait_for_falling_edge().await; + + press_count += 1; + + defmt::info!("Button pressed! Count: {}", press_count); + Timer::after_millis(50).await; + } +} diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs new file mode 100644 index 000000000..bfd963540 --- /dev/null +++ b/examples/mcxa/src/bin/clkout.rs @@ -0,0 +1,69 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; +use embassy_mcxa::clocks::PoweredClock; +use embassy_mcxa::gpio::{DriveStrength, SlewRate}; +use embassy_mcxa::{Level, Output}; +use embassy_time::Timer; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Demonstrate CLKOUT, using Pin P4.2 +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + let mut pin = p.P4_2; + let mut clkout = p.CLKOUT; + + loop { + defmt::info!("Set Low..."); + let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); + Timer::after_millis(500).await; + + defmt::info!("Set High..."); + output.set_high(); + Timer::after_millis(400).await; + + defmt::info!("Set Low..."); + output.set_low(); + Timer::after_millis(500).await; + + defmt::info!("16k..."); + // Run Clock Out with the 16K clock + let _clock_out = ClockOut::new( + clkout.reborrow(), + pin.reborrow(), + Config { + sel: ClockOutSel::Clk16K, + div: Div4::no_div(), + level: PoweredClock::NormalEnabledDeepSleepDisabled, + }, + ) + .unwrap(); + + Timer::after_millis(3000).await; + + defmt::info!("Set Low..."); + drop(_clock_out); + + let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); + Timer::after_millis(500).await; + + // Run Clock Out with the 12M clock, divided by 3 + defmt::info!("4M..."); + let _clock_out = ClockOut::new( + clkout.reborrow(), + pin.reborrow(), + Config { + sel: ClockOutSel::Fro12M, + div: const { Div4::from_divisor(3).unwrap() }, + level: PoweredClock::NormalEnabledDeepSleepDisabled, + }, + ) + .unwrap(); + + // Let it run for 3 seconds... + Timer::after_millis(3000).await; + } +} diff --git a/examples/mcxa/src/bin/dma_channel_link.rs b/examples/mcxa/src/bin/dma_channel_link.rs new file mode 100644 index 000000000..92c7a9681 --- /dev/null +++ b/examples/mcxa/src/bin/dma_channel_link.rs @@ -0,0 +1,372 @@ +//! DMA channel linking example for MCXA276. +//! +//! This example demonstrates DMA channel linking (minor and major loop linking): +//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: +//! - Minor Link to Channel 1 (triggers CH1 after each minor loop) +//! - Major Link to Channel 2 (triggers CH2 after major loop completes) +//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) +//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) +//! +//! # Embassy-style features demonstrated: +//! - `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 embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler, DmaChannel}; +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 _}; + +// Buffers +static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; +static mut DEST_BUFFER0: [u32; 4] = [0; 4]; +static mut DEST_BUFFER1: [u32; 4] = [0; 4]; +static mut DEST_BUFFER2: [u32; 4] = [0; 4]; + +// 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 => DmaCh0InterruptHandler; + DMA_CH1 => DmaCh1InterruptHandler; + DMA_CH2 => DmaCh2InterruptHandler; +}); + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA channel link example starting..."); + + // DMA is initialized during hal::init() - no need to call ensure_init() + + let pac_periphs = unsafe { pac::Peripherals::steal() }; + let dma0 = &pac_periphs.dma0; + let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; + + // Clear any residual state + for i in 0..3 { + let t = edma.tcd(i); + t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + t.ch_es().write(|w| w.err().clear_bit_by_one()); + t.ch_mux().write(|w| unsafe { w.bits(0) }); + } + + // Clear Global Halt/Error state + dma0.mp_csr().modify(|_, w| { + w.halt() + .normal_operation() + .hae() + .normal_operation() + .ecx() + .normal_operation() + .cx() + .normal_operation() + }); + + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH2); + } + + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n").unwrap(); + + // Initialize buffers + unsafe { + SRC_BUFFER = [1, 2, 3, 4]; + DEST_BUFFER0 = [0; 4]; + DEST_BUFFER1 = [0; 4]; + DEST_BUFFER2 = [0; 4]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST0 (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST1 (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST2 (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA channels with Embassy-style API...\r\n") + .unwrap(); + + let ch0 = DmaChannel::new(p.DMA_CH0); + let ch1 = DmaChannel::new(p.DMA_CH1); + 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 + + // Helper to configure TCD for memory-to-memory transfer + // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt + #[allow(clippy::too_many_arguments)] + unsafe fn configure_tcd( + edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, + ch: usize, + src: u32, + dst: u32, + width: u8, + nbytes: u32, + count: u16, + enable_int: bool, + ) { + let t = edma.tcd(ch); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr().write(|w| w.saddr().bits(src)); + t.tcd_daddr().write(|w| w.daddr().bits(dst)); + + // Offsets: increment by width + t.tcd_soff().write(|w| w.soff().bits(width as u16)); + t.tcd_doff().write(|w| w.doff().bits(width as u16)); + + // Attributes: size = log2(width) + let size = match width { + 1 => 0, + 2 => 1, + 4 => 2, + _ => 0, + }; + t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); + + // Number of bytes per minor loop + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Major loop: reset source address after major loop + let total_bytes = nbytes * count as u32; + t.tcd_slast_sda() + .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); + t.tcd_dlast_sga() + .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); + + // Major loop count + t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); + + // Control/status: enable interrupt if requested + if enable_int { + t.tcd_csr().write(|w| w.intmajor().set_bit()); + } else { + t.tcd_csr().write(|w| w.intmajor().clear_bit()); + } + + cortex_m::asm::dsb(); + } + + unsafe { + // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) + // Minor Link -> Channel 1 + // Major Link -> Channel 2 + configure_tcd( + edma, + 0, + core::ptr::addr_of!(SRC_BUFFER) as u32, + core::ptr::addr_of_mut!(DEST_BUFFER0) as u32, + 4, // src width + 8, // nbytes (minor loop = 2 words) + 2, // count (major loop = 2 iterations) + false, // no interrupt + ); + 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( + edma, + 1, + core::ptr::addr_of!(SRC_BUFFER) as u32, + core::ptr::addr_of_mut!(DEST_BUFFER1) as u32, + 4, + 16, // full buffer in one minor loop + 1, // 1 major iteration + false, + ); + + // Channel 2: Transfer 16 bytes (triggered by CH0 major link) + configure_tcd( + edma, + 2, + core::ptr::addr_of!(SRC_BUFFER) as u32, + core::ptr::addr_of_mut!(DEST_BUFFER2) as u32, + 4, + 16, // full buffer in one minor loop + 1, // 1 major iteration + true, // enable interrupt + ); + } + + tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n") + .unwrap(); + + // Trigger first minor loop of CH0 + unsafe { + ch0.trigger_start(); + } + + // Wait for CH1 to complete (triggered by CH0 minor link) + while !ch1.is_done() { + cortex_m::asm::nop(); + } + 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(); + } + + // Wait for CH0 major loop to complete + while !ch0.is_done() { + cortex_m::asm::nop(); + } + 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) + // 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(); + + tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n").unwrap(); + + tx.blocking_write(b"DEST0 (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST1 (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"DEST2 (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify all buffers match source + let mut success = true; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC_BUFFER) as *const u32; + let dst0_ptr = core::ptr::addr_of!(DEST_BUFFER0) as *const u32; + let dst1_ptr = core::ptr::addr_of!(DEST_BUFFER1) as *const u32; + let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32; + + for i in 0..4 { + if *dst0_ptr.add(i) != *src_ptr.add(i) { + success = false; + } + if *dst1_ptr.add(i) != *src_ptr.add(i) { + success = false; + } + if *dst2_ptr.add(i) != *src_ptr.add(i) { + success = false; + } + } + } + + if success { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } else { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_interleave_transfer.rs b/examples/mcxa/src/bin/dma_interleave_transfer.rs new file mode 100644 index 000000000..7876e8978 --- /dev/null +++ b/examples/mcxa/src/bin/dma_interleave_transfer.rs @@ -0,0 +1,215 @@ +//! DMA interleaved transfer example for MCXA276. +//! +//! This example demonstrates using DMA with custom source/destination offsets +//! to interleave data during transfer. +//! +//! # Embassy-style features demonstrated: +//! - `TransferOptions::default()` for configuration (used internally) +//! - DMA channel with `DmaChannel::new()` + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; +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 _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +const BUFFER_LENGTH: usize = 16; +const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; + +// Buffers in RAM +static mut SRC_BUFFER: [u32; HALF_BUFF_LENGTH] = [0; HALF_BUFF_LENGTH]; +static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA interleave transfer example starting..."); + + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA interleave transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC_BUFFER = [1, 2, 3, 4, 5, 6, 7, 8]; + DEST_BUFFER = [0; BUFFER_LENGTH]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, HALF_BUFF_LENGTH); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // Create DMA channel using Embassy-style API + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // 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 = dma_ch0.tcd(); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr() + .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32)); + t.tcd_daddr() + .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + + // Custom offsets for interleaving + t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read + t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write + + // Attributes: 32-bit transfers (size = 2) + t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); + + // Transfer entire source buffer in one minor loop + let nbytes = (HALF_BUFF_LENGTH * 4) as u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Reset source address after major loop + t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); + // Destination uses 2x offset, so adjust accordingly + let dst_total = (HALF_BUFF_LENGTH * 8) as u32; + t.tcd_dlast_sga() + .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + dma_ch0.trigger_start(); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done() { + cortex_m::asm::nop(); + } + unsafe { + dma_ch0.clear_done(); + } + + tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 + let mut mismatch = false; + unsafe { + for i in 0..BUFFER_LENGTH { + if i % 2 == 0 { + if DEST_BUFFER[i] != SRC_BUFFER[i / 2] { + mismatch = true; + } + } else if DEST_BUFFER[i] != 0 { + mismatch = true; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs new file mode 100644 index 000000000..68f70e742 --- /dev/null +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs @@ -0,0 +1,229 @@ +//! DMA memory-to-memory transfer example for MCXA276. +//! +//! This example demonstrates using DMA to copy data between memory buffers +//! using the Embassy-style async API with type-safe transfers. +//! +//! # Embassy-style features demonstrated: +//! - `TransferOptions` for configuration +//! - Type-safe `mem_to_mem()` method with async `.await` +//! - `Transfer` Future that can be `.await`ed +//! - `Word` trait for automatic transfer width detection +//! - `memset()` method for filling memory with a pattern + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; +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 _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +const BUFFER_LENGTH: usize = 4; + +// Buffers in RAM (static mut is automatically placed in .bss/.data) +static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; +static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; +static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; // u32 max is 4294967295 (10 digits) + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer as [v1, v2, v3, v4] to UART +/// Takes a raw pointer to avoid warnings about shared references to mutable statics +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) { + tx.blocking_write(b"[").ok(); + unsafe { + let buf = &*buf_ptr; + for (i, val) in buf.iter().enumerate() { + write_u32(tx, *val); + if i < buf.len() - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA memory-to-memory example starting..."); + + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + // Create UART for debug output + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC_BUFFER = [1, 2, 3, 4]; + DEST_BUFFER = [0; BUFFER_LENGTH]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, &raw const SRC_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, &raw const DEST_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // Create DMA channel + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure transfer options (Embassy-style) + // TransferOptions defaults to: complete_transfer_interrupt = true + let options = TransferOptions::default(); + + // ========================================================================= + // Part 1: Embassy-style async API demonstration (mem_to_mem) + // ========================================================================= + // + // Use the new type-safe `mem_to_mem()` method: + // - Automatically determines transfer width from buffer element type (u32) + // - Returns a `Transfer` future that can be `.await`ed + // - Uses TransferOptions for consistent configuration + // + // Using async `.await` - the executor can run other tasks while waiting! + + // Perform type-safe memory-to-memory transfer using Embassy-style async API + unsafe { + let src = &*core::ptr::addr_of!(SRC_BUFFER); + let dst = &mut *core::ptr::addr_of_mut!(DEST_BUFFER); + + // Using async `.await` - the executor can run other tasks while waiting! + let transfer = dma_ch0.mem_to_mem(src, dst, options); + transfer.await; + } + + tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, &raw const DEST_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + // Verify data + let mut mismatch = false; + unsafe { + for i in 0..BUFFER_LENGTH { + if SRC_BUFFER[i] != DEST_BUFFER[i] { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap(); + defmt::error!("FAIL: mem_to_mem mismatch!"); + } else { + tx.blocking_write(b"PASS: mem_to_mem verified.\r\n\r\n").unwrap(); + defmt::info!("PASS: mem_to_mem verified."); + } + + // ========================================================================= + // Part 2: memset() demonstration + // ========================================================================= + // + // The `memset()` method fills a buffer with a pattern value: + // - Fixed source address (pattern is read repeatedly) + // - Incrementing destination address + // - Uses the same Transfer future pattern + + tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n") + .unwrap(); + + tx.blocking_write(b"Memset Buffer (before): ").unwrap(); + print_buffer(&mut tx, &raw const MEMSET_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + // Fill buffer with a pattern value using DMA memset + let pattern: u32 = 0xDEADBEEF; + tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap(); + + unsafe { + let dst = &mut *core::ptr::addr_of_mut!(MEMSET_BUFFER); + + // Using blocking_wait() for demonstration - also shows non-async usage + let transfer = dma_ch0.memset(&pattern, dst, options); + transfer.blocking_wait(); + } + + tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap(); + tx.blocking_write(b"Memset Buffer (after): ").unwrap(); + print_buffer(&mut tx, &raw const MEMSET_BUFFER); + tx.blocking_write(b"\r\n").unwrap(); + + // Verify memset result + let mut memset_ok = true; + unsafe { + #[allow(clippy::needless_range_loop)] + for i in 0..BUFFER_LENGTH { + if MEMSET_BUFFER[i] != pattern { + memset_ok = false; + break; + } + } + } + + if !memset_ok { + tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap(); + defmt::error!("FAIL: memset mismatch!"); + } else { + tx.blocking_write(b"PASS: memset verified.\r\n\r\n").unwrap(); + defmt::info!("PASS: memset verified."); + } + + tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap(); + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_memset.rs b/examples/mcxa/src/bin/dma_memset.rs new file mode 100644 index 000000000..95e365e47 --- /dev/null +++ b/examples/mcxa/src/bin/dma_memset.rs @@ -0,0 +1,218 @@ +//! DMA memset example for MCXA276. +//! +//! This example demonstrates using DMA to fill a buffer with a repeated pattern. +//! The source address stays fixed while the destination increments. +//! +//! # Embassy-style features demonstrated: +//! - `DmaChannel::is_done()` and `clear_done()` helper methods +//! - No need to pass register block around + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; +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 _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +const BUFFER_LENGTH: usize = 4; + +// Buffers in RAM +static mut PATTERN: u32 = 0; +static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA memset example starting..."); + + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA memset example begin.\r\n\r\n").unwrap(); + + // Initialize buffers + unsafe { + PATTERN = 0xDEADBEEF; + DEST_BUFFER = [0; BUFFER_LENGTH]; + } + + tx.blocking_write(b"Pattern value: 0x").unwrap(); + // Print pattern in hex + unsafe { + let hex_chars = b"0123456789ABCDEF"; + let mut hex_buf = [0u8; 8]; + let mut val = PATTERN; + for i in (0..8).rev() { + hex_buf[i] = hex_chars[(val & 0xF) as usize]; + val >>= 4; + } + tx.blocking_write(&hex_buf).ok(); + } + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // Create DMA channel using Embassy-style API + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure memset transfer using direct TCD access: + // Source stays fixed (soff = 0, reads same pattern repeatedly) + // Destination increments (doff = 4) + unsafe { + let t = dma_ch0.tcd(); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source address (pattern) - fixed + t.tcd_saddr() + .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32)); + // Destination address - increments + t.tcd_daddr() + .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + + // Source offset = 0 (stays fixed), Dest offset = 4 (increments) + t.tcd_soff().write(|w| w.soff().bits(0)); + t.tcd_doff().write(|w| w.doff().bits(4)); + + // Attributes: 32-bit transfers (size = 2) + t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); + + // Transfer entire buffer in one minor loop + let nbytes = (BUFFER_LENGTH * 4) as u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Source doesn't need adjustment (stays fixed) + t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); + // Reset dest address after major loop + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + dma_ch0.trigger_start(); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done() { + cortex_m::asm::nop(); + } + unsafe { + dma_ch0.clear_done(); + } + + tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n").unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: All elements should equal PATTERN + let mut mismatch = false; + unsafe { + #[allow(clippy::needless_range_loop)] + for i in 0..BUFFER_LENGTH { + if DEST_BUFFER[i] != PATTERN { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs new file mode 100644 index 000000000..f8f543382 --- /dev/null +++ b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs @@ -0,0 +1,376 @@ +//! DMA ping-pong/double-buffer transfer example for MCXA276. +//! +//! This example demonstrates two approaches for ping-pong/double-buffering: +//! +//! ## Approach 1: Scatter/Gather with linked TCDs (manual) +//! - Two TCDs link to each other for alternating transfers +//! - 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 +//! - Uses half-transfer interrupt to know when first half is ready +//! - Application can process first half while second half is being filled +//! +//! # Embassy-style features demonstrated: +//! - `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] +#![no_main] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{self, DmaCh1InterruptHandler, DmaChannel, Tcd, TransferOptions}; +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 _}; + +// Source and destination buffers for Approach 1 (scatter/gather) +static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; +static mut DST: [u32; 8] = [0; 8]; + +// Source and destination buffers for Approach 2 (wait_half) +static mut SRC2: [u32; 8] = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; +static mut DST2: [u32; 8] = [0; 8]; + +// TCD pool for scatter/gather - must be 32-byte aligned +#[repr(C, align(32))] +struct TcdPool([Tcd; 2]); + +static mut TCD_POOL: TcdPool = TcdPool( + [Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + 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 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() { + // 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; +}); + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA ping-pong transfer example starting..."); + + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA ping-pong transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC = [1, 2, 3, 4, 5, 6, 7, 8]; + DST = [0; 8]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring ping-pong DMA with Embassy-style API...\r\n") + .unwrap(); + + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure ping-pong transfer using direct TCD access: + // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. + // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. + // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. + unsafe { + let tcds = &mut *core::ptr::addr_of_mut!(TCD_POOL.0); + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; + + let half_len = 4usize; + let half_bytes = (half_len * 4) as u32; + + let tcd0_addr = &tcds[0] as *const _ as u32; + let tcd1_addr = &tcds[1] as *const _ as u32; + + // TCD0: First half -> Links to TCD1 + tcds[0] = Tcd { + saddr: src_ptr as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: half_bytes, + slast: 0, + daddr: dst_ptr as u32, + doff: 4, + citer: 1, + dlast_sga: tcd1_addr as i32, + csr: 0x0012, // ESG | INTMAJOR + biter: 1, + }; + + // TCD1: Second half -> Links to TCD0 + tcds[1] = Tcd { + saddr: src_ptr.add(half_len) as u32, + soff: 4, + attr: 0x0202, + nbytes: half_bytes, + slast: 0, + daddr: dst_ptr.add(half_len) as u32, + doff: 4, + citer: 1, + dlast_sga: tcd0_addr as i32, + csr: 0x0012, + biter: 1, + }; + + // Load TCD0 into hardware registers + 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(); + } + + // Wait for first half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"First half transferred.\r\n").unwrap(); + tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); + + // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) + unsafe { + dma_ch0.trigger_start(); + } + + // Wait for second half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); + + tx.blocking_write(b"EDMA ping-pong transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: DST should match SRC + let mut mismatch = false; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of!(DST) as *const u32; + for i in 0..8 { + if *src_ptr.add(i) != *dst_ptr.add(i) { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Approach 1 mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Approach 1 mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Approach 1 data verified.\r\n\r\n").unwrap(); + defmt::info!("PASS: Approach 1 data verified."); + } + + // ========================================================================= + // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!) + // ========================================================================= + // + // This approach uses a single continuous DMA transfer with half-transfer + // interrupt enabled. The wait_half() method allows you to be notified + // when the first half of the buffer is complete, so you can process it + // while the second half is still being filled. + // + // Benefits: + // - Simpler setup (no TCD pool needed) + // - True async/await support + // - Good for streaming data processing + + tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n") + .unwrap(); + + // Enable DMA CH1 interrupt + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + } + + // Initialize approach 2 buffers + unsafe { + SRC2 = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; + DST2 = [0; 8]; + } + + tx.blocking_write(b"SRC2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + let dma_ch1 = DmaChannel::new(p.DMA_CH1); + + // Configure transfer with half-transfer interrupt enabled + let mut options = TransferOptions::default(); + options.half_transfer_interrupt = true; // Enable half-transfer interrupt + options.complete_transfer_interrupt = true; + + tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n") + .unwrap(); + + unsafe { + let src = &*core::ptr::addr_of!(SRC2); + let dst = &mut *core::ptr::addr_of_mut!(DST2); + + // Create the transfer + let mut transfer = dma_ch1.mem_to_mem(src, dst, options); + + // Wait for half-transfer (first 4 elements) + tx.blocking_write(b"Waiting for first half...\r\n").unwrap(); + let half_ok = transfer.wait_half().await; + + if half_ok { + tx.blocking_write(b"Half-transfer complete! First half of DST2: ") + .unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b"(Processing first half while second half transfers...)\r\n") + .unwrap(); + } + + // Wait for complete transfer + tx.blocking_write(b"Waiting for second half...\r\n").unwrap(); + transfer.await; + } + + tx.blocking_write(b"Transfer complete! Full DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify approach 2 + let mut mismatch2 = false; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC2) as *const u32; + let dst_ptr = core::ptr::addr_of!(DST2) as *const u32; + for i in 0..8 { + if *src_ptr.add(i) != *dst_ptr.add(i) { + mismatch2 = true; + break; + } + } + } + + if mismatch2 { + tx.blocking_write(b"FAIL: Approach 2 mismatch!\r\n").unwrap(); + defmt::error!("FAIL: Approach 2 mismatch!"); + } else { + tx.blocking_write(b"PASS: Approach 2 verified.\r\n").unwrap(); + defmt::info!("PASS: Approach 2 verified."); + } + + tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n") + .unwrap(); + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_scatter_gather.rs b/examples/mcxa/src/bin/dma_scatter_gather.rs new file mode 100644 index 000000000..4b26bc2ed --- /dev/null +++ b/examples/mcxa/src/bin/dma_scatter_gather.rs @@ -0,0 +1,262 @@ +//! DMA scatter-gather transfer example for MCXA276. +//! +//! This example demonstrates using DMA with scatter/gather to chain multiple +//! transfer descriptors. The first TCD transfers the first half of the buffer, +//! then automatically loads the second TCD to transfer the second half. +//! +//! # Embassy-style features demonstrated: +//! - `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] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{self, DmaChannel, Tcd}; +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 _}; + +// Source and destination buffers +static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; +static mut DST: [u32; 8] = [0; 8]; + +// TCD pool for scatter/gather - must be 32-byte aligned +#[repr(C, align(32))] +struct TcdPool([Tcd; 2]); + +static mut TCD_POOL: TcdPool = TcdPool( + [Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + 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 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() { + // 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; +}); + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA scatter-gather transfer example starting..."); + + // DMA is initialized during hal::init() - no need to call ensure_init() + + // Enable DMA interrupt + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA scatter-gather transfer example begin.\r\n\r\n") + .unwrap(); + + // Initialize buffers + unsafe { + SRC = [1, 2, 3, 4, 5, 6, 7, 8]; + DST = [0; 8]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n") + .unwrap(); + + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure scatter-gather transfer using direct TCD access: + // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. + // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. + // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. + unsafe { + let tcds = core::slice::from_raw_parts_mut(core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, 2); + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; + + let num_tcds = 2usize; + let chunk_len = 4usize; // 8 / 2 + let chunk_bytes = (chunk_len * 4) as u32; + + for i in 0..num_tcds { + let is_last = i == num_tcds - 1; + let next_tcd_addr = if is_last { + 0 // No next TCD + } else { + &tcds[i + 1] as *const _ as u32 + }; + + tcds[i] = Tcd { + saddr: src_ptr.add(i * chunk_len) as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: chunk_bytes, + slast: 0, + daddr: dst_ptr.add(i * chunk_len) as u32, + doff: 4, + citer: 1, + dlast_sga: next_tcd_addr as i32, + // ESG (scatter/gather) for non-last, INTMAJOR for all + csr: if is_last { 0x0002 } else { 0x0012 }, + biter: 1, + }; + } + + // Load TCD0 into hardware registers + 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]) + // TCD0 is currently loaded. + unsafe { + dma_ch0.trigger_start(); + } + + // Wait for first half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"First half transferred.\r\n").unwrap(); + tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); + + // 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(); + } + + // Wait for second half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); + + tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: DST should match SRC + let mut mismatch = false; + unsafe { + let src_ptr = core::ptr::addr_of!(SRC) as *const u32; + let dst_ptr = core::ptr::addr_of!(DST) as *const u32; + for i in 0..8 { + if *src_ptr.add(i) != *dst_ptr.add(i) { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs new file mode 100644 index 000000000..e483bb81f --- /dev/null +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs @@ -0,0 +1,231 @@ +//! DMA Scatter-Gather Builder example for MCXA276. +//! +//! This example demonstrates using the new `ScatterGatherBuilder` API for +//! chaining multiple DMA transfers with a type-safe builder pattern. +//! +//! # Features demonstrated: +//! - `ScatterGatherBuilder::new()` for creating a builder +//! - `add_transfer()` for adding memory-to-memory segments +//! - `build()` to start the chained transfer +//! - Automatic TCD linking and ESG bit management +//! +//! # Comparison with manual scatter-gather: +//! The manual approach (see `dma_scatter_gather.rs`) requires: +//! - Manual TCD pool allocation and alignment +//! - Manual CSR/ESG/INTMAJOR bit manipulation +//! - Manual dlast_sga address calculations +//! +//! The builder approach handles all of this automatically! + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, ScatterGatherBuilder}; +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 _}; + +// Bind DMA channel 0 interrupt +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +// Source buffers (multiple segments) +static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444]; +static mut SRC2: [u32; 4] = [0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]; +static mut SRC3: [u32; 4] = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]; + +// Destination buffers (one per segment) +static mut DST1: [u32; 4] = [0; 4]; +static mut DST2: [u32; 4] = [0; 4]; +static mut DST3: [u32; 4] = [0; 4]; + +/// Helper to write a u32 as hex to UART +fn write_hex(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + const HEX: &[u8; 16] = b"0123456789ABCDEF"; + for i in (0..8).rev() { + let nibble = ((val >> (i * 4)) & 0xF) as usize; + tx.blocking_write(&[HEX[nibble]]).ok(); + } +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_hex(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA Scatter-Gather Builder example starting..."); + + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + // Create UART for debug output + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap(); + tx.blocking_write(b"===================================\r\n\r\n") + .unwrap(); + + // Show source buffers + tx.blocking_write(b"Source buffers:\r\n").unwrap(); + tx.blocking_write(b" SRC1: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" SRC2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" SRC3: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(SRC3) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + tx.blocking_write(b"Destination buffers (before):\r\n").unwrap(); + tx.blocking_write(b" DST1: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST3: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Create DMA channel + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n") + .unwrap(); + + // ========================================================================= + // ScatterGatherBuilder API demonstration + // ========================================================================= + // + // The builder pattern makes scatter-gather transfers much easier: + // 1. Create a builder + // 2. Add transfer segments with add_transfer() + // 3. Call build() to start the entire chain + // No manual TCD manipulation required! + + let mut builder = ScatterGatherBuilder::::new(); + + // Add three transfer segments - the builder handles TCD linking automatically + unsafe { + let src1 = &*core::ptr::addr_of!(SRC1); + let dst1 = &mut *core::ptr::addr_of_mut!(DST1); + builder.add_transfer(src1, dst1); + } + + unsafe { + let src2 = &*core::ptr::addr_of!(SRC2); + let dst2 = &mut *core::ptr::addr_of_mut!(DST2); + builder.add_transfer(src2, dst2); + } + + unsafe { + let src3 = &*core::ptr::addr_of!(SRC3); + let dst3 = &mut *core::ptr::addr_of_mut!(DST3); + builder.add_transfer(src3, dst3); + } + + tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap(); + tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n") + .unwrap(); + + // Build and execute the scatter-gather chain + // The build() method: + // - Links all TCDs together with ESG bit + // - Sets INTMAJOR on all TCDs + // - Loads the first TCD into hardware + // - Returns a Transfer future + unsafe { + let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); + transfer.blocking_wait(); + } + + tx.blocking_write(b"Scatter-gather transfer complete!\r\n\r\n").unwrap(); + + // Show results + tx.blocking_write(b"Destination buffers (after):\r\n").unwrap(); + tx.blocking_write(b" DST1: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST2: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + tx.blocking_write(b" DST3: ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify all three segments + let mut all_ok = true; + unsafe { + let src1 = core::ptr::addr_of!(SRC1) as *const u32; + let dst1 = core::ptr::addr_of!(DST1) as *const u32; + for i in 0..4 { + if *src1.add(i) != *dst1.add(i) { + all_ok = false; + } + } + + let src2 = core::ptr::addr_of!(SRC2) as *const u32; + let dst2 = core::ptr::addr_of!(DST2) as *const u32; + for i in 0..4 { + if *src2.add(i) != *dst2.add(i) { + all_ok = false; + } + } + + let src3 = core::ptr::addr_of!(SRC3) as *const u32; + let dst3 = core::ptr::addr_of!(DST3) as *const u32; + for i in 0..4 { + if *src3.add(i) != *dst3.add(i) { + all_ok = false; + } + } + } + + if all_ok { + tx.blocking_write(b"PASS: All segments verified!\r\n").unwrap(); + defmt::info!("PASS: All segments verified!"); + } else { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } + + tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n") + .unwrap(); + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/dma_wrap_transfer.rs b/examples/mcxa/src/bin/dma_wrap_transfer.rs new file mode 100644 index 000000000..82936d9d0 --- /dev/null +++ b/examples/mcxa/src/bin/dma_wrap_transfer.rs @@ -0,0 +1,222 @@ +//! DMA wrap transfer example for MCXA276. +//! +//! This example demonstrates using DMA with modulo addressing to wrap around +//! a source buffer, effectively repeating the source data in the destination. +//! +//! # Embassy-style features demonstrated: +//! - `DmaChannel::is_done()` and `clear_done()` helper methods +//! - No need to pass register block around + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; +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 _}; + +// Bind DMA channel 0 interrupt using Embassy-style macro +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; +}); + +// Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo +#[repr(align(16))] +struct AlignedSrc([u32; 4]); + +static mut SRC: AlignedSrc = AlignedSrc([0; 4]); +static mut DST: [u32; 8] = [0; 8]; + +/// Helper to write a u32 as decimal ASCII to UART +fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { + let mut buf = [0u8; 10]; + let mut n = val; + let mut i = buf.len(); + + if n == 0 { + tx.blocking_write(b"0").ok(); + return; + } + + while n > 0 { + i -= 1; + buf[i] = b'0' + (n % 10) as u8; + n /= 10; + } + + tx.blocking_write(&buf[i..]).ok(); +} + +/// Helper to print a buffer to UART +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { + tx.blocking_write(b"[").ok(); + unsafe { + for i in 0..len { + write_u32(tx, *buf_ptr.add(i)); + if i < len - 1 { + tx.blocking_write(b", ").ok(); + } + } + } + tx.blocking_write(b"]").ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA wrap transfer example starting..."); + + // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); + } + + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); + let (mut tx, _rx) = lpuart.split(); + + tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n").unwrap(); + + // Initialize buffers + unsafe { + SRC.0 = [1, 2, 3, 4]; + DST = [0; 8]; + } + + tx.blocking_write(b"Source Buffer: ").unwrap(); + print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Destination Buffer (before): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n").unwrap(); + + tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") + .unwrap(); + + // Create DMA channel using Embassy-style API + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // 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. + // DST modulo is 0 (disabled). + // This causes the source address to wrap around after 16 bytes, + // effectively repeating the source data. + unsafe { + let t = dma_ch0.tcd(); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr() + .write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32)); + t.tcd_daddr() + .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32)); + + // Offsets: both increment by 4 bytes + t.tcd_soff().write(|w| w.soff().bits(4)); + t.tcd_doff().write(|w| w.doff().bits(4)); + + // Attributes: 32-bit transfers (size = 2) + // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled) + t.tcd_attr().write(|w| { + w.ssize() + .bits(2) + .dsize() + .bits(2) + .smod() + .bits(4) // Source modulo: 2^4 = 16 bytes + .dmod() + .bits(0) // Dest modulo: disabled + }); + + // Transfer 32 bytes total in one minor loop + let nbytes = 32u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Source wraps via modulo, no adjustment needed + t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); + // Reset dest address after major loop + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + dma_ch0.trigger_start(); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done() { + cortex_m::asm::nop(); + } + unsafe { + dma_ch0.clear_done(); + } + + tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") + .unwrap(); + tx.blocking_write(b"Destination Buffer (after): ").unwrap(); + print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); + tx.blocking_write(b"\r\n\r\n").unwrap(); + + // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4] + let expected = [1u32, 2, 3, 4, 1, 2, 3, 4]; + let mut mismatch = false; + unsafe { + for i in 0..8 { + if DST[i] != expected[i] { + mismatch = true; + break; + } + } + } + + if mismatch { + tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); + defmt::error!("FAIL: Mismatch detected!"); + } else { + tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); + defmt::info!("PASS: Data verified."); + } + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/hello.rs b/examples/mcxa/src/bin/hello.rs new file mode 100644 index 000000000..e371d9413 --- /dev/null +++ b/examples/mcxa/src/bin/hello.rs @@ -0,0 +1,119 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use hal::lpuart::{Blocking, Config, Lpuart}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Simple helper to write a byte as hex to UART +fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) { + const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; + let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); + let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("boot"); + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX + let mut uart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.P2_2, // TX pin + p.P2_3, // RX pin + config, + ) + .unwrap(); + + // Print welcome message before any async delays to guarantee early console output + uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); + uart.write_str_blocking("Available commands:\r\n"); + uart.write_str_blocking(" help - Show this help\r\n"); + uart.write_str_blocking(" echo - Echo back the text\r\n"); + uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); + uart.write_str_blocking("Type a command: "); + + let mut buffer = [0u8; 64]; + let mut buf_idx = 0; + + loop { + // Read a byte from UART + let byte = uart.read_byte_blocking(); + + // Echo the character back + if byte == b'\r' || byte == b'\n' { + // Enter pressed - process command + uart.write_str_blocking("\r\n"); + + if buf_idx > 0 { + let command = &buffer[0..buf_idx]; + + if command == b"help" { + uart.write_str_blocking("Available commands:\r\n"); + uart.write_str_blocking(" help - Show this help\r\n"); + uart.write_str_blocking(" echo - Echo back the text\r\n"); + uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); + } else if command.starts_with(b"echo ") && command.len() > 5 { + uart.write_str_blocking("Echo: "); + uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or("")); + uart.write_str_blocking("\r\n"); + } else if command.starts_with(b"hex ") && command.len() > 4 { + // Parse the byte value + let num_str = &command[4..]; + if let Ok(num) = parse_u8(num_str) { + uart.write_str_blocking("Hex: 0x"); + write_hex_byte(&mut uart, num); + uart.write_str_blocking("\r\n"); + } else { + uart.write_str_blocking("Invalid number for hex command\r\n"); + } + } else if !command.is_empty() { + uart.write_str_blocking("Unknown command: "); + uart.write_str_blocking(core::str::from_utf8(command).unwrap_or("")); + uart.write_str_blocking("\r\n"); + } + } + + // Reset buffer and prompt + buf_idx = 0; + uart.write_str_blocking("Type a command: "); + } else if byte == 8 || byte == 127 { + // Backspace + if buf_idx > 0 { + buf_idx -= 1; + uart.write_str_blocking("\x08 \x08"); // Erase character + } + } else if buf_idx < buffer.len() - 1 { + // Regular character + buffer[buf_idx] = byte; + buf_idx += 1; + let _ = uart.write_byte(byte); + } + } +} + +/// Simple parser for u8 from ASCII bytes +fn parse_u8(bytes: &[u8]) -> Result { + let mut result = 0u8; + for &b in bytes { + if b.is_ascii_digit() { + result = result.checked_mul(10).ok_or(())?; + result = result.checked_add(b - b'0').ok_or(())?; + } else { + return Err(()); + } + } + Ok(result) +} diff --git a/examples/mcxa/src/bin/i2c-blocking.rs b/examples/mcxa/src/bin/i2c-blocking.rs new file mode 100644 index 000000000..0f6c8cbae --- /dev/null +++ b/examples/mcxa/src/bin/i2c-blocking.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use hal::clocks::config::Div8; +use hal::config::Config; +use hal::i2c::controller::{self, I2c, Speed}; +use tmp108::Tmp108; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); + + defmt::info!("I2C example"); + + let mut config = controller::Config::default(); + config.speed = Speed::Standard; + let i2c = I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap(); + let mut tmp = Tmp108::new_with_a0_gnd(i2c); + + loop { + let temperature = tmp.temperature().unwrap(); + defmt::info!("Temperature: {}C", temperature); + Timer::after_secs(1).await; + } +} diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs new file mode 100644 index 000000000..4e203597b --- /dev/null +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::gpio::Pull; +use embassy_mcxa::Input; +use embassy_time::Timer; +use hal::clocks::config::Div8; +use hal::config::Config; +use hal::i2c::controller::{self, I2c, Speed}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); + + defmt::info!("I2C example"); + + let mut config = controller::Config::default(); + config.speed = Speed::Standard; + + // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and + // defaults to SWO on the debug peripheral. Explicitly make it a high-z + // input. + let _pin = Input::new(p.P0_2, Pull::Disabled); + let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap(); + + for addr in 0x01..=0x7f { + let result = i2c.blocking_write(addr, &[]); + if result.is_ok() { + defmt::info!("Device found at addr {:02x}", addr); + } + } + + loop { + Timer::after_secs(10).await; + } +} diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs new file mode 100644 index 000000000..420589d00 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_buffered.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::lpuart::buffered::BufferedLpuart; +use embassy_mcxa::lpuart::Config; +use embassy_mcxa::{bind_interrupts, lpuart}; +use embedded_io_async::Write; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver +bind_interrupts!(struct Irqs { + LPUART2 => lpuart::buffered::BufferedInterruptHandler::; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + // Configure NVIC for LPUART2 + hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); + + // UART configuration (enable both TX and RX) + let config = Config { + baudrate_bps: 115_200, + rx_fifo_watermark: 0, + tx_fifo_watermark: 0, + ..Default::default() + }; + + let mut tx_buf = [0u8; 256]; + let mut rx_buf = [0u8; 256]; + + // Create a buffered LPUART2 instance with both TX and RX + let mut uart = BufferedLpuart::new( + p.LPUART2, + p.P2_2, // TX pin + p.P2_3, // RX pin + Irqs, + &mut tx_buf, + &mut rx_buf, + config, + ) + .unwrap(); + + // Split into TX and RX parts + let (tx, rx) = uart.split_ref(); + + tx.write(b"Hello buffered LPUART.\r\n").await.unwrap(); + tx.write(b"Type characters to echo them back.\r\n").await.unwrap(); + + // Echo loop + let mut buf = [0u8; 4]; + loop { + let used = rx.read(&mut buf).await.unwrap(); + tx.write_all(&buf[..used]).await.unwrap(); + } +} diff --git a/examples/mcxa/src/bin/lpuart_dma.rs b/examples/mcxa/src/bin/lpuart_dma.rs new file mode 100644 index 000000000..5497f8646 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_dma.rs @@ -0,0 +1,81 @@ +//! LPUART DMA example for MCXA276. +//! +//! 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::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; +use embassy_mcxa::lpuart::{Config, LpuartDma}; +use embassy_mcxa::{bind_interrupts, pac}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// 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) { + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("LPUART DMA example starting..."); + + // 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); + } + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + // Create UART instance with DMA channels + let mut lpuart = LpuartDma::new( + p.LPUART2, p.P2_2, // TX pin + p.P2_3, // RX pin + p.DMA_CH0, // TX DMA channel + p.DMA_CH1, // RX DMA channel + config, + ) + .unwrap(); + + // 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(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(prompt).await.unwrap(); + + // Receive 16 characters using DMA + let mut rx_buf = [0u8; 16]; + 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(echo_prefix).await.unwrap(); + lpuart.write_dma(&rx_buf).await.unwrap(); + let done_msg = b"\r\nDone!\r\n"; + lpuart.write_dma(done_msg).await.unwrap(); + + defmt::info!("Example complete"); +} diff --git a/examples/mcxa/src/bin/lpuart_polling.rs b/examples/mcxa/src/bin/lpuart_polling.rs new file mode 100644 index 000000000..b80668834 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_polling.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +use crate::hal::lpuart::{Config, Lpuart}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("boot"); + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX + let lpuart = Lpuart::new_blocking( + p.LPUART2, // Peripheral + p.P2_2, // TX pin + p.P2_3, // RX pin + config, + ) + .unwrap(); + + // Split into separate TX and RX parts + let (mut tx, mut rx) = lpuart.split(); + + // Write hello messages + tx.blocking_write(b"Hello world.\r\n").unwrap(); + tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap(); + + // Echo loop + loop { + let mut buf = [0u8; 1]; + rx.blocking_read(&mut buf).unwrap(); + tx.blocking_write(&buf).unwrap(); + } +} diff --git a/examples/mcxa/src/bin/lpuart_ring_buffer.rs b/examples/mcxa/src/bin/lpuart_ring_buffer.rs new file mode 100644 index 000000000..1d1a51970 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_ring_buffer.rs @@ -0,0 +1,130 @@ +//! LPUART Ring Buffer DMA example for MCXA276. +//! +//! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()` +//! API for continuous circular DMA reception from a UART peripheral. +//! +//! # Features demonstrated: +//! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA +//! - `RingBuffer` for async reading of received data +//! - Handling of potential overrun conditions +//! - Half-transfer and complete-transfer interrupts for timely wakeups +//! +//! # How it works: +//! 1. Create an `LpuartRxDma` driver with a DMA channel +//! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration +//! 3. Application asynchronously reads data as it arrives via `ring_buf.read()` +//! 4. Both half-transfer and complete-transfer interrupts wake the reader + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +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 {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind DMA channel interrupts +bind_interrupts!(struct Irqs { + DMA_CH0 => DmaCh0InterruptHandler; + DMA_CH1 => DmaCh1InterruptHandler; +}); + +// Ring buffer for RX - power of 2 is ideal for modulo efficiency +static mut RX_RING_BUFFER: [u8; 64] = [0; 64]; + +/// Helper to write a byte as hex to UART +fn write_hex( + tx: &mut LpuartTxDma<'_, T, C>, + byte: u8, +) { + const HEX: &[u8; 16] = b"0123456789ABCDEF"; + let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]]; + tx.blocking_write(&buf).ok(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("LPUART Ring Buffer DMA example starting..."); + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + ..Default::default() + }; + + // Create LPUART with DMA support for both TX and RX, then split + // This is the proper Embassy pattern - create once, split into TX and RX + let lpuart = LpuartDma::new(p.LPUART2, p.P2_2, p.P2_3, p.DMA_CH1, p.DMA_CH0, config).unwrap(); + let (mut tx, rx) = lpuart.split(); + + tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap(); + tx.blocking_write(b"==============================\r\n\r\n").unwrap(); + + tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n") + .unwrap(); + + // 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(); + } + + tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n") + .unwrap(); + tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n") + .unwrap(); + + // Main loop: read from ring buffer and echo back + let mut read_buf = [0u8; 16]; + let mut total_received: usize = 0; + + loop { + // Async read - waits until data is available + match ring_buf.read(&mut read_buf).await { + Ok(n) if n > 0 => { + total_received += n; + + // Echo back what we received + tx.blocking_write(b"RX[").unwrap(); + for (i, &byte) in read_buf.iter().enumerate().take(n) { + write_hex(&mut tx, byte); + if i < n - 1 { + tx.blocking_write(b" ").unwrap(); + } + } + tx.blocking_write(b"]: ").unwrap(); + tx.blocking_write(&read_buf[..n]).unwrap(); + tx.blocking_write(b"\r\n").unwrap(); + + defmt::info!("Received {} bytes, total: {}", n, total_received); + } + Ok(_) => { + // No data, shouldn't happen with async read + } + Err(_) => { + // Overrun detected + tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap(); + defmt::error!("Ring buffer overrun!"); + ring_buf.clear(); + } + } + } +} diff --git a/examples/mcxa/src/bin/rtc_alarm.rs b/examples/mcxa/src/bin/rtc_alarm.rs new file mode 100644 index 000000000..a7800a2d1 --- /dev/null +++ b/examples/mcxa/src/bin/rtc_alarm.rs @@ -0,0 +1,74 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use hal::rtc::{RtcDateTime, RtcInterruptEnable}; +use hal::InterruptExt; + +type MyRtc = hal::rtc::Rtc<'static, hal::rtc::Rtc0>; + +use embassy_mcxa::bind_interrupts; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RTC => hal::rtc::RtcHandler; +}); + +#[used] +#[no_mangle] +static KEEP_RTC: unsafe extern "C" fn() = RTC; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("=== RTC Alarm Example ==="); + + let rtc_config = hal::rtc::get_default_config(); + + let rtc = MyRtc::new(p.RTC0, rtc_config); + + let now = RtcDateTime { + year: 2025, + month: 10, + day: 15, + hour: 14, + minute: 30, + second: 0, + }; + + rtc.stop(); + + defmt::info!("Time set to: 2025-10-15 14:30:00"); + rtc.set_datetime(now); + + let mut alarm = now; + alarm.second += 10; + + rtc.set_alarm(alarm); + defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)"); + + rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); + + unsafe { + hal::interrupt::RTC.enable(); + } + + unsafe { + cortex_m::interrupt::enable(); + } + + rtc.start(); + + defmt::info!("RTC started, waiting for alarm..."); + + loop { + if rtc.is_alarm_triggered() { + defmt::info!("*** ALARM TRIGGERED! ***"); + break; + } + } + + defmt::info!("Example complete - Test PASSED!"); +} diff --git a/examples/mcxa/src/lib.rs b/examples/mcxa/src/lib.rs new file mode 100644 index 000000000..2573a6adc --- /dev/null +++ b/examples/mcxa/src/lib.rs @@ -0,0 +1,16 @@ +#![no_std] +#![allow(clippy::missing_safety_doc)] + +//! Shared board-specific helpers for the FRDM-MCXA276 examples. +//! These live with the examples so the HAL stays generic. + +use hal::{clocks, pins}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Initialize clocks and pin muxing for ADC. +pub unsafe fn init_adc_pins() { + // NOTE: Lpuart has been updated to properly enable + reset its own clocks. + // GPIO has not. + _ = clocks::enable_and_reset::(&clocks::periph_helpers::NoConfig); + pins::configure_adc_pins(); +} diff --git a/examples/memory.x b/examples/memory.x deleted file mode 100644 index 315ced58a..000000000 --- a/examples/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x00000000, LENGTH = 1M - RAM : ORIGIN = 0x20000000, LENGTH = 128K -} diff --git a/examples/src/bin/adc_interrupt.rs b/examples/src/bin/adc_interrupt.rs deleted file mode 100644 index 83d8046b3..000000000 --- a/examples/src/bin/adc_interrupt.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa_examples::init_adc_pins; -use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; -use hal::clocks::periph_helpers::{AdcClockSel, Div4}; -use hal::clocks::PoweredClock; -use hal::pac::adc1::cfg::{Pwrsel, Refsel}; -use hal::pac::adc1::cmdl1::{Adch, Mode}; -use hal::pac::adc1::ctrl::CalAvgs; -use hal::pac::adc1::tctrl::Tcmd; -use hal::{bind_interrupts, InterruptExt}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -bind_interrupts!(struct Irqs { - ADC1 => hal::adc::AdcHandler; -}); - -#[used] -#[no_mangle] -static KEEP_ADC: unsafe extern "C" fn() = ADC1; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - defmt::info!("ADC interrupt Example"); - - unsafe { - init_adc_pins(); - } - - let adc_config = LpadcConfig { - enable_in_doze_mode: true, - conversion_average_mode: CalAvgs::Average128, - enable_analog_preliminary: true, - power_up_delay: 0x80, - reference_voltage_source: Refsel::Option3, - power_level_mode: Pwrsel::Lowest, - trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, - enable_conv_pause: false, - conv_pause_delay: 0, - fifo_watermark: 0, - power: PoweredClock::NormalEnabledDeepSleepDisabled, - source: AdcClockSel::FroLfDiv, - div: Div4::no_div(), - }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); - - adc.do_offset_calibration(); - adc.do_auto_calibration(); - - let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config); - - let mut conv_trigger_config = adc.get_default_conv_trigger_config(); - conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; - conv_trigger_config.enable_hardware_trigger = false; - adc.set_conv_trigger_config(0, &conv_trigger_config); - - defmt::info!("ADC configuration done..."); - - adc.enable_interrupt(0x1); - - unsafe { - hal::interrupt::ADC1.enable(); - } - - unsafe { - cortex_m::interrupt::enable(); - } - - loop { - adc.do_software_trigger(1); - while !adc.is_interrupt_triggered() { - // Wait until the interrupt is triggered - } - defmt::info!("*** ADC interrupt TRIGGERED! ***"); - //TBD need to print the value - } -} diff --git a/examples/src/bin/adc_polling.rs b/examples/src/bin/adc_polling.rs deleted file mode 100644 index ddf3f586b..000000000 --- a/examples/src/bin/adc_polling.rs +++ /dev/null @@ -1,68 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa_examples::init_adc_pins; -use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; -use hal::clocks::periph_helpers::{AdcClockSel, Div4}; -use hal::clocks::PoweredClock; -use hal::pac::adc1::cfg::{Pwrsel, Refsel}; -use hal::pac::adc1::cmdl1::{Adch, Mode}; -use hal::pac::adc1::ctrl::CalAvgs; -use hal::pac::adc1::tctrl::Tcmd; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -const G_LPADC_RESULT_SHIFT: u32 = 0; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - unsafe { - init_adc_pins(); - } - - defmt::info!("=== ADC polling Example ==="); - - let adc_config = LpadcConfig { - enable_in_doze_mode: true, - conversion_average_mode: CalAvgs::Average128, - enable_analog_preliminary: true, - power_up_delay: 0x80, - reference_voltage_source: Refsel::Option3, - power_level_mode: Pwrsel::Lowest, - trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, - enable_conv_pause: false, - conv_pause_delay: 0, - fifo_watermark: 0, - power: PoweredClock::NormalEnabledDeepSleepDisabled, - source: AdcClockSel::FroLfDiv, - div: Div4::no_div(), - }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); - - adc.do_offset_calibration(); - adc.do_auto_calibration(); - - let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config); - - let mut conv_trigger_config = adc.get_default_conv_trigger_config(); - conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; - conv_trigger_config.enable_hardware_trigger = false; - adc.set_conv_trigger_config(0, &conv_trigger_config); - - defmt::info!("=== ADC configuration done... ==="); - - loop { - adc.do_software_trigger(1); - let mut result: Option = None; - while result.is_none() { - result = hal::adc::get_conv_result(); - } - let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; - defmt::info!("value: {=u16}", value); - } -} diff --git a/examples/src/bin/blinky.rs b/examples/src/bin/blinky.rs deleted file mode 100644 index dd08ec0d9..000000000 --- a/examples/src/bin/blinky.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_time::Timer; -use hal::gpio::{DriveStrength, Level, Output, SlewRate}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - defmt::info!("Blink example"); - - let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast); - let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast); - let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast); - - loop { - defmt::info!("Toggle LEDs"); - - red.toggle(); - Timer::after_millis(250).await; - - red.toggle(); - green.toggle(); - Timer::after_millis(250).await; - - green.toggle(); - blue.toggle(); - Timer::after_millis(250).await; - blue.toggle(); - - Timer::after_millis(250).await; - } -} diff --git a/examples/src/bin/button.rs b/examples/src/bin/button.rs deleted file mode 100644 index 943edbb15..000000000 --- a/examples/src/bin/button.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_time::Timer; -use hal::gpio::{Input, Pull}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - defmt::info!("Button example"); - - // This button is labeled "WAKEUP" on the FRDM-MCXA276 - // The board already has a 10K pullup - let monitor = Input::new(p.P1_7, Pull::Disabled); - - loop { - defmt::info!("Pin level is {:?}", monitor.get_level()); - Timer::after_millis(1000).await; - } -} diff --git a/examples/src/bin/button_async.rs b/examples/src/bin/button_async.rs deleted file mode 100644 index 6cc7b62cd..000000000 --- a/examples/src/bin/button_async.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_time::Timer; -use hal::gpio::{Input, Pull}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - defmt::info!("GPIO interrupt example"); - - // This button is labeled "WAKEUP" on the FRDM-MCXA276 - // The board already has a 10K pullup - let mut pin = Input::new(p.P1_7, Pull::Disabled); - - let mut press_count = 0u32; - - loop { - pin.wait_for_falling_edge().await; - - press_count += 1; - - defmt::info!("Button pressed! Count: {}", press_count); - Timer::after_millis(50).await; - } -} diff --git a/examples/src/bin/clkout.rs b/examples/src/bin/clkout.rs deleted file mode 100644 index bfd963540..000000000 --- a/examples/src/bin/clkout.rs +++ /dev/null @@ -1,69 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; -use embassy_mcxa::clocks::PoweredClock; -use embassy_mcxa::gpio::{DriveStrength, SlewRate}; -use embassy_mcxa::{Level, Output}; -use embassy_time::Timer; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -/// Demonstrate CLKOUT, using Pin P4.2 -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - let mut pin = p.P4_2; - let mut clkout = p.CLKOUT; - - loop { - defmt::info!("Set Low..."); - let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); - Timer::after_millis(500).await; - - defmt::info!("Set High..."); - output.set_high(); - Timer::after_millis(400).await; - - defmt::info!("Set Low..."); - output.set_low(); - Timer::after_millis(500).await; - - defmt::info!("16k..."); - // Run Clock Out with the 16K clock - let _clock_out = ClockOut::new( - clkout.reborrow(), - pin.reborrow(), - Config { - sel: ClockOutSel::Clk16K, - div: Div4::no_div(), - level: PoweredClock::NormalEnabledDeepSleepDisabled, - }, - ) - .unwrap(); - - Timer::after_millis(3000).await; - - defmt::info!("Set Low..."); - drop(_clock_out); - - let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); - Timer::after_millis(500).await; - - // Run Clock Out with the 12M clock, divided by 3 - defmt::info!("4M..."); - let _clock_out = ClockOut::new( - clkout.reborrow(), - pin.reborrow(), - Config { - sel: ClockOutSel::Fro12M, - div: const { Div4::from_divisor(3).unwrap() }, - level: PoweredClock::NormalEnabledDeepSleepDisabled, - }, - ) - .unwrap(); - - // Let it run for 3 seconds... - Timer::after_millis(3000).await; - } -} diff --git a/examples/src/bin/dma_channel_link.rs b/examples/src/bin/dma_channel_link.rs deleted file mode 100644 index 92c7a9681..000000000 --- a/examples/src/bin/dma_channel_link.rs +++ /dev/null @@ -1,372 +0,0 @@ -//! DMA channel linking example for MCXA276. -//! -//! This example demonstrates DMA channel linking (minor and major loop linking): -//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: -//! - Minor Link to Channel 1 (triggers CH1 after each minor loop) -//! - Major Link to Channel 2 (triggers CH2 after major loop completes) -//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) -//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) -//! -//! # Embassy-style features demonstrated: -//! - `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 embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler, DmaChannel}; -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 _}; - -// Buffers -static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; -static mut DEST_BUFFER0: [u32; 4] = [0; 4]; -static mut DEST_BUFFER1: [u32; 4] = [0; 4]; -static mut DEST_BUFFER2: [u32; 4] = [0; 4]; - -// 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 => DmaCh0InterruptHandler; - DMA_CH1 => DmaCh1InterruptHandler; - DMA_CH2 => DmaCh2InterruptHandler; -}); - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA channel link example starting..."); - - // DMA is initialized during hal::init() - no need to call ensure_init() - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - let dma0 = &pac_periphs.dma0; - let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; - - // Clear any residual state - for i in 0..3 { - let t = edma.tcd(i); - t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - t.ch_es().write(|w| w.err().clear_bit_by_one()); - t.ch_mux().write(|w| unsafe { w.bits(0) }); - } - - // Clear Global Halt/Error state - dma0.mp_csr().modify(|_, w| { - w.halt() - .normal_operation() - .hae() - .normal_operation() - .ecx() - .normal_operation() - .cx() - .normal_operation() - }); - - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH2); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n").unwrap(); - - // Initialize buffers - unsafe { - SRC_BUFFER = [1, 2, 3, 4]; - DEST_BUFFER0 = [0; 4]; - DEST_BUFFER1 = [0; 4]; - DEST_BUFFER2 = [0; 4]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST0 (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST1 (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST2 (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA channels with Embassy-style API...\r\n") - .unwrap(); - - let ch0 = DmaChannel::new(p.DMA_CH0); - let ch1 = DmaChannel::new(p.DMA_CH1); - 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 - - // Helper to configure TCD for memory-to-memory transfer - // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt - #[allow(clippy::too_many_arguments)] - unsafe fn configure_tcd( - edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, - ch: usize, - src: u32, - dst: u32, - width: u8, - nbytes: u32, - count: u16, - enable_int: bool, - ) { - let t = edma.tcd(ch); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source/destination addresses - t.tcd_saddr().write(|w| w.saddr().bits(src)); - t.tcd_daddr().write(|w| w.daddr().bits(dst)); - - // Offsets: increment by width - t.tcd_soff().write(|w| w.soff().bits(width as u16)); - t.tcd_doff().write(|w| w.doff().bits(width as u16)); - - // Attributes: size = log2(width) - let size = match width { - 1 => 0, - 2 => 1, - 4 => 2, - _ => 0, - }; - t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); - - // Number of bytes per minor loop - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Major loop: reset source address after major loop - let total_bytes = nbytes * count as u32; - t.tcd_slast_sda() - .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); - t.tcd_dlast_sga() - .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); - - // Major loop count - t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); - - // Control/status: enable interrupt if requested - if enable_int { - t.tcd_csr().write(|w| w.intmajor().set_bit()); - } else { - t.tcd_csr().write(|w| w.intmajor().clear_bit()); - } - - cortex_m::asm::dsb(); - } - - unsafe { - // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) - // Minor Link -> Channel 1 - // Major Link -> Channel 2 - configure_tcd( - edma, - 0, - core::ptr::addr_of!(SRC_BUFFER) as u32, - core::ptr::addr_of_mut!(DEST_BUFFER0) as u32, - 4, // src width - 8, // nbytes (minor loop = 2 words) - 2, // count (major loop = 2 iterations) - false, // no interrupt - ); - 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( - edma, - 1, - core::ptr::addr_of!(SRC_BUFFER) as u32, - core::ptr::addr_of_mut!(DEST_BUFFER1) as u32, - 4, - 16, // full buffer in one minor loop - 1, // 1 major iteration - false, - ); - - // Channel 2: Transfer 16 bytes (triggered by CH0 major link) - configure_tcd( - edma, - 2, - core::ptr::addr_of!(SRC_BUFFER) as u32, - core::ptr::addr_of_mut!(DEST_BUFFER2) as u32, - 4, - 16, // full buffer in one minor loop - 1, // 1 major iteration - true, // enable interrupt - ); - } - - tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n") - .unwrap(); - - // Trigger first minor loop of CH0 - unsafe { - ch0.trigger_start(); - } - - // Wait for CH1 to complete (triggered by CH0 minor link) - while !ch1.is_done() { - cortex_m::asm::nop(); - } - 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(); - } - - // Wait for CH0 major loop to complete - while !ch0.is_done() { - cortex_m::asm::nop(); - } - 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) - // 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(); - - tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n").unwrap(); - - tx.blocking_write(b"DEST0 (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST1 (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST2 (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify all buffers match source - let mut success = true; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC_BUFFER) as *const u32; - let dst0_ptr = core::ptr::addr_of!(DEST_BUFFER0) as *const u32; - let dst1_ptr = core::ptr::addr_of!(DEST_BUFFER1) as *const u32; - let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32; - - for i in 0..4 { - if *dst0_ptr.add(i) != *src_ptr.add(i) { - success = false; - } - if *dst1_ptr.add(i) != *src_ptr.add(i) { - success = false; - } - if *dst2_ptr.add(i) != *src_ptr.add(i) { - success = false; - } - } - } - - if success { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } else { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_interleave_transfer.rs b/examples/src/bin/dma_interleave_transfer.rs deleted file mode 100644 index 7876e8978..000000000 --- a/examples/src/bin/dma_interleave_transfer.rs +++ /dev/null @@ -1,215 +0,0 @@ -//! DMA interleaved transfer example for MCXA276. -//! -//! This example demonstrates using DMA with custom source/destination offsets -//! to interleave data during transfer. -//! -//! # Embassy-style features demonstrated: -//! - `TransferOptions::default()` for configuration (used internally) -//! - DMA channel with `DmaChannel::new()` - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; -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 _}; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); - -const BUFFER_LENGTH: usize = 16; -const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; - -// Buffers in RAM -static mut SRC_BUFFER: [u32; HALF_BUFF_LENGTH] = [0; HALF_BUFF_LENGTH]; -static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA interleave transfer example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA interleave transfer example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - unsafe { - SRC_BUFFER = [1, 2, 3, 4, 5, 6, 7, 8]; - DEST_BUFFER = [0; BUFFER_LENGTH]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, HALF_BUFF_LENGTH); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); - - // Create DMA channel using Embassy-style API - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // 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 = dma_ch0.tcd(); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source/destination addresses - t.tcd_saddr() - .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32)); - t.tcd_daddr() - .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); - - // Custom offsets for interleaving - t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read - t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write - - // Attributes: 32-bit transfers (size = 2) - t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); - - // Transfer entire source buffer in one minor loop - let nbytes = (HALF_BUFF_LENGTH * 4) as u32; - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Reset source address after major loop - t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); - // Destination uses 2x offset, so adjust accordingly - let dst_total = (HALF_BUFF_LENGTH * 8) as u32; - t.tcd_dlast_sga() - .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); - - // Major loop count = 1 - t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); - - // Enable interrupt on major loop completion - t.tcd_csr().write(|w| w.intmajor().set_bit()); - - cortex_m::asm::dsb(); - - tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(); - } - - // Wait for completion using channel helper method - while !dma_ch0.is_done() { - cortex_m::asm::nop(); - } - unsafe { - dma_ch0.clear_done(); - } - - tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 - let mut mismatch = false; - unsafe { - for i in 0..BUFFER_LENGTH { - if i % 2 == 0 { - if DEST_BUFFER[i] != SRC_BUFFER[i / 2] { - mismatch = true; - } - } else if DEST_BUFFER[i] != 0 { - mismatch = true; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_mem_to_mem.rs b/examples/src/bin/dma_mem_to_mem.rs deleted file mode 100644 index 68f70e742..000000000 --- a/examples/src/bin/dma_mem_to_mem.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! DMA memory-to-memory transfer example for MCXA276. -//! -//! This example demonstrates using DMA to copy data between memory buffers -//! using the Embassy-style async API with type-safe transfers. -//! -//! # Embassy-style features demonstrated: -//! - `TransferOptions` for configuration -//! - Type-safe `mem_to_mem()` method with async `.await` -//! - `Transfer` Future that can be `.await`ed -//! - `Word` trait for automatic transfer width detection -//! - `memset()` method for filling memory with a pattern - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; -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 _}; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); - -const BUFFER_LENGTH: usize = 4; - -// Buffers in RAM (static mut is automatically placed in .bss/.data) -static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; // u32 max is 4294967295 (10 digits) - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer as [v1, v2, v3, v4] to UART -/// Takes a raw pointer to avoid warnings about shared references to mutable statics -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) { - tx.blocking_write(b"[").ok(); - unsafe { - let buf = &*buf_ptr; - for (i, val) in buf.iter().enumerate() { - write_u32(tx, *val); - if i < buf.len() - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA memory-to-memory example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - // Create UART for debug output - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - unsafe { - SRC_BUFFER = [1, 2, 3, 4]; - DEST_BUFFER = [0; BUFFER_LENGTH]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, &raw const SRC_BUFFER); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, &raw const DEST_BUFFER); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); - - // Create DMA channel - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure transfer options (Embassy-style) - // TransferOptions defaults to: complete_transfer_interrupt = true - let options = TransferOptions::default(); - - // ========================================================================= - // Part 1: Embassy-style async API demonstration (mem_to_mem) - // ========================================================================= - // - // Use the new type-safe `mem_to_mem()` method: - // - Automatically determines transfer width from buffer element type (u32) - // - Returns a `Transfer` future that can be `.await`ed - // - Uses TransferOptions for consistent configuration - // - // Using async `.await` - the executor can run other tasks while waiting! - - // Perform type-safe memory-to-memory transfer using Embassy-style async API - unsafe { - let src = &*core::ptr::addr_of!(SRC_BUFFER); - let dst = &mut *core::ptr::addr_of_mut!(DEST_BUFFER); - - // Using async `.await` - the executor can run other tasks while waiting! - let transfer = dma_ch0.mem_to_mem(src, dst, options); - transfer.await; - } - - tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, &raw const DEST_BUFFER); - tx.blocking_write(b"\r\n").unwrap(); - - // Verify data - let mut mismatch = false; - unsafe { - for i in 0..BUFFER_LENGTH { - if SRC_BUFFER[i] != DEST_BUFFER[i] { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap(); - defmt::error!("FAIL: mem_to_mem mismatch!"); - } else { - tx.blocking_write(b"PASS: mem_to_mem verified.\r\n\r\n").unwrap(); - defmt::info!("PASS: mem_to_mem verified."); - } - - // ========================================================================= - // Part 2: memset() demonstration - // ========================================================================= - // - // The `memset()` method fills a buffer with a pattern value: - // - Fixed source address (pattern is read repeatedly) - // - Incrementing destination address - // - Uses the same Transfer future pattern - - tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n") - .unwrap(); - - tx.blocking_write(b"Memset Buffer (before): ").unwrap(); - print_buffer(&mut tx, &raw const MEMSET_BUFFER); - tx.blocking_write(b"\r\n").unwrap(); - - // Fill buffer with a pattern value using DMA memset - let pattern: u32 = 0xDEADBEEF; - tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap(); - - unsafe { - let dst = &mut *core::ptr::addr_of_mut!(MEMSET_BUFFER); - - // Using blocking_wait() for demonstration - also shows non-async usage - let transfer = dma_ch0.memset(&pattern, dst, options); - transfer.blocking_wait(); - } - - tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap(); - tx.blocking_write(b"Memset Buffer (after): ").unwrap(); - print_buffer(&mut tx, &raw const MEMSET_BUFFER); - tx.blocking_write(b"\r\n").unwrap(); - - // Verify memset result - let mut memset_ok = true; - unsafe { - #[allow(clippy::needless_range_loop)] - for i in 0..BUFFER_LENGTH { - if MEMSET_BUFFER[i] != pattern { - memset_ok = false; - break; - } - } - } - - if !memset_ok { - tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap(); - defmt::error!("FAIL: memset mismatch!"); - } else { - tx.blocking_write(b"PASS: memset verified.\r\n\r\n").unwrap(); - defmt::info!("PASS: memset verified."); - } - - tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap(); - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_memset.rs b/examples/src/bin/dma_memset.rs deleted file mode 100644 index 95e365e47..000000000 --- a/examples/src/bin/dma_memset.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! DMA memset example for MCXA276. -//! -//! This example demonstrates using DMA to fill a buffer with a repeated pattern. -//! The source address stays fixed while the destination increments. -//! -//! # Embassy-style features demonstrated: -//! - `DmaChannel::is_done()` and `clear_done()` helper methods -//! - No need to pass register block around - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; -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 _}; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); - -const BUFFER_LENGTH: usize = 4; - -// Buffers in RAM -static mut PATTERN: u32 = 0; -static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA memset example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA memset example begin.\r\n\r\n").unwrap(); - - // Initialize buffers - unsafe { - PATTERN = 0xDEADBEEF; - DEST_BUFFER = [0; BUFFER_LENGTH]; - } - - tx.blocking_write(b"Pattern value: 0x").unwrap(); - // Print pattern in hex - unsafe { - let hex_chars = b"0123456789ABCDEF"; - let mut hex_buf = [0u8; 8]; - let mut val = PATTERN; - for i in (0..8).rev() { - hex_buf[i] = hex_chars[(val & 0xF) as usize]; - val >>= 4; - } - tx.blocking_write(&hex_buf).ok(); - } - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); - - // Create DMA channel using Embassy-style API - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure memset transfer using direct TCD access: - // Source stays fixed (soff = 0, reads same pattern repeatedly) - // Destination increments (doff = 4) - unsafe { - let t = dma_ch0.tcd(); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source address (pattern) - fixed - t.tcd_saddr() - .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32)); - // Destination address - increments - t.tcd_daddr() - .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); - - // Source offset = 0 (stays fixed), Dest offset = 4 (increments) - t.tcd_soff().write(|w| w.soff().bits(0)); - t.tcd_doff().write(|w| w.doff().bits(4)); - - // Attributes: 32-bit transfers (size = 2) - t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); - - // Transfer entire buffer in one minor loop - let nbytes = (BUFFER_LENGTH * 4) as u32; - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Source doesn't need adjustment (stays fixed) - t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); - // Reset dest address after major loop - t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); - - // Major loop count = 1 - t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); - - // Enable interrupt on major loop completion - t.tcd_csr().write(|w| w.intmajor().set_bit()); - - cortex_m::asm::dsb(); - - tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(); - } - - // Wait for completion using channel helper method - while !dma_ch0.is_done() { - cortex_m::asm::nop(); - } - unsafe { - dma_ch0.clear_done(); - } - - tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n").unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: All elements should equal PATTERN - let mut mismatch = false; - unsafe { - #[allow(clippy::needless_range_loop)] - for i in 0..BUFFER_LENGTH { - if DEST_BUFFER[i] != PATTERN { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_ping_pong_transfer.rs b/examples/src/bin/dma_ping_pong_transfer.rs deleted file mode 100644 index f8f543382..000000000 --- a/examples/src/bin/dma_ping_pong_transfer.rs +++ /dev/null @@ -1,376 +0,0 @@ -//! DMA ping-pong/double-buffer transfer example for MCXA276. -//! -//! This example demonstrates two approaches for ping-pong/double-buffering: -//! -//! ## Approach 1: Scatter/Gather with linked TCDs (manual) -//! - Two TCDs link to each other for alternating transfers -//! - 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 -//! - Uses half-transfer interrupt to know when first half is ready -//! - Application can process first half while second half is being filled -//! -//! # Embassy-style features demonstrated: -//! - `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] -#![no_main] - -use core::sync::atomic::{AtomicBool, Ordering}; - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaCh1InterruptHandler, DmaChannel, Tcd, TransferOptions}; -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 _}; - -// Source and destination buffers for Approach 1 (scatter/gather) -static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; -static mut DST: [u32; 8] = [0; 8]; - -// Source and destination buffers for Approach 2 (wait_half) -static mut SRC2: [u32; 8] = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; -static mut DST2: [u32; 8] = [0; 8]; - -// TCD pool for scatter/gather - must be 32-byte aligned -#[repr(C, align(32))] -struct TcdPool([Tcd; 2]); - -static mut TCD_POOL: TcdPool = TcdPool( - [Tcd { - saddr: 0, - soff: 0, - attr: 0, - nbytes: 0, - slast: 0, - daddr: 0, - doff: 0, - citer: 0, - dlast_sga: 0, - csr: 0, - 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 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() { - // 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; -}); - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA ping-pong transfer example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA ping-pong transfer example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - unsafe { - SRC = [1, 2, 3, 4, 5, 6, 7, 8]; - DST = [0; 8]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring ping-pong DMA with Embassy-style API...\r\n") - .unwrap(); - - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure ping-pong transfer using direct TCD access: - // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. - // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. - // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. - unsafe { - let tcds = &mut *core::ptr::addr_of_mut!(TCD_POOL.0); - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; - - let half_len = 4usize; - let half_bytes = (half_len * 4) as u32; - - let tcd0_addr = &tcds[0] as *const _ as u32; - let tcd1_addr = &tcds[1] as *const _ as u32; - - // TCD0: First half -> Links to TCD1 - tcds[0] = Tcd { - saddr: src_ptr as u32, - soff: 4, - attr: 0x0202, // 32-bit src/dst - nbytes: half_bytes, - slast: 0, - daddr: dst_ptr as u32, - doff: 4, - citer: 1, - dlast_sga: tcd1_addr as i32, - csr: 0x0012, // ESG | INTMAJOR - biter: 1, - }; - - // TCD1: Second half -> Links to TCD0 - tcds[1] = Tcd { - saddr: src_ptr.add(half_len) as u32, - soff: 4, - attr: 0x0202, - nbytes: half_bytes, - slast: 0, - daddr: dst_ptr.add(half_len) as u32, - doff: 4, - citer: 1, - dlast_sga: tcd0_addr as i32, - csr: 0x0012, - biter: 1, - }; - - // Load TCD0 into hardware registers - 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(); - } - - // Wait for first half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"First half transferred.\r\n").unwrap(); - tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); - - // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) - unsafe { - dma_ch0.trigger_start(); - } - - // Wait for second half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); - - tx.blocking_write(b"EDMA ping-pong transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: DST should match SRC - let mut mismatch = false; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of!(DST) as *const u32; - for i in 0..8 { - if *src_ptr.add(i) != *dst_ptr.add(i) { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Approach 1 mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Approach 1 mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Approach 1 data verified.\r\n\r\n").unwrap(); - defmt::info!("PASS: Approach 1 data verified."); - } - - // ========================================================================= - // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!) - // ========================================================================= - // - // This approach uses a single continuous DMA transfer with half-transfer - // interrupt enabled. The wait_half() method allows you to be notified - // when the first half of the buffer is complete, so you can process it - // while the second half is still being filled. - // - // Benefits: - // - Simpler setup (no TCD pool needed) - // - True async/await support - // - Good for streaming data processing - - tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n") - .unwrap(); - - // Enable DMA CH1 interrupt - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); - } - - // Initialize approach 2 buffers - unsafe { - SRC2 = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; - DST2 = [0; 8]; - } - - tx.blocking_write(b"SRC2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - let dma_ch1 = DmaChannel::new(p.DMA_CH1); - - // Configure transfer with half-transfer interrupt enabled - let mut options = TransferOptions::default(); - options.half_transfer_interrupt = true; // Enable half-transfer interrupt - options.complete_transfer_interrupt = true; - - tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n") - .unwrap(); - - unsafe { - let src = &*core::ptr::addr_of!(SRC2); - let dst = &mut *core::ptr::addr_of_mut!(DST2); - - // Create the transfer - let mut transfer = dma_ch1.mem_to_mem(src, dst, options); - - // Wait for half-transfer (first 4 elements) - tx.blocking_write(b"Waiting for first half...\r\n").unwrap(); - let half_ok = transfer.wait_half().await; - - if half_ok { - tx.blocking_write(b"Half-transfer complete! First half of DST2: ") - .unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b"(Processing first half while second half transfers...)\r\n") - .unwrap(); - } - - // Wait for complete transfer - tx.blocking_write(b"Waiting for second half...\r\n").unwrap(); - transfer.await; - } - - tx.blocking_write(b"Transfer complete! Full DST2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify approach 2 - let mut mismatch2 = false; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC2) as *const u32; - let dst_ptr = core::ptr::addr_of!(DST2) as *const u32; - for i in 0..8 { - if *src_ptr.add(i) != *dst_ptr.add(i) { - mismatch2 = true; - break; - } - } - } - - if mismatch2 { - tx.blocking_write(b"FAIL: Approach 2 mismatch!\r\n").unwrap(); - defmt::error!("FAIL: Approach 2 mismatch!"); - } else { - tx.blocking_write(b"PASS: Approach 2 verified.\r\n").unwrap(); - defmt::info!("PASS: Approach 2 verified."); - } - - tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n") - .unwrap(); - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs deleted file mode 100644 index 4b26bc2ed..000000000 --- a/examples/src/bin/dma_scatter_gather.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! DMA scatter-gather transfer example for MCXA276. -//! -//! This example demonstrates using DMA with scatter/gather to chain multiple -//! transfer descriptors. The first TCD transfers the first half of the buffer, -//! then automatically loads the second TCD to transfer the second half. -//! -//! # Embassy-style features demonstrated: -//! - `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] - -use core::sync::atomic::{AtomicBool, Ordering}; - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, Tcd}; -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 _}; - -// Source and destination buffers -static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; -static mut DST: [u32; 8] = [0; 8]; - -// TCD pool for scatter/gather - must be 32-byte aligned -#[repr(C, align(32))] -struct TcdPool([Tcd; 2]); - -static mut TCD_POOL: TcdPool = TcdPool( - [Tcd { - saddr: 0, - soff: 0, - attr: 0, - nbytes: 0, - slast: 0, - daddr: 0, - doff: 0, - citer: 0, - dlast_sga: 0, - csr: 0, - 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 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() { - // 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; -}); - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA scatter-gather transfer example starting..."); - - // DMA is initialized during hal::init() - no need to call ensure_init() - - // Enable DMA interrupt - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA scatter-gather transfer example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - unsafe { - SRC = [1, 2, 3, 4, 5, 6, 7, 8]; - DST = [0; 8]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n") - .unwrap(); - - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure scatter-gather transfer using direct TCD access: - // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. - // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. - // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. - unsafe { - let tcds = core::slice::from_raw_parts_mut(core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, 2); - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; - - let num_tcds = 2usize; - let chunk_len = 4usize; // 8 / 2 - let chunk_bytes = (chunk_len * 4) as u32; - - for i in 0..num_tcds { - let is_last = i == num_tcds - 1; - let next_tcd_addr = if is_last { - 0 // No next TCD - } else { - &tcds[i + 1] as *const _ as u32 - }; - - tcds[i] = Tcd { - saddr: src_ptr.add(i * chunk_len) as u32, - soff: 4, - attr: 0x0202, // 32-bit src/dst - nbytes: chunk_bytes, - slast: 0, - daddr: dst_ptr.add(i * chunk_len) as u32, - doff: 4, - citer: 1, - dlast_sga: next_tcd_addr as i32, - // ESG (scatter/gather) for non-last, INTMAJOR for all - csr: if is_last { 0x0002 } else { 0x0012 }, - biter: 1, - }; - } - - // Load TCD0 into hardware registers - 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]) - // TCD0 is currently loaded. - unsafe { - dma_ch0.trigger_start(); - } - - // Wait for first half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"First half transferred.\r\n").unwrap(); - tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); - - // 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(); - } - - // Wait for second half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); - - tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: DST should match SRC - let mut mismatch = false; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of!(DST) as *const u32; - for i in 0..8 { - if *src_ptr.add(i) != *dst_ptr.add(i) { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_scatter_gather_builder.rs b/examples/src/bin/dma_scatter_gather_builder.rs deleted file mode 100644 index e483bb81f..000000000 --- a/examples/src/bin/dma_scatter_gather_builder.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! DMA Scatter-Gather Builder example for MCXA276. -//! -//! This example demonstrates using the new `ScatterGatherBuilder` API for -//! chaining multiple DMA transfers with a type-safe builder pattern. -//! -//! # Features demonstrated: -//! - `ScatterGatherBuilder::new()` for creating a builder -//! - `add_transfer()` for adding memory-to-memory segments -//! - `build()` to start the chained transfer -//! - Automatic TCD linking and ESG bit management -//! -//! # Comparison with manual scatter-gather: -//! The manual approach (see `dma_scatter_gather.rs`) requires: -//! - Manual TCD pool allocation and alignment -//! - Manual CSR/ESG/INTMAJOR bit manipulation -//! - Manual dlast_sga address calculations -//! -//! The builder approach handles all of this automatically! - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, ScatterGatherBuilder}; -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 _}; - -// Bind DMA channel 0 interrupt -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); - -// Source buffers (multiple segments) -static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444]; -static mut SRC2: [u32; 4] = [0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]; -static mut SRC3: [u32; 4] = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]; - -// Destination buffers (one per segment) -static mut DST1: [u32; 4] = [0; 4]; -static mut DST2: [u32; 4] = [0; 4]; -static mut DST3: [u32; 4] = [0; 4]; - -/// Helper to write a u32 as hex to UART -fn write_hex(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - const HEX: &[u8; 16] = b"0123456789ABCDEF"; - for i in (0..8).rev() { - let nibble = ((val >> (i * 4)) & 0xF) as usize; - tx.blocking_write(&[HEX[nibble]]).ok(); - } -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_hex(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA Scatter-Gather Builder example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - // Create UART for debug output - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap(); - tx.blocking_write(b"===================================\r\n\r\n") - .unwrap(); - - // Show source buffers - tx.blocking_write(b"Source buffers:\r\n").unwrap(); - tx.blocking_write(b" SRC1: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" SRC2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" SRC3: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC3) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - tx.blocking_write(b"Destination buffers (before):\r\n").unwrap(); - tx.blocking_write(b" DST1: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST3: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Create DMA channel - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n") - .unwrap(); - - // ========================================================================= - // ScatterGatherBuilder API demonstration - // ========================================================================= - // - // The builder pattern makes scatter-gather transfers much easier: - // 1. Create a builder - // 2. Add transfer segments with add_transfer() - // 3. Call build() to start the entire chain - // No manual TCD manipulation required! - - let mut builder = ScatterGatherBuilder::::new(); - - // Add three transfer segments - the builder handles TCD linking automatically - unsafe { - let src1 = &*core::ptr::addr_of!(SRC1); - let dst1 = &mut *core::ptr::addr_of_mut!(DST1); - builder.add_transfer(src1, dst1); - } - - unsafe { - let src2 = &*core::ptr::addr_of!(SRC2); - let dst2 = &mut *core::ptr::addr_of_mut!(DST2); - builder.add_transfer(src2, dst2); - } - - unsafe { - let src3 = &*core::ptr::addr_of!(SRC3); - let dst3 = &mut *core::ptr::addr_of_mut!(DST3); - builder.add_transfer(src3, dst3); - } - - tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap(); - tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n") - .unwrap(); - - // Build and execute the scatter-gather chain - // The build() method: - // - Links all TCDs together with ESG bit - // - Sets INTMAJOR on all TCDs - // - Loads the first TCD into hardware - // - Returns a Transfer future - unsafe { - let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); - transfer.blocking_wait(); - } - - tx.blocking_write(b"Scatter-gather transfer complete!\r\n\r\n").unwrap(); - - // Show results - tx.blocking_write(b"Destination buffers (after):\r\n").unwrap(); - tx.blocking_write(b" DST1: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST3: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify all three segments - let mut all_ok = true; - unsafe { - let src1 = core::ptr::addr_of!(SRC1) as *const u32; - let dst1 = core::ptr::addr_of!(DST1) as *const u32; - for i in 0..4 { - if *src1.add(i) != *dst1.add(i) { - all_ok = false; - } - } - - let src2 = core::ptr::addr_of!(SRC2) as *const u32; - let dst2 = core::ptr::addr_of!(DST2) as *const u32; - for i in 0..4 { - if *src2.add(i) != *dst2.add(i) { - all_ok = false; - } - } - - let src3 = core::ptr::addr_of!(SRC3) as *const u32; - let dst3 = core::ptr::addr_of!(DST3) as *const u32; - for i in 0..4 { - if *src3.add(i) != *dst3.add(i) { - all_ok = false; - } - } - } - - if all_ok { - tx.blocking_write(b"PASS: All segments verified!\r\n").unwrap(); - defmt::info!("PASS: All segments verified!"); - } else { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } - - tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n") - .unwrap(); - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/dma_wrap_transfer.rs b/examples/src/bin/dma_wrap_transfer.rs deleted file mode 100644 index 82936d9d0..000000000 --- a/examples/src/bin/dma_wrap_transfer.rs +++ /dev/null @@ -1,222 +0,0 @@ -//! DMA wrap transfer example for MCXA276. -//! -//! This example demonstrates using DMA with modulo addressing to wrap around -//! a source buffer, effectively repeating the source data in the destination. -//! -//! # Embassy-style features demonstrated: -//! - `DmaChannel::is_done()` and `clear_done()` helper methods -//! - No need to pass register block around - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; -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 _}; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); - -// Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo -#[repr(align(16))] -struct AlignedSrc([u32; 4]); - -static mut SRC: AlignedSrc = AlignedSrc([0; 4]); -static mut DST: [u32; 8] = [0; 8]; - -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA wrap transfer example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n").unwrap(); - - // Initialize buffers - unsafe { - SRC.0 = [1, 2, 3, 4]; - DST = [0; 8]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); - - // Create DMA channel using Embassy-style API - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // 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. - // DST modulo is 0 (disabled). - // This causes the source address to wrap around after 16 bytes, - // effectively repeating the source data. - unsafe { - let t = dma_ch0.tcd(); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source/destination addresses - t.tcd_saddr() - .write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32)); - t.tcd_daddr() - .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32)); - - // Offsets: both increment by 4 bytes - t.tcd_soff().write(|w| w.soff().bits(4)); - t.tcd_doff().write(|w| w.doff().bits(4)); - - // Attributes: 32-bit transfers (size = 2) - // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled) - t.tcd_attr().write(|w| { - w.ssize() - .bits(2) - .dsize() - .bits(2) - .smod() - .bits(4) // Source modulo: 2^4 = 16 bytes - .dmod() - .bits(0) // Dest modulo: disabled - }); - - // Transfer 32 bytes total in one minor loop - let nbytes = 32u32; - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Source wraps via modulo, no adjustment needed - t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); - // Reset dest address after major loop - t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); - - // Major loop count = 1 - t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); - - // Enable interrupt on major loop completion - t.tcd_csr().write(|w| w.intmajor().set_bit()); - - cortex_m::asm::dsb(); - - tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(); - } - - // Wait for completion using channel helper method - while !dma_ch0.is_done() { - cortex_m::asm::nop(); - } - unsafe { - dma_ch0.clear_done(); - } - - tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4] - let expected = [1u32, 2, 3, 4, 1, 2, 3, 4]; - let mut mismatch = false; - unsafe { - for i in 0..8 { - if DST[i] != expected[i] { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/src/bin/hello.rs b/examples/src/bin/hello.rs deleted file mode 100644 index e371d9413..000000000 --- a/examples/src/bin/hello.rs +++ /dev/null @@ -1,119 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use hal::lpuart::{Blocking, Config, Lpuart}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -/// Simple helper to write a byte as hex to UART -fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) { - const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; - let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); - let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("boot"); - - // Create UART configuration - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX - let mut uart = Lpuart::new_blocking( - p.LPUART2, // Peripheral - p.P2_2, // TX pin - p.P2_3, // RX pin - config, - ) - .unwrap(); - - // Print welcome message before any async delays to guarantee early console output - uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); - uart.write_str_blocking("Available commands:\r\n"); - uart.write_str_blocking(" help - Show this help\r\n"); - uart.write_str_blocking(" echo - Echo back the text\r\n"); - uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); - uart.write_str_blocking("Type a command: "); - - let mut buffer = [0u8; 64]; - let mut buf_idx = 0; - - loop { - // Read a byte from UART - let byte = uart.read_byte_blocking(); - - // Echo the character back - if byte == b'\r' || byte == b'\n' { - // Enter pressed - process command - uart.write_str_blocking("\r\n"); - - if buf_idx > 0 { - let command = &buffer[0..buf_idx]; - - if command == b"help" { - uart.write_str_blocking("Available commands:\r\n"); - uart.write_str_blocking(" help - Show this help\r\n"); - uart.write_str_blocking(" echo - Echo back the text\r\n"); - uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); - } else if command.starts_with(b"echo ") && command.len() > 5 { - uart.write_str_blocking("Echo: "); - uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or("")); - uart.write_str_blocking("\r\n"); - } else if command.starts_with(b"hex ") && command.len() > 4 { - // Parse the byte value - let num_str = &command[4..]; - if let Ok(num) = parse_u8(num_str) { - uart.write_str_blocking("Hex: 0x"); - write_hex_byte(&mut uart, num); - uart.write_str_blocking("\r\n"); - } else { - uart.write_str_blocking("Invalid number for hex command\r\n"); - } - } else if !command.is_empty() { - uart.write_str_blocking("Unknown command: "); - uart.write_str_blocking(core::str::from_utf8(command).unwrap_or("")); - uart.write_str_blocking("\r\n"); - } - } - - // Reset buffer and prompt - buf_idx = 0; - uart.write_str_blocking("Type a command: "); - } else if byte == 8 || byte == 127 { - // Backspace - if buf_idx > 0 { - buf_idx -= 1; - uart.write_str_blocking("\x08 \x08"); // Erase character - } - } else if buf_idx < buffer.len() - 1 { - // Regular character - buffer[buf_idx] = byte; - buf_idx += 1; - let _ = uart.write_byte(byte); - } - } -} - -/// Simple parser for u8 from ASCII bytes -fn parse_u8(bytes: &[u8]) -> Result { - let mut result = 0u8; - for &b in bytes { - if b.is_ascii_digit() { - result = result.checked_mul(10).ok_or(())?; - result = result.checked_add(b - b'0').ok_or(())?; - } else { - return Err(()); - } - } - Ok(result) -} diff --git a/examples/src/bin/i2c-blocking.rs b/examples/src/bin/i2c-blocking.rs deleted file mode 100644 index 0f6c8cbae..000000000 --- a/examples/src/bin/i2c-blocking.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_time::Timer; -use hal::clocks::config::Div8; -use hal::config::Config; -use hal::i2c::controller::{self, I2c, Speed}; -use tmp108::Tmp108; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut config = Config::default(); - config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); - - let p = hal::init(config); - - defmt::info!("I2C example"); - - let mut config = controller::Config::default(); - config.speed = Speed::Standard; - let i2c = I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap(); - let mut tmp = Tmp108::new_with_a0_gnd(i2c); - - loop { - let temperature = tmp.temperature().unwrap(); - defmt::info!("Temperature: {}C", temperature); - Timer::after_secs(1).await; - } -} diff --git a/examples/src/bin/i2c-scan-blocking.rs b/examples/src/bin/i2c-scan-blocking.rs deleted file mode 100644 index 4e203597b..000000000 --- a/examples/src/bin/i2c-scan-blocking.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::gpio::Pull; -use embassy_mcxa::Input; -use embassy_time::Timer; -use hal::clocks::config::Div8; -use hal::config::Config; -use hal::i2c::controller::{self, I2c, Speed}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut config = Config::default(); - config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); - - let p = hal::init(config); - - defmt::info!("I2C example"); - - let mut config = controller::Config::default(); - config.speed = Speed::Standard; - - // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and - // defaults to SWO on the debug peripheral. Explicitly make it a high-z - // input. - let _pin = Input::new(p.P0_2, Pull::Disabled); - let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap(); - - for addr in 0x01..=0x7f { - let result = i2c.blocking_write(addr, &[]); - if result.is_ok() { - defmt::info!("Device found at addr {:02x}", addr); - } - } - - loop { - Timer::after_secs(10).await; - } -} diff --git a/examples/src/bin/lpuart_buffered.rs b/examples/src/bin/lpuart_buffered.rs deleted file mode 100644 index 420589d00..000000000 --- a/examples/src/bin/lpuart_buffered.rs +++ /dev/null @@ -1,62 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::lpuart::buffered::BufferedLpuart; -use embassy_mcxa::lpuart::Config; -use embassy_mcxa::{bind_interrupts, lpuart}; -use embedded_io_async::Write; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver -bind_interrupts!(struct Irqs { - LPUART2 => lpuart::buffered::BufferedInterruptHandler::; -}); - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - // Configure NVIC for LPUART2 - hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); - - // UART configuration (enable both TX and RX) - let config = Config { - baudrate_bps: 115_200, - rx_fifo_watermark: 0, - tx_fifo_watermark: 0, - ..Default::default() - }; - - let mut tx_buf = [0u8; 256]; - let mut rx_buf = [0u8; 256]; - - // Create a buffered LPUART2 instance with both TX and RX - let mut uart = BufferedLpuart::new( - p.LPUART2, - p.P2_2, // TX pin - p.P2_3, // RX pin - Irqs, - &mut tx_buf, - &mut rx_buf, - config, - ) - .unwrap(); - - // Split into TX and RX parts - let (tx, rx) = uart.split_ref(); - - tx.write(b"Hello buffered LPUART.\r\n").await.unwrap(); - tx.write(b"Type characters to echo them back.\r\n").await.unwrap(); - - // Echo loop - let mut buf = [0u8; 4]; - loop { - let used = rx.read(&mut buf).await.unwrap(); - tx.write_all(&buf[..used]).await.unwrap(); - } -} diff --git a/examples/src/bin/lpuart_dma.rs b/examples/src/bin/lpuart_dma.rs deleted file mode 100644 index 5497f8646..000000000 --- a/examples/src/bin/lpuart_dma.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! LPUART DMA example for MCXA276. -//! -//! 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::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; -use embassy_mcxa::lpuart::{Config, LpuartDma}; -use embassy_mcxa::{bind_interrupts, pac}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -// 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) { - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("LPUART DMA example starting..."); - - // 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); - } - - // Create UART configuration - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - // Create UART instance with DMA channels - let mut lpuart = LpuartDma::new( - p.LPUART2, p.P2_2, // TX pin - p.P2_3, // RX pin - p.DMA_CH0, // TX DMA channel - p.DMA_CH1, // RX DMA channel - config, - ) - .unwrap(); - - // 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(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(prompt).await.unwrap(); - - // Receive 16 characters using DMA - let mut rx_buf = [0u8; 16]; - 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(echo_prefix).await.unwrap(); - lpuart.write_dma(&rx_buf).await.unwrap(); - let done_msg = b"\r\nDone!\r\n"; - lpuart.write_dma(done_msg).await.unwrap(); - - defmt::info!("Example complete"); -} diff --git a/examples/src/bin/lpuart_polling.rs b/examples/src/bin/lpuart_polling.rs deleted file mode 100644 index b80668834..000000000 --- a/examples/src/bin/lpuart_polling.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -use crate::hal::lpuart::{Config, Lpuart}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("boot"); - - // Create UART configuration - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX - let lpuart = Lpuart::new_blocking( - p.LPUART2, // Peripheral - p.P2_2, // TX pin - p.P2_3, // RX pin - config, - ) - .unwrap(); - - // Split into separate TX and RX parts - let (mut tx, mut rx) = lpuart.split(); - - // Write hello messages - tx.blocking_write(b"Hello world.\r\n").unwrap(); - tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap(); - - // Echo loop - loop { - let mut buf = [0u8; 1]; - rx.blocking_read(&mut buf).unwrap(); - tx.blocking_write(&buf).unwrap(); - } -} diff --git a/examples/src/bin/lpuart_ring_buffer.rs b/examples/src/bin/lpuart_ring_buffer.rs deleted file mode 100644 index 1d1a51970..000000000 --- a/examples/src/bin/lpuart_ring_buffer.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! LPUART Ring Buffer DMA example for MCXA276. -//! -//! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()` -//! API for continuous circular DMA reception from a UART peripheral. -//! -//! # Features demonstrated: -//! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA -//! - `RingBuffer` for async reading of received data -//! - Handling of potential overrun conditions -//! - Half-transfer and complete-transfer interrupts for timely wakeups -//! -//! # How it works: -//! 1. Create an `LpuartRxDma` driver with a DMA channel -//! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration -//! 3. Application asynchronously reads data as it arrives via `ring_buf.read()` -//! 4. Both half-transfer and complete-transfer interrupts wake the reader - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -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 {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -// Bind DMA channel interrupts -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; - DMA_CH1 => DmaCh1InterruptHandler; -}); - -// Ring buffer for RX - power of 2 is ideal for modulo efficiency -static mut RX_RING_BUFFER: [u8; 64] = [0; 64]; - -/// Helper to write a byte as hex to UART -fn write_hex( - tx: &mut LpuartTxDma<'_, T, C>, - byte: u8, -) { - const HEX: &[u8; 16] = b"0123456789ABCDEF"; - let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]]; - tx.blocking_write(&buf).ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("LPUART Ring Buffer DMA example starting..."); - - // Create UART configuration - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - // Create LPUART with DMA support for both TX and RX, then split - // This is the proper Embassy pattern - create once, split into TX and RX - let lpuart = LpuartDma::new(p.LPUART2, p.P2_2, p.P2_3, p.DMA_CH1, p.DMA_CH0, config).unwrap(); - let (mut tx, rx) = lpuart.split(); - - tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap(); - tx.blocking_write(b"==============================\r\n\r\n").unwrap(); - - tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n") - .unwrap(); - - // 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(); - } - - tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n") - .unwrap(); - tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n") - .unwrap(); - - // Main loop: read from ring buffer and echo back - let mut read_buf = [0u8; 16]; - let mut total_received: usize = 0; - - loop { - // Async read - waits until data is available - match ring_buf.read(&mut read_buf).await { - Ok(n) if n > 0 => { - total_received += n; - - // Echo back what we received - tx.blocking_write(b"RX[").unwrap(); - for (i, &byte) in read_buf.iter().enumerate().take(n) { - write_hex(&mut tx, byte); - if i < n - 1 { - tx.blocking_write(b" ").unwrap(); - } - } - tx.blocking_write(b"]: ").unwrap(); - tx.blocking_write(&read_buf[..n]).unwrap(); - tx.blocking_write(b"\r\n").unwrap(); - - defmt::info!("Received {} bytes, total: {}", n, total_received); - } - Ok(_) => { - // No data, shouldn't happen with async read - } - Err(_) => { - // Overrun detected - tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap(); - defmt::error!("Ring buffer overrun!"); - ring_buf.clear(); - } - } - } -} diff --git a/examples/src/bin/rtc_alarm.rs b/examples/src/bin/rtc_alarm.rs deleted file mode 100644 index a7800a2d1..000000000 --- a/examples/src/bin/rtc_alarm.rs +++ /dev/null @@ -1,74 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa as hal; -use hal::rtc::{RtcDateTime, RtcInterruptEnable}; -use hal::InterruptExt; - -type MyRtc = hal::rtc::Rtc<'static, hal::rtc::Rtc0>; - -use embassy_mcxa::bind_interrupts; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - RTC => hal::rtc::RtcHandler; -}); - -#[used] -#[no_mangle] -static KEEP_RTC: unsafe extern "C" fn() = RTC; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - defmt::info!("=== RTC Alarm Example ==="); - - let rtc_config = hal::rtc::get_default_config(); - - let rtc = MyRtc::new(p.RTC0, rtc_config); - - let now = RtcDateTime { - year: 2025, - month: 10, - day: 15, - hour: 14, - minute: 30, - second: 0, - }; - - rtc.stop(); - - defmt::info!("Time set to: 2025-10-15 14:30:00"); - rtc.set_datetime(now); - - let mut alarm = now; - alarm.second += 10; - - rtc.set_alarm(alarm); - defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)"); - - rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); - - unsafe { - hal::interrupt::RTC.enable(); - } - - unsafe { - cortex_m::interrupt::enable(); - } - - rtc.start(); - - defmt::info!("RTC started, waiting for alarm..."); - - loop { - if rtc.is_alarm_triggered() { - defmt::info!("*** ALARM TRIGGERED! ***"); - break; - } - } - - defmt::info!("Example complete - Test PASSED!"); -} diff --git a/examples/src/lib.rs b/examples/src/lib.rs deleted file mode 100644 index 2573a6adc..000000000 --- a/examples/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![no_std] -#![allow(clippy::missing_safety_doc)] - -//! Shared board-specific helpers for the FRDM-MCXA276 examples. -//! These live with the examples so the HAL stays generic. - -use hal::{clocks, pins}; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -/// Initialize clocks and pin muxing for ADC. -pub unsafe fn init_adc_pins() { - // NOTE: Lpuart has been updated to properly enable + reset its own clocks. - // GPIO has not. - _ = clocks::enable_and_reset::(&clocks::periph_helpers::NoConfig); - pins::configure_adc_pins(); -} -- cgit From 1ce2083f9a0fcf1cfbb10de0fb3ed44b460a5cc7 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 5 Dec 2025 14:57:25 +0100 Subject: Enable DMA interrupts in constructors --- examples/mcxa/src/bin/lpuart_dma.rs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/lpuart_dma.rs b/examples/mcxa/src/bin/lpuart_dma.rs index 5497f8646..1fc6595e6 100644 --- a/examples/mcxa/src/bin/lpuart_dma.rs +++ b/examples/mcxa/src/bin/lpuart_dma.rs @@ -32,12 +32,6 @@ async fn main(_spawner: Spawner) { defmt::info!("LPUART DMA example starting..."); - // 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); - } - // Create UART configuration let config = Config { baudrate_bps: 115_200, -- cgit 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 --- examples/mcxa/Cargo.toml | 1 + examples/mcxa/src/bin/lpuart_dma.rs | 2 +- examples/mcxa/src/bin/lpuart_ring_buffer.rs | 15 ++++----------- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'examples') 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 From 787bf84963ecd32306b6b2993504b2196f71cf72 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 5 Dec 2025 15:55:28 +0100 Subject: use core::fmt::Write instead of home-rolled fmt --- examples/mcxa/src/bin/dma_channel_link.rs | 32 ++------------------- examples/mcxa/src/bin/dma_interleave_transfer.rs | 32 ++------------------- examples/mcxa/src/bin/dma_mem_to_mem.rs | 33 ++-------------------- examples/mcxa/src/bin/dma_memset.rs | 32 ++------------------- examples/mcxa/src/bin/dma_ping_pong_transfer.rs | 32 ++------------------- examples/mcxa/src/bin/dma_scatter_gather.rs | 31 ++------------------ .../mcxa/src/bin/dma_scatter_gather_builder.rs | 21 ++------------ examples/mcxa/src/bin/dma_wrap_transfer.rs | 32 ++------------------- 8 files changed, 16 insertions(+), 229 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_channel_link.rs b/examples/mcxa/src/bin/dma_channel_link.rs index 92c7a9681..f7ab5d8fd 100644 --- a/examples/mcxa/src/bin/dma_channel_link.rs +++ b/examples/mcxa/src/bin/dma_channel_link.rs @@ -22,6 +22,7 @@ use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2In 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 _}; +use core::fmt::Write as _; // Buffers static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; @@ -37,38 +38,9 @@ bind_interrupts!(struct Irqs { DMA_CH2 => DmaCh2InterruptHandler; }); -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_interleave_transfer.rs b/examples/mcxa/src/bin/dma_interleave_transfer.rs index 7876e8978..98e301a7c 100644 --- a/examples/mcxa/src/bin/dma_interleave_transfer.rs +++ b/examples/mcxa/src/bin/dma_interleave_transfer.rs @@ -16,6 +16,7 @@ use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; 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 _}; +use core::fmt::Write as _; // Bind DMA channel 0 interrupt using Embassy-style macro bind_interrupts!(struct Irqs { @@ -29,38 +30,9 @@ const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; static mut SRC_BUFFER: [u32; HALF_BUFF_LENGTH] = [0; HALF_BUFF_LENGTH]; static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs index 68f70e742..149e37326 100644 --- a/examples/mcxa/src/bin/dma_mem_to_mem.rs +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs @@ -19,6 +19,7 @@ use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; 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 _}; +use core::fmt::Write as _; // Bind DMA channel 0 interrupt using Embassy-style macro bind_interrupts!(struct Irqs { @@ -32,40 +33,10 @@ static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; // u32 max is 4294967295 (10 digits) - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - /// Helper to print a buffer as [v1, v2, v3, v4] to UART /// Takes a raw pointer to avoid warnings about shared references to mutable statics fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) { - tx.blocking_write(b"[").ok(); - unsafe { - let buf = &*buf_ptr; - for (i, val) in buf.iter().enumerate() { - write_u32(tx, *val); - if i < buf.len() - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { &*buf_ptr }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_memset.rs b/examples/mcxa/src/bin/dma_memset.rs index 95e365e47..bc4e78701 100644 --- a/examples/mcxa/src/bin/dma_memset.rs +++ b/examples/mcxa/src/bin/dma_memset.rs @@ -16,6 +16,7 @@ use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; 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 _}; +use core::fmt::Write as _; // Bind DMA channel 0 interrupt using Embassy-style macro bind_interrupts!(struct Irqs { @@ -28,38 +29,9 @@ const BUFFER_LENGTH: usize = 4; static mut PATTERN: u32 = 0; static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs index f8f543382..728e4d408 100644 --- a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs +++ b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs @@ -31,6 +31,7 @@ use embassy_mcxa::dma::{self, DmaCh1InterruptHandler, DmaChannel, Tcd, TransferO 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 _}; +use core::fmt::Write as _; // Source and destination buffers for Approach 1 (scatter/gather) static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; @@ -87,38 +88,9 @@ bind_interrupts!(struct Irqs { DMA_CH1 => DmaCh1InterruptHandler; }); -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_scatter_gather.rs b/examples/mcxa/src/bin/dma_scatter_gather.rs index 4b26bc2ed..ea553b843 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather.rs @@ -20,6 +20,7 @@ use embassy_mcxa::dma::{self, DmaChannel, Tcd}; 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 _}; +use core::fmt::Write as _; // Source and destination buffers static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; @@ -72,38 +73,10 @@ bind_interrupts!(struct Irqs { DMA_CH0 => ScatterGatherDmaHandler; }); -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs index e483bb81f..29c54ca42 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs @@ -26,6 +26,7 @@ use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, ScatterGatherBuilder 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 _}; +use core::fmt::Write as _; // Bind DMA channel 0 interrupt bind_interrupts!(struct Irqs { @@ -42,27 +43,9 @@ static mut DST1: [u32; 4] = [0; 4]; static mut DST2: [u32; 4] = [0; 4]; static mut DST3: [u32; 4] = [0; 4]; -/// Helper to write a u32 as hex to UART -fn write_hex(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - const HEX: &[u8; 16] = b"0123456789ABCDEF"; - for i in (0..8).rev() { - let nibble = ((val >> (i * 4)) & 0xF) as usize; - tx.blocking_write(&[HEX[nibble]]).ok(); - } -} - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_hex(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:08X?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] diff --git a/examples/mcxa/src/bin/dma_wrap_transfer.rs b/examples/mcxa/src/bin/dma_wrap_transfer.rs index 82936d9d0..7fea4bf76 100644 --- a/examples/mcxa/src/bin/dma_wrap_transfer.rs +++ b/examples/mcxa/src/bin/dma_wrap_transfer.rs @@ -16,6 +16,7 @@ use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; 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 _}; +use core::fmt::Write as _; // Bind DMA channel 0 interrupt using Embassy-style macro bind_interrupts!(struct Irqs { @@ -29,38 +30,9 @@ struct AlignedSrc([u32; 4]); static mut SRC: AlignedSrc = AlignedSrc([0; 4]); static mut DST: [u32; 8] = [0; 8]; -/// Helper to write a u32 as decimal ASCII to UART -fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) { - let mut buf = [0u8; 10]; - let mut n = val; - let mut i = buf.len(); - - if n == 0 { - tx.blocking_write(b"0").ok(); - return; - } - - while n > 0 { - i -= 1; - buf[i] = b'0' + (n % 10) as u8; - n /= 10; - } - - tx.blocking_write(&buf[i..]).ok(); -} - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - tx.blocking_write(b"[").ok(); - unsafe { - for i in 0..len { - write_u32(tx, *buf_ptr.add(i)); - if i < len - 1 { - tx.blocking_write(b", ").ok(); - } - } - } - tx.blocking_write(b"]").ok(); + write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); } #[embassy_executor::main] -- cgit From 5d8f3a3d18eda339e258193295cf332d7e01882e Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 5 Dec 2025 20:45:59 +0100 Subject: Clean up some common PAC operations using helper methods --- examples/mcxa/src/bin/dma_mem_to_mem.rs | 75 ++++++++++----------------------- 1 file changed, 22 insertions(+), 53 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs index 149e37326..b78745464 100644 --- a/examples/mcxa/src/bin/dma_mem_to_mem.rs +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs @@ -18,6 +18,7 @@ use embassy_mcxa::clocks::config::Div8; use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; use embassy_mcxa::{bind_interrupts, pac}; +use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; use core::fmt::Write as _; @@ -29,14 +30,14 @@ bind_interrupts!(struct Irqs { const BUFFER_LENGTH: usize = 4; // Buffers in RAM (static mut is automatically placed in .bss/.data) -static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; -static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; +static SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]); +static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); +static MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); /// Helper to print a buffer as [v1, v2, v3, v4] to UART /// Takes a raw pointer to avoid warnings about shared references to mutable statics -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) { - write!(tx, "{:?}", unsafe { &*buf_ptr }).ok(); +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: &[u32; BUFFER_LENGTH]) { + write!(tx, "{:?}", buf_ptr).ok(); } #[embassy_executor::main] @@ -70,18 +71,16 @@ async fn main(_spawner: Spawner) { tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n") .unwrap(); - // Initialize buffers - unsafe { - SRC_BUFFER = [1, 2, 3, 4]; - DEST_BUFFER = [0; BUFFER_LENGTH]; - } + let src = SRC_BUFFER.take(); + let dst = DEST_BUFFER.take(); + let mst = MEMSET_BUFFER.take(); tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, &raw const SRC_BUFFER); + print_buffer(&mut tx, src); tx.blocking_write(b"\r\n").unwrap(); tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, &raw const DEST_BUFFER); + print_buffer(&mut tx, dst); tx.blocking_write(b"\r\n").unwrap(); tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") @@ -106,30 +105,17 @@ async fn main(_spawner: Spawner) { // Using async `.await` - the executor can run other tasks while waiting! // Perform type-safe memory-to-memory transfer using Embassy-style async API - unsafe { - let src = &*core::ptr::addr_of!(SRC_BUFFER); - let dst = &mut *core::ptr::addr_of_mut!(DEST_BUFFER); - - // Using async `.await` - the executor can run other tasks while waiting! - let transfer = dma_ch0.mem_to_mem(src, dst, options); - transfer.await; - } + // Using async `.await` - the executor can run other tasks while waiting! + let transfer = dma_ch0.mem_to_mem(src, dst, options); + transfer.await; tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap(); tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, &raw const DEST_BUFFER); + print_buffer(&mut tx, dst); tx.blocking_write(b"\r\n").unwrap(); // Verify data - let mut mismatch = false; - unsafe { - for i in 0..BUFFER_LENGTH { - if SRC_BUFFER[i] != DEST_BUFFER[i] { - mismatch = true; - break; - } - } - } + let mut mismatch = src != dst; if mismatch { tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap(); @@ -152,37 +138,24 @@ async fn main(_spawner: Spawner) { .unwrap(); tx.blocking_write(b"Memset Buffer (before): ").unwrap(); - print_buffer(&mut tx, &raw const MEMSET_BUFFER); + print_buffer(&mut tx, mst); tx.blocking_write(b"\r\n").unwrap(); // Fill buffer with a pattern value using DMA memset let pattern: u32 = 0xDEADBEEF; tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap(); - unsafe { - let dst = &mut *core::ptr::addr_of_mut!(MEMSET_BUFFER); - - // Using blocking_wait() for demonstration - also shows non-async usage - let transfer = dma_ch0.memset(&pattern, dst, options); - transfer.blocking_wait(); - } + // Using blocking_wait() for demonstration - also shows non-async usage + let transfer = dma_ch0.memset(&pattern, mst, options); + transfer.blocking_wait(); tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap(); tx.blocking_write(b"Memset Buffer (after): ").unwrap(); - print_buffer(&mut tx, &raw const MEMSET_BUFFER); + print_buffer(&mut tx, mst); tx.blocking_write(b"\r\n").unwrap(); // Verify memset result - let mut memset_ok = true; - unsafe { - #[allow(clippy::needless_range_loop)] - for i in 0..BUFFER_LENGTH { - if MEMSET_BUFFER[i] != pattern { - memset_ok = false; - break; - } - } - } + let memset_ok = mst.iter().all(|&v| v == pattern); if !memset_ok { tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap(); @@ -193,8 +166,4 @@ async fn main(_spawner: Spawner) { } tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap(); - - loop { - cortex_m::asm::wfe(); - } } -- cgit From 6680ef22fa4b46adb4cda46d6cdbc9dac39dc78c Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 8 Dec 2025 17:58:00 +0100 Subject: Clean up examples, move interrupts to be more fully managed --- examples/mcxa/src/bin/adc_interrupt.rs | 4 +- examples/mcxa/src/bin/adc_polling.rs | 2 +- examples/mcxa/src/bin/dma_channel_link.rs | 21 +---- examples/mcxa/src/bin/dma_interleave_transfer.rs | 58 +++++-------- examples/mcxa/src/bin/dma_mem_to_mem.rs | 81 ++++-------------- examples/mcxa/src/bin/dma_memset.rs | 97 ++++------------------ examples/mcxa/src/bin/dma_ping_pong_transfer.rs | 5 +- examples/mcxa/src/bin/dma_scatter_gather.rs | 3 +- .../mcxa/src/bin/dma_scatter_gather_builder.rs | 16 +--- examples/mcxa/src/bin/dma_wrap_transfer.rs | 16 +--- examples/mcxa/src/bin/i2c-async.rs | 2 +- examples/mcxa/src/bin/i2c-scan-blocking.rs | 2 +- examples/mcxa/src/bin/lpuart_buffered.rs | 2 +- examples/mcxa/src/bin/lpuart_dma.rs | 11 +-- examples/mcxa/src/bin/lpuart_ring_buffer.rs | 8 -- 15 files changed, 72 insertions(+), 256 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index c88b1fe8d..83d8046b3 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -4,13 +4,13 @@ use embassy_executor::Spawner; use embassy_mcxa_examples::init_adc_pins; use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; -use hal::clocks::PoweredClock; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::clocks::PoweredClock; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; use hal::pac::adc1::tctrl::Tcmd; -use hal::{InterruptExt, bind_interrupts}; +use hal::{bind_interrupts, InterruptExt}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index 07c50f224..ddf3f586b 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -4,8 +4,8 @@ use embassy_executor::Spawner; use embassy_mcxa_examples::init_adc_pins; use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; -use hal::clocks::PoweredClock; use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::clocks::PoweredClock; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; diff --git a/examples/mcxa/src/bin/dma_channel_link.rs b/examples/mcxa/src/bin/dma_channel_link.rs index f7ab5d8fd..2d757a636 100644 --- a/examples/mcxa/src/bin/dma_channel_link.rs +++ b/examples/mcxa/src/bin/dma_channel_link.rs @@ -16,13 +16,14 @@ #![no_std] #![no_main] +use core::fmt::Write as _; + use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler, DmaChannel}; +use embassy_mcxa::dma::DmaChannel; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::{bind_interrupts, pac}; +use embassy_mcxa::pac; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -use core::fmt::Write as _; // Buffers static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; @@ -30,14 +31,6 @@ static mut DEST_BUFFER0: [u32; 4] = [0; 4]; static mut DEST_BUFFER1: [u32; 4] = [0; 4]; static mut DEST_BUFFER2: [u32; 4] = [0; 4]; -// 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 => DmaCh0InterruptHandler; - DMA_CH1 => DmaCh1InterruptHandler; - DMA_CH2 => DmaCh2InterruptHandler; -}); - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); @@ -84,12 +77,6 @@ async fn main(_spawner: Spawner) { .normal_operation() }); - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH2); - } - let config = Config { baudrate_bps: 115_200, ..Default::default() diff --git a/examples/mcxa/src/bin/dma_interleave_transfer.rs b/examples/mcxa/src/bin/dma_interleave_transfer.rs index 98e301a7c..03441fc32 100644 --- a/examples/mcxa/src/bin/dma_interleave_transfer.rs +++ b/examples/mcxa/src/bin/dma_interleave_transfer.rs @@ -10,29 +10,25 @@ #![no_std] #![no_main] +use core::fmt::Write as _; + use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; +use embassy_mcxa::dma::DmaChannel; use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::{bind_interrupts, pac}; +use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -use core::fmt::Write as _; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); const BUFFER_LENGTH: usize = 16; const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; // Buffers in RAM -static mut SRC_BUFFER: [u32; HALF_BUFF_LENGTH] = [0; HALF_BUFF_LENGTH]; -static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; +static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); +static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); /// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); +fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf: &[u32]) { + write!(tx, "{:?}", buf).ok(); } #[embassy_executor::main] @@ -49,11 +45,6 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA interleave transfer example starting..."); - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - let config = Config { baudrate_bps: 115_200, ..Default::default() @@ -66,17 +57,16 @@ async fn main(_spawner: Spawner) { .unwrap(); // Initialize buffers - unsafe { - SRC_BUFFER = [1, 2, 3, 4, 5, 6, 7, 8]; - DEST_BUFFER = [0; BUFFER_LENGTH]; - } + let src = SRC_BUFFER.take(); + *src = [1, 2, 3, 4, 5, 6, 7, 8]; + let dst = DEST_BUFFER.take(); tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, HALF_BUFF_LENGTH); + print_buffer(&mut tx, src); tx.blocking_write(b"\r\n").unwrap(); tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + print_buffer(&mut tx, dst); tx.blocking_write(b"\r\n").unwrap(); tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") @@ -109,10 +99,8 @@ async fn main(_spawner: Spawner) { t.ch_int().write(|w| w.int().clear_bit_by_one()); // Source/destination addresses - t.tcd_saddr() - .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32)); - t.tcd_daddr() - .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32)); + t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); // Custom offsets for interleaving t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read @@ -156,21 +144,15 @@ async fn main(_spawner: Spawner) { tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") .unwrap(); tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); + print_buffer(&mut tx, dst); tx.blocking_write(b"\r\n\r\n").unwrap(); // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 let mut mismatch = false; - unsafe { - for i in 0..BUFFER_LENGTH { - if i % 2 == 0 { - if DEST_BUFFER[i] != SRC_BUFFER[i / 2] { - mismatch = true; - } - } else if DEST_BUFFER[i] != 0 { - mismatch = true; - } - } + let diter = dst.chunks_exact(2); + let siter = src.iter(); + for (ch, src) in diter.zip(siter) { + mismatch |= !matches!(ch, [a, 0] if a == src); } if mismatch { diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs index b78745464..b20068b79 100644 --- a/examples/mcxa/src/bin/dma_mem_to_mem.rs +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs @@ -15,17 +15,9 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, TransferOptions}; -use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::{bind_interrupts, pac}; +use embassy_mcxa::dma::{DmaChannel, TransferOptions}; use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -use core::fmt::Write as _; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); const BUFFER_LENGTH: usize = 4; @@ -34,12 +26,6 @@ static SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new( static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); static MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); -/// Helper to print a buffer as [v1, v2, v3, v4] to UART -/// Takes a raw pointer to avoid warnings about shared references to mutable statics -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: &[u32; BUFFER_LENGTH]) { - write!(tx, "{:?}", buf_ptr).ok(); -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { // Small delay to allow probe-rs to attach after reset @@ -54,37 +40,15 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA memory-to-memory example starting..."); - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - // Create UART for debug output - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n") - .unwrap(); + defmt::info!("EDMA memory to memory example begin."); let src = SRC_BUFFER.take(); let dst = DEST_BUFFER.take(); let mst = MEMSET_BUFFER.take(); - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, src); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, dst); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); + defmt::info!("Source Buffer: {=[?]}", src.as_slice()); + defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); + defmt::info!("Configuring DMA with Embassy-style API..."); // Create DMA channel let dma_ch0 = DmaChannel::new(p.DMA_CH0); @@ -109,19 +73,13 @@ async fn main(_spawner: Spawner) { let transfer = dma_ch0.mem_to_mem(src, dst, options); transfer.await; - tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n").unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, dst); - tx.blocking_write(b"\r\n").unwrap(); + defmt::info!("DMA mem-to-mem transfer complete!"); + defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); // Verify data - let mut mismatch = src != dst; - - if mismatch { - tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap(); + if src != dst { defmt::error!("FAIL: mem_to_mem mismatch!"); } else { - tx.blocking_write(b"PASS: mem_to_mem verified.\r\n\r\n").unwrap(); defmt::info!("PASS: mem_to_mem verified."); } @@ -134,36 +92,27 @@ async fn main(_spawner: Spawner) { // - Incrementing destination address // - Uses the same Transfer future pattern - tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n") - .unwrap(); + defmt::info!("--- Demonstrating memset() feature ---"); - tx.blocking_write(b"Memset Buffer (before): ").unwrap(); - print_buffer(&mut tx, mst); - tx.blocking_write(b"\r\n").unwrap(); + defmt::info!("Memset Buffer (before): {=[?]}", mst.as_slice()); // Fill buffer with a pattern value using DMA memset let pattern: u32 = 0xDEADBEEF; - tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap(); + defmt::info!("Filling with pattern 0xDEADBEEF..."); // Using blocking_wait() for demonstration - also shows non-async usage let transfer = dma_ch0.memset(&pattern, mst, options); transfer.blocking_wait(); - tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap(); - tx.blocking_write(b"Memset Buffer (after): ").unwrap(); - print_buffer(&mut tx, mst); - tx.blocking_write(b"\r\n").unwrap(); + defmt::info!("DMA memset complete!"); + defmt::info!("Memset Buffer (after): {=[?]}", mst.as_slice()); // Verify memset result - let memset_ok = mst.iter().all(|&v| v == pattern); - - if !memset_ok { - tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap(); + if !mst.iter().all(|&v| v == pattern) { defmt::error!("FAIL: memset mismatch!"); } else { - tx.blocking_write(b"PASS: memset verified.\r\n\r\n").unwrap(); defmt::info!("PASS: memset verified."); } - tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap(); + defmt::info!("=== All DMA tests complete ==="); } diff --git a/examples/mcxa/src/bin/dma_memset.rs b/examples/mcxa/src/bin/dma_memset.rs index bc4e78701..d7b03e91b 100644 --- a/examples/mcxa/src/bin/dma_memset.rs +++ b/examples/mcxa/src/bin/dma_memset.rs @@ -12,27 +12,15 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; -use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::{bind_interrupts, pac}; +use embassy_mcxa::dma::DmaChannel; +use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -use core::fmt::Write as _; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); const BUFFER_LENGTH: usize = 4; // Buffers in RAM -static mut PATTERN: u32 = 0; -static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH]; - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); -} +static PATTERN: u32 = 0xDEADBEEF; +static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -47,48 +35,14 @@ async fn main(_spawner: Spawner) { let p = hal::init(cfg); defmt::info!("DMA memset example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA memset example begin.\r\n\r\n").unwrap(); + defmt::info!("EDMA memset example begin."); // Initialize buffers - unsafe { - PATTERN = 0xDEADBEEF; - DEST_BUFFER = [0; BUFFER_LENGTH]; - } - - tx.blocking_write(b"Pattern value: 0x").unwrap(); - // Print pattern in hex - unsafe { - let hex_chars = b"0123456789ABCDEF"; - let mut hex_buf = [0u8; 8]; - let mut val = PATTERN; - for i in (0..8).rev() { - hex_buf[i] = hex_chars[(val & 0xF) as usize]; - val >>= 4; - } - tx.blocking_write(&hex_buf).ok(); - } - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); + let pat = &PATTERN; + let dst = DEST_BUFFER.take(); + defmt::info!("Pattern Value: {=u32}", pat); + defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); + defmt::info!("Configuring DMA with Embassy-style API..."); // Create DMA channel using Embassy-style API let dma_ch0 = DmaChannel::new(p.DMA_CH0); @@ -116,11 +70,9 @@ async fn main(_spawner: Spawner) { t.ch_int().write(|w| w.int().clear_bit_by_one()); // Source address (pattern) - fixed - t.tcd_saddr() - .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32)); + t.tcd_saddr().write(|w| w.saddr().bits(pat as *const _ as u32)); // Destination address - increments - t.tcd_daddr() - .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32)); + t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); // Source offset = 0 (stays fixed), Dest offset = 4 (increments) t.tcd_soff().write(|w| w.soff().bits(0)); @@ -147,7 +99,7 @@ async fn main(_spawner: Spawner) { cortex_m::asm::dsb(); - tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); + defmt::info!("Triggering transfer..."); dma_ch0.trigger_start(); } @@ -159,32 +111,15 @@ async fn main(_spawner: Spawner) { dma_ch0.clear_done(); } - tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n").unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH); - tx.blocking_write(b"\r\n\r\n").unwrap(); + defmt::info!("EDMA memset example finish."); + defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); // Verify: All elements should equal PATTERN - let mut mismatch = false; - unsafe { - #[allow(clippy::needless_range_loop)] - for i in 0..BUFFER_LENGTH { - if DEST_BUFFER[i] != PATTERN { - mismatch = true; - break; - } - } - } + let mismatch = dst.iter().any(|i| *i != *pat); if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); defmt::error!("FAIL: Mismatch detected!"); } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); defmt::info!("PASS: Data verified."); } - - loop { - cortex_m::asm::wfe(); - } } diff --git a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs index 728e4d408..58f643b80 100644 --- a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs +++ b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs @@ -23,15 +23,15 @@ #![no_std] #![no_main] +use core::fmt::Write as _; use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaCh1InterruptHandler, DmaChannel, Tcd, TransferOptions}; +use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; 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 _}; -use core::fmt::Write as _; // Source and destination buffers for Approach 1 (scatter/gather) static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; @@ -85,7 +85,6 @@ impl embassy_mcxa::interrupt::typelevel::Handler PingPongDmaHandler; - DMA_CH1 => DmaCh1InterruptHandler; }); /// Helper to print a buffer to UART diff --git a/examples/mcxa/src/bin/dma_scatter_gather.rs b/examples/mcxa/src/bin/dma_scatter_gather.rs index ea553b843..3e34e95b1 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather.rs @@ -12,6 +12,7 @@ #![no_std] #![no_main] +use core::fmt::Write as _; use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::Spawner; @@ -20,7 +21,6 @@ use embassy_mcxa::dma::{self, DmaChannel, Tcd}; 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 _}; -use core::fmt::Write as _; // Source and destination buffers static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; @@ -73,7 +73,6 @@ bind_interrupts!(struct Irqs { DMA_CH0 => ScatterGatherDmaHandler; }); - /// Helper to print a buffer to UART fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs index 29c54ca42..d0f9ae9c4 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs @@ -20,18 +20,13 @@ #![no_std] #![no_main] +use core::fmt::Write as _; + use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel, ScatterGatherBuilder}; +use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder}; 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 _}; -use core::fmt::Write as _; - -// Bind DMA channel 0 interrupt -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); // Source buffers (multiple segments) static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444]; @@ -62,11 +57,6 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA Scatter-Gather Builder example starting..."); - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - // Create UART for debug output let config = Config { baudrate_bps: 115_200, diff --git a/examples/mcxa/src/bin/dma_wrap_transfer.rs b/examples/mcxa/src/bin/dma_wrap_transfer.rs index 7fea4bf76..acfd29f08 100644 --- a/examples/mcxa/src/bin/dma_wrap_transfer.rs +++ b/examples/mcxa/src/bin/dma_wrap_transfer.rs @@ -10,18 +10,13 @@ #![no_std] #![no_main] +use core::fmt::Write as _; + use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel}; +use embassy_mcxa::dma::DmaChannel; 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 _}; -use core::fmt::Write as _; - -// Bind DMA channel 0 interrupt using Embassy-style macro -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; -}); // Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo #[repr(align(16))] @@ -49,11 +44,6 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA wrap transfer example starting..."); - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - let config = Config { baudrate_bps: 115_200, ..Default::default() diff --git a/examples/mcxa/src/bin/i2c-async.rs b/examples/mcxa/src/bin/i2c-async.rs index edcfd5f22..47b5f3cbe 100644 --- a/examples/mcxa/src/bin/i2c-async.rs +++ b/examples/mcxa/src/bin/i2c-async.rs @@ -6,8 +6,8 @@ use embassy_time::Timer; use hal::bind_interrupts; use hal::clocks::config::Div8; use hal::config::Config; -use hal::i2c::InterruptHandler; use hal::i2c::controller::{self, I2c, Speed}; +use hal::i2c::InterruptHandler; use hal::peripherals::LPI2C3; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs index 0197f9b1d..4e203597b 100644 --- a/examples/mcxa/src/bin/i2c-scan-blocking.rs +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs @@ -2,8 +2,8 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa::Input; use embassy_mcxa::gpio::Pull; +use embassy_mcxa::Input; use embassy_time::Timer; use hal::clocks::config::Div8; use hal::config::Config; diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs index 47b56b7c7..420589d00 100644 --- a/examples/mcxa/src/bin/lpuart_buffered.rs +++ b/examples/mcxa/src/bin/lpuart_buffered.rs @@ -3,8 +3,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::lpuart::Config; use embassy_mcxa::lpuart::buffered::BufferedLpuart; +use embassy_mcxa::lpuart::Config; use embassy_mcxa::{bind_interrupts, lpuart}; use embedded_io_async::Write; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; diff --git a/examples/mcxa/src/bin/lpuart_dma.rs b/examples/mcxa/src/bin/lpuart_dma.rs index 34d343452..cc86f6a40 100644 --- a/examples/mcxa/src/bin/lpuart_dma.rs +++ b/examples/mcxa/src/bin/lpuart_dma.rs @@ -12,17 +12,9 @@ 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; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -// 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) { let mut cfg = hal::config::Config::default(); @@ -40,7 +32,8 @@ async fn main(_spawner: Spawner) { // Create UART instance with DMA channels let mut lpuart = LpuartDma::new( - p.LPUART2, p.P2_2, // TX pin + p.LPUART2, // Instance + p.P2_2, // TX pin p.P2_3, // RX pin p.DMA_CH0, // TX DMA channel p.DMA_CH1, // RX DMA channel diff --git a/examples/mcxa/src/bin/lpuart_ring_buffer.rs b/examples/mcxa/src/bin/lpuart_ring_buffer.rs index b707e20f8..be7fd4534 100644 --- a/examples/mcxa/src/bin/lpuart_ring_buffer.rs +++ b/examples/mcxa/src/bin/lpuart_ring_buffer.rs @@ -19,19 +19,11 @@ #![no_main] use embassy_executor::Spawner; -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 -bind_interrupts!(struct Irqs { - DMA_CH0 => DmaCh0InterruptHandler; - DMA_CH1 => DmaCh1InterruptHandler; -}); - // Ring buffer for RX - power of 2 is ideal for modulo efficiency static RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]); -- cgit From f7d5abfe946e71d426c9d0e96231e48faf27f6cd Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 8 Dec 2025 20:33:45 +0100 Subject: Work on scatter-gather-builder --- .../mcxa/src/bin/dma_scatter_gather_builder.rs | 168 +++++++-------------- 1 file changed, 57 insertions(+), 111 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs index d0f9ae9c4..ba8682cee 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs @@ -25,23 +25,18 @@ use core::fmt::Write as _; use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder}; -use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; +use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Source buffers (multiple segments) -static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444]; -static mut SRC2: [u32; 4] = [0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]; -static mut SRC3: [u32; 4] = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]; +static SRC1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x11111111, 0x22222222, 0x33333333, 0x44444444]); +static SRC2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]); +static SRC3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]); // Destination buffers (one per segment) -static mut DST1: [u32; 4] = [0; 4]; -static mut DST2: [u32; 4] = [0; 4]; -static mut DST3: [u32; 4] = [0; 4]; - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - write!(tx, "{:08X?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); -} +static DST1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); +static DST2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); +static DST3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -57,47 +52,30 @@ async fn main(_spawner: Spawner) { defmt::info!("DMA Scatter-Gather Builder example starting..."); - // Create UART for debug output - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap(); - tx.blocking_write(b"===================================\r\n\r\n") - .unwrap(); + defmt::info!("DMA Scatter-Gather Builder Example"); + defmt::info!("==================================="); + let src1 = SRC1.take(); + let src2 = SRC2.take(); + let src3 = SRC3.take(); + let dst1 = DST1.take(); + let dst2 = DST2.take(); + let dst3 = DST3.take(); // Show source buffers - tx.blocking_write(b"Source buffers:\r\n").unwrap(); - tx.blocking_write(b" SRC1: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" SRC2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" SRC3: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC3) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - tx.blocking_write(b"Destination buffers (before):\r\n").unwrap(); - tx.blocking_write(b" DST1: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST3: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); + defmt::info!("Source buffers:"); + defmt::info!(" SRC1: {=[?]}", src1.as_slice()); + defmt::info!(" SRC2: {=[?]}", src2.as_slice()); + defmt::info!(" SRC3: {=[?]}", src3.as_slice()); + + defmt::info!("Destination buffers (before):"); + defmt::info!(" DST1: {=[?]}", dst1.as_slice()); + defmt::info!(" DST2: {=[?]}", dst2.as_slice()); + defmt::info!(" DST3: {=[?]}", dst3.as_slice()); // Create DMA channel let dma_ch0 = DmaChannel::new(p.DMA_CH0); - tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n") - .unwrap(); + defmt::info!("Building scatter-gather chain with builder API..."); // ========================================================================= // ScatterGatherBuilder API demonstration @@ -112,27 +90,24 @@ async fn main(_spawner: Spawner) { let mut builder = ScatterGatherBuilder::::new(); // Add three transfer segments - the builder handles TCD linking automatically - unsafe { - let src1 = &*core::ptr::addr_of!(SRC1); - let dst1 = &mut *core::ptr::addr_of_mut!(DST1); - builder.add_transfer(src1, dst1); - } - - unsafe { - let src2 = &*core::ptr::addr_of!(SRC2); - let dst2 = &mut *core::ptr::addr_of_mut!(DST2); - builder.add_transfer(src2, dst2); - } - - unsafe { - let src3 = &*core::ptr::addr_of!(SRC3); - let dst3 = &mut *core::ptr::addr_of_mut!(DST3); - builder.add_transfer(src3, dst3); - } - - tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap(); - tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n") - .unwrap(); + builder.add_transfer(src1, dst1); + builder.add_transfer(src2, dst2); + builder.add_transfer(src3, dst3); + + defmt::info!("Added 3 transfer segments to chain."); + defmt::info!("Starting scatter-gather transfer with .await..."); + + // TODO START + defmt::info!("Destination buffers (after):"); + defmt::info!(" DST1: {=[?]}", dst1.as_slice()); + defmt::info!(" DST2: {=[?]}", dst2.as_slice()); + defmt::info!(" DST3: {=[?]}", dst3.as_slice()); + // TODO: If we want to make the `builder.build()` below safe, the above prints SHOULD NOT + // compile. We need to make sure that the lifetime of the builder reflects that it is + // "consuming" the slices until the builder is dropped, since we can access them to print here, + // that means that the borrow checker isn't enforcing that yet. + todo!("ABOVE CODE SHOULDN'T COMPILE"); + // TODO END // Build and execute the scatter-gather chain // The build() method: @@ -145,60 +120,31 @@ async fn main(_spawner: Spawner) { transfer.blocking_wait(); } - tx.blocking_write(b"Scatter-gather transfer complete!\r\n\r\n").unwrap(); + defmt::info!("Scatter-gather transfer complete!"); // Show results - tx.blocking_write(b"Destination buffers (after):\r\n").unwrap(); - tx.blocking_write(b" DST1: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b" DST3: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); + defmt::info!("Destination buffers (after):"); + defmt::info!(" DST1: {=[?]}", dst1.as_slice()); + defmt::info!(" DST2: {=[?]}", dst2.as_slice()); + defmt::info!(" DST3: {=[?]}", dst3.as_slice()); + + let comps = [ + (src1, dst1), + (src2, dst2), + (src3, dst3), + ]; // Verify all three segments let mut all_ok = true; - unsafe { - let src1 = core::ptr::addr_of!(SRC1) as *const u32; - let dst1 = core::ptr::addr_of!(DST1) as *const u32; - for i in 0..4 { - if *src1.add(i) != *dst1.add(i) { - all_ok = false; - } - } - - let src2 = core::ptr::addr_of!(SRC2) as *const u32; - let dst2 = core::ptr::addr_of!(DST2) as *const u32; - for i in 0..4 { - if *src2.add(i) != *dst2.add(i) { - all_ok = false; - } - } - - let src3 = core::ptr::addr_of!(SRC3) as *const u32; - let dst3 = core::ptr::addr_of!(DST3) as *const u32; - for i in 0..4 { - if *src3.add(i) != *dst3.add(i) { - all_ok = false; - } - } + for (src, dst) in comps { + all_ok &= src == dst; } if all_ok { - tx.blocking_write(b"PASS: All segments verified!\r\n").unwrap(); defmt::info!("PASS: All segments verified!"); } else { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); defmt::error!("FAIL: Mismatch detected!"); } - tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n") - .unwrap(); - - loop { - cortex_m::asm::wfe(); - } + defmt::info!("=== Scatter-Gather Builder example complete ==="); } -- cgit From 4386b39e2516c453966d894b4fd265fae9d82c1a Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 9 Dec 2025 14:41:19 +0100 Subject: Enforce scatter gather API statically --- examples/mcxa/src/bin/dma_scatter_gather_builder.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs index ba8682cee..1691129f6 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs @@ -20,8 +20,6 @@ #![no_std] #![no_main] -use core::fmt::Write as _; - use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder}; @@ -97,28 +95,14 @@ async fn main(_spawner: Spawner) { defmt::info!("Added 3 transfer segments to chain."); defmt::info!("Starting scatter-gather transfer with .await..."); - // TODO START - defmt::info!("Destination buffers (after):"); - defmt::info!(" DST1: {=[?]}", dst1.as_slice()); - defmt::info!(" DST2: {=[?]}", dst2.as_slice()); - defmt::info!(" DST3: {=[?]}", dst3.as_slice()); - // TODO: If we want to make the `builder.build()` below safe, the above prints SHOULD NOT - // compile. We need to make sure that the lifetime of the builder reflects that it is - // "consuming" the slices until the builder is dropped, since we can access them to print here, - // that means that the borrow checker isn't enforcing that yet. - todo!("ABOVE CODE SHOULDN'T COMPILE"); - // TODO END - // Build and execute the scatter-gather chain // The build() method: // - Links all TCDs together with ESG bit // - Sets INTMAJOR on all TCDs // - Loads the first TCD into hardware // - Returns a Transfer future - unsafe { - let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); - transfer.blocking_wait(); - } + let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); + transfer.blocking_wait(); defmt::info!("Scatter-gather transfer complete!"); -- cgit From e962f5568a9f6433dcd6ad3e41d3faabb8b7c552 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 9 Dec 2025 15:52:12 +0100 Subject: Clean up remaining examples, move some to "raw" examples --- examples/mcxa/src/bin/dma_channel_link.rs | 331 -------------------- examples/mcxa/src/bin/dma_interleave_transfer.rs | 169 ---------- examples/mcxa/src/bin/dma_memset.rs | 125 -------- examples/mcxa/src/bin/dma_ping_pong_transfer.rs | 347 --------------------- examples/mcxa/src/bin/dma_scatter_gather.rs | 234 -------------- .../mcxa/src/bin/dma_scatter_gather_builder.rs | 6 +- examples/mcxa/src/bin/raw_dma_channel_link.rs | 279 +++++++++++++++++ .../mcxa/src/bin/raw_dma_interleave_transfer.rs | 141 +++++++++ examples/mcxa/src/bin/raw_dma_memset.rs | 129 ++++++++ .../mcxa/src/bin/raw_dma_ping_pong_transfer.rs | 269 ++++++++++++++++ examples/mcxa/src/bin/raw_dma_scatter_gather.rs | 188 +++++++++++ 11 files changed, 1007 insertions(+), 1211 deletions(-) delete mode 100644 examples/mcxa/src/bin/dma_channel_link.rs delete mode 100644 examples/mcxa/src/bin/dma_interleave_transfer.rs delete mode 100644 examples/mcxa/src/bin/dma_memset.rs delete mode 100644 examples/mcxa/src/bin/dma_ping_pong_transfer.rs delete mode 100644 examples/mcxa/src/bin/dma_scatter_gather.rs create mode 100644 examples/mcxa/src/bin/raw_dma_channel_link.rs create mode 100644 examples/mcxa/src/bin/raw_dma_interleave_transfer.rs create mode 100644 examples/mcxa/src/bin/raw_dma_memset.rs create mode 100644 examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs create mode 100644 examples/mcxa/src/bin/raw_dma_scatter_gather.rs (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_channel_link.rs b/examples/mcxa/src/bin/dma_channel_link.rs deleted file mode 100644 index 2d757a636..000000000 --- a/examples/mcxa/src/bin/dma_channel_link.rs +++ /dev/null @@ -1,331 +0,0 @@ -//! DMA channel linking example for MCXA276. -//! -//! This example demonstrates DMA channel linking (minor and major loop linking): -//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: -//! - Minor Link to Channel 1 (triggers CH1 after each minor loop) -//! - Major Link to Channel 2 (triggers CH2 after major loop completes) -//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) -//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) -//! -//! # Embassy-style features demonstrated: -//! - `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::fmt::Write as _; - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::DmaChannel; -use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use embassy_mcxa::pac; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -// Buffers -static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; -static mut DEST_BUFFER0: [u32; 4] = [0; 4]; -static mut DEST_BUFFER1: [u32; 4] = [0; 4]; -static mut DEST_BUFFER2: [u32; 4] = [0; 4]; - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA channel link example starting..."); - - // DMA is initialized during hal::init() - no need to call ensure_init() - - let pac_periphs = unsafe { pac::Peripherals::steal() }; - let dma0 = &pac_periphs.dma0; - let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; - - // Clear any residual state - for i in 0..3 { - let t = edma.tcd(i); - t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - t.ch_es().write(|w| w.err().clear_bit_by_one()); - t.ch_mux().write(|w| unsafe { w.bits(0) }); - } - - // Clear Global Halt/Error state - dma0.mp_csr().modify(|_, w| { - w.halt() - .normal_operation() - .hae() - .normal_operation() - .ecx() - .normal_operation() - .cx() - .normal_operation() - }); - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n").unwrap(); - - // Initialize buffers - unsafe { - SRC_BUFFER = [1, 2, 3, 4]; - DEST_BUFFER0 = [0; 4]; - DEST_BUFFER1 = [0; 4]; - DEST_BUFFER2 = [0; 4]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST0 (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST1 (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST2 (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA channels with Embassy-style API...\r\n") - .unwrap(); - - let ch0 = DmaChannel::new(p.DMA_CH0); - let ch1 = DmaChannel::new(p.DMA_CH1); - 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 - - // Helper to configure TCD for memory-to-memory transfer - // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt - #[allow(clippy::too_many_arguments)] - unsafe fn configure_tcd( - edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, - ch: usize, - src: u32, - dst: u32, - width: u8, - nbytes: u32, - count: u16, - enable_int: bool, - ) { - let t = edma.tcd(ch); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source/destination addresses - t.tcd_saddr().write(|w| w.saddr().bits(src)); - t.tcd_daddr().write(|w| w.daddr().bits(dst)); - - // Offsets: increment by width - t.tcd_soff().write(|w| w.soff().bits(width as u16)); - t.tcd_doff().write(|w| w.doff().bits(width as u16)); - - // Attributes: size = log2(width) - let size = match width { - 1 => 0, - 2 => 1, - 4 => 2, - _ => 0, - }; - t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); - - // Number of bytes per minor loop - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Major loop: reset source address after major loop - let total_bytes = nbytes * count as u32; - t.tcd_slast_sda() - .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); - t.tcd_dlast_sga() - .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); - - // Major loop count - t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); - - // Control/status: enable interrupt if requested - if enable_int { - t.tcd_csr().write(|w| w.intmajor().set_bit()); - } else { - t.tcd_csr().write(|w| w.intmajor().clear_bit()); - } - - cortex_m::asm::dsb(); - } - - unsafe { - // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) - // Minor Link -> Channel 1 - // Major Link -> Channel 2 - configure_tcd( - edma, - 0, - core::ptr::addr_of!(SRC_BUFFER) as u32, - core::ptr::addr_of_mut!(DEST_BUFFER0) as u32, - 4, // src width - 8, // nbytes (minor loop = 2 words) - 2, // count (major loop = 2 iterations) - false, // no interrupt - ); - 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( - edma, - 1, - core::ptr::addr_of!(SRC_BUFFER) as u32, - core::ptr::addr_of_mut!(DEST_BUFFER1) as u32, - 4, - 16, // full buffer in one minor loop - 1, // 1 major iteration - false, - ); - - // Channel 2: Transfer 16 bytes (triggered by CH0 major link) - configure_tcd( - edma, - 2, - core::ptr::addr_of!(SRC_BUFFER) as u32, - core::ptr::addr_of_mut!(DEST_BUFFER2) as u32, - 4, - 16, // full buffer in one minor loop - 1, // 1 major iteration - true, // enable interrupt - ); - } - - tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n") - .unwrap(); - - // Trigger first minor loop of CH0 - unsafe { - ch0.trigger_start(); - } - - // Wait for CH1 to complete (triggered by CH0 minor link) - while !ch1.is_done() { - cortex_m::asm::nop(); - } - 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(); - } - - // Wait for CH0 major loop to complete - while !ch0.is_done() { - cortex_m::asm::nop(); - } - 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) - // 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(); - - tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n").unwrap(); - - tx.blocking_write(b"DEST0 (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST1 (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"DEST2 (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify all buffers match source - let mut success = true; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC_BUFFER) as *const u32; - let dst0_ptr = core::ptr::addr_of!(DEST_BUFFER0) as *const u32; - let dst1_ptr = core::ptr::addr_of!(DEST_BUFFER1) as *const u32; - let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32; - - for i in 0..4 { - if *dst0_ptr.add(i) != *src_ptr.add(i) { - success = false; - } - if *dst1_ptr.add(i) != *src_ptr.add(i) { - success = false; - } - if *dst2_ptr.add(i) != *src_ptr.add(i) { - success = false; - } - } - } - - if success { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } else { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/mcxa/src/bin/dma_interleave_transfer.rs b/examples/mcxa/src/bin/dma_interleave_transfer.rs deleted file mode 100644 index 03441fc32..000000000 --- a/examples/mcxa/src/bin/dma_interleave_transfer.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! DMA interleaved transfer example for MCXA276. -//! -//! This example demonstrates using DMA with custom source/destination offsets -//! to interleave data during transfer. -//! -//! # Embassy-style features demonstrated: -//! - `TransferOptions::default()` for configuration (used internally) -//! - DMA channel with `DmaChannel::new()` - -#![no_std] -#![no_main] - -use core::fmt::Write as _; - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::DmaChannel; -use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; -use static_cell::ConstStaticCell; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -const BUFFER_LENGTH: usize = 16; -const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; - -// Buffers in RAM -static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); -static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf: &[u32]) { - write!(tx, "{:?}", buf).ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA interleave transfer example starting..."); - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA interleave transfer example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - let src = SRC_BUFFER.take(); - *src = [1, 2, 3, 4, 5, 6, 7, 8]; - let dst = DEST_BUFFER.take(); - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, src); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, dst); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") - .unwrap(); - - // Create DMA channel using Embassy-style API - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // 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 = dma_ch0.tcd(); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source/destination addresses - t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32)); - t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); - - // Custom offsets for interleaving - t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read - t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write - - // Attributes: 32-bit transfers (size = 2) - t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); - - // Transfer entire source buffer in one minor loop - let nbytes = (HALF_BUFF_LENGTH * 4) as u32; - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Reset source address after major loop - t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); - // Destination uses 2x offset, so adjust accordingly - let dst_total = (HALF_BUFF_LENGTH * 8) as u32; - t.tcd_dlast_sga() - .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); - - // Major loop count = 1 - t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); - - // Enable interrupt on major loop completion - t.tcd_csr().write(|w| w.intmajor().set_bit()); - - cortex_m::asm::dsb(); - - tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); - dma_ch0.trigger_start(); - } - - // Wait for completion using channel helper method - while !dma_ch0.is_done() { - cortex_m::asm::nop(); - } - unsafe { - dma_ch0.clear_done(); - } - - tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, dst); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 - let mut mismatch = false; - let diter = dst.chunks_exact(2); - let siter = src.iter(); - for (ch, src) in diter.zip(siter) { - mismatch |= !matches!(ch, [a, 0] if a == src); - } - - if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/mcxa/src/bin/dma_memset.rs b/examples/mcxa/src/bin/dma_memset.rs deleted file mode 100644 index d7b03e91b..000000000 --- a/examples/mcxa/src/bin/dma_memset.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! DMA memset example for MCXA276. -//! -//! This example demonstrates using DMA to fill a buffer with a repeated pattern. -//! The source address stays fixed while the destination increments. -//! -//! # Embassy-style features demonstrated: -//! - `DmaChannel::is_done()` and `clear_done()` helper methods -//! - No need to pass register block around - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::DmaChannel; -use static_cell::ConstStaticCell; -use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; - -const BUFFER_LENGTH: usize = 4; - -// Buffers in RAM -static PATTERN: u32 = 0xDEADBEEF; -static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA memset example starting..."); - defmt::info!("EDMA memset example begin."); - - // Initialize buffers - let pat = &PATTERN; - let dst = DEST_BUFFER.take(); - defmt::info!("Pattern Value: {=u32}", pat); - defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); - defmt::info!("Configuring DMA with Embassy-style API..."); - - // Create DMA channel using Embassy-style API - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure memset transfer using direct TCD access: - // Source stays fixed (soff = 0, reads same pattern repeatedly) - // Destination increments (doff = 4) - unsafe { - let t = dma_ch0.tcd(); - - // Reset channel state - t.ch_csr().write(|w| { - w.erq() - .disable() - .earq() - .disable() - .eei() - .no_error() - .ebw() - .disable() - .done() - .clear_bit_by_one() - }); - t.ch_es().write(|w| w.bits(0)); - t.ch_int().write(|w| w.int().clear_bit_by_one()); - - // Source address (pattern) - fixed - t.tcd_saddr().write(|w| w.saddr().bits(pat as *const _ as u32)); - // Destination address - increments - t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); - - // Source offset = 0 (stays fixed), Dest offset = 4 (increments) - t.tcd_soff().write(|w| w.soff().bits(0)); - t.tcd_doff().write(|w| w.doff().bits(4)); - - // Attributes: 32-bit transfers (size = 2) - t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); - - // Transfer entire buffer in one minor loop - let nbytes = (BUFFER_LENGTH * 4) as u32; - t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); - - // Source doesn't need adjustment (stays fixed) - t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); - // Reset dest address after major loop - t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); - - // Major loop count = 1 - t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); - t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); - - // Enable interrupt on major loop completion - t.tcd_csr().write(|w| w.intmajor().set_bit()); - - cortex_m::asm::dsb(); - - defmt::info!("Triggering transfer..."); - dma_ch0.trigger_start(); - } - - // Wait for completion using channel helper method - while !dma_ch0.is_done() { - cortex_m::asm::nop(); - } - unsafe { - dma_ch0.clear_done(); - } - - defmt::info!("EDMA memset example finish."); - defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); - - // Verify: All elements should equal PATTERN - let mismatch = dst.iter().any(|i| *i != *pat); - - if mismatch { - defmt::error!("FAIL: Mismatch detected!"); - } else { - defmt::info!("PASS: Data verified."); - } -} diff --git a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/dma_ping_pong_transfer.rs deleted file mode 100644 index 58f643b80..000000000 --- a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs +++ /dev/null @@ -1,347 +0,0 @@ -//! DMA ping-pong/double-buffer transfer example for MCXA276. -//! -//! This example demonstrates two approaches for ping-pong/double-buffering: -//! -//! ## Approach 1: Scatter/Gather with linked TCDs (manual) -//! - Two TCDs link to each other for alternating transfers -//! - 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 -//! - Uses half-transfer interrupt to know when first half is ready -//! - Application can process first half while second half is being filled -//! -//! # Embassy-style features demonstrated: -//! - `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] -#![no_main] - -use core::fmt::Write as _; -use core::sync::atomic::{AtomicBool, Ordering}; - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; -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 _}; - -// Source and destination buffers for Approach 1 (scatter/gather) -static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; -static mut DST: [u32; 8] = [0; 8]; - -// Source and destination buffers for Approach 2 (wait_half) -static mut SRC2: [u32; 8] = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; -static mut DST2: [u32; 8] = [0; 8]; - -// TCD pool for scatter/gather - must be 32-byte aligned -#[repr(C, align(32))] -struct TcdPool([Tcd; 2]); - -static mut TCD_POOL: TcdPool = TcdPool( - [Tcd { - saddr: 0, - soff: 0, - attr: 0, - nbytes: 0, - slast: 0, - daddr: 0, - doff: 0, - citer: 0, - dlast_sga: 0, - csr: 0, - 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 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() { - // 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; -}); - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA ping-pong transfer example starting..."); - - // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA ping-pong transfer example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - unsafe { - SRC = [1, 2, 3, 4, 5, 6, 7, 8]; - DST = [0; 8]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring ping-pong DMA with Embassy-style API...\r\n") - .unwrap(); - - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure ping-pong transfer using direct TCD access: - // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. - // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. - // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. - unsafe { - let tcds = &mut *core::ptr::addr_of_mut!(TCD_POOL.0); - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; - - let half_len = 4usize; - let half_bytes = (half_len * 4) as u32; - - let tcd0_addr = &tcds[0] as *const _ as u32; - let tcd1_addr = &tcds[1] as *const _ as u32; - - // TCD0: First half -> Links to TCD1 - tcds[0] = Tcd { - saddr: src_ptr as u32, - soff: 4, - attr: 0x0202, // 32-bit src/dst - nbytes: half_bytes, - slast: 0, - daddr: dst_ptr as u32, - doff: 4, - citer: 1, - dlast_sga: tcd1_addr as i32, - csr: 0x0012, // ESG | INTMAJOR - biter: 1, - }; - - // TCD1: Second half -> Links to TCD0 - tcds[1] = Tcd { - saddr: src_ptr.add(half_len) as u32, - soff: 4, - attr: 0x0202, - nbytes: half_bytes, - slast: 0, - daddr: dst_ptr.add(half_len) as u32, - doff: 4, - citer: 1, - dlast_sga: tcd0_addr as i32, - csr: 0x0012, - biter: 1, - }; - - // Load TCD0 into hardware registers - 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(); - } - - // Wait for first half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"First half transferred.\r\n").unwrap(); - tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); - - // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) - unsafe { - dma_ch0.trigger_start(); - } - - // Wait for second half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); - - tx.blocking_write(b"EDMA ping-pong transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: DST should match SRC - let mut mismatch = false; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of!(DST) as *const u32; - for i in 0..8 { - if *src_ptr.add(i) != *dst_ptr.add(i) { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Approach 1 mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Approach 1 mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Approach 1 data verified.\r\n\r\n").unwrap(); - defmt::info!("PASS: Approach 1 data verified."); - } - - // ========================================================================= - // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!) - // ========================================================================= - // - // This approach uses a single continuous DMA transfer with half-transfer - // interrupt enabled. The wait_half() method allows you to be notified - // when the first half of the buffer is complete, so you can process it - // while the second half is still being filled. - // - // Benefits: - // - Simpler setup (no TCD pool needed) - // - True async/await support - // - Good for streaming data processing - - tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n") - .unwrap(); - - // Enable DMA CH1 interrupt - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); - } - - // Initialize approach 2 buffers - unsafe { - SRC2 = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; - DST2 = [0; 8]; - } - - tx.blocking_write(b"SRC2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - let dma_ch1 = DmaChannel::new(p.DMA_CH1); - - // Configure transfer with half-transfer interrupt enabled - let mut options = TransferOptions::default(); - options.half_transfer_interrupt = true; // Enable half-transfer interrupt - options.complete_transfer_interrupt = true; - - tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n") - .unwrap(); - - unsafe { - let src = &*core::ptr::addr_of!(SRC2); - let dst = &mut *core::ptr::addr_of_mut!(DST2); - - // Create the transfer - let mut transfer = dma_ch1.mem_to_mem(src, dst, options); - - // Wait for half-transfer (first 4 elements) - tx.blocking_write(b"Waiting for first half...\r\n").unwrap(); - let half_ok = transfer.wait_half().await; - - if half_ok { - tx.blocking_write(b"Half-transfer complete! First half of DST2: ") - .unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); - tx.blocking_write(b"\r\n").unwrap(); - tx.blocking_write(b"(Processing first half while second half transfers...)\r\n") - .unwrap(); - } - - // Wait for complete transfer - tx.blocking_write(b"Waiting for second half...\r\n").unwrap(); - transfer.await; - } - - tx.blocking_write(b"Transfer complete! Full DST2: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify approach 2 - let mut mismatch2 = false; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC2) as *const u32; - let dst_ptr = core::ptr::addr_of!(DST2) as *const u32; - for i in 0..8 { - if *src_ptr.add(i) != *dst_ptr.add(i) { - mismatch2 = true; - break; - } - } - } - - if mismatch2 { - tx.blocking_write(b"FAIL: Approach 2 mismatch!\r\n").unwrap(); - defmt::error!("FAIL: Approach 2 mismatch!"); - } else { - tx.blocking_write(b"PASS: Approach 2 verified.\r\n").unwrap(); - defmt::info!("PASS: Approach 2 verified."); - } - - tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n") - .unwrap(); - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/mcxa/src/bin/dma_scatter_gather.rs b/examples/mcxa/src/bin/dma_scatter_gather.rs deleted file mode 100644 index 3e34e95b1..000000000 --- a/examples/mcxa/src/bin/dma_scatter_gather.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! DMA scatter-gather transfer example for MCXA276. -//! -//! This example demonstrates using DMA with scatter/gather to chain multiple -//! transfer descriptors. The first TCD transfers the first half of the buffer, -//! then automatically loads the second TCD to transfer the second half. -//! -//! # Embassy-style features demonstrated: -//! - `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] - -use core::fmt::Write as _; -use core::sync::atomic::{AtomicBool, Ordering}; - -use embassy_executor::Spawner; -use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, Tcd}; -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 _}; - -// Source and destination buffers -static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; -static mut DST: [u32; 8] = [0; 8]; - -// TCD pool for scatter/gather - must be 32-byte aligned -#[repr(C, align(32))] -struct TcdPool([Tcd; 2]); - -static mut TCD_POOL: TcdPool = TcdPool( - [Tcd { - saddr: 0, - soff: 0, - attr: 0, - nbytes: 0, - slast: 0, - daddr: 0, - doff: 0, - citer: 0, - dlast_sga: 0, - csr: 0, - 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 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() { - // 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; -}); - -/// Helper to print a buffer to UART -fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { - write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // Small delay to allow probe-rs to attach after reset - for _ in 0..100_000 { - cortex_m::asm::nop(); - } - - let mut cfg = hal::config::Config::default(); - cfg.clock_cfg.sirc.fro_12m_enabled = true; - cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); - let p = hal::init(cfg); - - defmt::info!("DMA scatter-gather transfer example starting..."); - - // DMA is initialized during hal::init() - no need to call ensure_init() - - // Enable DMA interrupt - unsafe { - cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); - } - - let config = Config { - baudrate_bps: 115_200, - ..Default::default() - }; - - let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); - let (mut tx, _rx) = lpuart.split(); - - tx.blocking_write(b"EDMA scatter-gather transfer example begin.\r\n\r\n") - .unwrap(); - - // Initialize buffers - unsafe { - SRC = [1, 2, 3, 4, 5, 6, 7, 8]; - DST = [0; 8]; - } - - tx.blocking_write(b"Source Buffer: ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Destination Buffer (before): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n").unwrap(); - - tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n") - .unwrap(); - - let dma_ch0 = DmaChannel::new(p.DMA_CH0); - - // Configure scatter-gather transfer using direct TCD access: - // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. - // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. - // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. - unsafe { - let tcds = core::slice::from_raw_parts_mut(core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, 2); - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; - - let num_tcds = 2usize; - let chunk_len = 4usize; // 8 / 2 - let chunk_bytes = (chunk_len * 4) as u32; - - for i in 0..num_tcds { - let is_last = i == num_tcds - 1; - let next_tcd_addr = if is_last { - 0 // No next TCD - } else { - &tcds[i + 1] as *const _ as u32 - }; - - tcds[i] = Tcd { - saddr: src_ptr.add(i * chunk_len) as u32, - soff: 4, - attr: 0x0202, // 32-bit src/dst - nbytes: chunk_bytes, - slast: 0, - daddr: dst_ptr.add(i * chunk_len) as u32, - doff: 4, - citer: 1, - dlast_sga: next_tcd_addr as i32, - // ESG (scatter/gather) for non-last, INTMAJOR for all - csr: if is_last { 0x0002 } else { 0x0012 }, - biter: 1, - }; - } - - // Load TCD0 into hardware registers - 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]) - // TCD0 is currently loaded. - unsafe { - dma_ch0.trigger_start(); - } - - // Wait for first half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"First half transferred.\r\n").unwrap(); - tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); - - // 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(); - } - - // Wait for second half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); - } - TRANSFER_DONE.store(false, Ordering::Release); - - tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); - - tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n") - .unwrap(); - tx.blocking_write(b"Destination Buffer (after): ").unwrap(); - print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); - tx.blocking_write(b"\r\n\r\n").unwrap(); - - // Verify: DST should match SRC - let mut mismatch = false; - unsafe { - let src_ptr = core::ptr::addr_of!(SRC) as *const u32; - let dst_ptr = core::ptr::addr_of!(DST) as *const u32; - for i in 0..8 { - if *src_ptr.add(i) != *dst_ptr.add(i) { - mismatch = true; - break; - } - } - } - - if mismatch { - tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); - defmt::error!("FAIL: Mismatch detected!"); - } else { - tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); - defmt::info!("PASS: Data verified."); - } - - loop { - cortex_m::asm::wfe(); - } -} diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs index 1691129f6..30ce20c96 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs @@ -112,11 +112,7 @@ async fn main(_spawner: Spawner) { defmt::info!(" DST2: {=[?]}", dst2.as_slice()); defmt::info!(" DST3: {=[?]}", dst3.as_slice()); - let comps = [ - (src1, dst1), - (src2, dst2), - (src3, dst3), - ]; + let comps = [(src1, dst1), (src2, dst2), (src3, dst3)]; // Verify all three segments let mut all_ok = true; diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs new file mode 100644 index 000000000..987f1ba43 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs @@ -0,0 +1,279 @@ +//! DMA channel linking example for MCXA276. +//! +//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have +//! a high-level and safe API for. It should not be taken as typical, recommended, or +//! stable usage! +//! +//! This example demonstrates DMA channel linking (minor and major loop linking): +//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: +//! - Minor Link to Channel 1 (triggers CH1 after each minor loop) +//! - Major Link to Channel 2 (triggers CH2 after major loop completes) +//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) +//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) +//! +//! # Embassy-style features demonstrated: +//! - `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 embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::DmaChannel; +use embassy_mcxa::pac; +use static_cell::ConstStaticCell; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Buffers +static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]); +static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); +static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); +static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA channel link example starting..."); + + // DMA is initialized during hal::init() - no need to call ensure_init() + + let pac_periphs = unsafe { pac::Peripherals::steal() }; + let dma0 = &pac_periphs.dma0; + let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; + + // Clear any residual state + for i in 0..3 { + let t = edma.tcd(i); + t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + t.ch_es().write(|w| w.err().clear_bit_by_one()); + t.ch_mux().write(|w| unsafe { w.bits(0) }); + } + + // Clear Global Halt/Error state + dma0.mp_csr().modify(|_, w| { + w.halt() + .normal_operation() + .hae() + .normal_operation() + .ecx() + .normal_operation() + .cx() + .normal_operation() + }); + + defmt::info!("EDMA channel link example begin."); + + // Initialize buffers + let src = SRC_BUFFER.take(); + let dst0 = DEST_BUFFER0.take(); + let dst1 = DEST_BUFFER1.take(); + let dst2 = DEST_BUFFER2.take(); + + defmt::info!("Source Buffer: {=[?]}", src.as_slice()); + defmt::info!("DEST0 (before): {=[?]}", dst0.as_slice()); + defmt::info!("DEST1 (before): {=[?]}", dst1.as_slice()); + defmt::info!("DEST2 (before): {=[?]}", dst2.as_slice()); + + defmt::info!("Configuring DMA channels with Embassy-style API..."); + + let ch0 = DmaChannel::new(p.DMA_CH0); + let ch1 = DmaChannel::new(p.DMA_CH1); + 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 + + // Helper to configure TCD for memory-to-memory transfer + // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt + #[allow(clippy::too_many_arguments)] + unsafe fn configure_tcd( + edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, + ch: usize, + src: u32, + dst: u32, + width: u8, + nbytes: u32, + count: u16, + enable_int: bool, + ) { + let t = edma.tcd(ch); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr().write(|w| w.saddr().bits(src)); + t.tcd_daddr().write(|w| w.daddr().bits(dst)); + + // Offsets: increment by width + t.tcd_soff().write(|w| w.soff().bits(width as u16)); + t.tcd_doff().write(|w| w.doff().bits(width as u16)); + + // Attributes: size = log2(width) + let size = match width { + 1 => 0, + 2 => 1, + 4 => 2, + _ => 0, + }; + t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); + + // Number of bytes per minor loop + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Major loop: reset source address after major loop + let total_bytes = nbytes * count as u32; + t.tcd_slast_sda() + .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); + t.tcd_dlast_sga() + .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); + + // Major loop count + t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); + + // Control/status: enable interrupt if requested + if enable_int { + t.tcd_csr().write(|w| w.intmajor().set_bit()); + } else { + t.tcd_csr().write(|w| w.intmajor().clear_bit()); + } + + cortex_m::asm::dsb(); + } + + unsafe { + // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) + // Minor Link -> Channel 1 + // Major Link -> Channel 2 + configure_tcd( + edma, + 0, + src.as_ptr() as u32, + dst0.as_mut_ptr() as u32, + 4, // src width + 8, // nbytes (minor loop = 2 words) + 2, // count (major loop = 2 iterations) + false, // no interrupt + ); + 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( + edma, + 1, + src.as_ptr() as u32, + dst1.as_mut_ptr() as u32, + 4, + 16, // full buffer in one minor loop + 1, // 1 major iteration + false, + ); + + // Channel 2: Transfer 16 bytes (triggered by CH0 major link) + configure_tcd( + edma, + 2, + src.as_ptr() as u32, + dst2.as_mut_ptr() as u32, + 4, + 16, // full buffer in one minor loop + 1, // 1 major iteration + true, // enable interrupt + ); + } + + defmt::info!("Triggering Channel 0 (1st minor loop)..."); + + // Trigger first minor loop of CH0 + unsafe { + ch0.trigger_start(); + } + + // Wait for CH1 to complete (triggered by CH0 minor link) + while !ch1.is_done() { + cortex_m::asm::nop(); + } + unsafe { + ch1.clear_done(); + } + + defmt::info!("CH1 done (via minor link)."); + defmt::info!("Triggering Channel 0 (2nd minor loop)..."); + + // Trigger second minor loop of CH0 + unsafe { + ch0.trigger_start(); + } + + // Wait for CH0 major loop to complete + while !ch0.is_done() { + cortex_m::asm::nop(); + } + unsafe { + ch0.clear_done(); + } + + defmt::info!("CH0 major loop done."); + + // Wait for CH2 to complete (triggered by CH0 major link) + // 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(); + } + + defmt::info!("CH2 done (via major link)."); + + defmt::info!("EDMA channel link example finish."); + + defmt::info!("DEST0 (after): {=[?]}", dst0.as_slice()); + defmt::info!("DEST1 (after): {=[?]}", dst1.as_slice()); + defmt::info!("DEST2 (after): {=[?]}", dst2.as_slice()); + + // Verify all buffers match source + let mut success = true; + for sli in [dst0, dst1, dst2] { + success &= sli == src; + } + + if success { + defmt::info!("PASS: Data verified."); + } else { + defmt::error!("FAIL: Mismatch detected!"); + } + + loop { + cortex_m::asm::wfe(); + } +} diff --git a/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs new file mode 100644 index 000000000..a383b6cf4 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs @@ -0,0 +1,141 @@ +//! DMA interleaved transfer example for MCXA276. +//! +//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have +//! a high-level and safe API for. It should not be taken as typical, recommended, or +//! stable usage! +//! +//! This example demonstrates using DMA with custom source/destination offsets +//! to interleave data during transfer. +//! +//! # Embassy-style features demonstrated: +//! - `TransferOptions::default()` for configuration (used internally) +//! - DMA channel with `DmaChannel::new()` + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::DmaChannel; +use static_cell::ConstStaticCell; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const BUFFER_LENGTH: usize = 16; +const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; + +// Buffers in RAM +static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); +static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA interleave transfer example starting..."); + + defmt::info!("EDMA interleave transfer example begin."); + + // Initialize buffers + let src = SRC_BUFFER.take(); + *src = [1, 2, 3, 4, 5, 6, 7, 8]; + let dst = DEST_BUFFER.take(); + + defmt::info!("Source Buffer: {=[?]}", src.as_slice()); + defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); + + defmt::info!("Configuring DMA with Embassy-style API..."); + + // Create DMA channel using Embassy-style API + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // 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 = dma_ch0.tcd(); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source/destination addresses + t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32)); + t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); + + // Custom offsets for interleaving + t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read + t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write + + // Attributes: 32-bit transfers (size = 2) + t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); + + // Transfer entire source buffer in one minor loop + let nbytes = (HALF_BUFF_LENGTH * 4) as u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Reset source address after major loop + t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); + // Destination uses 2x offset, so adjust accordingly + let dst_total = (HALF_BUFF_LENGTH * 8) as u32; + t.tcd_dlast_sga() + .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + defmt::info!("Triggering transfer..."); + dma_ch0.trigger_start(); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done() { + cortex_m::asm::nop(); + } + unsafe { + dma_ch0.clear_done(); + } + + defmt::info!("EDMA interleave transfer example finish."); + defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); + + // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 + let mut mismatch = false; + let diter = dst.chunks_exact(2); + let siter = src.iter(); + for (ch, src) in diter.zip(siter) { + mismatch |= !matches!(ch, [a, 0] if a == src); + } + + if mismatch { + defmt::error!("FAIL: Mismatch detected!"); + } else { + defmt::info!("PASS: Data verified."); + } +} diff --git a/examples/mcxa/src/bin/raw_dma_memset.rs b/examples/mcxa/src/bin/raw_dma_memset.rs new file mode 100644 index 000000000..7b3c06ffa --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_memset.rs @@ -0,0 +1,129 @@ +//! DMA memset example for MCXA276. +//! +//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have +//! a high-level and safe API for. It should not be taken as typical, recommended, or +//! stable usage! +//! +//! This example demonstrates using DMA to fill a buffer with a repeated pattern. +//! The source address stays fixed while the destination increments. +//! +//! # Embassy-style features demonstrated: +//! - `DmaChannel::is_done()` and `clear_done()` helper methods +//! - No need to pass register block around + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::DmaChannel; +use static_cell::ConstStaticCell; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const BUFFER_LENGTH: usize = 4; + +// Buffers in RAM +static PATTERN: u32 = 0xDEADBEEF; +static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA memset example starting..."); + defmt::info!("EDMA memset example begin."); + + // Initialize buffers + let pat = &PATTERN; + let dst = DEST_BUFFER.take(); + defmt::info!("Pattern Value: {=u32}", pat); + defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); + defmt::info!("Configuring DMA with Embassy-style API..."); + + // Create DMA channel using Embassy-style API + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure memset transfer using direct TCD access: + // Source stays fixed (soff = 0, reads same pattern repeatedly) + // Destination increments (doff = 4) + unsafe { + let t = dma_ch0.tcd(); + + // Reset channel state + t.ch_csr().write(|w| { + w.erq() + .disable() + .earq() + .disable() + .eei() + .no_error() + .ebw() + .disable() + .done() + .clear_bit_by_one() + }); + t.ch_es().write(|w| w.bits(0)); + t.ch_int().write(|w| w.int().clear_bit_by_one()); + + // Source address (pattern) - fixed + t.tcd_saddr().write(|w| w.saddr().bits(pat as *const _ as u32)); + // Destination address - increments + t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); + + // Source offset = 0 (stays fixed), Dest offset = 4 (increments) + t.tcd_soff().write(|w| w.soff().bits(0)); + t.tcd_doff().write(|w| w.doff().bits(4)); + + // Attributes: 32-bit transfers (size = 2) + t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); + + // Transfer entire buffer in one minor loop + let nbytes = (BUFFER_LENGTH * 4) as u32; + t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); + + // Source doesn't need adjustment (stays fixed) + t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); + // Reset dest address after major loop + t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); + + // Major loop count = 1 + t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); + t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); + + // Enable interrupt on major loop completion + t.tcd_csr().write(|w| w.intmajor().set_bit()); + + cortex_m::asm::dsb(); + + defmt::info!("Triggering transfer..."); + dma_ch0.trigger_start(); + } + + // Wait for completion using channel helper method + while !dma_ch0.is_done() { + cortex_m::asm::nop(); + } + unsafe { + dma_ch0.clear_done(); + } + + defmt::info!("EDMA memset example finish."); + defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); + + // Verify: All elements should equal PATTERN + let mismatch = dst.iter().any(|i| *i != *pat); + + if mismatch { + defmt::error!("FAIL: Mismatch detected!"); + } else { + defmt::info!("PASS: Data verified."); + } +} diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs new file mode 100644 index 000000000..4a64b2498 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs @@ -0,0 +1,269 @@ +//! DMA ping-pong/double-buffer transfer example for MCXA276. +//! +//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have +//! a high-level and safe API for. It should not be taken as typical, recommended, or +//! stable usage! +//! +//! This example demonstrates two approaches for ping-pong/double-buffering: +//! +//! ## Approach 1: Scatter/Gather with linked TCDs (manual) +//! - Two TCDs link to each other for alternating transfers +//! - 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 +//! - Uses half-transfer interrupt to know when first half is ready +//! - Application can process first half while second half is being filled +//! +//! # Embassy-style features demonstrated: +//! - `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] +#![no_main] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; +use embassy_mcxa::{bind_interrupts, pac}; +use static_cell::ConstStaticCell; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Source and destination buffers for Approach 1 (scatter/gather) +static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); +static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); + +// Source and destination buffers for Approach 2 (wait_half) +static SRC2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]); +static DST2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); + +// TCD pool for scatter/gather - must be 32-byte aligned +#[repr(C, align(32))] +struct TcdPool([Tcd; 2]); + +static TCD_POOL: ConstStaticCell = ConstStaticCell::new(TcdPool( + [Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + 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 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() { + // 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; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA ping-pong transfer example starting..."); + + defmt::info!("EDMA ping-pong transfer example begin."); + + // Initialize buffers + let src = SRC.take(); + let dst = DST.take(); + + defmt::info!("Source Buffer: {=[?]}", src.as_slice()); + defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); + + defmt::info!("Configuring ping-pong DMA with Embassy-style API..."); + + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure ping-pong transfer using direct TCD access: + // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. + // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. + // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. + unsafe { + let tcds = &mut TCD_POOL.take().0; + + let half_len = 4usize; + let half_bytes = (half_len * 4) as u32; + + let tcd0_addr = &tcds[0] as *const _ as u32; + let tcd1_addr = &tcds[1] as *const _ as u32; + + // TCD0: First half -> Links to TCD1 + tcds[0] = Tcd { + saddr: src.as_ptr() as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: half_bytes, + slast: 0, + daddr: dst.as_mut_ptr() as u32, + doff: 4, + citer: 1, + dlast_sga: tcd1_addr as i32, + csr: 0x0012, // ESG | INTMAJOR + biter: 1, + }; + + // TCD1: Second half -> Links to TCD0 + tcds[1] = Tcd { + saddr: src.as_ptr().add(half_len) as u32, + soff: 4, + attr: 0x0202, + nbytes: half_bytes, + slast: 0, + daddr: dst.as_mut_ptr().add(half_len) as u32, + doff: 4, + citer: 1, + dlast_sga: tcd0_addr as i32, + csr: 0x0012, + biter: 1, + }; + + // Load TCD0 into hardware registers + dma_ch0.load_tcd(&tcds[0]); + } + + defmt::info!("Triggering first half transfer..."); + + // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) + unsafe { + dma_ch0.trigger_start(); + } + + // Wait for first half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + defmt::info!("First half transferred."); + defmt::info!("Triggering second half transfer..."); + + // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) + unsafe { + dma_ch0.trigger_start(); + } + + // Wait for second half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + defmt::info!("Second half transferred."); + + defmt::info!("EDMA ping-pong transfer example finish."); + defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); + + // Verify: DST should match SRC + let mismatch = src != dst; + + if mismatch { + defmt::error!("FAIL: Approach 1 mismatch detected!"); + } else { + defmt::info!("PASS: Approach 1 data verified."); + } + + // ========================================================================= + // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!) + // ========================================================================= + // + // This approach uses a single continuous DMA transfer with half-transfer + // interrupt enabled. The wait_half() method allows you to be notified + // when the first half of the buffer is complete, so you can process it + // while the second half is still being filled. + // + // Benefits: + // - Simpler setup (no TCD pool needed) + // - True async/await support + // - Good for streaming data processing + + defmt::info!("--- Approach 2: wait_half() demo ---"); + + // Enable DMA CH1 interrupt + unsafe { + cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); + } + + // Initialize approach 2 buffers + let src2 = SRC2.take(); + let dst2 = DST2.take(); + + defmt::info!("SRC2: {=[?]}", src2.as_slice()); + + let dma_ch1 = DmaChannel::new(p.DMA_CH1); + + // Configure transfer with half-transfer interrupt enabled + let mut options = TransferOptions::default(); + options.half_transfer_interrupt = true; // Enable half-transfer interrupt + options.complete_transfer_interrupt = true; + + defmt::info!("Starting transfer with half_transfer_interrupt..."); + + // Create the transfer + let mut transfer = dma_ch1.mem_to_mem(src2, dst2, options); + + // Wait for half-transfer (first 4 elements) + defmt::info!("Waiting for first half..."); + let _ok = transfer.wait_half().await; + + defmt::info!("Half-transfer complete!"); + + // Wait for complete transfer + defmt::info!("Waiting for second half..."); + transfer.await; + + defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice()); + + // Verify approach 2 + let mismatch2 = src2 != dst2; + + if mismatch2 { + defmt::error!("FAIL: Approach 2 mismatch!"); + } else { + defmt::info!("PASS: Approach 2 verified."); + } + + defmt::info!("=== All ping-pong demos complete ==="); +} diff --git a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs new file mode 100644 index 000000000..057e56826 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs @@ -0,0 +1,188 @@ +//! DMA scatter-gather transfer example for MCXA276. +//! +//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have +//! a high-level and safe API for. It should not be taken as typical, recommended, or +//! stable usage! +//! +//! This example demonstrates using DMA with scatter/gather to chain multiple +//! transfer descriptors. The first TCD transfers the first half of the buffer, +//! then automatically loads the second TCD to transfer the second half. +//! +//! # Embassy-style features demonstrated: +//! - `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] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::dma::{self, DmaChannel, Tcd}; +use static_cell::ConstStaticCell; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Source and destination buffers +static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); +static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); + +// TCD pool for scatter/gather - must be 32-byte aligned +#[repr(C, align(32))] +struct TcdPool([Tcd; 2]); + +static TCD_POOL: ConstStaticCell = ConstStaticCell::new(TcdPool( + [Tcd { + saddr: 0, + soff: 0, + attr: 0, + nbytes: 0, + slast: 0, + daddr: 0, + doff: 0, + citer: 0, + dlast_sga: 0, + csr: 0, + 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 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() { + // 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; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Small delay to allow probe-rs to attach after reset + for _ in 0..100_000 { + cortex_m::asm::nop(); + } + + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sirc.fro_12m_enabled = true; + cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + let p = hal::init(cfg); + + defmt::info!("DMA scatter-gather transfer example starting..."); + + defmt::info!("EDMA scatter-gather transfer example begin."); + + // Initialize buffers + let src = SRC.take(); + let dst = DST.take(); + + defmt::info!("Source Buffer: {=[?]}", src.as_slice()); + defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); + defmt::info!("Configuring scatter-gather DMA with Embassy-style API..."); + + let dma_ch0 = DmaChannel::new(p.DMA_CH0); + + // Configure scatter-gather transfer using direct TCD access: + // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. + // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. + // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. + unsafe { + let tcds = &mut TCD_POOL.take().0; + let src_ptr = src.as_ptr(); + let dst_ptr = dst.as_mut_ptr(); + + let num_tcds = 2usize; + let chunk_len = 4usize; // 8 / 2 + let chunk_bytes = (chunk_len * 4) as u32; + + for i in 0..num_tcds { + let is_last = i == num_tcds - 1; + let next_tcd_addr = if is_last { + 0 // No next TCD + } else { + &tcds[i + 1] as *const _ as u32 + }; + + tcds[i] = Tcd { + saddr: src_ptr.add(i * chunk_len) as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: chunk_bytes, + slast: 0, + daddr: dst_ptr.add(i * chunk_len) as u32, + doff: 4, + citer: 1, + dlast_sga: next_tcd_addr as i32, + // ESG (scatter/gather) for non-last, INTMAJOR for all + csr: if is_last { 0x0002 } else { 0x0012 }, + biter: 1, + }; + } + + // Load TCD0 into hardware registers + dma_ch0.load_tcd(&tcds[0]); + } + + defmt::info!("Triggering first half transfer..."); + + // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) + // TCD0 is currently loaded. + unsafe { + dma_ch0.trigger_start(); + } + + // Wait for first half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + defmt::info!("First half transferred."); + defmt::info!("Triggering second half transfer..."); + + // 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(); + } + + // Wait for second half + while !TRANSFER_DONE.load(Ordering::Acquire) { + cortex_m::asm::nop(); + } + TRANSFER_DONE.store(false, Ordering::Release); + + defmt::info!("Second half transferred."); + + defmt::info!("EDMA scatter-gather transfer example finish."); + defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); + + // Verify: DST should match SRC + let mismatch = src != dst; + + if mismatch { + defmt::error!("FAIL: Mismatch detected!"); + } else { + defmt::info!("PASS: Data verified."); + } +} -- cgit From e267cb52d774e8f9db87a9b0cfe63cfa51d4fe8b Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 9 Dec 2025 16:01:50 +0100 Subject: rustfmt --- examples/mcxa/src/bin/adc_interrupt.rs | 4 ++-- examples/mcxa/src/bin/adc_polling.rs | 2 +- examples/mcxa/src/bin/i2c-async.rs | 2 +- examples/mcxa/src/bin/i2c-scan-blocking.rs | 2 +- examples/mcxa/src/bin/lpuart_buffered.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs index 83d8046b3..c88b1fe8d 100644 --- a/examples/mcxa/src/bin/adc_interrupt.rs +++ b/examples/mcxa/src/bin/adc_interrupt.rs @@ -4,13 +4,13 @@ use embassy_executor::Spawner; use embassy_mcxa_examples::init_adc_pins; use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; -use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::clocks::PoweredClock; +use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; use hal::pac::adc1::tctrl::Tcmd; -use hal::{bind_interrupts, InterruptExt}; +use hal::{InterruptExt, bind_interrupts}; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs index ddf3f586b..07c50f224 100644 --- a/examples/mcxa/src/bin/adc_polling.rs +++ b/examples/mcxa/src/bin/adc_polling.rs @@ -4,8 +4,8 @@ use embassy_executor::Spawner; use embassy_mcxa_examples::init_adc_pins; use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; -use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::clocks::PoweredClock; +use hal::clocks::periph_helpers::{AdcClockSel, Div4}; use hal::pac::adc1::cfg::{Pwrsel, Refsel}; use hal::pac::adc1::cmdl1::{Adch, Mode}; use hal::pac::adc1::ctrl::CalAvgs; diff --git a/examples/mcxa/src/bin/i2c-async.rs b/examples/mcxa/src/bin/i2c-async.rs index 47b5f3cbe..edcfd5f22 100644 --- a/examples/mcxa/src/bin/i2c-async.rs +++ b/examples/mcxa/src/bin/i2c-async.rs @@ -6,8 +6,8 @@ use embassy_time::Timer; use hal::bind_interrupts; use hal::clocks::config::Div8; use hal::config::Config; -use hal::i2c::controller::{self, I2c, Speed}; use hal::i2c::InterruptHandler; +use hal::i2c::controller::{self, I2c, Speed}; use hal::peripherals::LPI2C3; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs index 4e203597b..0197f9b1d 100644 --- a/examples/mcxa/src/bin/i2c-scan-blocking.rs +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs @@ -2,8 +2,8 @@ #![no_main] use embassy_executor::Spawner; -use embassy_mcxa::gpio::Pull; use embassy_mcxa::Input; +use embassy_mcxa::gpio::Pull; use embassy_time::Timer; use hal::clocks::config::Div8; use hal::config::Config; diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs index 420589d00..47b56b7c7 100644 --- a/examples/mcxa/src/bin/lpuart_buffered.rs +++ b/examples/mcxa/src/bin/lpuart_buffered.rs @@ -3,8 +3,8 @@ use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::lpuart::buffered::BufferedLpuart; use embassy_mcxa::lpuart::Config; +use embassy_mcxa::lpuart::buffered::BufferedLpuart; use embassy_mcxa::{bind_interrupts, lpuart}; use embedded_io_async::Write; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; -- cgit From 2dd5229c8c4fea6de7a7d52cedc6b6490d567ecf Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 9 Dec 2025 16:48:43 +0100 Subject: Use saddr read instead of interrupt to avoid double handler definition --- examples/mcxa/src/bin/raw_dma_channel_link.rs | 1 - .../mcxa/src/bin/raw_dma_ping_pong_transfer.rs | 55 +++------- examples/mcxa/src/bin/raw_dma_scatter_gather.rs | 115 +++++++++------------ 3 files changed, 61 insertions(+), 110 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs index 987f1ba43..74785e4f3 100644 --- a/examples/mcxa/src/bin/raw_dma_channel_link.rs +++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs @@ -15,7 +15,6 @@ //! - `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] diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs index 4a64b2498..51a7bc275 100644 --- a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs +++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs @@ -27,12 +27,10 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; - use embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; -use embassy_mcxa::{bind_interrupts, pac}; +use embassy_mcxa::dma::{DmaChannel, Tcd, TransferOptions}; +use embassy_mcxa::pac; use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -64,32 +62,6 @@ static TCD_POOL: ConstStaticCell = ConstStaticCell::new(TcdPool( }; 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 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() { - // 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; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { // Small delay to allow probe-rs to attach after reset @@ -121,12 +93,12 @@ async fn main(_spawner: Spawner) { // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. - unsafe { - let tcds = &mut TCD_POOL.take().0; + let tcds = &mut TCD_POOL.take().0; - let half_len = 4usize; - let half_bytes = (half_len * 4) as u32; + let half_len = 4usize; + let half_bytes = (half_len * 4) as u32; + unsafe { let tcd0_addr = &tcds[0] as *const _ as u32; let tcd1_addr = &tcds[1] as *const _ as u32; @@ -171,11 +143,13 @@ async fn main(_spawner: Spawner) { dma_ch0.trigger_start(); } + let tcd = dma_ch0.tcd(); // Wait for first half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); + loop { + if tcd.tcd_saddr().read().bits() != src.as_ptr() as u32 { + break; + } } - TRANSFER_DONE.store(false, Ordering::Release); defmt::info!("First half transferred."); defmt::info!("Triggering second half transfer..."); @@ -186,10 +160,11 @@ async fn main(_spawner: Spawner) { } // Wait for second half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); + loop { + if tcd.tcd_saddr().read().bits() != unsafe { src.as_ptr().add(half_len) } as u32 { + break; + } } - TRANSFER_DONE.store(false, Ordering::Release); defmt::info!("Second half transferred."); diff --git a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs index 057e56826..eb9960764 100644 --- a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs +++ b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs @@ -16,18 +16,15 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; - use embassy_executor::Spawner; -use embassy_mcxa::bind_interrupts; use embassy_mcxa::clocks::config::Div8; -use embassy_mcxa::dma::{self, DmaChannel, Tcd}; +use embassy_mcxa::dma::{DmaChannel, Tcd}; use static_cell::ConstStaticCell; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; // Source and destination buffers -static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); -static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); +static SRC: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); +static DST: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([0; 12]); // TCD pool for scatter/gather - must be 32-byte aligned #[repr(C, align(32))] @@ -49,33 +46,6 @@ static TCD_POOL: ConstStaticCell = ConstStaticCell::new(TcdPool( }; 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 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() { - // 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; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { // Small delay to allow probe-rs to attach after reset @@ -101,43 +71,46 @@ async fn main(_spawner: Spawner) { defmt::info!("Configuring scatter-gather DMA with Embassy-style API..."); let dma_ch0 = DmaChannel::new(p.DMA_CH0); + let src_ptr = src.as_ptr(); + let dst_ptr = dst.as_mut_ptr(); // Configure scatter-gather transfer using direct TCD access: // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. - // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. + // TCD1 transfers second half (SRC[4..12] -> DST[4..12]), last TCD. unsafe { let tcds = &mut TCD_POOL.take().0; - let src_ptr = src.as_ptr(); - let dst_ptr = dst.as_mut_ptr(); - - let num_tcds = 2usize; - let chunk_len = 4usize; // 8 / 2 - let chunk_bytes = (chunk_len * 4) as u32; - - for i in 0..num_tcds { - let is_last = i == num_tcds - 1; - let next_tcd_addr = if is_last { - 0 // No next TCD - } else { - &tcds[i + 1] as *const _ as u32 - }; - - tcds[i] = Tcd { - saddr: src_ptr.add(i * chunk_len) as u32, - soff: 4, - attr: 0x0202, // 32-bit src/dst - nbytes: chunk_bytes, - slast: 0, - daddr: dst_ptr.add(i * chunk_len) as u32, - doff: 4, - citer: 1, - dlast_sga: next_tcd_addr as i32, - // ESG (scatter/gather) for non-last, INTMAJOR for all - csr: if is_last { 0x0002 } else { 0x0012 }, - biter: 1, - }; - } + + // In the first transfer, copy + tcds[0] = Tcd { + saddr: src_ptr as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: 4 * 4, + slast: 0, + daddr: dst_ptr as u32, + doff: 4, + citer: 1, + dlast_sga: tcds.as_ptr().add(1) as i32, + // ESG (scatter/gather) for non-last, INTMAJOR for all + csr: 0x0012, + biter: 1, + }; + + tcds[1] = Tcd { + saddr: src_ptr.add(4) as u32, + soff: 4, + attr: 0x0202, // 32-bit src/dst + nbytes: 8 * 4, + slast: 0, + daddr: dst_ptr.add(4) as u32, + doff: 4, + citer: 1, + dlast_sga: 0, + // ESG (scatter/gather) for non-last, INTMAJOR for all + csr: 0x0002, + biter: 1, + }; // Load TCD0 into hardware registers dma_ch0.load_tcd(&tcds[0]); @@ -145,6 +118,8 @@ async fn main(_spawner: Spawner) { defmt::info!("Triggering first half transfer..."); + let tcd = dma_ch0.tcd(); + // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) // TCD0 is currently loaded. unsafe { @@ -152,10 +127,13 @@ async fn main(_spawner: Spawner) { } // Wait for first half - while !TRANSFER_DONE.load(Ordering::Acquire) { - cortex_m::asm::nop(); + loop { + if tcd.tcd_saddr().read().bits() != src_ptr as u32 { + defmt::info!("saddr: {=u32}", tcd.tcd_saddr().read().bits()); + defmt::info!("srptr: {=u32}", src_ptr as u32); + break; + } } - TRANSFER_DONE.store(false, Ordering::Release); defmt::info!("First half transferred."); defmt::info!("Triggering second half transfer..."); @@ -167,10 +145,9 @@ async fn main(_spawner: Spawner) { } // Wait for second half - while !TRANSFER_DONE.load(Ordering::Acquire) { + while !dma_ch0.is_done() { cortex_m::asm::nop(); } - TRANSFER_DONE.store(false, Ordering::Release); defmt::info!("Second half transferred."); -- cgit From 3e7de3a5d81e32e77aeb5232e5a7f512ce39db0e Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 9 Dec 2025 17:53:45 +0100 Subject: Change transfer to return a result --- examples/mcxa/src/bin/dma_mem_to_mem.rs | 2 +- examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs index b20068b79..a9ee9ffdf 100644 --- a/examples/mcxa/src/bin/dma_mem_to_mem.rs +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs @@ -70,7 +70,7 @@ async fn main(_spawner: Spawner) { // Perform type-safe memory-to-memory transfer using Embassy-style async API // Using async `.await` - the executor can run other tasks while waiting! - let transfer = dma_ch0.mem_to_mem(src, dst, options); + let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap(); transfer.await; defmt::info!("DMA mem-to-mem transfer complete!"); diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs index 51a7bc275..1e16e60ba 100644 --- a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs +++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs @@ -217,7 +217,7 @@ async fn main(_spawner: Spawner) { defmt::info!("Starting transfer with half_transfer_interrupt..."); // Create the transfer - let mut transfer = dma_ch1.mem_to_mem(src2, dst2, options); + let mut transfer = dma_ch1.mem_to_mem(src2, dst2, options).unwrap(); // Wait for half-transfer (first 4 elements) defmt::info!("Waiting for first half..."); -- cgit From a75aa12d928d1ba0c1759052a652426ab965f739 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 9 Dec 2025 18:33:01 +0100 Subject: Add error state for DMA transfers --- examples/mcxa/src/bin/dma_mem_to_mem.rs | 2 +- examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs index a9ee9ffdf..b38baccb5 100644 --- a/examples/mcxa/src/bin/dma_mem_to_mem.rs +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs @@ -71,7 +71,7 @@ async fn main(_spawner: Spawner) { // Perform type-safe memory-to-memory transfer using Embassy-style async API // Using async `.await` - the executor can run other tasks while waiting! let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap(); - transfer.await; + transfer.await.unwrap(); defmt::info!("DMA mem-to-mem transfer complete!"); defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs index 1e16e60ba..80df40449 100644 --- a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs +++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs @@ -221,13 +221,13 @@ async fn main(_spawner: Spawner) { // Wait for half-transfer (first 4 elements) defmt::info!("Waiting for first half..."); - let _ok = transfer.wait_half().await; + let _ok = transfer.wait_half().await.unwrap(); defmt::info!("Half-transfer complete!"); // Wait for complete transfer defmt::info!("Waiting for second half..."); - transfer.await; + transfer.await.unwrap(); defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice()); -- cgit