aboutsummaryrefslogtreecommitdiff
path: root/examples/mcxa/src/bin/raw_dma_channel_link.rs
diff options
context:
space:
mode:
authorRaul Alimbekov <[email protected]>2025-12-16 09:05:22 +0300
committerGitHub <[email protected]>2025-12-16 09:05:22 +0300
commitc9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch)
tree6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /examples/mcxa/src/bin/raw_dma_channel_link.rs
parentcde24a3ef1117653ba5ed4184102b33f745782fb (diff)
parent5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff)
Merge branch 'main' into main
Diffstat (limited to 'examples/mcxa/src/bin/raw_dma_channel_link.rs')
-rw-r--r--examples/mcxa/src/bin/raw_dma_channel_link.rs278
1 files changed, 278 insertions, 0 deletions
diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs
new file mode 100644
index 000000000..74785e4f3
--- /dev/null
+++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs
@@ -0,0 +1,278 @@
1//! DMA channel linking example for MCXA276.
2//!
3//! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have
4//! a high-level and safe API for. It should not be taken as typical, recommended, or
5//! stable usage!
6//!
7//! This example demonstrates DMA channel linking (minor and major loop linking):
8//! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with:
9//! - Minor Link to Channel 1 (triggers CH1 after each minor loop)
10//! - Major Link to Channel 2 (triggers CH2 after major loop completes)
11//! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link)
12//! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link)
13//!
14//! # Embassy-style features demonstrated:
15//! - `DmaChannel::new()` for channel creation
16//! - `DmaChannel::is_done()` and `clear_done()` helper methods
17//! - Channel linking with `set_minor_link()` and `set_major_link()`
18
19#![no_std]
20#![no_main]
21
22use embassy_executor::Spawner;
23use embassy_mcxa::clocks::config::Div8;
24use embassy_mcxa::dma::DmaChannel;
25use embassy_mcxa::pac;
26use static_cell::ConstStaticCell;
27use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
28
29// Buffers
30static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]);
31static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
32static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
33static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 // Small delay to allow probe-rs to attach after reset
38 for _ in 0..100_000 {
39 cortex_m::asm::nop();
40 }
41
42 let mut cfg = hal::config::Config::default();
43 cfg.clock_cfg.sirc.fro_12m_enabled = true;
44 cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
45 let p = hal::init(cfg);
46
47 defmt::info!("DMA channel link example starting...");
48
49 // DMA is initialized during hal::init() - no need to call ensure_init()
50
51 let pac_periphs = unsafe { pac::Peripherals::steal() };
52 let dma0 = &pac_periphs.dma0;
53 let edma = unsafe { &*pac::Edma0Tcd0::ptr() };
54
55 // Clear any residual state
56 for i in 0..3 {
57 let t = edma.tcd(i);
58 t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one());
59 t.ch_int().write(|w| w.int().clear_bit_by_one());
60 t.ch_es().write(|w| w.err().clear_bit_by_one());
61 t.ch_mux().write(|w| unsafe { w.bits(0) });
62 }
63
64 // Clear Global Halt/Error state
65 dma0.mp_csr().modify(|_, w| {
66 w.halt()
67 .normal_operation()
68 .hae()
69 .normal_operation()
70 .ecx()
71 .normal_operation()
72 .cx()
73 .normal_operation()
74 });
75
76 defmt::info!("EDMA channel link example begin.");
77
78 // Initialize buffers
79 let src = SRC_BUFFER.take();
80 let dst0 = DEST_BUFFER0.take();
81 let dst1 = DEST_BUFFER1.take();
82 let dst2 = DEST_BUFFER2.take();
83
84 defmt::info!("Source Buffer: {=[?]}", src.as_slice());
85 defmt::info!("DEST0 (before): {=[?]}", dst0.as_slice());
86 defmt::info!("DEST1 (before): {=[?]}", dst1.as_slice());
87 defmt::info!("DEST2 (before): {=[?]}", dst2.as_slice());
88
89 defmt::info!("Configuring DMA channels with Embassy-style API...");
90
91 let ch0 = DmaChannel::new(p.DMA_CH0);
92 let ch1 = DmaChannel::new(p.DMA_CH1);
93 let ch2 = DmaChannel::new(p.DMA_CH2);
94
95 // Configure channels using direct TCD access (advanced feature demo)
96 // This example demonstrates channel linking which requires direct TCD manipulation
97
98 // Helper to configure TCD for memory-to-memory transfer
99 // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt
100 #[allow(clippy::too_many_arguments)]
101 unsafe fn configure_tcd(
102 edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock,
103 ch: usize,
104 src: u32,
105 dst: u32,
106 width: u8,
107 nbytes: u32,
108 count: u16,
109 enable_int: bool,
110 ) {
111 let t = edma.tcd(ch);
112
113 // Reset channel state
114 t.ch_csr().write(|w| {
115 w.erq()
116 .disable()
117 .earq()
118 .disable()
119 .eei()
120 .no_error()
121 .ebw()
122 .disable()
123 .done()
124 .clear_bit_by_one()
125 });
126 t.ch_es().write(|w| w.bits(0));
127 t.ch_int().write(|w| w.int().clear_bit_by_one());
128
129 // Source/destination addresses
130 t.tcd_saddr().write(|w| w.saddr().bits(src));
131 t.tcd_daddr().write(|w| w.daddr().bits(dst));
132
133 // Offsets: increment by width
134 t.tcd_soff().write(|w| w.soff().bits(width as u16));
135 t.tcd_doff().write(|w| w.doff().bits(width as u16));
136
137 // Attributes: size = log2(width)
138 let size = match width {
139 1 => 0,
140 2 => 1,
141 4 => 2,
142 _ => 0,
143 };
144 t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size));
145
146 // Number of bytes per minor loop
147 t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes));
148
149 // Major loop: reset source address after major loop
150 let total_bytes = nbytes * count as u32;
151 t.tcd_slast_sda()
152 .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32));
153 t.tcd_dlast_sga()
154 .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32));
155
156 // Major loop count
157 t.tcd_biter_elinkno().write(|w| w.biter().bits(count));
158 t.tcd_citer_elinkno().write(|w| w.citer().bits(count));
159
160 // Control/status: enable interrupt if requested
161 if enable_int {
162 t.tcd_csr().write(|w| w.intmajor().set_bit());
163 } else {
164 t.tcd_csr().write(|w| w.intmajor().clear_bit());
165 }
166
167 cortex_m::asm::dsb();
168 }
169
170 unsafe {
171 // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations)
172 // Minor Link -> Channel 1
173 // Major Link -> Channel 2
174 configure_tcd(
175 edma,
176 0,
177 src.as_ptr() as u32,
178 dst0.as_mut_ptr() as u32,
179 4, // src width
180 8, // nbytes (minor loop = 2 words)
181 2, // count (major loop = 2 iterations)
182 false, // no interrupt
183 );
184 ch0.set_minor_link(1); // Link to CH1 after each minor loop
185 ch0.set_major_link(2); // Link to CH2 after major loop
186
187 // Channel 1: Transfer 16 bytes (triggered by CH0 minor link)
188 configure_tcd(
189 edma,
190 1,
191 src.as_ptr() as u32,
192 dst1.as_mut_ptr() as u32,
193 4,
194 16, // full buffer in one minor loop
195 1, // 1 major iteration
196 false,
197 );
198
199 // Channel 2: Transfer 16 bytes (triggered by CH0 major link)
200 configure_tcd(
201 edma,
202 2,
203 src.as_ptr() as u32,
204 dst2.as_mut_ptr() as u32,
205 4,
206 16, // full buffer in one minor loop
207 1, // 1 major iteration
208 true, // enable interrupt
209 );
210 }
211
212 defmt::info!("Triggering Channel 0 (1st minor loop)...");
213
214 // Trigger first minor loop of CH0
215 unsafe {
216 ch0.trigger_start();
217 }
218
219 // Wait for CH1 to complete (triggered by CH0 minor link)
220 while !ch1.is_done() {
221 cortex_m::asm::nop();
222 }
223 unsafe {
224 ch1.clear_done();
225 }
226
227 defmt::info!("CH1 done (via minor link).");
228 defmt::info!("Triggering Channel 0 (2nd minor loop)...");
229
230 // Trigger second minor loop of CH0
231 unsafe {
232 ch0.trigger_start();
233 }
234
235 // Wait for CH0 major loop to complete
236 while !ch0.is_done() {
237 cortex_m::asm::nop();
238 }
239 unsafe {
240 ch0.clear_done();
241 }
242
243 defmt::info!("CH0 major loop done.");
244
245 // Wait for CH2 to complete (triggered by CH0 major link)
246 // Using is_done() instead of AtomicBool - the standard interrupt handler
247 // clears the interrupt flag and wakes wakers, but DONE bit remains set
248 while !ch2.is_done() {
249 cortex_m::asm::nop();
250 }
251 unsafe {
252 ch2.clear_done();
253 }
254
255 defmt::info!("CH2 done (via major link).");
256
257 defmt::info!("EDMA channel link example finish.");
258
259 defmt::info!("DEST0 (after): {=[?]}", dst0.as_slice());
260 defmt::info!("DEST1 (after): {=[?]}", dst1.as_slice());
261 defmt::info!("DEST2 (after): {=[?]}", dst2.as_slice());
262
263 // Verify all buffers match source
264 let mut success = true;
265 for sli in [dst0, dst1, dst2] {
266 success &= sli == src;
267 }
268
269 if success {
270 defmt::info!("PASS: Data verified.");
271 } else {
272 defmt::error!("FAIL: Mismatch detected!");
273 }
274
275 loop {
276 cortex_m::asm::wfe();
277 }
278}