diff options
| author | James Munns <[email protected]> | 2025-12-09 15:52:12 +0100 |
|---|---|---|
| committer | James Munns <[email protected]> | 2025-12-09 15:52:12 +0100 |
| commit | e962f5568a9f6433dcd6ad3e41d3faabb8b7c552 (patch) | |
| tree | 0bcf67cbd0d53039580042b856e1b9dd58a528aa /examples/mcxa | |
| parent | 4386b39e2516c453966d894b4fd265fae9d82c1a (diff) | |
Clean up remaining examples, move some to "raw" examples
Diffstat (limited to 'examples/mcxa')
| -rw-r--r-- | examples/mcxa/src/bin/dma_scatter_gather_builder.rs | 6 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_channel_link.rs (renamed from examples/mcxa/src/bin/dma_channel_link.rs) | 124 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_interleave_transfer.rs (renamed from examples/mcxa/src/bin/dma_interleave_transfer.rs) | 50 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_memset.rs (renamed from examples/mcxa/src/bin/dma_memset.rs) | 4 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs (renamed from examples/mcxa/src/bin/dma_ping_pong_transfer.rs) | 176 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_scatter_gather.rs (renamed from examples/mcxa/src/bin/dma_scatter_gather.rs) | 98 |
6 files changed, 127 insertions, 331 deletions
diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs index 1691129f6..30ce20c96 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs | |||
| @@ -112,11 +112,7 @@ async fn main(_spawner: Spawner) { | |||
| 112 | defmt::info!(" DST2: {=[?]}", dst2.as_slice()); | 112 | defmt::info!(" DST2: {=[?]}", dst2.as_slice()); |
| 113 | defmt::info!(" DST3: {=[?]}", dst3.as_slice()); | 113 | defmt::info!(" DST3: {=[?]}", dst3.as_slice()); |
| 114 | 114 | ||
| 115 | let comps = [ | 115 | let comps = [(src1, dst1), (src2, dst2), (src3, dst3)]; |
| 116 | (src1, dst1), | ||
| 117 | (src2, dst2), | ||
| 118 | (src3, dst3), | ||
| 119 | ]; | ||
| 120 | 116 | ||
| 121 | // Verify all three segments | 117 | // Verify all three segments |
| 122 | let mut all_ok = true; | 118 | let mut all_ok = true; |
diff --git a/examples/mcxa/src/bin/dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs index 2d757a636..987f1ba43 100644 --- a/examples/mcxa/src/bin/dma_channel_link.rs +++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | //! DMA channel linking example for MCXA276. | 1 | //! DMA channel linking example for MCXA276. |
| 2 | //! | 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 | //! | ||
| 3 | //! This example demonstrates DMA channel linking (minor and major loop linking): | 7 | //! This example demonstrates DMA channel linking (minor and major loop linking): |
| 4 | //! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: | 8 | //! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: |
| 5 | //! - Minor Link to Channel 1 (triggers CH1 after each minor loop) | 9 | //! - Minor Link to Channel 1 (triggers CH1 after each minor loop) |
| @@ -16,25 +20,18 @@ | |||
| 16 | #![no_std] | 20 | #![no_std] |
| 17 | #![no_main] | 21 | #![no_main] |
| 18 | 22 | ||
| 19 | use core::fmt::Write as _; | ||
| 20 | |||
| 21 | use embassy_executor::Spawner; | 23 | use embassy_executor::Spawner; |
| 22 | use embassy_mcxa::clocks::config::Div8; | 24 | use embassy_mcxa::clocks::config::Div8; |
| 23 | use embassy_mcxa::dma::DmaChannel; | 25 | use embassy_mcxa::dma::DmaChannel; |
| 24 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 25 | use embassy_mcxa::pac; | 26 | use embassy_mcxa::pac; |
| 27 | use static_cell::ConstStaticCell; | ||
| 26 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 28 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 27 | 29 | ||
| 28 | // Buffers | 30 | // Buffers |
| 29 | static mut SRC_BUFFER: [u32; 4] = [1, 2, 3, 4]; | 31 | static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]); |
| 30 | static mut DEST_BUFFER0: [u32; 4] = [0; 4]; | 32 | static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); |
| 31 | static mut DEST_BUFFER1: [u32; 4] = [0; 4]; | 33 | static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); |
| 32 | static mut DEST_BUFFER2: [u32; 4] = [0; 4]; | 34 | static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); |
| 33 | |||
| 34 | /// Helper to print a buffer to UART | ||
| 35 | fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { | ||
| 36 | write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); | ||
| 37 | } | ||
| 38 | 35 | ||
| 39 | #[embassy_executor::main] | 36 | #[embassy_executor::main] |
| 40 | async fn main(_spawner: Spawner) { | 37 | async fn main(_spawner: Spawner) { |
| @@ -77,42 +74,20 @@ async fn main(_spawner: Spawner) { | |||
| 77 | .normal_operation() | 74 | .normal_operation() |
| 78 | }); | 75 | }); |
| 79 | 76 | ||
| 80 | let config = Config { | 77 | defmt::info!("EDMA channel link example begin."); |
| 81 | baudrate_bps: 115_200, | ||
| 82 | ..Default::default() | ||
| 83 | }; | ||
| 84 | |||
| 85 | let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); | ||
| 86 | let (mut tx, _rx) = lpuart.split(); | ||
| 87 | |||
| 88 | tx.blocking_write(b"EDMA channel link example begin.\r\n\r\n").unwrap(); | ||
| 89 | 78 | ||
| 90 | // Initialize buffers | 79 | // Initialize buffers |
| 91 | unsafe { | 80 | let src = SRC_BUFFER.take(); |
| 92 | SRC_BUFFER = [1, 2, 3, 4]; | 81 | let dst0 = DEST_BUFFER0.take(); |
| 93 | DEST_BUFFER0 = [0; 4]; | 82 | let dst1 = DEST_BUFFER1.take(); |
| 94 | DEST_BUFFER1 = [0; 4]; | 83 | let dst2 = DEST_BUFFER2.take(); |
| 95 | DEST_BUFFER2 = [0; 4]; | ||
| 96 | } | ||
| 97 | 84 | ||
| 98 | tx.blocking_write(b"Source Buffer: ").unwrap(); | 85 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); |
| 99 | print_buffer(&mut tx, core::ptr::addr_of!(SRC_BUFFER) as *const u32, 4); | 86 | defmt::info!("DEST0 (before): {=[?]}", dst0.as_slice()); |
| 100 | tx.blocking_write(b"\r\n").unwrap(); | 87 | defmt::info!("DEST1 (before): {=[?]}", dst1.as_slice()); |
| 88 | defmt::info!("DEST2 (before): {=[?]}", dst2.as_slice()); | ||
| 101 | 89 | ||
| 102 | tx.blocking_write(b"DEST0 (before): ").unwrap(); | 90 | defmt::info!("Configuring DMA channels with Embassy-style API..."); |
| 103 | print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); | ||
| 104 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 105 | |||
| 106 | tx.blocking_write(b"DEST1 (before): ").unwrap(); | ||
| 107 | print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); | ||
| 108 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 109 | |||
| 110 | tx.blocking_write(b"DEST2 (before): ").unwrap(); | ||
| 111 | print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); | ||
| 112 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 113 | |||
| 114 | tx.blocking_write(b"Configuring DMA channels with Embassy-style API...\r\n") | ||
| 115 | .unwrap(); | ||
| 116 | 91 | ||
| 117 | let ch0 = DmaChannel::new(p.DMA_CH0); | 92 | let ch0 = DmaChannel::new(p.DMA_CH0); |
| 118 | let ch1 = DmaChannel::new(p.DMA_CH1); | 93 | let ch1 = DmaChannel::new(p.DMA_CH1); |
| @@ -200,8 +175,8 @@ async fn main(_spawner: Spawner) { | |||
| 200 | configure_tcd( | 175 | configure_tcd( |
| 201 | edma, | 176 | edma, |
| 202 | 0, | 177 | 0, |
| 203 | core::ptr::addr_of!(SRC_BUFFER) as u32, | 178 | src.as_ptr() as u32, |
| 204 | core::ptr::addr_of_mut!(DEST_BUFFER0) as u32, | 179 | dst0.as_mut_ptr() as u32, |
| 205 | 4, // src width | 180 | 4, // src width |
| 206 | 8, // nbytes (minor loop = 2 words) | 181 | 8, // nbytes (minor loop = 2 words) |
| 207 | 2, // count (major loop = 2 iterations) | 182 | 2, // count (major loop = 2 iterations) |
| @@ -214,8 +189,8 @@ async fn main(_spawner: Spawner) { | |||
| 214 | configure_tcd( | 189 | configure_tcd( |
| 215 | edma, | 190 | edma, |
| 216 | 1, | 191 | 1, |
| 217 | core::ptr::addr_of!(SRC_BUFFER) as u32, | 192 | src.as_ptr() as u32, |
| 218 | core::ptr::addr_of_mut!(DEST_BUFFER1) as u32, | 193 | dst1.as_mut_ptr() as u32, |
| 219 | 4, | 194 | 4, |
| 220 | 16, // full buffer in one minor loop | 195 | 16, // full buffer in one minor loop |
| 221 | 1, // 1 major iteration | 196 | 1, // 1 major iteration |
| @@ -226,8 +201,8 @@ async fn main(_spawner: Spawner) { | |||
| 226 | configure_tcd( | 201 | configure_tcd( |
| 227 | edma, | 202 | edma, |
| 228 | 2, | 203 | 2, |
| 229 | core::ptr::addr_of!(SRC_BUFFER) as u32, | 204 | src.as_ptr() as u32, |
| 230 | core::ptr::addr_of_mut!(DEST_BUFFER2) as u32, | 205 | dst2.as_mut_ptr() as u32, |
| 231 | 4, | 206 | 4, |
| 232 | 16, // full buffer in one minor loop | 207 | 16, // full buffer in one minor loop |
| 233 | 1, // 1 major iteration | 208 | 1, // 1 major iteration |
| @@ -235,8 +210,7 @@ async fn main(_spawner: Spawner) { | |||
| 235 | ); | 210 | ); |
| 236 | } | 211 | } |
| 237 | 212 | ||
| 238 | tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n") | 213 | defmt::info!("Triggering Channel 0 (1st minor loop)..."); |
| 239 | .unwrap(); | ||
| 240 | 214 | ||
| 241 | // Trigger first minor loop of CH0 | 215 | // Trigger first minor loop of CH0 |
| 242 | unsafe { | 216 | unsafe { |
| @@ -251,9 +225,8 @@ async fn main(_spawner: Spawner) { | |||
| 251 | ch1.clear_done(); | 225 | ch1.clear_done(); |
| 252 | } | 226 | } |
| 253 | 227 | ||
| 254 | tx.blocking_write(b"CH1 done (via minor link).\r\n").unwrap(); | 228 | defmt::info!("CH1 done (via minor link)."); |
| 255 | tx.blocking_write(b"Triggering Channel 0 (2nd minor loop)...\r\n") | 229 | defmt::info!("Triggering Channel 0 (2nd minor loop)..."); |
| 256 | .unwrap(); | ||
| 257 | 230 | ||
| 258 | // Trigger second minor loop of CH0 | 231 | // Trigger second minor loop of CH0 |
| 259 | unsafe { | 232 | unsafe { |
| @@ -268,7 +241,7 @@ async fn main(_spawner: Spawner) { | |||
| 268 | ch0.clear_done(); | 241 | ch0.clear_done(); |
| 269 | } | 242 | } |
| 270 | 243 | ||
| 271 | tx.blocking_write(b"CH0 major loop done.\r\n").unwrap(); | 244 | defmt::info!("CH0 major loop done."); |
| 272 | 245 | ||
| 273 | // Wait for CH2 to complete (triggered by CH0 major link) | 246 | // Wait for CH2 to complete (triggered by CH0 major link) |
| 274 | // Using is_done() instead of AtomicBool - the standard interrupt handler | 247 | // Using is_done() instead of AtomicBool - the standard interrupt handler |
| @@ -280,48 +253,23 @@ async fn main(_spawner: Spawner) { | |||
| 280 | ch2.clear_done(); | 253 | ch2.clear_done(); |
| 281 | } | 254 | } |
| 282 | 255 | ||
| 283 | tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap(); | 256 | defmt::info!("CH2 done (via major link)."); |
| 284 | |||
| 285 | tx.blocking_write(b"EDMA channel link example finish.\r\n\r\n").unwrap(); | ||
| 286 | 257 | ||
| 287 | tx.blocking_write(b"DEST0 (after): ").unwrap(); | 258 | defmt::info!("EDMA channel link example finish."); |
| 288 | print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER0) as *const u32, 4); | ||
| 289 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 290 | 259 | ||
| 291 | tx.blocking_write(b"DEST1 (after): ").unwrap(); | 260 | defmt::info!("DEST0 (after): {=[?]}", dst0.as_slice()); |
| 292 | print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER1) as *const u32, 4); | 261 | defmt::info!("DEST1 (after): {=[?]}", dst1.as_slice()); |
| 293 | tx.blocking_write(b"\r\n").unwrap(); | 262 | defmt::info!("DEST2 (after): {=[?]}", dst2.as_slice()); |
| 294 | |||
| 295 | tx.blocking_write(b"DEST2 (after): ").unwrap(); | ||
| 296 | print_buffer(&mut tx, core::ptr::addr_of!(DEST_BUFFER2) as *const u32, 4); | ||
| 297 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 298 | 263 | ||
| 299 | // Verify all buffers match source | 264 | // Verify all buffers match source |
| 300 | let mut success = true; | 265 | let mut success = true; |
| 301 | unsafe { | 266 | for sli in [dst0, dst1, dst2] { |
| 302 | let src_ptr = core::ptr::addr_of!(SRC_BUFFER) as *const u32; | 267 | success &= sli == src; |
| 303 | let dst0_ptr = core::ptr::addr_of!(DEST_BUFFER0) as *const u32; | ||
| 304 | let dst1_ptr = core::ptr::addr_of!(DEST_BUFFER1) as *const u32; | ||
| 305 | let dst2_ptr = core::ptr::addr_of!(DEST_BUFFER2) as *const u32; | ||
| 306 | |||
| 307 | for i in 0..4 { | ||
| 308 | if *dst0_ptr.add(i) != *src_ptr.add(i) { | ||
| 309 | success = false; | ||
| 310 | } | ||
| 311 | if *dst1_ptr.add(i) != *src_ptr.add(i) { | ||
| 312 | success = false; | ||
| 313 | } | ||
| 314 | if *dst2_ptr.add(i) != *src_ptr.add(i) { | ||
| 315 | success = false; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | 268 | } |
| 319 | 269 | ||
| 320 | if success { | 270 | if success { |
| 321 | tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); | ||
| 322 | defmt::info!("PASS: Data verified."); | 271 | defmt::info!("PASS: Data verified."); |
| 323 | } else { | 272 | } else { |
| 324 | tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); | ||
| 325 | defmt::error!("FAIL: Mismatch detected!"); | 273 | defmt::error!("FAIL: Mismatch detected!"); |
| 326 | } | 274 | } |
| 327 | 275 | ||
diff --git a/examples/mcxa/src/bin/dma_interleave_transfer.rs b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs index 03441fc32..a383b6cf4 100644 --- a/examples/mcxa/src/bin/dma_interleave_transfer.rs +++ b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | //! DMA interleaved transfer example for MCXA276. | 1 | //! DMA interleaved transfer example for MCXA276. |
| 2 | //! | 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 | //! | ||
| 3 | //! This example demonstrates using DMA with custom source/destination offsets | 7 | //! This example demonstrates using DMA with custom source/destination offsets |
| 4 | //! to interleave data during transfer. | 8 | //! to interleave data during transfer. |
| 5 | //! | 9 | //! |
| @@ -10,12 +14,9 @@ | |||
| 10 | #![no_std] | 14 | #![no_std] |
| 11 | #![no_main] | 15 | #![no_main] |
| 12 | 16 | ||
| 13 | use core::fmt::Write as _; | ||
| 14 | |||
| 15 | use embassy_executor::Spawner; | 17 | use embassy_executor::Spawner; |
| 16 | use embassy_mcxa::clocks::config::Div8; | 18 | use embassy_mcxa::clocks::config::Div8; |
| 17 | use embassy_mcxa::dma::DmaChannel; | 19 | use embassy_mcxa::dma::DmaChannel; |
| 18 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 19 | use static_cell::ConstStaticCell; | 20 | use static_cell::ConstStaticCell; |
| 20 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 21 | 22 | ||
| @@ -26,11 +27,6 @@ const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; | |||
| 26 | static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); | 27 | static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); |
| 27 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | 28 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); |
| 28 | 29 | ||
| 29 | /// Helper to print a buffer to UART | ||
| 30 | fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf: &[u32]) { | ||
| 31 | write!(tx, "{:?}", buf).ok(); | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::main] | 30 | #[embassy_executor::main] |
| 35 | async fn main(_spawner: Spawner) { | 31 | async fn main(_spawner: Spawner) { |
| 36 | // Small delay to allow probe-rs to attach after reset | 32 | // Small delay to allow probe-rs to attach after reset |
| @@ -45,32 +41,17 @@ async fn main(_spawner: Spawner) { | |||
| 45 | 41 | ||
| 46 | defmt::info!("DMA interleave transfer example starting..."); | 42 | defmt::info!("DMA interleave transfer example starting..."); |
| 47 | 43 | ||
| 48 | let config = Config { | 44 | defmt::info!("EDMA interleave transfer example begin."); |
| 49 | baudrate_bps: 115_200, | ||
| 50 | ..Default::default() | ||
| 51 | }; | ||
| 52 | |||
| 53 | let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); | ||
| 54 | let (mut tx, _rx) = lpuart.split(); | ||
| 55 | |||
| 56 | tx.blocking_write(b"EDMA interleave transfer example begin.\r\n\r\n") | ||
| 57 | .unwrap(); | ||
| 58 | 45 | ||
| 59 | // Initialize buffers | 46 | // Initialize buffers |
| 60 | let src = SRC_BUFFER.take(); | 47 | let src = SRC_BUFFER.take(); |
| 61 | *src = [1, 2, 3, 4, 5, 6, 7, 8]; | 48 | *src = [1, 2, 3, 4, 5, 6, 7, 8]; |
| 62 | let dst = DEST_BUFFER.take(); | 49 | let dst = DEST_BUFFER.take(); |
| 63 | 50 | ||
| 64 | tx.blocking_write(b"Source Buffer: ").unwrap(); | 51 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); |
| 65 | print_buffer(&mut tx, src); | 52 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); |
| 66 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 67 | |||
| 68 | tx.blocking_write(b"Destination Buffer (before): ").unwrap(); | ||
| 69 | print_buffer(&mut tx, dst); | ||
| 70 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 71 | 53 | ||
| 72 | tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") | 54 | defmt::info!("Configuring DMA with Embassy-style API..."); |
| 73 | .unwrap(); | ||
| 74 | 55 | ||
| 75 | // Create DMA channel using Embassy-style API | 56 | // Create DMA channel using Embassy-style API |
| 76 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | 57 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); |
| @@ -129,7 +110,7 @@ async fn main(_spawner: Spawner) { | |||
| 129 | 110 | ||
| 130 | cortex_m::asm::dsb(); | 111 | cortex_m::asm::dsb(); |
| 131 | 112 | ||
| 132 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); | 113 | defmt::info!("Triggering transfer..."); |
| 133 | dma_ch0.trigger_start(); | 114 | dma_ch0.trigger_start(); |
| 134 | } | 115 | } |
| 135 | 116 | ||
| @@ -141,11 +122,8 @@ async fn main(_spawner: Spawner) { | |||
| 141 | dma_ch0.clear_done(); | 122 | dma_ch0.clear_done(); |
| 142 | } | 123 | } |
| 143 | 124 | ||
| 144 | tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") | 125 | defmt::info!("EDMA interleave transfer example finish."); |
| 145 | .unwrap(); | 126 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); |
| 146 | tx.blocking_write(b"Destination Buffer (after): ").unwrap(); | ||
| 147 | print_buffer(&mut tx, dst); | ||
| 148 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 149 | 127 | ||
| 150 | // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 | 128 | // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 |
| 151 | let mut mismatch = false; | 129 | let mut mismatch = false; |
| @@ -156,14 +134,8 @@ async fn main(_spawner: Spawner) { | |||
| 156 | } | 134 | } |
| 157 | 135 | ||
| 158 | if mismatch { | 136 | if mismatch { |
| 159 | tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); | ||
| 160 | defmt::error!("FAIL: Mismatch detected!"); | 137 | defmt::error!("FAIL: Mismatch detected!"); |
| 161 | } else { | 138 | } else { |
| 162 | tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); | ||
| 163 | defmt::info!("PASS: Data verified."); | 139 | defmt::info!("PASS: Data verified."); |
| 164 | } | 140 | } |
| 165 | |||
| 166 | loop { | ||
| 167 | cortex_m::asm::wfe(); | ||
| 168 | } | ||
| 169 | } | 141 | } |
diff --git a/examples/mcxa/src/bin/dma_memset.rs b/examples/mcxa/src/bin/raw_dma_memset.rs index d7b03e91b..7b3c06ffa 100644 --- a/examples/mcxa/src/bin/dma_memset.rs +++ b/examples/mcxa/src/bin/raw_dma_memset.rs | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | //! DMA memset example for MCXA276. | 1 | //! DMA memset example for MCXA276. |
| 2 | //! | 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 | //! | ||
| 3 | //! This example demonstrates using DMA to fill a buffer with a repeated pattern. | 7 | //! This example demonstrates using DMA to fill a buffer with a repeated pattern. |
| 4 | //! The source address stays fixed while the destination increments. | 8 | //! The source address stays fixed while the destination increments. |
| 5 | //! | 9 | //! |
diff --git a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs index 58f643b80..4a64b2498 100644 --- a/examples/mcxa/src/bin/dma_ping_pong_transfer.rs +++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | //! DMA ping-pong/double-buffer transfer example for MCXA276. | 1 | //! DMA ping-pong/double-buffer transfer example for MCXA276. |
| 2 | //! | 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 | //! | ||
| 3 | //! This example demonstrates two approaches for ping-pong/double-buffering: | 7 | //! This example demonstrates two approaches for ping-pong/double-buffering: |
| 4 | //! | 8 | //! |
| 5 | //! ## Approach 1: Scatter/Gather with linked TCDs (manual) | 9 | //! ## Approach 1: Scatter/Gather with linked TCDs (manual) |
| @@ -23,29 +27,28 @@ | |||
| 23 | #![no_std] | 27 | #![no_std] |
| 24 | #![no_main] | 28 | #![no_main] |
| 25 | 29 | ||
| 26 | use core::fmt::Write as _; | ||
| 27 | use core::sync::atomic::{AtomicBool, Ordering}; | 30 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 28 | 31 | ||
| 29 | use embassy_executor::Spawner; | 32 | use embassy_executor::Spawner; |
| 30 | use embassy_mcxa::clocks::config::Div8; | 33 | use embassy_mcxa::clocks::config::Div8; |
| 31 | use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; | 34 | use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; |
| 32 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 33 | use embassy_mcxa::{bind_interrupts, pac}; | 35 | use embassy_mcxa::{bind_interrupts, pac}; |
| 36 | use static_cell::ConstStaticCell; | ||
| 34 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 37 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 35 | 38 | ||
| 36 | // Source and destination buffers for Approach 1 (scatter/gather) | 39 | // Source and destination buffers for Approach 1 (scatter/gather) |
| 37 | static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; | 40 | static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); |
| 38 | static mut DST: [u32; 8] = [0; 8]; | 41 | static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); |
| 39 | 42 | ||
| 40 | // Source and destination buffers for Approach 2 (wait_half) | 43 | // Source and destination buffers for Approach 2 (wait_half) |
| 41 | static mut SRC2: [u32; 8] = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; | 44 | static SRC2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]); |
| 42 | static mut DST2: [u32; 8] = [0; 8]; | 45 | static DST2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); |
| 43 | 46 | ||
| 44 | // TCD pool for scatter/gather - must be 32-byte aligned | 47 | // TCD pool for scatter/gather - must be 32-byte aligned |
| 45 | #[repr(C, align(32))] | 48 | #[repr(C, align(32))] |
| 46 | struct TcdPool([Tcd; 2]); | 49 | struct TcdPool([Tcd; 2]); |
| 47 | 50 | ||
| 48 | static mut TCD_POOL: TcdPool = TcdPool( | 51 | static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool( |
| 49 | [Tcd { | 52 | [Tcd { |
| 50 | saddr: 0, | 53 | saddr: 0, |
| 51 | soff: 0, | 54 | soff: 0, |
| @@ -59,7 +62,7 @@ static mut TCD_POOL: TcdPool = TcdPool( | |||
| 59 | csr: 0, | 62 | csr: 0, |
| 60 | biter: 0, | 63 | biter: 0, |
| 61 | }; 2], | 64 | }; 2], |
| 62 | ); | 65 | )); |
| 63 | 66 | ||
| 64 | // AtomicBool to track scatter/gather completion | 67 | // AtomicBool to track scatter/gather completion |
| 65 | // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, | 68 | // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, |
| @@ -87,11 +90,6 @@ bind_interrupts!(struct Irqs { | |||
| 87 | DMA_CH0 => PingPongDmaHandler; | 90 | DMA_CH0 => PingPongDmaHandler; |
| 88 | }); | 91 | }); |
| 89 | 92 | ||
| 90 | /// Helper to print a buffer to UART | ||
| 91 | fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { | ||
| 92 | write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); | ||
| 93 | } | ||
| 94 | |||
| 95 | #[embassy_executor::main] | 93 | #[embassy_executor::main] |
| 96 | async fn main(_spawner: Spawner) { | 94 | async fn main(_spawner: Spawner) { |
| 97 | // Small delay to allow probe-rs to attach after reset | 95 | // Small delay to allow probe-rs to attach after reset |
| @@ -106,38 +104,16 @@ async fn main(_spawner: Spawner) { | |||
| 106 | 104 | ||
| 107 | defmt::info!("DMA ping-pong transfer example starting..."); | 105 | defmt::info!("DMA ping-pong transfer example starting..."); |
| 108 | 106 | ||
| 109 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) | 107 | defmt::info!("EDMA ping-pong transfer example begin."); |
| 110 | unsafe { | ||
| 111 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | ||
| 112 | } | ||
| 113 | |||
| 114 | let config = Config { | ||
| 115 | baudrate_bps: 115_200, | ||
| 116 | ..Default::default() | ||
| 117 | }; | ||
| 118 | |||
| 119 | let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); | ||
| 120 | let (mut tx, _rx) = lpuart.split(); | ||
| 121 | |||
| 122 | tx.blocking_write(b"EDMA ping-pong transfer example begin.\r\n\r\n") | ||
| 123 | .unwrap(); | ||
| 124 | 108 | ||
| 125 | // Initialize buffers | 109 | // Initialize buffers |
| 126 | unsafe { | 110 | let src = SRC.take(); |
| 127 | SRC = [1, 2, 3, 4, 5, 6, 7, 8]; | 111 | let dst = DST.take(); |
| 128 | DST = [0; 8]; | ||
| 129 | } | ||
| 130 | |||
| 131 | tx.blocking_write(b"Source Buffer: ").unwrap(); | ||
| 132 | print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); | ||
| 133 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 134 | 112 | ||
| 135 | tx.blocking_write(b"Destination Buffer (before): ").unwrap(); | 113 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); |
| 136 | print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); | 114 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); |
| 137 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 138 | 115 | ||
| 139 | tx.blocking_write(b"Configuring ping-pong DMA with Embassy-style API...\r\n") | 116 | defmt::info!("Configuring ping-pong DMA with Embassy-style API..."); |
| 140 | .unwrap(); | ||
| 141 | 117 | ||
| 142 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | 118 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); |
| 143 | 119 | ||
| @@ -146,9 +122,7 @@ async fn main(_spawner: Spawner) { | |||
| 146 | // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. | 122 | // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. |
| 147 | // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. | 123 | // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. |
| 148 | unsafe { | 124 | unsafe { |
| 149 | let tcds = &mut *core::ptr::addr_of_mut!(TCD_POOL.0); | 125 | let tcds = &mut TCD_POOL.take().0; |
| 150 | let src_ptr = core::ptr::addr_of!(SRC) as *const u32; | ||
| 151 | let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; | ||
| 152 | 126 | ||
| 153 | let half_len = 4usize; | 127 | let half_len = 4usize; |
| 154 | let half_bytes = (half_len * 4) as u32; | 128 | let half_bytes = (half_len * 4) as u32; |
| @@ -158,12 +132,12 @@ async fn main(_spawner: Spawner) { | |||
| 158 | 132 | ||
| 159 | // TCD0: First half -> Links to TCD1 | 133 | // TCD0: First half -> Links to TCD1 |
| 160 | tcds[0] = Tcd { | 134 | tcds[0] = Tcd { |
| 161 | saddr: src_ptr as u32, | 135 | saddr: src.as_ptr() as u32, |
| 162 | soff: 4, | 136 | soff: 4, |
| 163 | attr: 0x0202, // 32-bit src/dst | 137 | attr: 0x0202, // 32-bit src/dst |
| 164 | nbytes: half_bytes, | 138 | nbytes: half_bytes, |
| 165 | slast: 0, | 139 | slast: 0, |
| 166 | daddr: dst_ptr as u32, | 140 | daddr: dst.as_mut_ptr() as u32, |
| 167 | doff: 4, | 141 | doff: 4, |
| 168 | citer: 1, | 142 | citer: 1, |
| 169 | dlast_sga: tcd1_addr as i32, | 143 | dlast_sga: tcd1_addr as i32, |
| @@ -173,12 +147,12 @@ async fn main(_spawner: Spawner) { | |||
| 173 | 147 | ||
| 174 | // TCD1: Second half -> Links to TCD0 | 148 | // TCD1: Second half -> Links to TCD0 |
| 175 | tcds[1] = Tcd { | 149 | tcds[1] = Tcd { |
| 176 | saddr: src_ptr.add(half_len) as u32, | 150 | saddr: src.as_ptr().add(half_len) as u32, |
| 177 | soff: 4, | 151 | soff: 4, |
| 178 | attr: 0x0202, | 152 | attr: 0x0202, |
| 179 | nbytes: half_bytes, | 153 | nbytes: half_bytes, |
| 180 | slast: 0, | 154 | slast: 0, |
| 181 | daddr: dst_ptr.add(half_len) as u32, | 155 | daddr: dst.as_mut_ptr().add(half_len) as u32, |
| 182 | doff: 4, | 156 | doff: 4, |
| 183 | citer: 1, | 157 | citer: 1, |
| 184 | dlast_sga: tcd0_addr as i32, | 158 | dlast_sga: tcd0_addr as i32, |
| @@ -190,7 +164,7 @@ async fn main(_spawner: Spawner) { | |||
| 190 | dma_ch0.load_tcd(&tcds[0]); | 164 | dma_ch0.load_tcd(&tcds[0]); |
| 191 | } | 165 | } |
| 192 | 166 | ||
| 193 | tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); | 167 | defmt::info!("Triggering first half transfer..."); |
| 194 | 168 | ||
| 195 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) | 169 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) |
| 196 | unsafe { | 170 | unsafe { |
| @@ -203,8 +177,8 @@ async fn main(_spawner: Spawner) { | |||
| 203 | } | 177 | } |
| 204 | TRANSFER_DONE.store(false, Ordering::Release); | 178 | TRANSFER_DONE.store(false, Ordering::Release); |
| 205 | 179 | ||
| 206 | tx.blocking_write(b"First half transferred.\r\n").unwrap(); | 180 | defmt::info!("First half transferred."); |
| 207 | tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); | 181 | defmt::info!("Triggering second half transfer..."); |
| 208 | 182 | ||
| 209 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) | 183 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) |
| 210 | unsafe { | 184 | unsafe { |
| @@ -217,32 +191,17 @@ async fn main(_spawner: Spawner) { | |||
| 217 | } | 191 | } |
| 218 | TRANSFER_DONE.store(false, Ordering::Release); | 192 | TRANSFER_DONE.store(false, Ordering::Release); |
| 219 | 193 | ||
| 220 | tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); | 194 | defmt::info!("Second half transferred."); |
| 221 | 195 | ||
| 222 | tx.blocking_write(b"EDMA ping-pong transfer example finish.\r\n\r\n") | 196 | defmt::info!("EDMA ping-pong transfer example finish."); |
| 223 | .unwrap(); | 197 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); |
| 224 | tx.blocking_write(b"Destination Buffer (after): ").unwrap(); | ||
| 225 | print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); | ||
| 226 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 227 | 198 | ||
| 228 | // Verify: DST should match SRC | 199 | // Verify: DST should match SRC |
| 229 | let mut mismatch = false; | 200 | let mismatch = src != dst; |
| 230 | unsafe { | ||
| 231 | let src_ptr = core::ptr::addr_of!(SRC) as *const u32; | ||
| 232 | let dst_ptr = core::ptr::addr_of!(DST) as *const u32; | ||
| 233 | for i in 0..8 { | ||
| 234 | if *src_ptr.add(i) != *dst_ptr.add(i) { | ||
| 235 | mismatch = true; | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
| 240 | 201 | ||
| 241 | if mismatch { | 202 | if mismatch { |
| 242 | tx.blocking_write(b"FAIL: Approach 1 mismatch detected!\r\n").unwrap(); | ||
| 243 | defmt::error!("FAIL: Approach 1 mismatch detected!"); | 203 | defmt::error!("FAIL: Approach 1 mismatch detected!"); |
| 244 | } else { | 204 | } else { |
| 245 | tx.blocking_write(b"PASS: Approach 1 data verified.\r\n\r\n").unwrap(); | ||
| 246 | defmt::info!("PASS: Approach 1 data verified."); | 205 | defmt::info!("PASS: Approach 1 data verified."); |
| 247 | } | 206 | } |
| 248 | 207 | ||
| @@ -260,8 +219,7 @@ async fn main(_spawner: Spawner) { | |||
| 260 | // - True async/await support | 219 | // - True async/await support |
| 261 | // - Good for streaming data processing | 220 | // - Good for streaming data processing |
| 262 | 221 | ||
| 263 | tx.blocking_write(b"--- Approach 2: wait_half() demo ---\r\n\r\n") | 222 | defmt::info!("--- Approach 2: wait_half() demo ---"); |
| 264 | .unwrap(); | ||
| 265 | 223 | ||
| 266 | // Enable DMA CH1 interrupt | 224 | // Enable DMA CH1 interrupt |
| 267 | unsafe { | 225 | unsafe { |
| @@ -269,14 +227,10 @@ async fn main(_spawner: Spawner) { | |||
| 269 | } | 227 | } |
| 270 | 228 | ||
| 271 | // Initialize approach 2 buffers | 229 | // Initialize approach 2 buffers |
| 272 | unsafe { | 230 | let src2 = SRC2.take(); |
| 273 | SRC2 = [0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]; | 231 | let dst2 = DST2.take(); |
| 274 | DST2 = [0; 8]; | ||
| 275 | } | ||
| 276 | 232 | ||
| 277 | tx.blocking_write(b"SRC2: ").unwrap(); | 233 | defmt::info!("SRC2: {=[?]}", src2.as_slice()); |
| 278 | print_buffer(&mut tx, core::ptr::addr_of!(SRC2) as *const u32, 8); | ||
| 279 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 280 | 234 | ||
| 281 | let dma_ch1 = DmaChannel::new(p.DMA_CH1); | 235 | let dma_ch1 = DmaChannel::new(p.DMA_CH1); |
| 282 | 236 | ||
| @@ -285,63 +239,31 @@ async fn main(_spawner: Spawner) { | |||
| 285 | options.half_transfer_interrupt = true; // Enable half-transfer interrupt | 239 | options.half_transfer_interrupt = true; // Enable half-transfer interrupt |
| 286 | options.complete_transfer_interrupt = true; | 240 | options.complete_transfer_interrupt = true; |
| 287 | 241 | ||
| 288 | tx.blocking_write(b"Starting transfer with half_transfer_interrupt...\r\n") | 242 | defmt::info!("Starting transfer with half_transfer_interrupt..."); |
| 289 | .unwrap(); | ||
| 290 | 243 | ||
| 291 | unsafe { | 244 | // Create the transfer |
| 292 | let src = &*core::ptr::addr_of!(SRC2); | 245 | let mut transfer = dma_ch1.mem_to_mem(src2, dst2, options); |
| 293 | let dst = &mut *core::ptr::addr_of_mut!(DST2); | 246 | |
| 294 | 247 | // Wait for half-transfer (first 4 elements) | |
| 295 | // Create the transfer | 248 | defmt::info!("Waiting for first half..."); |
| 296 | let mut transfer = dma_ch1.mem_to_mem(src, dst, options); | 249 | let _ok = transfer.wait_half().await; |
| 297 | |||
| 298 | // Wait for half-transfer (first 4 elements) | ||
| 299 | tx.blocking_write(b"Waiting for first half...\r\n").unwrap(); | ||
| 300 | let half_ok = transfer.wait_half().await; | ||
| 301 | |||
| 302 | if half_ok { | ||
| 303 | tx.blocking_write(b"Half-transfer complete! First half of DST2: ") | ||
| 304 | .unwrap(); | ||
| 305 | print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 4); | ||
| 306 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 307 | tx.blocking_write(b"(Processing first half while second half transfers...)\r\n") | ||
| 308 | .unwrap(); | ||
| 309 | } | ||
| 310 | |||
| 311 | // Wait for complete transfer | ||
| 312 | tx.blocking_write(b"Waiting for second half...\r\n").unwrap(); | ||
| 313 | transfer.await; | ||
| 314 | } | ||
| 315 | 250 | ||
| 316 | tx.blocking_write(b"Transfer complete! Full DST2: ").unwrap(); | 251 | defmt::info!("Half-transfer complete!"); |
| 317 | print_buffer(&mut tx, core::ptr::addr_of!(DST2) as *const u32, 8); | 252 | |
| 318 | tx.blocking_write(b"\r\n\r\n").unwrap(); | 253 | // Wait for complete transfer |
| 254 | defmt::info!("Waiting for second half..."); | ||
| 255 | transfer.await; | ||
| 256 | |||
| 257 | defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice()); | ||
| 319 | 258 | ||
| 320 | // Verify approach 2 | 259 | // Verify approach 2 |
| 321 | let mut mismatch2 = false; | 260 | let mismatch2 = src2 != dst2; |
| 322 | unsafe { | ||
| 323 | let src_ptr = core::ptr::addr_of!(SRC2) as *const u32; | ||
| 324 | let dst_ptr = core::ptr::addr_of!(DST2) as *const u32; | ||
| 325 | for i in 0..8 { | ||
| 326 | if *src_ptr.add(i) != *dst_ptr.add(i) { | ||
| 327 | mismatch2 = true; | ||
| 328 | break; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | 261 | ||
| 333 | if mismatch2 { | 262 | if mismatch2 { |
| 334 | tx.blocking_write(b"FAIL: Approach 2 mismatch!\r\n").unwrap(); | ||
| 335 | defmt::error!("FAIL: Approach 2 mismatch!"); | 263 | defmt::error!("FAIL: Approach 2 mismatch!"); |
| 336 | } else { | 264 | } else { |
| 337 | tx.blocking_write(b"PASS: Approach 2 verified.\r\n").unwrap(); | ||
| 338 | defmt::info!("PASS: Approach 2 verified."); | 265 | defmt::info!("PASS: Approach 2 verified."); |
| 339 | } | 266 | } |
| 340 | 267 | ||
| 341 | tx.blocking_write(b"\r\n=== All ping-pong demos complete ===\r\n") | 268 | defmt::info!("=== All ping-pong demos complete ==="); |
| 342 | .unwrap(); | ||
| 343 | |||
| 344 | loop { | ||
| 345 | cortex_m::asm::wfe(); | ||
| 346 | } | ||
| 347 | } | 269 | } |
diff --git a/examples/mcxa/src/bin/dma_scatter_gather.rs b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs index 3e34e95b1..057e56826 100644 --- a/examples/mcxa/src/bin/dma_scatter_gather.rs +++ b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | //! DMA scatter-gather transfer example for MCXA276. | 1 | //! DMA scatter-gather transfer example for MCXA276. |
| 2 | //! | 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 | //! | ||
| 3 | //! This example demonstrates using DMA with scatter/gather to chain multiple | 7 | //! This example demonstrates using DMA with scatter/gather to chain multiple |
| 4 | //! transfer descriptors. The first TCD transfers the first half of the buffer, | 8 | //! transfer descriptors. The first TCD transfers the first half of the buffer, |
| 5 | //! then automatically loads the second TCD to transfer the second half. | 9 | //! then automatically loads the second TCD to transfer the second half. |
| @@ -12,25 +16,24 @@ | |||
| 12 | #![no_std] | 16 | #![no_std] |
| 13 | #![no_main] | 17 | #![no_main] |
| 14 | 18 | ||
| 15 | use core::fmt::Write as _; | ||
| 16 | use core::sync::atomic::{AtomicBool, Ordering}; | 19 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 17 | 20 | ||
| 18 | use embassy_executor::Spawner; | 21 | use embassy_executor::Spawner; |
| 22 | use embassy_mcxa::bind_interrupts; | ||
| 19 | use embassy_mcxa::clocks::config::Div8; | 23 | use embassy_mcxa::clocks::config::Div8; |
| 20 | use embassy_mcxa::dma::{self, DmaChannel, Tcd}; | 24 | use embassy_mcxa::dma::{self, DmaChannel, Tcd}; |
| 21 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 25 | use static_cell::ConstStaticCell; |
| 22 | use embassy_mcxa::{bind_interrupts, pac}; | ||
| 23 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 26 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 24 | 27 | ||
| 25 | // Source and destination buffers | 28 | // Source and destination buffers |
| 26 | static mut SRC: [u32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; | 29 | static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); |
| 27 | static mut DST: [u32; 8] = [0; 8]; | 30 | static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); |
| 28 | 31 | ||
| 29 | // TCD pool for scatter/gather - must be 32-byte aligned | 32 | // TCD pool for scatter/gather - must be 32-byte aligned |
| 30 | #[repr(C, align(32))] | 33 | #[repr(C, align(32))] |
| 31 | struct TcdPool([Tcd; 2]); | 34 | struct TcdPool([Tcd; 2]); |
| 32 | 35 | ||
| 33 | static mut TCD_POOL: TcdPool = TcdPool( | 36 | static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool( |
| 34 | [Tcd { | 37 | [Tcd { |
| 35 | saddr: 0, | 38 | saddr: 0, |
| 36 | soff: 0, | 39 | soff: 0, |
| @@ -44,7 +47,7 @@ static mut TCD_POOL: TcdPool = TcdPool( | |||
| 44 | csr: 0, | 47 | csr: 0, |
| 45 | biter: 0, | 48 | biter: 0, |
| 46 | }; 2], | 49 | }; 2], |
| 47 | ); | 50 | )); |
| 48 | 51 | ||
| 49 | // AtomicBool to track scatter/gather completion | 52 | // AtomicBool to track scatter/gather completion |
| 50 | // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, | 53 | // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, |
| @@ -73,11 +76,6 @@ bind_interrupts!(struct Irqs { | |||
| 73 | DMA_CH0 => ScatterGatherDmaHandler; | 76 | DMA_CH0 => ScatterGatherDmaHandler; |
| 74 | }); | 77 | }); |
| 75 | 78 | ||
| 76 | /// Helper to print a buffer to UART | ||
| 77 | fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { | ||
| 78 | write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); | ||
| 79 | } | ||
| 80 | |||
| 81 | #[embassy_executor::main] | 79 | #[embassy_executor::main] |
| 82 | async fn main(_spawner: Spawner) { | 80 | async fn main(_spawner: Spawner) { |
| 83 | // Small delay to allow probe-rs to attach after reset | 81 | // Small delay to allow probe-rs to attach after reset |
| @@ -92,40 +90,15 @@ async fn main(_spawner: Spawner) { | |||
| 92 | 90 | ||
| 93 | defmt::info!("DMA scatter-gather transfer example starting..."); | 91 | defmt::info!("DMA scatter-gather transfer example starting..."); |
| 94 | 92 | ||
| 95 | // DMA is initialized during hal::init() - no need to call ensure_init() | 93 | defmt::info!("EDMA scatter-gather transfer example begin."); |
| 96 | |||
| 97 | // Enable DMA interrupt | ||
| 98 | unsafe { | ||
| 99 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | ||
| 100 | } | ||
| 101 | |||
| 102 | let config = Config { | ||
| 103 | baudrate_bps: 115_200, | ||
| 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 scatter-gather transfer example begin.\r\n\r\n") | ||
| 111 | .unwrap(); | ||
| 112 | 94 | ||
| 113 | // Initialize buffers | 95 | // Initialize buffers |
| 114 | unsafe { | 96 | let src = SRC.take(); |
| 115 | SRC = [1, 2, 3, 4, 5, 6, 7, 8]; | 97 | let dst = DST.take(); |
| 116 | DST = [0; 8]; | ||
| 117 | } | ||
| 118 | |||
| 119 | tx.blocking_write(b"Source Buffer: ").unwrap(); | ||
| 120 | print_buffer(&mut tx, core::ptr::addr_of!(SRC) as *const u32, 8); | ||
| 121 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 122 | 98 | ||
| 123 | tx.blocking_write(b"Destination Buffer (before): ").unwrap(); | 99 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); |
| 124 | print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); | 100 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); |
| 125 | tx.blocking_write(b"\r\n").unwrap(); | 101 | defmt::info!("Configuring scatter-gather DMA with Embassy-style API..."); |
| 126 | |||
| 127 | tx.blocking_write(b"Configuring scatter-gather DMA with Embassy-style API...\r\n") | ||
| 128 | .unwrap(); | ||
| 129 | 102 | ||
| 130 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | 103 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); |
| 131 | 104 | ||
| @@ -134,9 +107,9 @@ async fn main(_spawner: Spawner) { | |||
| 134 | // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. | 107 | // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. |
| 135 | // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. | 108 | // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), last TCD. |
| 136 | unsafe { | 109 | unsafe { |
| 137 | let tcds = core::slice::from_raw_parts_mut(core::ptr::addr_of_mut!(TCD_POOL.0) as *mut Tcd, 2); | 110 | let tcds = &mut TCD_POOL.take().0; |
| 138 | let src_ptr = core::ptr::addr_of!(SRC) as *const u32; | 111 | let src_ptr = src.as_ptr(); |
| 139 | let dst_ptr = core::ptr::addr_of_mut!(DST) as *mut u32; | 112 | let dst_ptr = dst.as_mut_ptr(); |
| 140 | 113 | ||
| 141 | let num_tcds = 2usize; | 114 | let num_tcds = 2usize; |
| 142 | let chunk_len = 4usize; // 8 / 2 | 115 | let chunk_len = 4usize; // 8 / 2 |
| @@ -170,7 +143,7 @@ async fn main(_spawner: Spawner) { | |||
| 170 | dma_ch0.load_tcd(&tcds[0]); | 143 | dma_ch0.load_tcd(&tcds[0]); |
| 171 | } | 144 | } |
| 172 | 145 | ||
| 173 | tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); | 146 | defmt::info!("Triggering first half transfer..."); |
| 174 | 147 | ||
| 175 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) | 148 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) |
| 176 | // TCD0 is currently loaded. | 149 | // TCD0 is currently loaded. |
| @@ -184,8 +157,8 @@ async fn main(_spawner: Spawner) { | |||
| 184 | } | 157 | } |
| 185 | TRANSFER_DONE.store(false, Ordering::Release); | 158 | TRANSFER_DONE.store(false, Ordering::Release); |
| 186 | 159 | ||
| 187 | tx.blocking_write(b"First half transferred.\r\n").unwrap(); | 160 | defmt::info!("First half transferred."); |
| 188 | tx.blocking_write(b"Triggering second half transfer...\r\n").unwrap(); | 161 | defmt::info!("Triggering second half transfer..."); |
| 189 | 162 | ||
| 190 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) | 163 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) |
| 191 | // TCD1 should have been loaded by the scatter/gather engine. | 164 | // TCD1 should have been loaded by the scatter/gather engine. |
| @@ -199,36 +172,17 @@ async fn main(_spawner: Spawner) { | |||
| 199 | } | 172 | } |
| 200 | TRANSFER_DONE.store(false, Ordering::Release); | 173 | TRANSFER_DONE.store(false, Ordering::Release); |
| 201 | 174 | ||
| 202 | tx.blocking_write(b"Second half transferred.\r\n\r\n").unwrap(); | 175 | defmt::info!("Second half transferred."); |
| 203 | 176 | ||
| 204 | tx.blocking_write(b"EDMA scatter-gather transfer example finish.\r\n\r\n") | 177 | defmt::info!("EDMA scatter-gather transfer example finish."); |
| 205 | .unwrap(); | 178 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); |
| 206 | tx.blocking_write(b"Destination Buffer (after): ").unwrap(); | ||
| 207 | print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); | ||
| 208 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 209 | 179 | ||
| 210 | // Verify: DST should match SRC | 180 | // Verify: DST should match SRC |
| 211 | let mut mismatch = false; | 181 | let mismatch = src != dst; |
| 212 | unsafe { | ||
| 213 | let src_ptr = core::ptr::addr_of!(SRC) as *const u32; | ||
| 214 | let dst_ptr = core::ptr::addr_of!(DST) as *const u32; | ||
| 215 | for i in 0..8 { | ||
| 216 | if *src_ptr.add(i) != *dst_ptr.add(i) { | ||
| 217 | mismatch = true; | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | 182 | ||
| 223 | if mismatch { | 183 | if mismatch { |
| 224 | tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); | ||
| 225 | defmt::error!("FAIL: Mismatch detected!"); | 184 | defmt::error!("FAIL: Mismatch detected!"); |
| 226 | } else { | 185 | } else { |
| 227 | tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); | ||
| 228 | defmt::info!("PASS: Data verified."); | 186 | defmt::info!("PASS: Data verified."); |
| 229 | } | 187 | } |
| 230 | |||
| 231 | loop { | ||
| 232 | cortex_m::asm::wfe(); | ||
| 233 | } | ||
| 234 | } | 188 | } |
