aboutsummaryrefslogtreecommitdiff
path: root/examples/mcxa/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'examples/mcxa/src/bin')
-rw-r--r--examples/mcxa/src/bin/raw_dma_channel_link.rs1
-rw-r--r--examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs55
-rw-r--r--examples/mcxa/src/bin/raw_dma_scatter_gather.rs115
3 files changed, 61 insertions, 110 deletions
diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs
index 987f1ba43..74785e4f3 100644
--- a/examples/mcxa/src/bin/raw_dma_channel_link.rs
+++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs
@@ -15,7 +15,6 @@
15//! - `DmaChannel::new()` for channel creation 15//! - `DmaChannel::new()` for channel creation
16//! - `DmaChannel::is_done()` and `clear_done()` helper methods 16//! - `DmaChannel::is_done()` and `clear_done()` helper methods
17//! - Channel linking with `set_minor_link()` and `set_major_link()` 17//! - Channel linking with `set_minor_link()` and `set_major_link()`
18//! - Standard `DmaCh*InterruptHandler` with `bind_interrupts!` macro
19 18
20#![no_std] 19#![no_std]
21#![no_main] 20#![no_main]
diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
index 4a64b2498..51a7bc275 100644
--- a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
+++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs
@@ -27,12 +27,10 @@
27#![no_std] 27#![no_std]
28#![no_main] 28#![no_main]
29 29
30use core::sync::atomic::{AtomicBool, Ordering};
31
32use embassy_executor::Spawner; 30use embassy_executor::Spawner;
33use embassy_mcxa::clocks::config::Div8; 31use embassy_mcxa::clocks::config::Div8;
34use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; 32use embassy_mcxa::dma::{DmaChannel, Tcd, TransferOptions};
35use embassy_mcxa::{bind_interrupts, pac}; 33use embassy_mcxa::pac;
36use static_cell::ConstStaticCell; 34use static_cell::ConstStaticCell;
37use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; 35use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
38 36
@@ -64,32 +62,6 @@ static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool(
64 }; 2], 62 }; 2],
65)); 63));
66 64
67// AtomicBool to track scatter/gather completion
68// Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads,
69// so we need this flag to detect when each transfer completes
70static TRANSFER_DONE: AtomicBool = AtomicBool::new(false);
71
72// Custom handler for scatter/gather that delegates to HAL's on_interrupt()
73// This follows the "interrupts as threads" pattern - the handler does minimal work
74// (delegates to HAL + sets a flag) and the main task does the actual processing
75pub struct PingPongDmaHandler;
76
77impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for PingPongDmaHandler {
78 unsafe fn on_interrupt() {
79 // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers
80 dma::on_interrupt(0);
81 // Signal completion for polling (needed because ESG clears DONE bit)
82 TRANSFER_DONE.store(true, Ordering::Release);
83 }
84}
85
86// Bind DMA channel interrupts
87// CH0: Custom handler for scatter/gather (delegates to on_interrupt + sets flag)
88// CH1: Standard handler for wait_half() demo
89bind_interrupts!(struct Irqs {
90 DMA_CH0 => PingPongDmaHandler;
91});
92
93#[embassy_executor::main] 65#[embassy_executor::main]
94async fn main(_spawner: Spawner) { 66async fn main(_spawner: Spawner) {
95 // Small delay to allow probe-rs to attach after reset 67 // Small delay to allow probe-rs to attach after reset
@@ -121,12 +93,12 @@ async fn main(_spawner: Spawner) {
121 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. 93 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
122 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. 94 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1.
123 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. 95 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0.
124 unsafe { 96 let tcds = &mut TCD_POOL.take().0;
125 let tcds = &mut TCD_POOL.take().0;
126 97
127 let half_len = 4usize; 98 let half_len = 4usize;
128 let half_bytes = (half_len * 4) as u32; 99 let half_bytes = (half_len * 4) as u32;
129 100
101 unsafe {
130 let tcd0_addr = &tcds[0] as *const _ as u32; 102 let tcd0_addr = &tcds[0] as *const _ as u32;
131 let tcd1_addr = &tcds[1] as *const _ as u32; 103 let tcd1_addr = &tcds[1] as *const _ as u32;
132 104
@@ -171,11 +143,13 @@ async fn main(_spawner: Spawner) {
171 dma_ch0.trigger_start(); 143 dma_ch0.trigger_start();
172 } 144 }
173 145
146 let tcd = dma_ch0.tcd();
174 // Wait for first half 147 // Wait for first half
175 while !TRANSFER_DONE.load(Ordering::Acquire) { 148 loop {
176 cortex_m::asm::nop(); 149 if tcd.tcd_saddr().read().bits() != src.as_ptr() as u32 {
150 break;
151 }
177 } 152 }
178 TRANSFER_DONE.store(false, Ordering::Release);
179 153
180 defmt::info!("First half transferred."); 154 defmt::info!("First half transferred.");
181 defmt::info!("Triggering second half transfer..."); 155 defmt::info!("Triggering second half transfer...");
@@ -186,10 +160,11 @@ async fn main(_spawner: Spawner) {
186 } 160 }
187 161
188 // Wait for second half 162 // Wait for second half
189 while !TRANSFER_DONE.load(Ordering::Acquire) { 163 loop {
190 cortex_m::asm::nop(); 164 if tcd.tcd_saddr().read().bits() != unsafe { src.as_ptr().add(half_len) } as u32 {
165 break;
166 }
191 } 167 }
192 TRANSFER_DONE.store(false, Ordering::Release);
193 168
194 defmt::info!("Second half transferred."); 169 defmt::info!("Second half transferred.");
195 170
diff --git a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs
index 057e56826..eb9960764 100644
--- a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs
+++ b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs
@@ -16,18 +16,15 @@
16#![no_std] 16#![no_std]
17#![no_main] 17#![no_main]
18 18
19use core::sync::atomic::{AtomicBool, Ordering};
20
21use embassy_executor::Spawner; 19use embassy_executor::Spawner;
22use embassy_mcxa::bind_interrupts;
23use embassy_mcxa::clocks::config::Div8; 20use embassy_mcxa::clocks::config::Div8;
24use embassy_mcxa::dma::{self, DmaChannel, Tcd}; 21use embassy_mcxa::dma::{DmaChannel, Tcd};
25use static_cell::ConstStaticCell; 22use static_cell::ConstStaticCell;
26use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; 23use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
27 24
28// Source and destination buffers 25// Source and destination buffers
29static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); 26static SRC: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
30static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); 27static DST: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([0; 12]);
31 28
32// TCD pool for scatter/gather - must be 32-byte aligned 29// TCD pool for scatter/gather - must be 32-byte aligned
33#[repr(C, align(32))] 30#[repr(C, align(32))]
@@ -49,33 +46,6 @@ static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool(
49 }; 2], 46 }; 2],
50)); 47));
51 48
52// AtomicBool to track scatter/gather completion
53// Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads,
54// so we need this flag to detect when each transfer completes
55static TRANSFER_DONE: AtomicBool = AtomicBool::new(false);
56
57// Custom handler for scatter/gather that delegates to HAL's on_interrupt()
58// This follows the "interrupts as threads" pattern - the handler does minimal work
59// (delegates to HAL + sets a flag) and the main task does the actual processing
60pub struct ScatterGatherDmaHandler;
61
62impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0>
63 for ScatterGatherDmaHandler
64{
65 unsafe fn on_interrupt() {
66 // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers
67 dma::on_interrupt(0);
68 // Signal completion for polling (needed because ESG clears DONE bit)
69 TRANSFER_DONE.store(true, Ordering::Release);
70 }
71}
72
73// Bind DMA channel interrupt
74// Custom handler for scatter/gather (delegates to on_interrupt + sets flag)
75bind_interrupts!(struct Irqs {
76 DMA_CH0 => ScatterGatherDmaHandler;
77});
78
79#[embassy_executor::main] 49#[embassy_executor::main]
80async fn main(_spawner: Spawner) { 50async fn main(_spawner: Spawner) {
81 // Small delay to allow probe-rs to attach after reset 51 // Small delay to allow probe-rs to attach after reset
@@ -101,43 +71,46 @@ async fn main(_spawner: Spawner) {
101 defmt::info!("Configuring scatter-gather DMA with Embassy-style API..."); 71 defmt::info!("Configuring scatter-gather DMA with Embassy-style API...");
102 72
103 let dma_ch0 = DmaChannel::new(p.DMA_CH0); 73 let dma_ch0 = DmaChannel::new(p.DMA_CH0);
74 let src_ptr = src.as_ptr();
75 let dst_ptr = dst.as_mut_ptr();
104 76
105 // Configure scatter-gather transfer using direct TCD access: 77 // Configure scatter-gather transfer using direct TCD access:
106 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. 78 // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel.
107 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. 79 // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1.
108 // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. 80 // TCD1 transfers second half (SRC[4..12] -> DST[4..12]), last TCD.
109 unsafe { 81 unsafe {
110 let tcds = &mut TCD_POOL.take().0; 82 let tcds = &mut TCD_POOL.take().0;
111 let src_ptr = src.as_ptr(); 83
112 let dst_ptr = dst.as_mut_ptr(); 84 // In the first transfer, copy
113 85 tcds[0] = Tcd {
114 let num_tcds = 2usize; 86 saddr: src_ptr as u32,
115 let chunk_len = 4usize; // 8 / 2 87 soff: 4,
116 let chunk_bytes = (chunk_len * 4) as u32; 88 attr: 0x0202, // 32-bit src/dst
117 89 nbytes: 4 * 4,
118 for i in 0..num_tcds { 90 slast: 0,
119 let is_last = i == num_tcds - 1; 91 daddr: dst_ptr as u32,
120 let next_tcd_addr = if is_last { 92 doff: 4,
121 0 // No next TCD 93 citer: 1,
122 } else { 94 dlast_sga: tcds.as_ptr().add(1) as i32,
123 &tcds[i + 1] as *const _ as u32 95 // ESG (scatter/gather) for non-last, INTMAJOR for all
124 }; 96 csr: 0x0012,
125 97 biter: 1,
126 tcds[i] = Tcd { 98 };
127 saddr: src_ptr.add(i * chunk_len) as u32, 99
128 soff: 4, 100 tcds[1] = Tcd {
129 attr: 0x0202, // 32-bit src/dst 101 saddr: src_ptr.add(4) as u32,
130 nbytes: chunk_bytes, 102 soff: 4,
131 slast: 0, 103 attr: 0x0202, // 32-bit src/dst
132 daddr: dst_ptr.add(i * chunk_len) as u32, 104 nbytes: 8 * 4,
133 doff: 4, 105 slast: 0,
134 citer: 1, 106 daddr: dst_ptr.add(4) as u32,
135 dlast_sga: next_tcd_addr as i32, 107 doff: 4,
136 // ESG (scatter/gather) for non-last, INTMAJOR for all 108 citer: 1,
137 csr: if is_last { 0x0002 } else { 0x0012 }, 109 dlast_sga: 0,
138 biter: 1, 110 // ESG (scatter/gather) for non-last, INTMAJOR for all
139 }; 111 csr: 0x0002,
140 } 112 biter: 1,
113 };
141 114
142 // Load TCD0 into hardware registers 115 // Load TCD0 into hardware registers
143 dma_ch0.load_tcd(&tcds[0]); 116 dma_ch0.load_tcd(&tcds[0]);
@@ -145,6 +118,8 @@ async fn main(_spawner: Spawner) {
145 118
146 defmt::info!("Triggering first half transfer..."); 119 defmt::info!("Triggering first half transfer...");
147 120
121 let tcd = dma_ch0.tcd();
122
148 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) 123 // Trigger first transfer (first half: SRC[0..4] -> DST[0..4])
149 // TCD0 is currently loaded. 124 // TCD0 is currently loaded.
150 unsafe { 125 unsafe {
@@ -152,10 +127,13 @@ async fn main(_spawner: Spawner) {
152 } 127 }
153 128
154 // Wait for first half 129 // Wait for first half
155 while !TRANSFER_DONE.load(Ordering::Acquire) { 130 loop {
156 cortex_m::asm::nop(); 131 if tcd.tcd_saddr().read().bits() != src_ptr as u32 {
132 defmt::info!("saddr: {=u32}", tcd.tcd_saddr().read().bits());
133 defmt::info!("srptr: {=u32}", src_ptr as u32);
134 break;
135 }
157 } 136 }
158 TRANSFER_DONE.store(false, Ordering::Release);
159 137
160 defmt::info!("First half transferred."); 138 defmt::info!("First half transferred.");
161 defmt::info!("Triggering second half transfer..."); 139 defmt::info!("Triggering second half transfer...");
@@ -167,10 +145,9 @@ async fn main(_spawner: Spawner) {
167 } 145 }
168 146
169 // Wait for second half 147 // Wait for second half
170 while !TRANSFER_DONE.load(Ordering::Acquire) { 148 while !dma_ch0.is_done() {
171 cortex_m::asm::nop(); 149 cortex_m::asm::nop();
172 } 150 }
173 TRANSFER_DONE.store(false, Ordering::Release);
174 151
175 defmt::info!("Second half transferred."); 152 defmt::info!("Second half transferred.");
176 153