diff options
Diffstat (limited to 'examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs')
| -rw-r--r-- | examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs | 55 |
1 files changed, 15 insertions, 40 deletions
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 | ||
| 30 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 31 | |||
| 32 | use embassy_executor::Spawner; | 30 | use embassy_executor::Spawner; |
| 33 | use embassy_mcxa::clocks::config::Div8; | 31 | use embassy_mcxa::clocks::config::Div8; |
| 34 | use embassy_mcxa::dma::{self, DmaChannel, Tcd, TransferOptions}; | 32 | use embassy_mcxa::dma::{DmaChannel, Tcd, TransferOptions}; |
| 35 | use embassy_mcxa::{bind_interrupts, pac}; | 33 | use embassy_mcxa::pac; |
| 36 | use static_cell::ConstStaticCell; | 34 | use static_cell::ConstStaticCell; |
| 37 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 35 | use {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 | ||
| 70 | static 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 | ||
| 75 | pub struct PingPongDmaHandler; | ||
| 76 | |||
| 77 | impl 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 | ||
| 89 | bind_interrupts!(struct Irqs { | ||
| 90 | DMA_CH0 => PingPongDmaHandler; | ||
| 91 | }); | ||
| 92 | |||
| 93 | #[embassy_executor::main] | 65 | #[embassy_executor::main] |
| 94 | async fn main(_spawner: Spawner) { | 66 | async 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 | ||
