diff options
| author | Bogdan Petru Chircu Mare <[email protected]> | 2025-11-27 21:39:31 -0800 |
|---|---|---|
| committer | Bogdan Petru Chircu Mare <[email protected]> | 2025-11-28 12:34:37 -0800 |
| commit | 5a6394666e23555e4f329f7b1bd470d0728434a1 (patch) | |
| tree | cf4d3f68d30224051707ff1a74769ff3588e1702 /examples | |
| parent | 03356a261801d7ee234490809eef3eac3c27cc52 (diff) | |
Updated per PR #52 feedback
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/src/bin/dma_channel_link.rs | 93 | ||||
| -rw-r--r-- | examples/src/bin/dma_interleave_transfer.rs | 31 | ||||
| -rw-r--r-- | examples/src/bin/dma_mem_to_mem.rs | 19 | ||||
| -rw-r--r-- | examples/src/bin/dma_memset.rs | 31 | ||||
| -rw-r--r-- | examples/src/bin/dma_ping_pong_transfer.rs | 60 | ||||
| -rw-r--r-- | examples/src/bin/dma_scatter_gather.rs | 54 | ||||
| -rw-r--r-- | examples/src/bin/dma_scatter_gather_builder.rs | 18 | ||||
| -rw-r--r-- | examples/src/bin/dma_wrap_transfer.rs | 31 | ||||
| -rw-r--r-- | examples/src/bin/lpuart_dma.rs | 78 | ||||
| -rw-r--r-- | examples/src/bin/lpuart_ring_buffer.rs | 23 |
10 files changed, 114 insertions, 324 deletions
diff --git a/examples/src/bin/dma_channel_link.rs b/examples/src/bin/dma_channel_link.rs index d585f8e3a..d541dc7f4 100644 --- a/examples/src/bin/dma_channel_link.rs +++ b/examples/src/bin/dma_channel_link.rs | |||
| @@ -8,20 +8,18 @@ | |||
| 8 | //! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) | 8 | //! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) |
| 9 | //! | 9 | //! |
| 10 | //! # Embassy-style features demonstrated: | 10 | //! # Embassy-style features demonstrated: |
| 11 | //! - `dma::edma_tcd()` accessor for simplified register access | ||
| 12 | //! - `DmaChannel::new()` for channel creation | 11 | //! - `DmaChannel::new()` for channel creation |
| 13 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | 12 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods |
| 14 | //! - Channel linking with `set_minor_link()` and `set_major_link()` | 13 | //! - Channel linking with `set_minor_link()` and `set_major_link()` |
| 14 | //! - Standard `DmaCh*InterruptHandler` with `bind_interrupts!` macro | ||
| 15 | 15 | ||
| 16 | #![no_std] | 16 | #![no_std] |
| 17 | #![no_main] | 17 | #![no_main] |
| 18 | 18 | ||
| 19 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 20 | use embassy_executor::Spawner; | 19 | use embassy_executor::Spawner; |
| 21 | use embassy_mcxa::clocks::config::Div8; | 20 | use embassy_mcxa::clocks::config::Div8; |
| 22 | use embassy_mcxa::clocks::Gate; | 21 | use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DmaCh2InterruptHandler}; |
| 23 | use embassy_mcxa::dma::{edma_tcd, DmaChannel}; | 22 | use embassy_mcxa::bind_interrupts; |
| 24 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 25 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 23 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 26 | use embassy_mcxa::pac; | 24 | use embassy_mcxa::pac; |
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 25 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -32,49 +30,12 @@ static mut DEST_BUFFER0: [u32; 4] = [0; 4]; | |||
| 32 | static mut DEST_BUFFER1: [u32; 4] = [0; 4]; | 30 | static mut DEST_BUFFER1: [u32; 4] = [0; 4]; |
| 33 | static mut DEST_BUFFER2: [u32; 4] = [0; 4]; | 31 | static mut DEST_BUFFER2: [u32; 4] = [0; 4]; |
| 34 | 32 | ||
| 35 | static DMA_CH2_DONE: AtomicBool = AtomicBool::new(false); | 33 | // Bind DMA channel interrupts using Embassy-style macro |
| 36 | 34 | // The standard handlers call on_interrupt() which wakes wakers and clears flags | |
| 37 | // Custom DMA interrupt handlers for channel linking | ||
| 38 | // CH0 and CH1 just clear flags, CH2 signals completion | ||
| 39 | |||
| 40 | pub struct Ch0Handler; | ||
| 41 | impl 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 | |||
| 51 | pub struct Ch1Handler; | ||
| 52 | impl 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 | |||
| 62 | pub struct Ch2Handler; | ||
| 63 | impl 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 | |||
| 74 | bind_interrupts!(struct Irqs { | 35 | bind_interrupts!(struct Irqs { |
| 75 | DMA_CH0 => Ch0Handler; | 36 | DMA_CH0 => DmaCh0InterruptHandler; |
| 76 | DMA_CH1 => Ch1Handler; | 37 | DMA_CH1 => DmaCh1InterruptHandler; |
| 77 | DMA_CH2 => Ch2Handler; | 38 | DMA_CH2 => DmaCh2InterruptHandler; |
| 78 | }); | 39 | }); |
| 79 | 40 | ||
| 80 | /// Helper to write a u32 as decimal ASCII to UART | 41 | /// Helper to write a u32 as decimal ASCII to UART |
| @@ -125,21 +86,12 @@ async fn main(_spawner: Spawner) { | |||
| 125 | 86 | ||
| 126 | defmt::info!("DMA channel link example starting..."); | 87 | defmt::info!("DMA channel link example starting..."); |
| 127 | 88 | ||
| 128 | // Enable DMA0 clock and release reset | 89 | // Ensure DMA is initialized (clock/reset/init handled automatically by HAL) |
| 129 | unsafe { | 90 | dma::ensure_init(); |
| 130 | hal::peripherals::DMA0::enable_clock(); | ||
| 131 | hal::peripherals::DMA0::release_reset(); | ||
| 132 | } | ||
| 133 | 91 | ||
| 134 | let pac_periphs = unsafe { pac::Peripherals::steal() }; | 92 | 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; | 93 | let dma0 = &pac_periphs.dma0; |
| 94 | let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; | ||
| 143 | 95 | ||
| 144 | // Clear any residual state | 96 | // Clear any residual state |
| 145 | for i in 0..3 { | 97 | for i in 0..3 { |
| @@ -206,7 +158,7 @@ async fn main(_spawner: Spawner) { | |||
| 206 | 158 | ||
| 207 | let ch0 = DmaChannel::new(p.DMA_CH0); | 159 | let ch0 = DmaChannel::new(p.DMA_CH0); |
| 208 | let ch1 = DmaChannel::new(p.DMA_CH1); | 160 | let ch1 = DmaChannel::new(p.DMA_CH1); |
| 209 | let _ch2 = DmaChannel::new(p.DMA_CH2); | 161 | let ch2 = DmaChannel::new(p.DMA_CH2); |
| 210 | 162 | ||
| 211 | // Configure channels using direct TCD access (advanced feature demo) | 163 | // Configure channels using direct TCD access (advanced feature demo) |
| 212 | // This example demonstrates channel linking which requires direct TCD manipulation | 164 | // This example demonstrates channel linking which requires direct TCD manipulation |
| @@ -291,8 +243,8 @@ async fn main(_spawner: Spawner) { | |||
| 291 | 2, // count (major loop = 2 iterations) | 243 | 2, // count (major loop = 2 iterations) |
| 292 | false, // no interrupt | 244 | false, // no interrupt |
| 293 | ); | 245 | ); |
| 294 | ch0.set_minor_link(edma, 1); // Link to CH1 after each minor loop | 246 | ch0.set_minor_link(1); // Link to CH1 after each minor loop |
| 295 | ch0.set_major_link(edma, 2); // Link to CH2 after major loop | 247 | ch0.set_major_link(2); // Link to CH2 after major loop |
| 296 | 248 | ||
| 297 | // Channel 1: Transfer 16 bytes (triggered by CH0 minor link) | 249 | // Channel 1: Transfer 16 bytes (triggered by CH0 minor link) |
| 298 | configure_tcd( | 250 | configure_tcd( |
| @@ -322,32 +274,35 @@ async fn main(_spawner: Spawner) { | |||
| 322 | tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n").unwrap(); | 274 | tx.blocking_write(b"Triggering Channel 0 (1st minor loop)...\r\n").unwrap(); |
| 323 | 275 | ||
| 324 | // Trigger first minor loop of CH0 | 276 | // Trigger first minor loop of CH0 |
| 325 | unsafe { ch0.trigger_start(edma); } | 277 | unsafe { ch0.trigger_start(); } |
| 326 | 278 | ||
| 327 | // Wait for CH1 to complete (triggered by CH0 minor link) | 279 | // Wait for CH1 to complete (triggered by CH0 minor link) |
| 328 | while !ch1.is_done(edma) { | 280 | while !ch1.is_done() { |
| 329 | cortex_m::asm::nop(); | 281 | cortex_m::asm::nop(); |
| 330 | } | 282 | } |
| 331 | unsafe { ch1.clear_done(edma); } | 283 | unsafe { ch1.clear_done(); } |
| 332 | 284 | ||
| 333 | tx.blocking_write(b"CH1 done (via minor link).\r\n").unwrap(); | 285 | 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(); | 286 | tx.blocking_write(b"Triggering Channel 0 (2nd minor loop)...\r\n").unwrap(); |
| 335 | 287 | ||
| 336 | // Trigger second minor loop of CH0 | 288 | // Trigger second minor loop of CH0 |
| 337 | unsafe { ch0.trigger_start(edma); } | 289 | unsafe { ch0.trigger_start(); } |
| 338 | 290 | ||
| 339 | // Wait for CH0 major loop to complete | 291 | // Wait for CH0 major loop to complete |
| 340 | while !ch0.is_done(edma) { | 292 | while !ch0.is_done() { |
| 341 | cortex_m::asm::nop(); | 293 | cortex_m::asm::nop(); |
| 342 | } | 294 | } |
| 343 | unsafe { ch0.clear_done(edma); } | 295 | unsafe { ch0.clear_done(); } |
| 344 | 296 | ||
| 345 | tx.blocking_write(b"CH0 major loop done.\r\n").unwrap(); | 297 | tx.blocking_write(b"CH0 major loop done.\r\n").unwrap(); |
| 346 | 298 | ||
| 347 | // Wait for CH2 to complete (triggered by CH0 major link) | 299 | // Wait for CH2 to complete (triggered by CH0 major link) |
| 348 | while !DMA_CH2_DONE.load(Ordering::Acquire) { | 300 | // Using is_done() instead of AtomicBool - the standard interrupt handler |
| 301 | // clears the interrupt flag and wakes wakers, but DONE bit remains set | ||
| 302 | while !ch2.is_done() { | ||
| 349 | cortex_m::asm::nop(); | 303 | cortex_m::asm::nop(); |
| 350 | } | 304 | } |
| 305 | unsafe { ch2.clear_done(); } | ||
| 351 | 306 | ||
| 352 | tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap(); | 307 | tx.blocking_write(b"CH2 done (via major link).\r\n\r\n").unwrap(); |
| 353 | 308 | ||
diff --git a/examples/src/bin/dma_interleave_transfer.rs b/examples/src/bin/dma_interleave_transfer.rs index 710f18de3..949ea0605 100644 --- a/examples/src/bin/dma_interleave_transfer.rs +++ b/examples/src/bin/dma_interleave_transfer.rs | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | //! to interleave data during transfer. | 4 | //! to interleave data during transfer. |
| 5 | //! | 5 | //! |
| 6 | //! # Embassy-style features demonstrated: | 6 | //! # Embassy-style features demonstrated: |
| 7 | //! - `dma::edma_tcd()` accessor for simplified register access | ||
| 8 | //! - `TransferOptions::default()` for configuration (used internally) | 7 | //! - `TransferOptions::default()` for configuration (used internally) |
| 9 | //! - DMA channel with `DmaChannel::new()` | 8 | //! - DMA channel with `DmaChannel::new()` |
| 10 | 9 | ||
| @@ -13,9 +12,8 @@ | |||
| 13 | 12 | ||
| 14 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 15 | use embassy_mcxa::clocks::config::Div8; | 14 | use embassy_mcxa::clocks::config::Div8; |
| 16 | use embassy_mcxa::clocks::Gate; | 15 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; |
| 17 | use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh0InterruptHandler}; | 16 | use embassy_mcxa::bind_interrupts; |
| 18 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 19 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 17 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 20 | use embassy_mcxa::pac; | 18 | use embassy_mcxa::pac; |
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 19 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -80,19 +78,7 @@ async fn main(_spawner: Spawner) { | |||
| 80 | 78 | ||
| 81 | defmt::info!("DMA interleave transfer example starting..."); | 79 | defmt::info!("DMA interleave transfer example starting..."); |
| 82 | 80 | ||
| 83 | // Enable DMA0 clock and release reset | 81 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 82 | unsafe { |
| 97 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 83 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 98 | } | 84 | } |
| @@ -130,15 +116,12 @@ async fn main(_spawner: Spawner) { | |||
| 130 | // Create DMA channel using Embassy-style API | 116 | // Create DMA channel using Embassy-style API |
| 131 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | 117 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); |
| 132 | 118 | ||
| 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: | 119 | // Configure interleaved transfer using direct TCD access: |
| 137 | // - src_offset = 4: advance source by 4 bytes after each read | 120 | // - src_offset = 4: advance source by 4 bytes after each read |
| 138 | // - dst_offset = 8: advance dest by 8 bytes after each write | 121 | // - dst_offset = 8: advance dest by 8 bytes after each write |
| 139 | // This spreads source data across every other word in destination | 122 | // This spreads source data across every other word in destination |
| 140 | unsafe { | 123 | unsafe { |
| 141 | let t = edma.tcd(0); | 124 | let t = dma_ch0.tcd(); |
| 142 | 125 | ||
| 143 | // Reset channel state | 126 | // Reset channel state |
| 144 | t.ch_csr().write(|w| { | 127 | t.ch_csr().write(|w| { |
| @@ -182,14 +165,14 @@ async fn main(_spawner: Spawner) { | |||
| 182 | cortex_m::asm::dsb(); | 165 | cortex_m::asm::dsb(); |
| 183 | 166 | ||
| 184 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); | 167 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); |
| 185 | dma_ch0.trigger_start(edma); | 168 | dma_ch0.trigger_start(); |
| 186 | } | 169 | } |
| 187 | 170 | ||
| 188 | // Wait for completion using channel helper method | 171 | // Wait for completion using channel helper method |
| 189 | while !dma_ch0.is_done(edma) { | 172 | while !dma_ch0.is_done() { |
| 190 | cortex_m::asm::nop(); | 173 | cortex_m::asm::nop(); |
| 191 | } | 174 | } |
| 192 | unsafe { dma_ch0.clear_done(edma); } | 175 | unsafe { dma_ch0.clear_done(); } |
| 193 | 176 | ||
| 194 | tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") | 177 | tx.blocking_write(b"\r\nEDMA interleave transfer example finish.\r\n\r\n") |
| 195 | .unwrap(); | 178 | .unwrap(); |
diff --git a/examples/src/bin/dma_mem_to_mem.rs b/examples/src/bin/dma_mem_to_mem.rs index e193e8c6a..01e5edb1e 100644 --- a/examples/src/bin/dma_mem_to_mem.rs +++ b/examples/src/bin/dma_mem_to_mem.rs | |||
| @@ -15,9 +15,8 @@ | |||
| 15 | 15 | ||
| 16 | use embassy_executor::Spawner; | 16 | use embassy_executor::Spawner; |
| 17 | use embassy_mcxa::clocks::config::Div8; | 17 | use embassy_mcxa::clocks::config::Div8; |
| 18 | use embassy_mcxa::clocks::Gate; | ||
| 19 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, TransferOptions}; | 18 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, TransferOptions}; |
| 20 | use embassy_mcxa::{bind_interrupts, dma}; | 19 | use embassy_mcxa::bind_interrupts; |
| 21 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 20 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 22 | use embassy_mcxa::pac; | 21 | use embassy_mcxa::pac; |
| 23 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 22 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -84,21 +83,7 @@ async fn main(_spawner: Spawner) { | |||
| 84 | 83 | ||
| 85 | defmt::info!("DMA memory-to-memory example starting..."); | 84 | defmt::info!("DMA memory-to-memory example starting..."); |
| 86 | 85 | ||
| 87 | // Enable DMA0 clock and release reset | 86 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 87 | unsafe { |
| 103 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 88 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 104 | } | 89 | } |
diff --git a/examples/src/bin/dma_memset.rs b/examples/src/bin/dma_memset.rs index b76ba988d..8a1636e57 100644 --- a/examples/src/bin/dma_memset.rs +++ b/examples/src/bin/dma_memset.rs | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | //! The source address stays fixed while the destination increments. | 4 | //! The source address stays fixed while the destination increments. |
| 5 | //! | 5 | //! |
| 6 | //! # Embassy-style features demonstrated: | 6 | //! # Embassy-style features demonstrated: |
| 7 | //! - `dma::edma_tcd()` accessor for simplified register access | ||
| 8 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | 7 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods |
| 9 | //! - No need to pass register block around | 8 | //! - No need to pass register block around |
| 10 | 9 | ||
| @@ -13,9 +12,8 @@ | |||
| 13 | 12 | ||
| 14 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 15 | use embassy_mcxa::clocks::config::Div8; | 14 | use embassy_mcxa::clocks::config::Div8; |
| 16 | use embassy_mcxa::clocks::Gate; | 15 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; |
| 17 | use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh0InterruptHandler}; | 16 | use embassy_mcxa::bind_interrupts; |
| 18 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 19 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 17 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 20 | use embassy_mcxa::pac; | 18 | use embassy_mcxa::pac; |
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 19 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -79,19 +77,7 @@ async fn main(_spawner: Spawner) { | |||
| 79 | 77 | ||
| 80 | defmt::info!("DMA memset example starting..."); | 78 | defmt::info!("DMA memset example starting..."); |
| 81 | 79 | ||
| 82 | // Enable DMA0 clock and release reset | 80 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 81 | unsafe { |
| 96 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 82 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 97 | } | 83 | } |
| @@ -139,14 +125,11 @@ async fn main(_spawner: Spawner) { | |||
| 139 | // Create DMA channel using Embassy-style API | 125 | // Create DMA channel using Embassy-style API |
| 140 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | 126 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); |
| 141 | 127 | ||
| 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: | 128 | // Configure memset transfer using direct TCD access: |
| 146 | // Source stays fixed (soff = 0, reads same pattern repeatedly) | 129 | // Source stays fixed (soff = 0, reads same pattern repeatedly) |
| 147 | // Destination increments (doff = 4) | 130 | // Destination increments (doff = 4) |
| 148 | unsafe { | 131 | unsafe { |
| 149 | let t = edma.tcd(0); | 132 | let t = dma_ch0.tcd(); |
| 150 | 133 | ||
| 151 | // Reset channel state | 134 | // Reset channel state |
| 152 | t.ch_csr().write(|w| { | 135 | t.ch_csr().write(|w| { |
| @@ -190,14 +173,14 @@ async fn main(_spawner: Spawner) { | |||
| 190 | cortex_m::asm::dsb(); | 173 | cortex_m::asm::dsb(); |
| 191 | 174 | ||
| 192 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); | 175 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); |
| 193 | dma_ch0.trigger_start(edma); | 176 | dma_ch0.trigger_start(); |
| 194 | } | 177 | } |
| 195 | 178 | ||
| 196 | // Wait for completion using channel helper method | 179 | // Wait for completion using channel helper method |
| 197 | while !dma_ch0.is_done(edma) { | 180 | while !dma_ch0.is_done() { |
| 198 | cortex_m::asm::nop(); | 181 | cortex_m::asm::nop(); |
| 199 | } | 182 | } |
| 200 | unsafe { dma_ch0.clear_done(edma); } | 183 | unsafe { dma_ch0.clear_done(); } |
| 201 | 184 | ||
| 202 | tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n") | 185 | tx.blocking_write(b"\r\nEDMA memset example finish.\r\n\r\n") |
| 203 | .unwrap(); | 186 | .unwrap(); |
diff --git a/examples/src/bin/dma_ping_pong_transfer.rs b/examples/src/bin/dma_ping_pong_transfer.rs index 13ad9782d..d765ea575 100644 --- a/examples/src/bin/dma_ping_pong_transfer.rs +++ b/examples/src/bin/dma_ping_pong_transfer.rs | |||
| @@ -4,7 +4,9 @@ | |||
| 4 | //! | 4 | //! |
| 5 | //! ## Approach 1: Scatter/Gather with linked TCDs (manual) | 5 | //! ## Approach 1: Scatter/Gather with linked TCDs (manual) |
| 6 | //! - Two TCDs link to each other for alternating transfers | 6 | //! - Two TCDs link to each other for alternating transfers |
| 7 | //! - Uses custom interrupt handler with AtomicBool flag | 7 | //! - Uses custom handler that delegates to on_interrupt() then signals completion |
| 8 | //! - Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, | ||
| 9 | //! so we need an AtomicBool to track completion | ||
| 8 | //! | 10 | //! |
| 9 | //! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!) | 11 | //! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!) |
| 10 | //! - Single continuous transfer over entire buffer | 12 | //! - Single continuous transfer over entire buffer |
| @@ -12,9 +14,10 @@ | |||
| 12 | //! - Application can process first half while second half is being filled | 14 | //! - Application can process first half while second half is being filled |
| 13 | //! | 15 | //! |
| 14 | //! # Embassy-style features demonstrated: | 16 | //! # Embassy-style features demonstrated: |
| 15 | //! - `dma::edma_tcd()` accessor for simplified register access | ||
| 16 | //! - `DmaChannel::new()` for channel creation | 17 | //! - `DmaChannel::new()` for channel creation |
| 17 | //! - Scatter/gather with linked TCDs | 18 | //! - Scatter/gather with linked TCDs |
| 19 | //! - Custom handler that delegates to HAL's `on_interrupt()` (best practice) | ||
| 20 | //! - Standard `DmaCh1InterruptHandler` with `bind_interrupts!` macro | ||
| 18 | //! - NEW: `wait_half()` for half-transfer interrupt handling | 21 | //! - NEW: `wait_half()` for half-transfer interrupt handling |
| 19 | 22 | ||
| 20 | #![no_std] | 23 | #![no_std] |
| @@ -23,9 +26,8 @@ | |||
| 23 | use core::sync::atomic::{AtomicBool, Ordering}; | 26 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 24 | use embassy_executor::Spawner; | 27 | use embassy_executor::Spawner; |
| 25 | use embassy_mcxa::clocks::config::Div8; | 28 | use embassy_mcxa::clocks::config::Div8; |
| 26 | use embassy_mcxa::clocks::Gate; | 29 | use embassy_mcxa::dma::{self, DmaChannel, DmaCh1InterruptHandler, Tcd, TransferOptions}; |
| 27 | use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh1InterruptHandler, Tcd, TransferOptions}; | 30 | use embassy_mcxa::bind_interrupts; |
| 28 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 29 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 31 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 30 | use embassy_mcxa::pac; | 32 | use embassy_mcxa::pac; |
| 31 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 33 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -56,30 +58,31 @@ static mut TCD_POOL: TcdPool = TcdPool([Tcd { | |||
| 56 | biter: 0, | 58 | biter: 0, |
| 57 | }; 2]); | 59 | }; 2]); |
| 58 | 60 | ||
| 61 | // AtomicBool to track scatter/gather completion | ||
| 62 | // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, | ||
| 63 | // so we need this flag to detect when each transfer completes | ||
| 59 | static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); | 64 | static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); |
| 60 | 65 | ||
| 61 | // Custom DMA interrupt handler for ping-pong transfer | 66 | // Custom handler for scatter/gather that delegates to HAL's on_interrupt() |
| 62 | // We need a custom handler because we signal completion via TRANSFER_DONE flag | 67 | // This follows the "interrupts as threads" pattern - the handler does minimal work |
| 63 | // and don't clear DONE bit when using Scatter/Gather (ESG=1) | 68 | // (delegates to HAL + sets a flag) and the main task does the actual processing |
| 64 | pub struct PingPongDmaHandler; | 69 | pub struct PingPongDmaHandler; |
| 65 | 70 | ||
| 66 | impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for PingPongDmaHandler { | 71 | impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for PingPongDmaHandler { |
| 67 | unsafe fn on_interrupt() { | 72 | unsafe fn on_interrupt() { |
| 68 | let edma = edma_tcd(); | 73 | // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers |
| 69 | 74 | dma::on_interrupt(0); | |
| 70 | // Clear interrupt flag | 75 | // Signal completion for polling (needed because ESG clears DONE bit) |
| 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); | 76 | TRANSFER_DONE.store(true, Ordering::Release); |
| 77 | } | 77 | } |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | // Bind DMA channel interrupts | ||
| 81 | // CH0: Custom handler for scatter/gather (delegates to on_interrupt + sets flag) | ||
| 82 | // CH1: Standard handler for wait_half() demo | ||
| 80 | bind_interrupts!(struct Irqs { | 83 | bind_interrupts!(struct Irqs { |
| 81 | DMA_CH0 => PingPongDmaHandler; | 84 | DMA_CH0 => PingPongDmaHandler; |
| 82 | DMA_CH1 => DmaCh1InterruptHandler; // For wait_half() demo | 85 | DMA_CH1 => DmaCh1InterruptHandler; |
| 83 | }); | 86 | }); |
| 84 | 87 | ||
| 85 | /// Helper to write a u32 as decimal ASCII to UART | 88 | /// Helper to write a u32 as decimal ASCII to UART |
| @@ -130,22 +133,7 @@ async fn main(_spawner: Spawner) { | |||
| 130 | 133 | ||
| 131 | defmt::info!("DMA ping-pong transfer example starting..."); | 134 | defmt::info!("DMA ping-pong transfer example starting..."); |
| 132 | 135 | ||
| 133 | // Enable DMA0 clock and release reset | 136 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 137 | unsafe { |
| 150 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 138 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 151 | } | 139 | } |
| @@ -228,14 +216,14 @@ async fn main(_spawner: Spawner) { | |||
| 228 | }; | 216 | }; |
| 229 | 217 | ||
| 230 | // Load TCD0 into hardware registers | 218 | // Load TCD0 into hardware registers |
| 231 | dma_ch0.load_tcd(edma, &tcds[0]); | 219 | dma_ch0.load_tcd(&tcds[0]); |
| 232 | } | 220 | } |
| 233 | 221 | ||
| 234 | tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); | 222 | tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); |
| 235 | 223 | ||
| 236 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) | 224 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) |
| 237 | unsafe { | 225 | unsafe { |
| 238 | dma_ch0.trigger_start(edma); | 226 | dma_ch0.trigger_start(); |
| 239 | } | 227 | } |
| 240 | 228 | ||
| 241 | // Wait for first half | 229 | // Wait for first half |
| @@ -249,7 +237,7 @@ async fn main(_spawner: Spawner) { | |||
| 249 | 237 | ||
| 250 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) | 238 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) |
| 251 | unsafe { | 239 | unsafe { |
| 252 | dma_ch0.trigger_start(edma); | 240 | dma_ch0.trigger_start(); |
| 253 | } | 241 | } |
| 254 | 242 | ||
| 255 | // Wait for second half | 243 | // Wait for second half |
diff --git a/examples/src/bin/dma_scatter_gather.rs b/examples/src/bin/dma_scatter_gather.rs index 86dd881cd..d78605acc 100644 --- a/examples/src/bin/dma_scatter_gather.rs +++ b/examples/src/bin/dma_scatter_gather.rs | |||
| @@ -5,9 +5,9 @@ | |||
| 5 | //! then automatically loads the second TCD to transfer the second half. | 5 | //! then automatically loads the second TCD to transfer the second half. |
| 6 | //! | 6 | //! |
| 7 | //! # Embassy-style features demonstrated: | 7 | //! # Embassy-style features demonstrated: |
| 8 | //! - `dma::edma_tcd()` accessor for simplified register access | ||
| 9 | //! - `DmaChannel::new()` for channel creation | 8 | //! - `DmaChannel::new()` for channel creation |
| 10 | //! - Scatter/gather with chained TCDs | 9 | //! - Scatter/gather with chained TCDs |
| 10 | //! - Custom handler that delegates to HAL's `on_interrupt()` (best practice) | ||
| 11 | 11 | ||
| 12 | #![no_std] | 12 | #![no_std] |
| 13 | #![no_main] | 13 | #![no_main] |
| @@ -15,9 +15,8 @@ | |||
| 15 | use core::sync::atomic::{AtomicBool, Ordering}; | 15 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 16 | use embassy_executor::Spawner; | 16 | use embassy_executor::Spawner; |
| 17 | use embassy_mcxa::clocks::config::Div8; | 17 | use embassy_mcxa::clocks::config::Div8; |
| 18 | use embassy_mcxa::clocks::Gate; | 18 | use embassy_mcxa::dma::{self, DmaChannel, Tcd}; |
| 19 | use embassy_mcxa::dma::{edma_tcd, DmaChannel, Tcd}; | 19 | use embassy_mcxa::bind_interrupts; |
| 20 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 21 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 20 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 22 | use embassy_mcxa::pac; | 21 | use embassy_mcxa::pac; |
| 23 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 22 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -44,30 +43,27 @@ static mut TCD_POOL: TcdPool = TcdPool([Tcd { | |||
| 44 | biter: 0, | 43 | biter: 0, |
| 45 | }; 2]); | 44 | }; 2]); |
| 46 | 45 | ||
| 46 | // AtomicBool to track scatter/gather completion | ||
| 47 | // Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, | ||
| 48 | // so we need this flag to detect when each transfer completes | ||
| 47 | static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); | 49 | static TRANSFER_DONE: AtomicBool = AtomicBool::new(false); |
| 48 | 50 | ||
| 49 | // Custom DMA interrupt handler for scatter-gather transfer | 51 | // Custom handler for scatter/gather that delegates to HAL's on_interrupt() |
| 50 | // We need a custom handler because we signal completion via TRANSFER_DONE flag | 52 | // This follows the "interrupts as threads" pattern - the handler does minimal work |
| 51 | // and need to conditionally clear DONE bit based on ESG status | 53 | // (delegates to HAL + sets a flag) and the main task does the actual processing |
| 52 | pub struct ScatterGatherDmaHandler; | 54 | pub struct ScatterGatherDmaHandler; |
| 53 | 55 | ||
| 54 | impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for ScatterGatherDmaHandler { | 56 | impl embassy_mcxa::interrupt::typelevel::Handler<embassy_mcxa::interrupt::typelevel::DMA_CH0> for ScatterGatherDmaHandler { |
| 55 | unsafe fn on_interrupt() { | 57 | unsafe fn on_interrupt() { |
| 56 | let edma = edma_tcd(); | 58 | // Delegate to HAL's on_interrupt() which clears INT flag and wakes wakers |
| 57 | 59 | dma::on_interrupt(0); | |
| 58 | // Clear interrupt flag | 60 | // Signal completion for polling (needed because ESG clears DONE bit) |
| 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); | 61 | TRANSFER_DONE.store(true, Ordering::Release); |
| 68 | } | 62 | } |
| 69 | } | 63 | } |
| 70 | 64 | ||
| 65 | // Bind DMA channel interrupt | ||
| 66 | // Custom handler for scatter/gather (delegates to on_interrupt + sets flag) | ||
| 71 | bind_interrupts!(struct Irqs { | 67 | bind_interrupts!(struct Irqs { |
| 72 | DMA_CH0 => ScatterGatherDmaHandler; | 68 | DMA_CH0 => ScatterGatherDmaHandler; |
| 73 | }); | 69 | }); |
| @@ -120,20 +116,8 @@ async fn main(_spawner: Spawner) { | |||
| 120 | 116 | ||
| 121 | defmt::info!("DMA scatter-gather transfer example starting..."); | 117 | defmt::info!("DMA scatter-gather transfer example starting..."); |
| 122 | 118 | ||
| 123 | // Enable DMA0 clock and release reset | 119 | // Ensure DMA is initialized (clock/reset/init handled automatically by HAL) |
| 124 | unsafe { | 120 | dma::ensure_init(); |
| 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 | 121 | ||
| 138 | // Enable DMA interrupt | 122 | // Enable DMA interrupt |
| 139 | unsafe { | 123 | unsafe { |
| @@ -213,7 +197,7 @@ async fn main(_spawner: Spawner) { | |||
| 213 | } | 197 | } |
| 214 | 198 | ||
| 215 | // Load TCD0 into hardware registers | 199 | // Load TCD0 into hardware registers |
| 216 | dma_ch0.load_tcd(edma, &tcds[0]); | 200 | dma_ch0.load_tcd(&tcds[0]); |
| 217 | } | 201 | } |
| 218 | 202 | ||
| 219 | tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); | 203 | tx.blocking_write(b"Triggering first half transfer...\r\n").unwrap(); |
| @@ -221,7 +205,7 @@ async fn main(_spawner: Spawner) { | |||
| 221 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) | 205 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) |
| 222 | // TCD0 is currently loaded. | 206 | // TCD0 is currently loaded. |
| 223 | unsafe { | 207 | unsafe { |
| 224 | dma_ch0.trigger_start(edma); | 208 | dma_ch0.trigger_start(); |
| 225 | } | 209 | } |
| 226 | 210 | ||
| 227 | // Wait for first half | 211 | // Wait for first half |
| @@ -236,7 +220,7 @@ async fn main(_spawner: Spawner) { | |||
| 236 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) | 220 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) |
| 237 | // TCD1 should have been loaded by the scatter/gather engine. | 221 | // TCD1 should have been loaded by the scatter/gather engine. |
| 238 | unsafe { | 222 | unsafe { |
| 239 | dma_ch0.trigger_start(edma); | 223 | dma_ch0.trigger_start(); |
| 240 | } | 224 | } |
| 241 | 225 | ||
| 242 | // Wait for second half | 226 | // Wait for second half |
diff --git a/examples/src/bin/dma_scatter_gather_builder.rs b/examples/src/bin/dma_scatter_gather_builder.rs index 078e26c60..51bfbeb67 100644 --- a/examples/src/bin/dma_scatter_gather_builder.rs +++ b/examples/src/bin/dma_scatter_gather_builder.rs | |||
| @@ -22,9 +22,8 @@ | |||
| 22 | 22 | ||
| 23 | use embassy_executor::Spawner; | 23 | use embassy_executor::Spawner; |
| 24 | use embassy_mcxa::clocks::config::Div8; | 24 | use embassy_mcxa::clocks::config::Div8; |
| 25 | use embassy_mcxa::clocks::Gate; | ||
| 26 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, ScatterGatherBuilder}; | 25 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, ScatterGatherBuilder}; |
| 27 | use embassy_mcxa::{bind_interrupts, dma}; | 26 | use embassy_mcxa::bind_interrupts; |
| 28 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 27 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 29 | use embassy_mcxa::pac; | 28 | use embassy_mcxa::pac; |
| 30 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 29 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -81,20 +80,7 @@ async fn main(_spawner: Spawner) { | |||
| 81 | 80 | ||
| 82 | defmt::info!("DMA Scatter-Gather Builder example starting..."); | 81 | defmt::info!("DMA Scatter-Gather Builder example starting..."); |
| 83 | 82 | ||
| 84 | // Enable DMA0 clock and release reset | 83 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 84 | unsafe { |
| 99 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 85 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 100 | } | 86 | } |
diff --git a/examples/src/bin/dma_wrap_transfer.rs b/examples/src/bin/dma_wrap_transfer.rs index b115a2c19..8e9aedbfb 100644 --- a/examples/src/bin/dma_wrap_transfer.rs +++ b/examples/src/bin/dma_wrap_transfer.rs | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | //! a source buffer, effectively repeating the source data in the destination. | 4 | //! a source buffer, effectively repeating the source data in the destination. |
| 5 | //! | 5 | //! |
| 6 | //! # Embassy-style features demonstrated: | 6 | //! # Embassy-style features demonstrated: |
| 7 | //! - `dma::edma_tcd()` accessor for simplified register access | ||
| 8 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | 7 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods |
| 9 | //! - No need to pass register block around | 8 | //! - No need to pass register block around |
| 10 | 9 | ||
| @@ -13,9 +12,8 @@ | |||
| 13 | 12 | ||
| 14 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 15 | use embassy_mcxa::clocks::config::Div8; | 14 | use embassy_mcxa::clocks::config::Div8; |
| 16 | use embassy_mcxa::clocks::Gate; | 15 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler}; |
| 17 | use embassy_mcxa::dma::{edma_tcd, DmaChannel, DmaCh0InterruptHandler}; | 16 | use embassy_mcxa::bind_interrupts; |
| 18 | use embassy_mcxa::{bind_interrupts, dma}; | ||
| 19 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 17 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 20 | use embassy_mcxa::pac; | 18 | use embassy_mcxa::pac; |
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 19 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -80,19 +78,7 @@ async fn main(_spawner: Spawner) { | |||
| 80 | 78 | ||
| 81 | defmt::info!("DMA wrap transfer example starting..."); | 79 | defmt::info!("DMA wrap transfer example starting..."); |
| 82 | 80 | ||
| 83 | // Enable DMA0 clock and release reset | 81 | // Enable DMA interrupt (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 82 | unsafe { |
| 97 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 83 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 98 | } | 84 | } |
| @@ -130,9 +116,6 @@ async fn main(_spawner: Spawner) { | |||
| 130 | // Create DMA channel using Embassy-style API | 116 | // Create DMA channel using Embassy-style API |
| 131 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | 117 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); |
| 132 | 118 | ||
| 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: | 119 | // Configure wrap transfer using direct TCD access: |
| 137 | // SRC is 16 bytes (4 * u32). We want to transfer 32 bytes (8 * u32). | 120 | // 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. | 121 | // SRC modulo is 16 bytes (2^4 = 16) - wraps source address. |
| @@ -140,7 +123,7 @@ async fn main(_spawner: Spawner) { | |||
| 140 | // This causes the source address to wrap around after 16 bytes, | 123 | // This causes the source address to wrap around after 16 bytes, |
| 141 | // effectively repeating the source data. | 124 | // effectively repeating the source data. |
| 142 | unsafe { | 125 | unsafe { |
| 143 | let t = edma.tcd(0); | 126 | let t = dma_ch0.tcd(); |
| 144 | 127 | ||
| 145 | // Reset channel state | 128 | // Reset channel state |
| 146 | t.ch_csr().write(|w| { | 129 | t.ch_csr().write(|w| { |
| @@ -189,14 +172,14 @@ async fn main(_spawner: Spawner) { | |||
| 189 | cortex_m::asm::dsb(); | 172 | cortex_m::asm::dsb(); |
| 190 | 173 | ||
| 191 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); | 174 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); |
| 192 | dma_ch0.trigger_start(edma); | 175 | dma_ch0.trigger_start(); |
| 193 | } | 176 | } |
| 194 | 177 | ||
| 195 | // Wait for completion using channel helper method | 178 | // Wait for completion using channel helper method |
| 196 | while !dma_ch0.is_done(edma) { | 179 | while !dma_ch0.is_done() { |
| 197 | cortex_m::asm::nop(); | 180 | cortex_m::asm::nop(); |
| 198 | } | 181 | } |
| 199 | unsafe { dma_ch0.clear_done(edma); } | 182 | unsafe { dma_ch0.clear_done(); } |
| 200 | 183 | ||
| 201 | tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") | 184 | tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") |
| 202 | .unwrap(); | 185 | .unwrap(); |
diff --git a/examples/src/bin/lpuart_dma.rs b/examples/src/bin/lpuart_dma.rs index 5ccf97ecc..4e321b111 100644 --- a/examples/src/bin/lpuart_dma.rs +++ b/examples/src/bin/lpuart_dma.rs | |||
| @@ -3,28 +3,25 @@ | |||
| 3 | //! This example demonstrates using DMA for UART TX and RX operations. | 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 | 4 | //! It sends a message using DMA, then waits for 16 characters to be received |
| 5 | //! via DMA and echoes them back. | 5 | //! via DMA and echoes them back. |
| 6 | //! | ||
| 7 | //! The DMA request sources are automatically derived from the LPUART instance type. | ||
| 8 | //! DMA clock/reset/init is handled automatically by the HAL. | ||
| 6 | 9 | ||
| 7 | #![no_std] | 10 | #![no_std] |
| 8 | #![no_main] | 11 | #![no_main] |
| 9 | 12 | ||
| 10 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 11 | use embassy_mcxa::clocks::config::Div8; | 14 | use embassy_mcxa::clocks::config::Div8; |
| 12 | use embassy_mcxa::clocks::Gate; | 15 | use embassy_mcxa::dma::{DmaCh0InterruptHandler, DmaCh1InterruptHandler}; |
| 13 | use embassy_mcxa::dma::{self, DMA_REQ_LPUART2_RX, DMA_REQ_LPUART2_TX}; | ||
| 14 | use embassy_mcxa::lpuart::{Config, LpuartDma}; | 16 | use embassy_mcxa::lpuart::{Config, LpuartDma}; |
| 15 | use embassy_mcxa::pac; | 17 | use embassy_mcxa::{bind_interrupts, pac}; |
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 18 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 17 | 19 | ||
| 18 | // DMA interrupt handlers | 20 | // Bind DMA channel interrupts using Embassy-style macro |
| 19 | #[no_mangle] | 21 | bind_interrupts!(struct Irqs { |
| 20 | pub extern "C" fn DMA_CH0() { | 22 | DMA_CH0 => DmaCh0InterruptHandler; |
| 21 | unsafe { dma::on_interrupt(0) }; | 23 | DMA_CH1 => DmaCh1InterruptHandler; |
| 22 | } | 24 | }); |
| 23 | |||
| 24 | #[no_mangle] | ||
| 25 | pub extern "C" fn DMA_CH1() { | ||
| 26 | unsafe { dma::on_interrupt(1) }; | ||
| 27 | } | ||
| 28 | 25 | ||
| 29 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| @@ -35,24 +32,7 @@ async fn main(_spawner: Spawner) { | |||
| 35 | 32 | ||
| 36 | defmt::info!("LPUART DMA example starting..."); | 33 | defmt::info!("LPUART DMA example starting..."); |
| 37 | 34 | ||
| 38 | // Enable DMA0 clock and release reset | 35 | // Enable DMA interrupts (per-channel, as needed) |
| 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 { | 36 | unsafe { |
| 57 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 37 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 58 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); | 38 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); |
| @@ -77,51 +57,29 @@ async fn main(_spawner: Spawner) { | |||
| 77 | ) | 57 | ) |
| 78 | .unwrap(); | 58 | .unwrap(); |
| 79 | 59 | ||
| 80 | // Send a message using DMA | 60 | // Send a message using DMA (DMA request source is automatically derived from LPUART2) |
| 81 | let tx_msg = b"Hello from LPUART2 DMA TX!\r\n"; | 61 | let tx_msg = b"Hello from LPUART2 DMA TX!\r\n"; |
| 82 | lpuart | 62 | lpuart.write_dma(tx_msg).await.unwrap(); |
| 83 | .write_dma(edma, DMA_REQ_LPUART2_TX, tx_msg) | ||
| 84 | .await | ||
| 85 | .unwrap(); | ||
| 86 | 63 | ||
| 87 | defmt::info!("TX DMA complete"); | 64 | defmt::info!("TX DMA complete"); |
| 88 | 65 | ||
| 89 | // Send prompt | 66 | // Send prompt |
| 90 | let prompt = b"Type 16 characters to echo via DMA:\r\n"; | 67 | let prompt = b"Type 16 characters to echo via DMA:\r\n"; |
| 91 | lpuart | 68 | lpuart.write_dma(prompt).await.unwrap(); |
| 92 | .write_dma(edma, DMA_REQ_LPUART2_TX, prompt) | ||
| 93 | .await | ||
| 94 | .unwrap(); | ||
| 95 | 69 | ||
| 96 | // Receive 16 characters using DMA | 70 | // Receive 16 characters using DMA |
| 97 | let mut rx_buf = [0u8; 16]; | 71 | let mut rx_buf = [0u8; 16]; |
| 98 | lpuart | 72 | lpuart.read_dma(&mut rx_buf).await.unwrap(); |
| 99 | .read_dma(edma, DMA_REQ_LPUART2_RX, &mut rx_buf) | ||
| 100 | .await | ||
| 101 | .unwrap(); | ||
| 102 | 73 | ||
| 103 | defmt::info!("RX DMA complete"); | 74 | defmt::info!("RX DMA complete"); |
| 104 | 75 | ||
| 105 | // Echo back the received data | 76 | // Echo back the received data |
| 106 | let echo_prefix = b"\r\nReceived: "; | 77 | let echo_prefix = b"\r\nReceived: "; |
| 107 | lpuart | 78 | lpuart.write_dma(echo_prefix).await.unwrap(); |
| 108 | .write_dma(edma, DMA_REQ_LPUART2_TX, echo_prefix) | 79 | lpuart.write_dma(&rx_buf).await.unwrap(); |
| 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"; | 80 | let done_msg = b"\r\nDone!\r\n"; |
| 116 | lpuart | 81 | lpuart.write_dma(done_msg).await.unwrap(); |
| 117 | .write_dma(edma, DMA_REQ_LPUART2_TX, done_msg) | ||
| 118 | .await | ||
| 119 | .unwrap(); | ||
| 120 | 82 | ||
| 121 | defmt::info!("Example complete"); | 83 | defmt::info!("Example complete"); |
| 122 | |||
| 123 | loop { | ||
| 124 | cortex_m::asm::wfe(); | ||
| 125 | } | ||
| 126 | } | 84 | } |
| 127 | 85 | ||
diff --git a/examples/src/bin/lpuart_ring_buffer.rs b/examples/src/bin/lpuart_ring_buffer.rs index bc666560c..d71876ade 100644 --- a/examples/src/bin/lpuart_ring_buffer.rs +++ b/examples/src/bin/lpuart_ring_buffer.rs | |||
| @@ -20,8 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | use embassy_executor::Spawner; | 21 | use embassy_executor::Spawner; |
| 22 | use embassy_mcxa::clocks::config::Div8; | 22 | use embassy_mcxa::clocks::config::Div8; |
| 23 | use embassy_mcxa::clocks::Gate; | 23 | use embassy_mcxa::dma::{DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX}; |
| 24 | use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX}; | ||
| 25 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | 24 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; |
| 26 | use embassy_mcxa::{bind_interrupts, pac}; | 25 | use embassy_mcxa::{bind_interrupts, pac}; |
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 26 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -56,20 +55,7 @@ async fn main(_spawner: Spawner) { | |||
| 56 | 55 | ||
| 57 | defmt::info!("LPUART Ring Buffer DMA example starting..."); | 56 | defmt::info!("LPUART Ring Buffer DMA example starting..."); |
| 58 | 57 | ||
| 59 | // Enable DMA0 clock and release reset | 58 | // Enable DMA interrupts (DMA clock/reset/init is handled automatically by HAL) |
| 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 { | 59 | unsafe { |
| 74 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | 60 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); |
| 75 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); | 61 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); |
| @@ -99,11 +85,10 @@ async fn main(_spawner: Spawner) { | |||
| 99 | 85 | ||
| 100 | // Create DMA channel for RX | 86 | // Create DMA channel for RX |
| 101 | let dma_ch_rx = DmaChannel::new(p.DMA_CH0); | 87 | let dma_ch_rx = DmaChannel::new(p.DMA_CH0); |
| 102 | let edma = dma::edma_tcd(); | ||
| 103 | 88 | ||
| 104 | // Configure the DMA mux for LPUART2 RX | 89 | // Configure the DMA mux for LPUART2 RX |
| 105 | unsafe { | 90 | unsafe { |
| 106 | dma_ch_rx.set_request_source(edma, DMA_REQ_LPUART2_RX); | 91 | dma_ch_rx.set_request_source(DMA_REQ_LPUART2_RX); |
| 107 | } | 92 | } |
| 108 | 93 | ||
| 109 | tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap(); | 94 | tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap(); |
| @@ -117,7 +102,7 @@ async fn main(_spawner: Spawner) { | |||
| 117 | 102 | ||
| 118 | // Enable DMA requests to start continuous reception | 103 | // Enable DMA requests to start continuous reception |
| 119 | unsafe { | 104 | unsafe { |
| 120 | dma_ch_rx.enable_request(edma); | 105 | dma_ch_rx.enable_request(); |
| 121 | } | 106 | } |
| 122 | 107 | ||
| 123 | tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap(); | 108 | tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap(); |
