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