aboutsummaryrefslogtreecommitdiff
path: root/examples/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'examples/src/bin')
-rw-r--r--examples/src/bin/dma_channel_link.rs396
-rw-r--r--examples/src/bin/dma_interleave_transfer.rs226
-rw-r--r--examples/src/bin/dma_mem_to_mem.rs248
-rw-r--r--examples/src/bin/dma_memset.rs232
-rw-r--r--examples/src/bin/dma_ping_pong_transfer.rs384
-rw-r--r--examples/src/bin/dma_scatter_gather.rs281
-rw-r--r--examples/src/bin/dma_scatter_gather_builder.rs244
-rw-r--r--examples/src/bin/dma_wrap_transfer.rs231
-rw-r--r--examples/src/bin/lpuart_dma.rs127
-rw-r--r--examples/src/bin/lpuart_ring_buffer.rs162
10 files changed, 2531 insertions, 0 deletions
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 @@
1//! DMA channel linking example for MCXA276.
2//!
3//! This example demonstrates DMA channel linking (minor and major loop linking):
4//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with:
5//! - Minor Link to Channel 1 (triggers CH1 after each minor loop)
6//! - Major Link to Channel 2 (triggers CH2 after major loop completes)
7//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link)
8//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link)
9//!
10//! # Embassy-style features demonstrated:
11//! - `dma::edma_tcd()` accessor for simplified register access
12//! - `DmaChannel::new()` for channel creation
13//! - `DmaChannel::is_done()` and `clear_done()` helper methods
14//! - Channel linking with `set_minor_link()` and `set_major_link()`
15
16#![no_std]
17#![no_main]
18
19use core::sync::atomic::{AtomicBool, Ordering};
20use embassy_executor::Spawner;
21use embassy_mcxa::clocks::config::Div8;
22use embassy_mcxa::clocks::Gate;
23use embassy_mcxa::dma::{edma_tcd, DmaChannel};
24use embassy_mcxa::{bind_interrupts, dma};
25use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
26use embassy_mcxa::pac;
27use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
28
29// Buffers
30static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4];
31static mut DEST_BUFFER0: [u32; 4] = [0; 4];
32static mut DEST_BUFFER1: [u32; 4] = [0; 4];
33static mut DEST_BUFFER2: [u32; 4] = [0; 4];
34
35static DMA_CH2_DONE: AtomicBool = AtomicBool::new(false);
36
37// Custom DMA interrupt handlers for channel linking
38// CH0 and CH1 just clear flags, CH2 signals completion
39
40pub struct Ch0Handler;
41impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for Ch0Handler {
42 unsafe fn on_interrupt() {
43 let edma = edma_tcd();
44 edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one());
45 if edma.tcd(0).ch_csr().read().done().bit_is_set() {
46 edma.tcd(0).ch_csr().write(|w| w.done().clear_bit_by_one());
47 }
48 }
49}
50
51pub struct Ch1Handler;
52impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH1> for Ch1Handler {
53 unsafe fn on_interrupt() {
54 let edma = edma_tcd();
55 edma.tcd(1).ch_int().write(|w| w.int().clear_bit_by_one());
56 if edma.tcd(1).ch_csr().read().done().bit_is_set() {
57 edma.tcd(1).ch_csr().write(|w| w.done().clear_bit_by_one());
58 }
59 }
60}
61
62pub struct Ch2Handler;
63impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH2> for Ch2Handler {
64 unsafe fn on_interrupt() {
65 let edma = edma_tcd();
66 edma.tcd(2).ch_int().write(|w| w.int().clear_bit_by_one());
67 if edma.tcd(2).ch_csr().read().done().bit_is_set() {
68 edma.tcd(2).ch_csr().write(|w| w.done().clear_bit_by_one());
69 }
70 DMA_CH2_DONE.store(true, Ordering::Release);
71 }
72}
73
74bind_interrupts!(struct Irqs {
75 DMA_CH0 => Ch0Handler;
76 DMA_CH1 => Ch1Handler;
77 DMA_CH2 => Ch2Handler;
78});
79
80/// Helper to write a u32 as decimal ASCII to UART
81fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
82 let mut buf = [0u8; 10];
83 let mut n = val;
84 let mut i = buf.len();
85
86 if n == 0 {
87 tx.blocking_write(b"0").ok();
88 return;
89 }
90
91 while n > 0 {
92 i -= 1;
93 buf[i] = b'0' + (n % 10) as u8;
94 n /= 10;
95 }
96
97 tx.blocking_write(&buf[i..]).ok();
98}
99
100/// Helper to print a buffer to UART
101fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
102 tx.blocking_write(b"[").ok();
103 unsafe {
104 for i in 0..len {
105 write_u32(tx, *buf_ptr.add(i));
106 if i < len - 1 {
107 tx.blocking_write(b", ").ok();
108 }
109 }
110 }
111 tx.blocking_write(b"]").ok();
112}
113
114#[embassy_executor::main]
115async fn main(_spawner: Spawner) {
116 // Small delay to allow probe-rs to attach after reset
117 for _ in 0..100_000 {
118 cortex_m::asm::nop();
119 }
120
121 let mut cfg = hal::config::Config::default();
122 cfg.clock_cfg.sirc.fro_12m_enabled = true;
123 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
124 let p = hal::init(cfg);
125
126 defmt::info!("DMA channel link example starting...");
127
128 // Enable DMA0 clock and release reset
129 unsafe {
130 hal::peripherals::DMA0::enable_clock();
131 hal::peripherals::DMA0::release_reset();
132 }
133
134 let pac_periphs = unsafe { pac::Peripherals::steal() };
135
136 unsafe {
137 dma::init(&pac_periphs);
138 }
139
140 // Use edma_tcd() accessor instead of passing register block around
141 let edma = edma_tcd();
142 let dma0 = &pac_periphs.dma0;
143
144 // Clear any residual state
145 for i in 0..3 {
146 let t = edma.tcd(i);
147 t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one());
148 t.ch_int().write(|w| w.int().clear_bit_by_one());
149 t.ch_es().write(|w| w.err().clear_bit_by_one());
150 t.ch_mux().write(|w| unsafe { w.bits(0) });
151 }
152
153 // Clear Global Halt/Error state
154 dma0.mp_csr().modify(|_, w| {
155 w.halt().normal_operation()
156 .hae().normal_operation()
157 .ecx().normal_operation()
158 .cx().normal_operation()
159 });
160
161 unsafe {
162 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
163 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1);
164 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH2);
165 }
166
167 let config = Config {
168 baudrate_bps: 115_200,
169 enable_tx: true,
170 enable_rx: false,
171 ..Default::default()
172 };
173
174 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
175 let (mut tx, _rx) = lpuart.split();
176
177 tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n")
178 .unwrap();
179
180 // Initialize buffers
181 unsafe {
182 SRC_BUFFER = [1, 2, 3, 4];
183 DEST_BUFFER0 = [0; 4];
184 DEST_BUFFER1 = [0; 4];
185 DEST_BUFFER2 = [0; 4];
186 }
187
188 tx.blocking_write(b"Source Buffer: ").unwrap();
189 print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, 4);
190 tx.blocking_write(b"\r\n").unwrap();
191
192 tx.blocking_write(b"DEST0 (before): ").unwrap();
193 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4);
194 tx.blocking_write(b"\r\n").unwrap();
195
196 tx.blocking_write(b"DEST1 (before): ").unwrap();
197 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4);
198 tx.blocking_write(b"\r\n").unwrap();
199
200 tx.blocking_write(b"DEST2 (before): ").unwrap();
201 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4);
202 tx.blocking_write(b"\r\n\r\n").unwrap();
203
204 tx.blocking_write(b"Configuring DMA channels with Embassy-style API...\r\n")
205 .unwrap();
206
207 let ch0 = DmaChannel::new(p.DMA_CH0);
208 let ch1 = DmaChannel::new(p.DMA_CH1);
209 let _ch2 = DmaChannel::new(p.DMA_CH2);
210
211 // Configure channels using direct TCD access (advanced feature demo)
212 // This example demonstrates channel linking which requires direct TCD manipulation
213
214 // Helper to configure TCD for memory-to-memory transfer
215 // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt
216 #[allow(clippy::too_many_arguments)]
217 unsafe fn configure_tcd(
218 edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock,
219 ch: usize,
220 src: u32,
221 dst: u32,
222 width: u8,
223 nbytes: u32,
224 count: u16,
225 enable_int: bool,
226 ) {
227 let t = edma.tcd(ch);
228
229 // Reset channel state
230 t.ch_csr().write(|w| {
231 w.erq().disable()
232 .earq().disable()
233 .eei().no_error()
234 .ebw().disable()
235 .done().clear_bit_by_one()
236 });
237 t.ch_es().write(|w| w.bits(0));
238 t.ch_int().write(|w| w.int().clear_bit_by_one());
239
240 // Source/destination addresses
241 t.tcd_saddr().write(|w| w.saddr().bits(src));
242 t.tcd_daddr().write(|w| w.daddr().bits(dst));
243
244 // Offsets: increment by width
245 t.tcd_soff().write(|w| w.soff().bits(width as u16));
246 t.tcd_doff().write(|w| w.doff().bits(width as u16));
247
248 // Attributes: size = log2(width)
249 let size = match width {
250 1 => 0,
251 2 => 1,
252 4 => 2,
253 _ => 0,
254 };
255 t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size));
256
257 // Number of bytes per minor loop
258 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
259
260 // Major loop: reset source address after major loop
261 let total_bytes = nbytes * count as u32;
262 t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32));
263 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32));
264
265 // Major loop count
266 t.tcd_biter_elinkno().write(|w| w.biter().bits(count));
267 t.tcd_citer_elinkno().write(|w| w.citer().bits(count));
268
269 // Control/status: enable interrupt if requested
270 if enable_int {
271 t.tcd_csr().write(|w| w.intmajor().set_bit());
272 } else {
273 t.tcd_csr().write(|w| w.intmajor().clear_bit());
274 }
275
276 cortex_m::asm::dsb();
277 }
278
279 unsafe {
280
281 // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations)
282 // Minor Link -> Channel 1
283 // Major Link -> Channel 2
284 configure_tcd(
285 edma,
286 0,
287 core::ptr::addr_of!(SRC_BUFFER) as u32,
288 core::ptr::addr_of_mut!(DEST_BUFFER0) as u32,
289 4, // src width
290 8, // nbytes (minor loop = 2 words)
291 2, // count (major loop = 2 iterations)
292 false, // no interrupt
293 );
294 ch0.set_minor_link(edma, 1); // Link to CH1 after each minor loop
295 ch0.set_major_link(edma, 2); // Link to CH2 after major loop
296
297 // Channel 1: Transfer 16 bytes (triggered by CH0 minor link)
298 configure_tcd(
299 edma,
300 1,
301 core::ptr::addr_of!(SRC_BUFFER) as u32,
302 core::ptr::addr_of_mut!(DEST_BUFFER1) as u32,
303 4,
304 16, // full buffer in one minor loop
305 1, // 1 major iteration
306 false,
307 );
308
309 // Channel 2: Transfer 16 bytes (triggered by CH0 major link)
310 configure_tcd(
311 edma,
312 2,
313 core::ptr::addr_of!(SRC_BUFFER) as u32,
314 core::ptr::addr_of_mut!(DEST_BUFFER2) as u32,
315 4,
316 16, // full buffer in one minor loop
317 1, // 1 major iteration
318 true, // enable interrupt
319 );
320 }
321
322 tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n").unwrap();
323
324 // Trigger first minor loop of CH0
325 unsafe { ch0.trigger_start(edma); }
326
327 // Wait for CH1 to complete (triggered by CH0 minor link)
328 while !ch1.is_done(edma) {
329 cortex_m::asm::nop();
330 }
331 unsafe { ch1.clear_done(edma); }
332
333 tx.blocking_write(b"CH1 done (via minor link).\r\n").unwrap();
334 tx.blocking_write(b"Triggering Channel 0 (2nd minor loop)...\r\n").unwrap();
335
336 // Trigger second minor loop of CH0
337 unsafe { ch0.trigger_start(edma); }
338
339 // Wait for CH0 major loop to complete
340 while !ch0.is_done(edma) {
341 cortex_m::asm::nop();
342 }
343 unsafe { ch0.clear_done(edma); }
344
345 tx.blocking_write(b"CH0 major loop done.\r\n").unwrap();
346
347 // Wait for CH2 to complete (triggered by CH0 major link)
348 while !DMA_CH2_DONE.load(Ordering::Acquire) {
349 cortex_m::asm::nop();
350 }
351
352 tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap();
353
354 tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n")
355 .unwrap();
356
357 tx.blocking_write(b"DEST0 (after): ").unwrap();
358 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4);
359 tx.blocking_write(b"\r\n").unwrap();
360
361 tx.blocking_write(b"DEST1 (after): ").unwrap();
362 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4);
363 tx.blocking_write(b"\r\n").unwrap();
364
365 tx.blocking_write(b"DEST2 (after): ").unwrap();
366 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4);
367 tx.blocking_write(b"\r\n\r\n").unwrap();
368
369 // Verify all buffers match source
370 let mut success = true;
371 unsafe {
372 let src_ptr = core::ptr::addr_of!(SRC_BUFFER) as *const u32;
373 let dst0_ptr = core::ptr::addr_of!(DEST_BUFFER0) as *const u32;
374 let dst1_ptr = core::ptr::addr_of!(DEST_BUFFER1) as *const u32;
375 let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32;
376
377 for i in 0..4 {
378 if *dst0_ptr.add(i) != *src_ptr.add(i) { success = false; }
379 if *dst1_ptr.add(i) != *src_ptr.add(i) { success = false; }
380 if *dst2_ptr.add(i) != *src_ptr.add(i) { success = false; }
381 }
382 }
383
384 if success {
385 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
386 defmt::info!("PASS: Data verified.");
387 } else {
388 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
389 defmt::error!("FAIL: Mismatch detected!");
390 }
391
392 loop {
393 cortex_m::asm::wfe();
394 }
395}
396
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
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 @@
1//! DMA memory-to-memory transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA to copy data between memory buffers
4//! using the Embassy-style async API with type-safe transfers.
5//!
6//! # Embassy-style features demonstrated:
7//! - `TransferOptions` for configuration
8//! - Type-safe `mem_to_mem<u32>()` method with async `.await`
9//! - `Transfer` Future that can be `.await`ed
10//! - `Word` trait for automatic transfer width detection
11//! - `memset()` method for filling memory with a pattern
12
13#![no_std]
14#![no_main]
15
16use embassy_executor::Spawner;
17use embassy_mcxa::clocks::config::Div8;
18use embassy_mcxa::clocks::Gate;
19use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, TransferOptions};
20use embassy_mcxa::{bind_interrupts, dma};
21use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
22use embassy_mcxa::pac;
23use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
24
25// Bind DMA channel 0 interrupt using Embassy-style macro
26bind_interrupts!(struct Irqs {
27 DMA_CH0 => DmaCh0InterruptHandler;
28});
29
30const BUFFER_LENGTH: usize = 4;
31
32// Buffers in RAM (static mut is automatically placed in .bss/.data)
33static mut SRC_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH];
34static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH];
35static mut MEMSET_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH];
36
37/// Helper to write a u32 as decimal ASCII to UART
38fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
39 let mut buf = [0u8; 10]; // u32 max is 4294967295 (10 digits)
40 let mut n = val;
41 let mut i = buf.len();
42
43 if n == 0 {
44 tx.blocking_write(b"0").ok();
45 return;
46 }
47
48 while n > 0 {
49 i -= 1;
50 buf[i] = b'0' + (n % 10) as u8;
51 n /= 10;
52 }
53
54 tx.blocking_write(&buf[i..]).ok();
55}
56
57/// Helper to print a buffer as [v1, v2, v3, v4] to UART
58/// Takes a raw pointer to avoid warnings about shared references to mutable statics
59fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const [u32; BUFFER_LENGTH]) {
60 tx.blocking_write(b"[").ok();
61 unsafe {
62 let buf = &*buf_ptr;
63 for (i, val) in buf.iter().enumerate() {
64 write_u32(tx, *val);
65 if i < buf.len() - 1 {
66 tx.blocking_write(b", ").ok();
67 }
68 }
69 }
70 tx.blocking_write(b"]").ok();
71}
72
73#[embassy_executor::main]
74async fn main(_spawner: Spawner) {
75 // Small delay to allow probe-rs to attach after reset
76 for _ in 0..100_000 {
77 cortex_m::asm::nop();
78 }
79
80 let mut cfg = hal::config::Config::default();
81 cfg.clock_cfg.sirc.fro_12m_enabled = true;
82 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
83 let p = hal::init(cfg);
84
85 defmt::info!("DMA memory-to-memory example starting...");
86
87 // Enable DMA0 clock and release reset
88 unsafe {
89 hal::peripherals::DMA0::enable_clock();
90 hal::peripherals::DMA0::release_reset();
91 }
92
93 // Get PAC peripherals for DMA init
94 let pac_periphs = unsafe { pac::Peripherals::steal() };
95
96 // Initialize DMA
97 unsafe {
98 dma::init(&pac_periphs);
99 }
100
101 // Enable DMA interrupt
102 unsafe {
103 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
104 }
105
106 // Create UART for debug output
107 let config = Config {
108 baudrate_bps: 115_200,
109 enable_tx: true,
110 enable_rx: false,
111 ..Default::default()
112 };
113
114 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
115 let (mut tx, _rx) = lpuart.split();
116
117 tx.blocking_write(b"EDMA memory to memory example begin.\r\n\r\n")
118 .unwrap();
119
120 // Initialize buffers
121 unsafe {
122 SRC_BUFFER = [1, 2, 3, 4];
123 DEST_BUFFER = [0; BUFFER_LENGTH];
124 }
125
126 tx.blocking_write(b"Source Buffer: ").unwrap();
127 print_buffer(&mut tx, &raw const SRC_BUFFER);
128 tx.blocking_write(b"\r\n").unwrap();
129
130 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
131 print_buffer(&mut tx, &raw const DEST_BUFFER);
132 tx.blocking_write(b"\r\n").unwrap();
133
134 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n")
135 .unwrap();
136
137 // Create DMA channel
138 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
139
140 // Configure transfer options (Embassy-style)
141 // TransferOptions defaults to: complete_transfer_interrupt = true
142 let options = TransferOptions::default();
143
144 // =========================================================================
145 // Part 1: Embassy-style async API demonstration (mem_to_mem)
146 // =========================================================================
147 //
148 // Use the new type-safe `mem_to_mem<u32>()` method:
149 // - Automatically determines transfer width from buffer element type (u32)
150 // - Returns a `Transfer` future that can be `.await`ed
151 // - Uses TransferOptions for consistent configuration
152 //
153 // Using async `.await` - the executor can run other tasks while waiting!
154
155 // Perform type-safe memory-to-memory transfer using Embassy-style async API
156 unsafe {
157 let src = &*core::ptr::addr_of!(SRC_BUFFER);
158 let dst = &mut *core::ptr::addr_of_mut!(DEST_BUFFER);
159
160 // Using async `.await` - the executor can run other tasks while waiting!
161 let transfer = dma_ch0.mem_to_mem(src, dst, options);
162 transfer.await;
163 }
164
165 tx.blocking_write(b"DMA mem-to-mem transfer complete!\r\n\r\n")
166 .unwrap();
167 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
168 print_buffer(&mut tx, &raw const DEST_BUFFER);
169 tx.blocking_write(b"\r\n").unwrap();
170
171 // Verify data
172 let mut mismatch = false;
173 unsafe {
174 for i in 0..BUFFER_LENGTH {
175 if SRC_BUFFER[i] != DEST_BUFFER[i] {
176 mismatch = true;
177 break;
178 }
179 }
180 }
181
182 if mismatch {
183 tx.blocking_write(b"FAIL: mem_to_mem mismatch!\r\n").unwrap();
184 defmt::error!("FAIL: mem_to_mem mismatch!");
185 } else {
186 tx.blocking_write(b"PASS: mem_to_mem verified.\r\n\r\n").unwrap();
187 defmt::info!("PASS: mem_to_mem verified.");
188 }
189
190 // =========================================================================
191 // Part 2: memset() demonstration
192 // =========================================================================
193 //
194 // The `memset()` method fills a buffer with a pattern value:
195 // - Fixed source address (pattern is read repeatedly)
196 // - Incrementing destination address
197 // - Uses the same Transfer future pattern
198
199 tx.blocking_write(b"--- Demonstrating memset() feature ---\r\n\r\n").unwrap();
200
201 tx.blocking_write(b"Memset Buffer (before): ").unwrap();
202 print_buffer(&mut tx, &raw const MEMSET_BUFFER);
203 tx.blocking_write(b"\r\n").unwrap();
204
205 // Fill buffer with a pattern value using DMA memset
206 let pattern: u32 = 0xDEADBEEF;
207 tx.blocking_write(b"Filling with pattern 0xDEADBEEF...\r\n").unwrap();
208
209 unsafe {
210 let dst = &mut *core::ptr::addr_of_mut!(MEMSET_BUFFER);
211
212 // Using blocking_wait() for demonstration - also shows non-async usage
213 let transfer = dma_ch0.memset(&pattern, dst, options);
214 transfer.blocking_wait();
215 }
216
217 tx.blocking_write(b"DMA memset complete!\r\n\r\n").unwrap();
218 tx.blocking_write(b"Memset Buffer (after): ").unwrap();
219 print_buffer(&mut tx, &raw const MEMSET_BUFFER);
220 tx.blocking_write(b"\r\n").unwrap();
221
222 // Verify memset result
223 let mut memset_ok = true;
224 unsafe {
225 #[allow(clippy::needless_range_loop)]
226 for i in 0..BUFFER_LENGTH {
227 if MEMSET_BUFFER[i] != pattern {
228 memset_ok = false;
229 break;
230 }
231 }
232 }
233
234 if !memset_ok {
235 tx.blocking_write(b"FAIL: memset mismatch!\r\n").unwrap();
236 defmt::error!("FAIL: memset mismatch!");
237 } else {
238 tx.blocking_write(b"PASS: memset verified.\r\n\r\n").unwrap();
239 defmt::info!("PASS: memset verified.");
240 }
241
242 tx.blocking_write(b"=== All DMA tests complete ===\r\n").unwrap();
243
244 loop {
245 cortex_m::asm::wfe();
246 }
247}
248
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 @@
1//! DMA memset example for MCXA276.
2//!
3//! This example demonstrates using DMA to fill a buffer with a repeated pattern.
4//! The source address stays fixed while the destination increments.
5//!
6//! # Embassy-style features demonstrated:
7//! - `dma::edma_tcd()` accessor for simplified register access
8//! - `DmaChannel::is_done()` and `clear_done()` helper methods
9//! - No need to pass register block around
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 = 4;
29
30// Buffers in RAM
31static mut PATTERN: u32 = 0;
32static mut DEST_BUFFER: [u32; BUFFER_LENGTH] = [0; BUFFER_LENGTH];
33
34/// Helper to write a u32 as decimal ASCII to UART
35fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
36 let mut buf = [0u8; 10];
37 let mut n = val;
38 let mut i = buf.len();
39
40 if n == 0 {
41 tx.blocking_write(b"0").ok();
42 return;
43 }
44
45 while n > 0 {
46 i -= 1;
47 buf[i] = b'0' + (n % 10) as u8;
48 n /= 10;
49 }
50
51 tx.blocking_write(&buf[i..]).ok();
52}
53
54/// Helper to print a buffer to UART
55fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
56 tx.blocking_write(b"[").ok();
57 unsafe {
58 for i in 0..len {
59 write_u32(tx, *buf_ptr.add(i));
60 if i < len - 1 {
61 tx.blocking_write(b", ").ok();
62 }
63 }
64 }
65 tx.blocking_write(b"]").ok();
66}
67
68#[embassy_executor::main]
69async fn main(_spawner: Spawner) {
70 // Small delay to allow probe-rs to attach after reset
71 for _ in 0..100_000 {
72 cortex_m::asm::nop();
73 }
74
75 let mut cfg = hal::config::Config::default();
76 cfg.clock_cfg.sirc.fro_12m_enabled = true;
77 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
78 let p = hal::init(cfg);
79
80 defmt::info!("DMA memset example starting...");
81
82 // Enable DMA0 clock and release reset
83 unsafe {
84 hal::peripherals::DMA0::enable_clock();
85 hal::peripherals::DMA0::release_reset();
86 }
87
88 let pac_periphs = unsafe { pac::Peripherals::steal() };
89
90 unsafe {
91 dma::init(&pac_periphs);
92 }
93
94 // Enable DMA interrupt
95 unsafe {
96 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
97 }
98
99 let config = Config {
100 baudrate_bps: 115_200,
101 enable_tx: true,
102 enable_rx: false,
103 ..Default::default()
104 };
105
106 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
107 let (mut tx, _rx) = lpuart.split();
108
109 tx.blocking_write(b"EDMA memset example begin.\r\n\r\n")
110 .unwrap();
111
112 // Initialize buffers
113 unsafe {
114 PATTERN = 0xDEADBEEF;
115 DEST_BUFFER = [0; BUFFER_LENGTH];
116 }
117
118 tx.blocking_write(b"Pattern value: 0x").unwrap();
119 // Print pattern in hex
120 unsafe {
121 let hex_chars = b"0123456789ABCDEF";
122 let mut hex_buf = [0u8; 8];
123 let mut val = PATTERN;
124 for i in (0..8).rev() {
125 hex_buf[i] = hex_chars[(val & 0xF) as usize];
126 val >>= 4;
127 }
128 tx.blocking_write(&hex_buf).ok();
129 }
130 tx.blocking_write(b"\r\n").unwrap();
131
132 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
133 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH);
134 tx.blocking_write(b"\r\n").unwrap();
135
136 tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n")
137 .unwrap();
138
139 // Create DMA channel using Embassy-style API
140 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
141
142 // Use edma_tcd() accessor instead of passing register block around
143 let edma = edma_tcd();
144
145 // Configure memset transfer using direct TCD access:
146 // Source stays fixed (soff = 0, reads same pattern repeatedly)
147 // Destination increments (doff = 4)
148 unsafe {
149 let t = edma.tcd(0);
150
151 // Reset channel state
152 t.ch_csr().write(|w| {
153 w.erq().disable()
154 .earq().disable()
155 .eei().no_error()
156 .ebw().disable()
157 .done().clear_bit_by_one()
158 });
159 t.ch_es().write(|w| w.bits(0));
160 t.ch_int().write(|w| w.int().clear_bit_by_one());
161
162 // Source address (pattern) - fixed
163 t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of_mut!(PATTERN) as u32));
164 // Destination address - increments
165 t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DEST_BUFFER) as u32));
166
167 // Source offset = 0 (stays fixed), Dest offset = 4 (increments)
168 t.tcd_soff().write(|w| w.soff().bits(0));
169 t.tcd_doff().write(|w| w.doff().bits(4));
170
171 // Attributes: 32-bit transfers (size = 2)
172 t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2));
173
174 // Transfer entire buffer in one minor loop
175 let nbytes = (BUFFER_LENGTH * 4) as u32;
176 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
177
178 // Source doesn't need adjustment (stays fixed)
179 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
180 // Reset dest address after major loop
181 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32));
182
183 // Major loop count = 1
184 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
185 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
186
187 // Enable interrupt on major loop completion
188 t.tcd_csr().write(|w| w.intmajor().set_bit());
189
190 cortex_m::asm::dsb();
191
192 tx.blocking_write(b"Triggering transfer...\r\n").unwrap();
193 dma_ch0.trigger_start(edma);
194 }
195
196 // Wait for completion using channel helper method
197 while !dma_ch0.is_done(edma) {
198 cortex_m::asm::nop();
199 }
200 unsafe { dma_ch0.clear_done(edma); }
201
202 tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n")
203 .unwrap();
204 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
205 print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER) as *const u32, BUFFER_LENGTH);
206 tx.blocking_write(b"\r\n\r\n").unwrap();
207
208 // Verify: All elements should equal PATTERN
209 let mut mismatch = false;
210 unsafe {
211 #[allow(clippy::needless_range_loop)]
212 for i in 0..BUFFER_LENGTH {
213 if DEST_BUFFER[i] != PATTERN {
214 mismatch = true;
215 break;
216 }
217 }
218 }
219
220 if mismatch {
221 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
222 defmt::error!("FAIL: Mismatch detected!");
223 } else {
224 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
225 defmt::info!("PASS: Data verified.");
226 }
227
228 loop {
229 cortex_m::asm::wfe();
230 }
231}
232
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 @@
1//! DMA ping-pong/double-buffer transfer example for MCXA276.
2//!
3//! This example demonstrates two approaches for ping-pong/double-buffering:
4//!
5//! ## Approach 1: Scatter/Gather with linked TCDs (manual)
6//! - Two TCDs link to each other for alternating transfers
7//! - Uses custom interrupt handler with AtomicBool flag
8//!
9//! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!)
10//! - Single continuous transfer over entire buffer
11//! - Uses half-transfer interrupt to know when first half is ready
12//! - Application can process first half while second half is being filled
13//!
14//! # Embassy-style features demonstrated:
15//! - `dma::edma_tcd()` accessor for simplified register access
16//! - `DmaChannel::new()` for channel creation
17//! - Scatter/gather with linked TCDs
18//! - NEW: `wait_half()` for half-transfer interrupt handling
19
20#![no_std]
21#![no_main]
22
23use core::sync::atomic::{AtomicBool, Ordering};
24use embassy_executor::Spawner;
25use embassy_mcxa::clocks::config::Div8;
26use embassy_mcxa::clocks::Gate;
27use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh1InterruptHandler, Tcd, TransferOptions};
28use embassy_mcxa::{bind_interrupts, dma};
29use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
30use embassy_mcxa::pac;
31use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
32
33// Source and destination buffers for Approach 1 (scatter/gather)
34static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
35static mut DST: [u32; 8] = [0; 8];
36
37// Source and destination buffers for Approach 2 (wait_half)
38static mut SRC2: [u32; 8] = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4];
39static mut DST2: [u32; 8] = [0; 8];
40
41// TCD pool for scatter/gather - must be 32-byte aligned
42#[repr(C, align(32))]
43struct TcdPool([Tcd; 2]);
44
45static mut TCD_POOL: TcdPool = TcdPool([Tcd {
46 saddr: 0,
47 soff: 0,
48 attr: 0,
49 nbytes: 0,
50 slast: 0,
51 daddr: 0,
52 doff: 0,
53 citer: 0,
54 dlast_sga: 0,
55 csr: 0,
56 biter: 0,
57}; 2]);
58
59static TRANSFER_DONE: AtomicBool = AtomicBool::new(false);
60
61// Custom DMA interrupt handler for ping-pong transfer
62// We need a custom handler because we signal completion via TRANSFER_DONE flag
63// and don't clear DONE bit when using Scatter/Gather (ESG=1)
64pub struct PingPongDmaHandler;
65
66impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for PingPongDmaHandler {
67 unsafe fn on_interrupt() {
68 let edma = edma_tcd();
69
70 // Clear interrupt flag
71 edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one());
72
73 // Do NOT clear DONE bit when using Scatter/Gather (ESG=1),
74 // as the hardware loads the next TCD which resets the status.
75
76 TRANSFER_DONE.store(true, Ordering::Release);
77 }
78}
79
80bind_interrupts!(struct Irqs {
81 DMA_CH0 => PingPongDmaHandler;
82 DMA_CH1 => DmaCh1InterruptHandler; // For wait_half() demo
83});
84
85/// Helper to write a u32 as decimal ASCII to UART
86fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
87 let mut buf = [0u8; 10];
88 let mut n = val;
89 let mut i = buf.len();
90
91 if n == 0 {
92 tx.blocking_write(b"0").ok();
93 return;
94 }
95
96 while n > 0 {
97 i -= 1;
98 buf[i] = b'0' + (n % 10) as u8;
99 n /= 10;
100 }
101
102 tx.blocking_write(&buf[i..]).ok();
103}
104
105/// Helper to print a buffer to UART
106fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
107 tx.blocking_write(b"[").ok();
108 unsafe {
109 for i in 0..len {
110 write_u32(tx, *buf_ptr.add(i));
111 if i < len - 1 {
112 tx.blocking_write(b", ").ok();
113 }
114 }
115 }
116 tx.blocking_write(b"]").ok();
117}
118
119#[embassy_executor::main]
120async fn main(_spawner: Spawner) {
121 // Small delay to allow probe-rs to attach after reset
122 for _ in 0..100_000 {
123 cortex_m::asm::nop();
124 }
125
126 let mut cfg = hal::config::Config::default();
127 cfg.clock_cfg.sirc.fro_12m_enabled = true;
128 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
129 let p = hal::init(cfg);
130
131 defmt::info!("DMA ping-pong transfer example starting...");
132
133 // Enable DMA0 clock and release reset
134 unsafe {
135 hal::peripherals::DMA0::enable_clock();
136 hal::peripherals::DMA0::release_reset();
137 }
138
139 let pac_periphs = unsafe { pac::Peripherals::steal() };
140
141 unsafe {
142 dma::init(&pac_periphs);
143 }
144
145 // Use edma_tcd() accessor instead of passing register block around
146 let edma = edma_tcd();
147
148 // Enable DMA interrupt
149 unsafe {
150 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
151 }
152
153 let config = Config {
154 baudrate_bps: 115_200,
155 enable_tx: true,
156 enable_rx: false,
157 ..Default::default()
158 };
159
160 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
161 let (mut tx, _rx) = lpuart.split();
162
163 tx.blocking_write(b"EDMA ping-pong transfer example begin.\r\n\r\n")
164 .unwrap();
165
166 // Initialize buffers
167 unsafe {
168 SRC = [1, 2, 3, 4, 5, 6, 7, 8];
169 DST = [0; 8];
170 }
171
172 tx.blocking_write(b"Source Buffer: ").unwrap();
173 print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8);
174 tx.blocking_write(b"\r\n").unwrap();
175
176 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
177 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
178 tx.blocking_write(b"\r\n").unwrap();
179
180 tx.blocking_write(b"Configuring ping-pong DMA with Embassy-style API...\r\n")
181 .unwrap();
182
183 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
184
185 // Configure ping-pong transfer using direct TCD access:
186 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
187 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1.
188 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0.
189 unsafe {
190 let tcds = &mut *core::ptr::addr_of_mut!(TCD_POOL.0);
191 let src_ptr = core::ptr::addr_of!(SRC) as *const u32;
192 let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32;
193
194 let half_len = 4usize;
195 let half_bytes = (half_len * 4) as u32;
196
197 let tcd0_addr = &tcds[0] as *const _ as u32;
198 let tcd1_addr = &tcds[1] as *const _ as u32;
199
200 // TCD0: First half -> Links to TCD1
201 tcds[0] = Tcd {
202 saddr: src_ptr as u32,
203 soff: 4,
204 attr: 0x0202, // 32-bit src/dst
205 nbytes: half_bytes,
206 slast: 0,
207 daddr: dst_ptr as u32,
208 doff: 4,
209 citer: 1,
210 dlast_sga: tcd1_addr as i32,
211 csr: 0x0012, // ESG | INTMAJOR
212 biter: 1,
213 };
214
215 // TCD1: Second half -> Links to TCD0
216 tcds[1] = Tcd {
217 saddr: src_ptr.add(half_len) as u32,
218 soff: 4,
219 attr: 0x0202,
220 nbytes: half_bytes,
221 slast: 0,
222 daddr: dst_ptr.add(half_len) as u32,
223 doff: 4,
224 citer: 1,
225 dlast_sga: tcd0_addr as i32,
226 csr: 0x0012,
227 biter: 1,
228 };
229
230 // Load TCD0 into hardware registers
231 dma_ch0.load_tcd(edma, &tcds[0]);
232 }
233
234 tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap();
235
236 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4])
237 unsafe {
238 dma_ch0.trigger_start(edma);
239 }
240
241 // Wait for first half
242 while !TRANSFER_DONE.load(Ordering::Acquire) {
243 cortex_m::asm::nop();
244 }
245 TRANSFER_DONE.store(false, Ordering::Release);
246
247 tx.blocking_write(b"First half transferred.\r\n").unwrap();
248 tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap();
249
250 // Trigger second transfer (second half: SRC[4..8] -> DST[4..8])
251 unsafe {
252 dma_ch0.trigger_start(edma);
253 }
254
255 // Wait for second half
256 while !TRANSFER_DONE.load(Ordering::Acquire) {
257 cortex_m::asm::nop();
258 }
259 TRANSFER_DONE.store(false, Ordering::Release);
260
261 tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap();
262
263 tx.blocking_write(b"EDMA ping-pong transfer example finish.\r\n\r\n")
264 .unwrap();
265 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
266 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
267 tx.blocking_write(b"\r\n\r\n").unwrap();
268
269 // Verify: DST should match SRC
270 let mut mismatch = false;
271 unsafe {
272 let src_ptr = core::ptr::addr_of!(SRC) as *const u32;
273 let dst_ptr = core::ptr::addr_of!(DST) as *const u32;
274 for i in 0..8 {
275 if *src_ptr.add(i) != *dst_ptr.add(i) {
276 mismatch = true;
277 break;
278 }
279 }
280 }
281
282 if mismatch {
283 tx.blocking_write(b"FAIL: Approach 1 mismatch detected!\r\n").unwrap();
284 defmt::error!("FAIL: Approach 1 mismatch detected!");
285 } else {
286 tx.blocking_write(b"PASS: Approach 1 data verified.\r\n\r\n").unwrap();
287 defmt::info!("PASS: Approach 1 data verified.");
288 }
289
290 // =========================================================================
291 // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!)
292 // =========================================================================
293 //
294 // This approach uses a single continuous DMA transfer with half-transfer
295 // interrupt enabled. The wait_half() method allows you to be notified
296 // when the first half of the buffer is complete, so you can process it
297 // while the second half is still being filled.
298 //
299 // Benefits:
300 // - Simpler setup (no TCD pool needed)
301 // - True async/await support
302 // - Good for streaming data processing
303
304 tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n").unwrap();
305
306 // Enable DMA CH1 interrupt
307 unsafe {
308 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1);
309 }
310
311 // Initialize approach 2 buffers
312 unsafe {
313 SRC2 = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4];
314 DST2 = [0; 8];
315 }
316
317 tx.blocking_write(b"SRC2: ").unwrap();
318 print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 8);
319 tx.blocking_write(b"\r\n").unwrap();
320
321 let dma_ch1 = DmaChannel::new(p.DMA_CH1);
322
323 // Configure transfer with half-transfer interrupt enabled
324 let mut options = TransferOptions::default();
325 options.half_transfer_interrupt = true; // Enable half-transfer interrupt
326 options.complete_transfer_interrupt = true;
327
328 tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n").unwrap();
329
330 unsafe {
331 let src = &*core::ptr::addr_of!(SRC2);
332 let dst = &mut *core::ptr::addr_of_mut!(DST2);
333
334 // Create the transfer
335 let mut transfer = dma_ch1.mem_to_mem(src, dst, options);
336
337 // Wait for half-transfer (first 4 elements)
338 tx.blocking_write(b"Waiting for first half...\r\n").unwrap();
339 let half_ok = transfer.wait_half().await;
340
341 if half_ok {
342 tx.blocking_write(b"Half-transfer complete! First half of DST2: ").unwrap();
343 print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4);
344 tx.blocking_write(b"\r\n").unwrap();
345 tx.blocking_write(b"(Processing first half while second half transfers...)\r\n").unwrap();
346 }
347
348 // Wait for complete transfer
349 tx.blocking_write(b"Waiting for second half...\r\n").unwrap();
350 transfer.await;
351 }
352
353 tx.blocking_write(b"Transfer complete! Full DST2: ").unwrap();
354 print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 8);
355 tx.blocking_write(b"\r\n\r\n").unwrap();
356
357 // Verify approach 2
358 let mut mismatch2 = false;
359 unsafe {
360 let src_ptr = core::ptr::addr_of!(SRC2) as *const u32;
361 let dst_ptr = core::ptr::addr_of!(DST2) as *const u32;
362 for i in 0..8 {
363 if *src_ptr.add(i) != *dst_ptr.add(i) {
364 mismatch2 = true;
365 break;
366 }
367 }
368 }
369
370 if mismatch2 {
371 tx.blocking_write(b"FAIL: Approach 2 mismatch!\r\n").unwrap();
372 defmt::error!("FAIL: Approach 2 mismatch!");
373 } else {
374 tx.blocking_write(b"PASS: Approach 2 verified.\r\n").unwrap();
375 defmt::info!("PASS: Approach 2 verified.");
376 }
377
378 tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n").unwrap();
379
380 loop {
381 cortex_m::asm::wfe();
382 }
383}
384
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 @@
1//! DMA scatter-gather transfer example for MCXA276.
2//!
3//! This example demonstrates using DMA with scatter/gather to chain multiple
4//! transfer descriptors. The first TCD transfers the first half of the buffer,
5//! then automatically loads the second TCD to transfer the second half.
6//!
7//! # Embassy-style features demonstrated:
8//! - `dma::edma_tcd()` accessor for simplified register access
9//! - `DmaChannel::new()` for channel creation
10//! - Scatter/gather with chained TCDs
11
12#![no_std]
13#![no_main]
14
15use core::sync::atomic::{AtomicBool, Ordering};
16use embassy_executor::Spawner;
17use embassy_mcxa::clocks::config::Div8;
18use embassy_mcxa::clocks::Gate;
19use embassy_mcxa::dma::{edma_tcd, DmaChannel, Tcd};
20use embassy_mcxa::{bind_interrupts, dma};
21use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
22use embassy_mcxa::pac;
23use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
24
25// Source and destination buffers
26static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
27static mut DST: [u32; 8] = [0; 8];
28
29// TCD pool for scatter/gather - must be 32-byte aligned
30#[repr(C, align(32))]
31struct TcdPool([Tcd; 2]);
32
33static mut TCD_POOL: TcdPool = TcdPool([Tcd {
34 saddr: 0,
35 soff: 0,
36 attr: 0,
37 nbytes: 0,
38 slast: 0,
39 daddr: 0,
40 doff: 0,
41 citer: 0,
42 dlast_sga: 0,
43 csr: 0,
44 biter: 0,
45}; 2]);
46
47static TRANSFER_DONE: AtomicBool = AtomicBool::new(false);
48
49// Custom DMA interrupt handler for scatter-gather transfer
50// We need a custom handler because we signal completion via TRANSFER_DONE flag
51// and need to conditionally clear DONE bit based on ESG status
52pub struct ScatterGatherDmaHandler;
53
54impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for ScatterGatherDmaHandler {
55 unsafe fn on_interrupt() {
56 let edma = edma_tcd();
57
58 // Clear interrupt flag
59 edma.tcd(0).ch_int().write(|w| w.int().clear_bit_by_one());
60
61 // If ESG=1 (Scatter/Gather), the hardware loads the next TCD and clears DONE.
62 // If ESG=0 (Last TCD), DONE remains set and must be cleared.
63 if edma.tcd(0).ch_csr().read().done().bit_is_set() {
64 edma.tcd(0).ch_csr().write(|w| w.done().clear_bit_by_one());
65 }
66
67 TRANSFER_DONE.store(true, Ordering::Release);
68 }
69}
70
71bind_interrupts!(struct Irqs {
72 DMA_CH0 => ScatterGatherDmaHandler;
73});
74
75/// Helper to write a u32 as decimal ASCII to UART
76fn write_u32(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
77 let mut buf = [0u8; 10];
78 let mut n = val;
79 let mut i = buf.len();
80
81 if n == 0 {
82 tx.blocking_write(b"0").ok();
83 return;
84 }
85
86 while n > 0 {
87 i -= 1;
88 buf[i] = b'0' + (n % 10) as u8;
89 n /= 10;
90 }
91
92 tx.blocking_write(&buf[i..]).ok();
93}
94
95/// Helper to print a buffer to UART
96fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
97 tx.blocking_write(b"[").ok();
98 unsafe {
99 for i in 0..len {
100 write_u32(tx, *buf_ptr.add(i));
101 if i < len - 1 {
102 tx.blocking_write(b", ").ok();
103 }
104 }
105 }
106 tx.blocking_write(b"]").ok();
107}
108
109#[embassy_executor::main]
110async fn main(_spawner: Spawner) {
111 // Small delay to allow probe-rs to attach after reset
112 for _ in 0..100_000 {
113 cortex_m::asm::nop();
114 }
115
116 let mut cfg = hal::config::Config::default();
117 cfg.clock_cfg.sirc.fro_12m_enabled = true;
118 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
119 let p = hal::init(cfg);
120
121 defmt::info!("DMA scatter-gather transfer example starting...");
122
123 // Enable DMA0 clock and release reset
124 unsafe {
125 hal::peripherals::DMA0::enable_clock();
126 hal::peripherals::DMA0::release_reset();
127 }
128
129 let pac_periphs = unsafe { pac::Peripherals::steal() };
130
131 unsafe {
132 dma::init(&pac_periphs);
133 }
134
135 // Use edma_tcd() accessor instead of passing register block around
136 let edma = edma_tcd();
137
138 // Enable DMA interrupt
139 unsafe {
140 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
141 }
142
143 let config = Config {
144 baudrate_bps: 115_200,
145 enable_tx: true,
146 enable_rx: false,
147 ..Default::default()
148 };
149
150 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
151 let (mut tx, _rx) = lpuart.split();
152
153 tx.blocking_write(b"EDMA scatter-gather transfer example begin.\r\n\r\n")
154 .unwrap();
155
156 // Initialize buffers
157 unsafe {
158 SRC = [1, 2, 3, 4, 5, 6, 7, 8];
159 DST = [0; 8];
160 }
161
162 tx.blocking_write(b"Source Buffer: ").unwrap();
163 print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8);
164 tx.blocking_write(b"\r\n").unwrap();
165
166 tx.blocking_write(b"Destination Buffer (before): ").unwrap();
167 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
168 tx.blocking_write(b"\r\n").unwrap();
169
170 tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n")
171 .unwrap();
172
173 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
174
175 // Configure scatter-gather transfer using direct TCD access:
176 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
177 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1.
178 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD.
179 unsafe {
180 let tcds = core::slice::from_raw_parts_mut(
181 core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd,
182 2,
183 );
184 let src_ptr = core::ptr::addr_of!(SRC) as *const u32;
185 let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32;
186
187 let num_tcds = 2usize;
188 let chunk_len = 4usize; // 8 / 2
189 let chunk_bytes = (chunk_len * 4) as u32;
190
191 for i in 0..num_tcds {
192 let is_last = i == num_tcds - 1;
193 let next_tcd_addr = if is_last {
194 0 // No next TCD
195 } else {
196 &tcds[i + 1] as *const _ as u32
197 };
198
199 tcds[i] = Tcd {
200 saddr: src_ptr.add(i * chunk_len) as u32,
201 soff: 4,
202 attr: 0x0202, // 32-bit src/dst
203 nbytes: chunk_bytes,
204 slast: 0,
205 daddr: dst_ptr.add(i * chunk_len) as u32,
206 doff: 4,
207 citer: 1,
208 dlast_sga: next_tcd_addr as i32,
209 // ESG (scatter/gather) for non-last, INTMAJOR for all
210 csr: if is_last { 0x0002 } else { 0x0012 },
211 biter: 1,
212 };
213 }
214
215 // Load TCD0 into hardware registers
216 dma_ch0.load_tcd(edma, &tcds[0]);
217 }
218
219 tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap();
220
221 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4])
222 // TCD0 is currently loaded.
223 unsafe {
224 dma_ch0.trigger_start(edma);
225 }
226
227 // Wait for first half
228 while !TRANSFER_DONE.load(Ordering::Acquire) {
229 cortex_m::asm::nop();
230 }
231 TRANSFER_DONE.store(false, Ordering::Release);
232
233 tx.blocking_write(b"First half transferred.\r\n").unwrap();
234 tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap();
235
236 // Trigger second transfer (second half: SRC[4..8] -> DST[4..8])
237 // TCD1 should have been loaded by the scatter/gather engine.
238 unsafe {
239 dma_ch0.trigger_start(edma);
240 }
241
242 // Wait for second half
243 while !TRANSFER_DONE.load(Ordering::Acquire) {
244 cortex_m::asm::nop();
245 }
246 TRANSFER_DONE.store(false, Ordering::Release);
247
248 tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap();
249
250 tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n")
251 .unwrap();
252 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
253 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
254 tx.blocking_write(b"\r\n\r\n").unwrap();
255
256 // Verify: DST should match SRC
257 let mut mismatch = false;
258 unsafe {
259 let src_ptr = core::ptr::addr_of!(SRC) as *const u32;
260 let dst_ptr = core::ptr::addr_of!(DST) as *const u32;
261 for i in 0..8 {
262 if *src_ptr.add(i) != *dst_ptr.add(i) {
263 mismatch = true;
264 break;
265 }
266 }
267 }
268
269 if mismatch {
270 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
271 defmt::error!("FAIL: Mismatch detected!");
272 } else {
273 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
274 defmt::info!("PASS: Data verified.");
275 }
276
277 loop {
278 cortex_m::asm::wfe();
279 }
280}
281
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 @@
1//! DMA Scatter-Gather Builder example for MCXA276.
2//!
3//! This example demonstrates using the new `ScatterGatherBuilder` API for
4//! chaining multiple DMA transfers with a type-safe builder pattern.
5//!
6//! # Features demonstrated:
7//! - `ScatterGatherBuilder::new()` for creating a builder
8//! - `add_transfer()` for adding memory-to-memory segments
9//! - `build()` to start the chained transfer
10//! - Automatic TCD linking and ESG bit management
11//!
12//! # Comparison with manual scatter-gather:
13//! The manual approach (see `dma_scatter_gather.rs`) requires:
14//! - Manual TCD pool allocation and alignment
15//! - Manual CSR/ESG/INTMAJOR bit manipulation
16//! - Manual dlast_sga address calculations
17//!
18//! The builder approach handles all of this automatically!
19
20#![no_std]
21#![no_main]
22
23use embassy_executor::Spawner;
24use embassy_mcxa::clocks::config::Div8;
25use embassy_mcxa::clocks::Gate;
26use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, ScatterGatherBuilder};
27use embassy_mcxa::{bind_interrupts, dma};
28use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
29use embassy_mcxa::pac;
30use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
31
32// Bind DMA channel 0 interrupt
33bind_interrupts!(struct Irqs {
34 DMA_CH0 => DmaCh0InterruptHandler;
35});
36
37// Source buffers (multiple segments)
38static mut SRC1: [u32; 4] = [0x11111111, 0x22222222, 0x33333333, 0x44444444];
39static mut SRC2: [u32; 4] = [0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD];
40static mut SRC3: [u32; 4] = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210];
41
42// Destination buffers (one per segment)
43static mut DST1: [u32; 4] = [0; 4];
44static mut DST2: [u32; 4] = [0; 4];
45static mut DST3: [u32; 4] = [0; 4];
46
47/// Helper to write a u32 as hex to UART
48fn write_hex(tx: &mut LpuartTx<'_, Blocking>, val: u32) {
49 const HEX: &[u8; 16] = b"0123456789ABCDEF";
50 for i in (0..8).rev() {
51 let nibble = ((val >> (i * 4)) & 0xF) as usize;
52 tx.blocking_write(&[HEX[nibble]]).ok();
53 }
54}
55
56/// Helper to print a buffer to UART
57fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) {
58 tx.blocking_write(b"[").ok();
59 unsafe {
60 for i in 0..len {
61 write_hex(tx, *buf_ptr.add(i));
62 if i < len - 1 {
63 tx.blocking_write(b", ").ok();
64 }
65 }
66 }
67 tx.blocking_write(b"]").ok();
68}
69
70#[embassy_executor::main]
71async fn main(_spawner: Spawner) {
72 // Small delay to allow probe-rs to attach after reset
73 for _ in 0..100_000 {
74 cortex_m::asm::nop();
75 }
76
77 let mut cfg = hal::config::Config::default();
78 cfg.clock_cfg.sirc.fro_12m_enabled = true;
79 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
80 let p = hal::init(cfg);
81
82 defmt::info!("DMA Scatter-Gather Builder example starting...");
83
84 // Enable DMA0 clock and release reset
85 unsafe {
86 hal::peripherals::DMA0::enable_clock();
87 hal::peripherals::DMA0::release_reset();
88 }
89
90 let pac_periphs = unsafe { pac::Peripherals::steal() };
91
92 // Initialize DMA
93 unsafe {
94 dma::init(&pac_periphs);
95 }
96
97 // Enable DMA interrupt
98 unsafe {
99 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
100 }
101
102 // Create UART for debug output
103 let config = Config {
104 baudrate_bps: 115_200,
105 enable_tx: true,
106 enable_rx: false,
107 ..Default::default()
108 };
109
110 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
111 let (mut tx, _rx) = lpuart.split();
112
113 tx.blocking_write(b"DMA Scatter-Gather Builder Example\r\n").unwrap();
114 tx.blocking_write(b"===================================\r\n\r\n").unwrap();
115
116 // Show source buffers
117 tx.blocking_write(b"Source buffers:\r\n").unwrap();
118 tx.blocking_write(b" SRC1: ").unwrap();
119 print_buffer(&mut tx, core::ptr::addr_of!(SRC1) as *const u32, 4);
120 tx.blocking_write(b"\r\n").unwrap();
121 tx.blocking_write(b" SRC2: ").unwrap();
122 print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 4);
123 tx.blocking_write(b"\r\n").unwrap();
124 tx.blocking_write(b" SRC3: ").unwrap();
125 print_buffer(&mut tx, core::ptr::addr_of!(SRC3) as *const u32, 4);
126 tx.blocking_write(b"\r\n\r\n").unwrap();
127
128 tx.blocking_write(b"Destination buffers (before):\r\n").unwrap();
129 tx.blocking_write(b" DST1: ").unwrap();
130 print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4);
131 tx.blocking_write(b"\r\n").unwrap();
132 tx.blocking_write(b" DST2: ").unwrap();
133 print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4);
134 tx.blocking_write(b"\r\n").unwrap();
135 tx.blocking_write(b" DST3: ").unwrap();
136 print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4);
137 tx.blocking_write(b"\r\n\r\n").unwrap();
138
139 // Create DMA channel
140 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
141
142 tx.blocking_write(b"Building scatter-gather chain with builder API...\r\n").unwrap();
143
144 // =========================================================================
145 // ScatterGatherBuilder API demonstration
146 // =========================================================================
147 //
148 // The builder pattern makes scatter-gather transfers much easier:
149 // 1. Create a builder
150 // 2. Add transfer segments with add_transfer()
151 // 3. Call build() to start the entire chain
152 // No manual TCD manipulation required!
153
154 let mut builder = ScatterGatherBuilder::<u32>::new();
155
156 // Add three transfer segments - the builder handles TCD linking automatically
157 unsafe {
158 let src1 = &*core::ptr::addr_of!(SRC1);
159 let dst1 = &mut *core::ptr::addr_of_mut!(DST1);
160 builder.add_transfer(src1, dst1);
161 }
162
163 unsafe {
164 let src2 = &*core::ptr::addr_of!(SRC2);
165 let dst2 = &mut *core::ptr::addr_of_mut!(DST2);
166 builder.add_transfer(src2, dst2);
167 }
168
169 unsafe {
170 let src3 = &*core::ptr::addr_of!(SRC3);
171 let dst3 = &mut *core::ptr::addr_of_mut!(DST3);
172 builder.add_transfer(src3, dst3);
173 }
174
175 tx.blocking_write(b"Added 3 transfer segments to chain.\r\n").unwrap();
176 tx.blocking_write(b"Starting scatter-gather transfer with .await...\r\n\r\n").unwrap();
177
178 // Build and execute the scatter-gather chain
179 // The build() method:
180 // - Links all TCDs together with ESG bit
181 // - Sets INTMAJOR on all TCDs
182 // - Loads the first TCD into hardware
183 // - Returns a Transfer future
184 unsafe {
185 let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather");
186 transfer.blocking_wait();
187 }
188
189 tx.blocking_write(b"Scatter-gather transfer complete!\r\n\r\n").unwrap();
190
191 // Show results
192 tx.blocking_write(b"Destination buffers (after):\r\n").unwrap();
193 tx.blocking_write(b" DST1: ").unwrap();
194 print_buffer(&mut tx, core::ptr::addr_of!(DST1) as *const u32, 4);
195 tx.blocking_write(b"\r\n").unwrap();
196 tx.blocking_write(b" DST2: ").unwrap();
197 print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4);
198 tx.blocking_write(b"\r\n").unwrap();
199 tx.blocking_write(b" DST3: ").unwrap();
200 print_buffer(&mut tx, core::ptr::addr_of!(DST3) as *const u32, 4);
201 tx.blocking_write(b"\r\n\r\n").unwrap();
202
203 // Verify all three segments
204 let mut all_ok = true;
205 unsafe {
206 let src1 = core::ptr::addr_of!(SRC1) as *const u32;
207 let dst1 = core::ptr::addr_of!(DST1) as *const u32;
208 for i in 0..4 {
209 if *src1.add(i) != *dst1.add(i) {
210 all_ok = false;
211 }
212 }
213
214 let src2 = core::ptr::addr_of!(SRC2) as *const u32;
215 let dst2 = core::ptr::addr_of!(DST2) as *const u32;
216 for i in 0..4 {
217 if *src2.add(i) != *dst2.add(i) {
218 all_ok = false;
219 }
220 }
221
222 let src3 = core::ptr::addr_of!(SRC3) as *const u32;
223 let dst3 = core::ptr::addr_of!(DST3) as *const u32;
224 for i in 0..4 {
225 if *src3.add(i) != *dst3.add(i) {
226 all_ok = false;
227 }
228 }
229 }
230
231 if all_ok {
232 tx.blocking_write(b"PASS: All segments verified!\r\n").unwrap();
233 defmt::info!("PASS: All segments verified!");
234 } else {
235 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
236 defmt::error!("FAIL: Mismatch detected!");
237 }
238
239 tx.blocking_write(b"\r\n=== Scatter-Gather Builder example complete ===\r\n").unwrap();
240
241 loop {
242 cortex_m::asm::wfe();
243 }
244}
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 @@
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//! - `dma::edma_tcd()` accessor for simplified register access
8//! - `DmaChannel::is_done()` and `clear_done()` helper methods
9//! - No need to pass register block around
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
28// Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo
29#[repr(align(16))]
30struct AlignedSrc([u32; 4]);
31
32static mut SRC: AlignedSrc = AlignedSrc([0; 4]);
33static mut DST: [u32; 8] = [0; 8];
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 wrap 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 wrap transfer example begin.\r\n\r\n")
111 .unwrap();
112
113 // Initialize buffers
114 unsafe {
115 SRC.0 = [1, 2, 3, 4];
116 DST = [0; 8];
117 }
118
119 tx.blocking_write(b"Source Buffer: ").unwrap();
120 print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4);
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!(DST) as *const u32, 8);
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 wrap transfer using direct TCD access:
137 // SRC is 16 bytes (4 * u32). We want to transfer 32 bytes (8 * u32).
138 // SRC modulo is 16 bytes (2^4 = 16) - wraps source address.
139 // DST modulo is 0 (disabled).
140 // This causes the source address to wrap around after 16 bytes,
141 // effectively repeating the source data.
142 unsafe {
143 let t = edma.tcd(0);
144
145 // Reset channel state
146 t.ch_csr().write(|w| {
147 w.erq().disable()
148 .earq().disable()
149 .eei().no_error()
150 .ebw().disable()
151 .done().clear_bit_by_one()
152 });
153 t.ch_es().write(|w| w.bits(0));
154 t.ch_int().write(|w| w.int().clear_bit_by_one());
155
156 // Source/destination addresses
157 t.tcd_saddr().write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32));
158 t.tcd_daddr().write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32));
159
160 // Offsets: both increment by 4 bytes
161 t.tcd_soff().write(|w| w.soff().bits(4));
162 t.tcd_doff().write(|w| w.doff().bits(4));
163
164 // Attributes: 32-bit transfers (size = 2)
165 // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled)
166 t.tcd_attr().write(|w| {
167 w.ssize().bits(2)
168 .dsize().bits(2)
169 .smod().bits(4) // Source modulo: 2^4 = 16 bytes
170 .dmod().bits(0) // Dest modulo: disabled
171 });
172
173 // Transfer 32 bytes total in one minor loop
174 let nbytes = 32u32;
175 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
176
177 // Source wraps via modulo, no adjustment needed
178 t.tcd_slast_sda().write(|w| w.slast_sda().bits(0));
179 // Reset dest address after major loop
180 t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32));
181
182 // Major loop count = 1
183 t.tcd_biter_elinkno().write(|w| w.biter().bits(1));
184 t.tcd_citer_elinkno().write(|w| w.citer().bits(1));
185
186 // Enable interrupt on major loop completion
187 t.tcd_csr().write(|w| w.intmajor().set_bit());
188
189 cortex_m::asm::dsb();
190
191 tx.blocking_write(b"Triggering transfer...\r\n").unwrap();
192 dma_ch0.trigger_start(edma);
193 }
194
195 // Wait for completion using channel helper method
196 while !dma_ch0.is_done(edma) {
197 cortex_m::asm::nop();
198 }
199 unsafe { dma_ch0.clear_done(edma); }
200
201 tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n")
202 .unwrap();
203 tx.blocking_write(b"Destination Buffer (after): ").unwrap();
204 print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8);
205 tx.blocking_write(b"\r\n\r\n").unwrap();
206
207 // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4]
208 let expected = [1u32, 2, 3, 4, 1, 2, 3, 4];
209 let mut mismatch = false;
210 unsafe {
211 for i in 0..8 {
212 if DST[i] != expected[i] {
213 mismatch = true;
214 break;
215 }
216 }
217 }
218
219 if mismatch {
220 tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap();
221 defmt::error!("FAIL: Mismatch detected!");
222 } else {
223 tx.blocking_write(b"PASS: Data verified.\r\n").unwrap();
224 defmt::info!("PASS: Data verified.");
225 }
226
227 loop {
228 cortex_m::asm::wfe();
229 }
230}
231
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 @@
1//! LPUART DMA example for MCXA276.
2//!
3//! This example demonstrates using DMA for UART TX and RX operations.
4//! It sends a message using DMA, then waits for 16 characters to be received
5//! via DMA and echoes them back.
6
7#![no_std]
8#![no_main]
9
10use embassy_executor::Spawner;
11use embassy_mcxa::clocks::config::Div8;
12use embassy_mcxa::clocks::Gate;
13use embassy_mcxa::dma::{self, DMA_REQ_LPUART2_RX, DMA_REQ_LPUART2_TX};
14use embassy_mcxa::lpuart::{Config, LpuartDma};
15use embassy_mcxa::pac;
16use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
17
18// DMA interrupt handlers
19#[no_mangle]
20pub extern "C" fn DMA_CH0() {
21 unsafe { dma::on_interrupt(0) };
22}
23
24#[no_mangle]
25pub extern "C" fn DMA_CH1() {
26 unsafe { dma::on_interrupt(1) };
27}
28
29#[embassy_executor::main]
30async fn main(_spawner: Spawner) {
31 let mut cfg = hal::config::Config::default();
32 cfg.clock_cfg.sirc.fro_12m_enabled = true;
33 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
34 let p = hal::init(cfg);
35
36 defmt::info!("LPUART DMA example starting...");
37
38 // Enable DMA0 clock and release reset
39 unsafe {
40 hal::peripherals::DMA0::enable_clock();
41 hal::peripherals::DMA0::release_reset();
42 }
43
44 // Get PAC peripherals for DMA init
45 let pac_periphs = unsafe { pac::Peripherals::steal() };
46
47 // Initialize DMA
48 unsafe {
49 dma::init(&pac_periphs);
50 }
51
52 // Get EDMA TCD register block for transfers
53 let edma = &pac_periphs.edma_0_tcd0;
54
55 // Enable DMA interrupts
56 unsafe {
57 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
58 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1);
59 }
60
61 // Create UART configuration
62 let config = Config {
63 baudrate_bps: 115_200,
64 enable_tx: true,
65 enable_rx: true,
66 ..Default::default()
67 };
68
69 // Create UART instance with DMA channels
70 let mut lpuart = LpuartDma::new(
71 p.LPUART2,
72 p.P2_2, // TX pin
73 p.P2_3, // RX pin
74 p.DMA_CH0, // TX DMA channel
75 p.DMA_CH1, // RX DMA channel
76 config,
77 )
78 .unwrap();
79
80 // Send a message using DMA
81 let tx_msg = b"Hello from LPUART2 DMA TX!\r\n";
82 lpuart
83 .write_dma(edma, DMA_REQ_LPUART2_TX, tx_msg)
84 .await
85 .unwrap();
86
87 defmt::info!("TX DMA complete");
88
89 // Send prompt
90 let prompt = b"Type 16 characters to echo via DMA:\r\n";
91 lpuart
92 .write_dma(edma, DMA_REQ_LPUART2_TX, prompt)
93 .await
94 .unwrap();
95
96 // Receive 16 characters using DMA
97 let mut rx_buf = [0u8; 16];
98 lpuart
99 .read_dma(edma, DMA_REQ_LPUART2_RX, &mut rx_buf)
100 .await
101 .unwrap();
102
103 defmt::info!("RX DMA complete");
104
105 // Echo back the received data
106 let echo_prefix = b"\r\nReceived: ";
107 lpuart
108 .write_dma(edma, DMA_REQ_LPUART2_TX, echo_prefix)
109 .await
110 .unwrap();
111 lpuart
112 .write_dma(edma, DMA_REQ_LPUART2_TX, &rx_buf)
113 .await
114 .unwrap();
115 let done_msg = b"\r\nDone!\r\n";
116 lpuart
117 .write_dma(edma, DMA_REQ_LPUART2_TX, done_msg)
118 .await
119 .unwrap();
120
121 defmt::info!("Example complete");
122
123 loop {
124 cortex_m::asm::wfe();
125 }
126}
127
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 @@
1//! LPUART Ring Buffer DMA example for MCXA276.
2//!
3//! This example demonstrates using the new `RingBuffer` API for continuous
4//! circular DMA reception from a UART peripheral.
5//!
6//! # Features demonstrated:
7//! - `setup_circular_read()` for continuous peripheral-to-memory DMA
8//! - `RingBuffer` for async reading of received data
9//! - Handling of potential overrun conditions
10//! - Half-transfer and complete-transfer interrupts for timely wakeups
11//!
12//! # How it works:
13//! 1. Set up a circular DMA transfer from LPUART RX to a ring buffer
14//! 2. DMA continuously writes received bytes into the buffer, wrapping around
15//! 3. Application asynchronously reads data as it arrives
16//! 4. Both half-transfer and complete-transfer interrupts wake the reader
17
18#![no_std]
19#![no_main]
20
21use embassy_executor::Spawner;
22use embassy_mcxa::clocks::config::Div8;
23use embassy_mcxa::clocks::Gate;
24use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX};
25use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
26use embassy_mcxa::{bind_interrupts, pac};
27use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
28
29// Bind DMA channel interrupts
30bind_interrupts!(struct Irqs {
31 DMA_CH0 => DmaCh0InterruptHandler;
32 DMA_CH1 => DmaCh1InterruptHandler;
33});
34
35// Ring buffer for RX - power of 2 is ideal for modulo efficiency
36static mut RX_RING_BUFFER: [u8; 64] = [0; 64];
37
38/// Helper to write a byte as hex to UART
39fn write_hex(tx: &mut LpuartTx<'_, Blocking>, byte: u8) {
40 const HEX: &[u8; 16] = b"0123456789ABCDEF";
41 let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]];
42 tx.blocking_write(&buf).ok();
43}
44
45#[embassy_executor::main]
46async fn main(_spawner: Spawner) {
47 // Small delay to allow probe-rs to attach after reset
48 for _ in 0..100_000 {
49 cortex_m::asm::nop();
50 }
51
52 let mut cfg = hal::config::Config::default();
53 cfg.clock_cfg.sirc.fro_12m_enabled = true;
54 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
55 let p = hal::init(cfg);
56
57 defmt::info!("LPUART Ring Buffer DMA example starting...");
58
59 // Enable DMA0 clock and release reset
60 unsafe {
61 hal::peripherals::DMA0::enable_clock();
62 hal::peripherals::DMA0::release_reset();
63 }
64
65 let pac_periphs = unsafe { pac::Peripherals::steal() };
66
67 // Initialize DMA
68 unsafe {
69 dma::init(&pac_periphs);
70 }
71
72 // Enable DMA interrupts
73 unsafe {
74 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
75 cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1);
76 }
77
78 // Create UART configuration
79 let config = Config {
80 baudrate_bps: 115_200,
81 enable_tx: true,
82 enable_rx: true,
83 ..Default::default()
84 };
85
86 // Create blocking UART for TX (we'll use DMA for RX only)
87 let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
88 let (mut tx, _rx) = lpuart.split();
89
90 tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap();
91 tx.blocking_write(b"==============================\r\n\r\n").unwrap();
92
93 // Get LPUART2 RX data register address for DMA
94 let lpuart2 = unsafe { &*pac::Lpuart2::ptr() };
95 let rx_data_addr = lpuart2.data().as_ptr() as *const u8;
96
97 // Enable RX DMA request in LPUART
98 lpuart2.baud().modify(|_, w| w.rdmae().enabled());
99
100 // Create DMA channel for RX
101 let dma_ch_rx = DmaChannel::new(p.DMA_CH0);
102 let edma = dma::edma_tcd();
103
104 // Configure the DMA mux for LPUART2 RX
105 unsafe {
106 dma_ch_rx.set_request_source(edma, DMA_REQ_LPUART2_RX);
107 }
108
109 tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap();
110
111 // Set up the ring buffer with circular DMA
112 // This configures the DMA for continuous reception
113 let ring_buf = unsafe {
114 let buf = &mut *core::ptr::addr_of_mut!(RX_RING_BUFFER);
115 dma_ch_rx.setup_circular_read(rx_data_addr, buf)
116 };
117
118 // Enable DMA requests to start continuous reception
119 unsafe {
120 dma_ch_rx.enable_request(edma);
121 }
122
123 tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap();
124 tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n").unwrap();
125
126 // Main loop: read from ring buffer and echo back
127 let mut read_buf = [0u8; 16];
128 let mut total_received: usize = 0;
129
130 loop {
131 // Async read - waits until data is available
132 match ring_buf.read(&mut read_buf).await {
133 Ok(n) if n > 0 => {
134 total_received += n;
135
136 // Echo back what we received
137 tx.blocking_write(b"RX[").unwrap();
138 for (i, &byte) in read_buf.iter().enumerate().take(n) {
139 write_hex(&mut tx, byte);
140 if i < n - 1 {
141 tx.blocking_write(b" ").unwrap();
142 }
143 }
144 tx.blocking_write(b"]: ").unwrap();
145 tx.blocking_write(&read_buf[..n]).unwrap();
146 tx.blocking_write(b"\r\n").unwrap();
147
148 defmt::info!("Received {} bytes, total: {}", n, total_received);
149 }
150 Ok(_) => {
151 // No data, shouldn't happen with async read
152 }
153 Err(_) => {
154 // Overrun detected
155 tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap();
156 defmt::error!("Ring buffer overrun!");
157 ring_buf.clear();
158 }
159 }
160 }
161}
162