aboutsummaryrefslogtreecommitdiff
path: root/examples/mcxa/src/bin/dma_wrap_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_wrap_transfer.rs
parent6a1eed83b9df8ffa81b93860f530f5bb3252d996 (diff)
Move
Diffstat (limited to 'examples/mcxa/src/bin/dma_wrap_transfer.rs')
-rw-r--r--examples/mcxa/src/bin/dma_wrap_transfer.rs222
1 files changed, 222 insertions, 0 deletions
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 @@
1//! DMA wrap transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA with modulo addressing to wrap around
4//! a source buffer, effectively repeating the source data in the destination.
5//!
6//! # Embassy-style features demonstrated:
7//! - `DmaChannel::is_done()` and `clear_done()` helper methods
8//! - No need to pass register block around
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
25// Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo
26#[repr(align(16))]
27struct AlignedSrc([u32; 4]);
28
29static mut SRC: AlignedSrc = AlignedSrc([0; 4]);
30static mut DST: [u32; 8] = [0; 8];
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 wrap 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 wrap transfer example begin.\r\n\r\n").unwrap();
94
95 // Initialize buffers
96 unsafe {
97 SRC.0 = [1, 2, 3, 4];
98 DST = [0; 8];
99 }
100
101 tx.blocking_write(b"Source Buffer: ").unwrap();
102 print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4);
103 tx.blocking_write(b"\r\n").unwrap();
104
105 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
106 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
107 tx.blocking_write(b"\r\n").unwrap();
108
109 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n")
110 .unwrap();
111
112 // Create DMA channel using Embassy-style API
113 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
114
115 // Configure wrap transfer using direct TCD access:
116 // SRC is 16 bytes (4 * u32). We want to transfer 32 bytes (8 * u32).
117 // SRC modulo is 16 bytes (2^4 = 16) - wraps source address.
118 // DST modulo is 0 (disabled).
119 // This causes the source address to wrap around after 16 bytes,
120 // effectively repeating the source data.
121 unsafe {
122 let t = dma_ch0.tcd();
123
124 // Reset channel state
125 t.ch_csr().write(|w| {
126 w.erq()
127 .disable()
128 .earq()
129 .disable()
130 .eei()
131 .no_error()
132 .ebw()
133 .disable()
134 .done()
135 .clear_bit_by_one()
136 });
137 t.ch_es().write(|w| w.bits(0));
138 t.ch_int().write(|w| w.int().clear_bit_by_one());
139
140 // Source/destination addresses
141 t.tcd_saddr()
142 .write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32));
143 t.tcd_daddr()
144 .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32));
145
146 // Offsets: both increment by 4 bytes
147 t.tcd_soff().write(|w| w.soff().bits(4));
148 t.tcd_doff().write(|w| w.doff().bits(4));
149
150 // Attributes: 32-bit transfers (size = 2)
151 // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled)
152 t.tcd_attr().write(|w| {
153 w.ssize()
154 .bits(2)
155 .dsize()
156 .bits(2)
157 .smod()
158 .bits(4) // Source modulo: 2^4 = 16 bytes
159 .dmod()
160 .bits(0) // Dest modulo: disabled
161 });
162
163 // Transfer 32 bytes total in one minor loop
164 let nbytes = 32u32;
165 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
166
167 // Source wraps via modulo, no adjustment needed
168 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
169 // Reset dest address after major loop
170 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32));
171
172 // Major loop count = 1
173 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
174 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
175
176 // Enable interrupt on major loop completion
177 t.tcd_csr().write(|w| w.intmajor().set_bit());
178
179 cortex_m::asm::dsb();
180
181 tx.blocking_write(b"Triggering transfer...\r\n").unwrap();
182 dma_ch0.trigger_start();
183 }
184
185 // Wait for completion using channel helper method
186 while !dma_ch0.is_done() {
187 cortex_m::asm::nop();
188 }
189 unsafe {
190 dma_ch0.clear_done();
191 }
192
193 tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n")
194 .unwrap();
195 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
196 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
197 tx.blocking_write(b"\r\n\r\n").unwrap();
198
199 // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4]
200 let expected = [1u32, 2, 3, 4, 1, 2, 3, 4];
201 let mut mismatch = false;
202 unsafe {
203 for i in 0..8 {
204 if DST[i] != expected[i] {
205 mismatch = true;
206 break;
207 }
208 }
209 }
210
211 if mismatch {
212 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
213 defmt::error!("FAIL: Mismatch detected!");
214 } else {
215 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
216 defmt::info!("PASS: Data verified.");
217 }
218
219 loop {
220 cortex_m::asm::wfe();
221 }
222}