aboutsummaryrefslogtreecommitdiff
path: root/examples/mcxa/src/bin/dma_interleave_transfer.rs
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-05 14:28:47 +0100
committerJames Munns <[email protected]>2025-12-05 14:28:47 +0100
commitb252db845e19603faf528cf93fe0c44757a27430 (patch)
tree99e646d17bed747df244dd607a15f5a67baa530a /examples/mcxa/src/bin/dma_interleave_transfer.rs
parent6a1eed83b9df8ffa81b93860f530f5bb3252d996 (diff)
Move
Diffstat (limited to 'examples/mcxa/src/bin/dma_interleave_transfer.rs')
-rw-r--r--examples/mcxa/src/bin/dma_interleave_transfer.rs215
1 files changed, 215 insertions, 0 deletions
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 @@
1//! DMA interleaved transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA with custom source/destination offsets
4//! to interleave data during transfer.
5//!
6//! # Embassy-style features demonstrated:
7//! - `TransferOptions::default()` for configuration (used internally)
8//! - DMA channel with `DmaChannel::new()`
9
10#![no_std]
11#![no_main]
12
13use embassy_executor::Spawner;
14use embassy_mcxa::clocks::config::Div8;
15use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaChannel};
16use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
17use embassy_mcxa::{bind_interrupts, pac};
18use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
19
20// Bind DMA channel 0 interrupt using Embassy-style macro
21bind_interrupts!(struct Irqs {
22 DMA_CH0 => DmaCh0InterruptHandler;
23});
24
25const BUFFER_LENGTH: usize = 16;
26const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2;
27
28// Buffers in RAM
29static mut SRC_BUFFER: [u32; HALF_BUFF_LENGTH] = [0; HALF_BUFF_LENGTH];
30static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH];
31
32/// Helper to write a u32 as decimal ASCII to UART
33fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
34 let mut buf = [0u8; 10];
35 let mut n = val;
36 let mut i = buf.len();
37
38 if n == 0 {
39 tx.blocking_write(b"0").ok();
40 return;
41 }
42
43 while n > 0 {
44 i -= 1;
45 buf[i] = b'0' + (n % 10) as u8;
46 n /= 10;
47 }
48
49 tx.blocking_write(&buf[i..]).ok();
50}
51
52/// Helper to print a buffer to UART
53fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
54 tx.blocking_write(b"[").ok();
55 unsafe {
56 for i in 0..len {
57 write_u32(tx, *buf_ptr.add(i));
58 if i < len - 1 {
59 tx.blocking_write(b", ").ok();
60 }
61 }
62 }
63 tx.blocking_write(b"]").ok();
64}
65
66#[embassy_executor::main]
67async fn main(_spawner: Spawner) {
68 // Small delay to allow probe-rs to attach after reset
69 for _ in 0..100_000 {
70 cortex_m::asm::nop();
71 }
72
73 let mut cfg = hal::config::Config::default();
74 cfg.clock_cfg.sirc.fro_12m_enabled = true;
75 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
76 let p = hal::init(cfg);
77
78 defmt::info!("DMA interleave transfer example starting...");
79
80 // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL)
81 unsafe {
82 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
83 }
84
85 let config = Config {
86 baudrate_bps: 115_200,
87 ..Default::default()
88 };
89
90 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
91 let (mut tx, _rx) = lpuart.split();
92
93 tx.blocking_write(b"EDMA interleave transfer example begin.\r\n\r\n")
94 .unwrap();
95
96 // Initialize buffers
97 unsafe {
98 SRC_BUFFER = [1, 2, 3, 4, 5, 6, 7, 8];
99 DEST_BUFFER = [0; BUFFER_LENGTH];
100 }
101
102 tx.blocking_write(b"Source Buffer: ").unwrap();
103 print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, HALF_BUFF_LENGTH);
104 tx.blocking_write(b"\r\n").unwrap();
105
106 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
107 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH);
108 tx.blocking_write(b"\r\n").unwrap();
109
110 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n")
111 .unwrap();
112
113 // Create DMA channel using Embassy-style API
114 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
115
116 // Configure interleaved transfer using direct TCD access:
117 // - src_offset = 4: advance source by 4 bytes after each read
118 // - dst_offset = 8: advance dest by 8 bytes after each write
119 // This spreads source data across every other word in destination
120 unsafe {
121 let t = dma_ch0.tcd();
122
123 // Reset channel state
124 t.ch_csr().write(|w| {
125 w.erq()
126 .disable()
127 .earq()
128 .disable()
129 .eei()
130 .no_error()
131 .ebw()
132 .disable()
133 .done()
134 .clear_bit_by_one()
135 });
136 t.ch_es().write(|w| w.bits(0));
137 t.ch_int().write(|w| w.int().clear_bit_by_one());
138
139 // Source/destination addresses
140 t.tcd_saddr()
141 .write(|w| w.saddr().bits(core::ptr::addr_of_mut!(SRC_BUFFER) as u32));
142 t.tcd_daddr()
143 .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32));
144
145 // Custom offsets for interleaving
146 t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read
147 t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write
148
149 // Attributes: 32-bit transfers (size = 2)
150 t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2));
151
152 // Transfer entire source buffer in one minor loop
153 let nbytes = (HALF_BUFF_LENGTH * 4) as u32;
154 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
155
156 // Reset source address after major loop
157 t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32));
158 // Destination uses 2x offset, so adjust accordingly
159 let dst_total = (HALF_BUFF_LENGTH * 8) as u32;
160 t.tcd_dlast_sga()
161 .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32));
162
163 // Major loop count = 1
164 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
165 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
166
167 // Enable interrupt on major loop completion
168 t.tcd_csr().write(|w| w.intmajor().set_bit());
169
170 cortex_m::asm::dsb();
171
172 tx.blocking_write(b"Triggering transfer...\r\n").unwrap();
173 dma_ch0.trigger_start();
174 }
175
176 // Wait for completion using channel helper method
177 while !dma_ch0.is_done() {
178 cortex_m::asm::nop();
179 }
180 unsafe {
181 dma_ch0.clear_done();
182 }
183
184 tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n")
185 .unwrap();
186 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
187 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH);
188 tx.blocking_write(b"\r\n\r\n").unwrap();
189
190 // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0
191 let mut mismatch = false;
192 unsafe {
193 for i in 0..BUFFER_LENGTH {
194 if i % 2 == 0 {
195 if DEST_BUFFER[i] != SRC_BUFFER[i / 2] {
196 mismatch = true;
197 }
198 } else if DEST_BUFFER[i] != 0 {
199 mismatch = true;
200 }
201 }
202 }
203
204 if mismatch {
205 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
206 defmt::error!("FAIL: Mismatch detected!");
207 } else {
208 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
209 defmt::info!("PASS: Data verified.");
210 }
211
212 loop {
213 cortex_m::asm::wfe();
214 }
215}