//! 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 embassy_executor::Spawner; use embassy_mcxa::clocks::config::Div8; 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; 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))] 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], )); #[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); 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..12] -> DST[4..12]), last TCD. unsafe { let tcds = &mut TCD_POOL.take().0; // 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]); } 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 { dma_ch0.trigger_start(); } // Wait for first half 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; } } 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 !dma_ch0.is_done() { cortex_m::asm::nop(); } 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."); } }