diff options
Diffstat (limited to 'examples/src/bin/lpuart_ring_buffer.rs')
| -rw-r--r-- | examples/src/bin/lpuart_ring_buffer.rs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/examples/src/bin/lpuart_ring_buffer.rs b/examples/src/bin/lpuart_ring_buffer.rs new file mode 100644 index 000000000..bc666560c --- /dev/null +++ b/examples/src/bin/lpuart_ring_buffer.rs | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | //! LPUART Ring Buffer DMA example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using the new `RingBuffer` API for continuous | ||
| 4 | //! circular DMA reception from a UART peripheral. | ||
| 5 | //! | ||
| 6 | //! # Features demonstrated: | ||
| 7 | //! - `setup_circular_read()` for continuous peripheral-to-memory DMA | ||
| 8 | //! - `RingBuffer` for async reading of received data | ||
| 9 | //! - Handling of potential overrun conditions | ||
| 10 | //! - Half-transfer and complete-transfer interrupts for timely wakeups | ||
| 11 | //! | ||
| 12 | //! # How it works: | ||
| 13 | //! 1. Set up a circular DMA transfer from LPUART RX to a ring buffer | ||
| 14 | //! 2. DMA continuously writes received bytes into the buffer, wrapping around | ||
| 15 | //! 3. Application asynchronously reads data as it arrives | ||
| 16 | //! 4. Both half-transfer and complete-transfer interrupts wake the reader | ||
| 17 | |||
| 18 | #![no_std] | ||
| 19 | #![no_main] | ||
| 20 | |||
| 21 | use embassy_executor::Spawner; | ||
| 22 | use embassy_mcxa::clocks::config::Div8; | ||
| 23 | use embassy_mcxa::clocks::Gate; | ||
| 24 | use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX}; | ||
| 25 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 26 | use embassy_mcxa::{bind_interrupts, pac}; | ||
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 28 | |||
| 29 | // Bind DMA channel interrupts | ||
| 30 | bind_interrupts!(struct Irqs { | ||
| 31 | DMA_CH0 => DmaCh0InterruptHandler; | ||
| 32 | DMA_CH1 => DmaCh1InterruptHandler; | ||
| 33 | }); | ||
| 34 | |||
| 35 | // Ring buffer for RX - power of 2 is ideal for modulo efficiency | ||
| 36 | static mut RX_RING_BUFFER: [u8; 64] = [0; 64]; | ||
| 37 | |||
| 38 | /// Helper to write a byte as hex to UART | ||
| 39 | fn write_hex(tx: &mut LpuartTx<'_, Blocking>, byte: u8) { | ||
| 40 | const HEX: &[u8; 16] = b"0123456789ABCDEF"; | ||
| 41 | let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]]; | ||
| 42 | tx.blocking_write(&buf).ok(); | ||
| 43 | } | ||
| 44 | |||
| 45 | #[embassy_executor::main] | ||
| 46 | async fn main(_spawner: Spawner) { | ||
| 47 | // Small delay to allow probe-rs to attach after reset | ||
| 48 | for _ in 0..100_000 { | ||
| 49 | cortex_m::asm::nop(); | ||
| 50 | } | ||
| 51 | |||
| 52 | let mut cfg = hal::config::Config::default(); | ||
| 53 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 54 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 55 | let p = hal::init(cfg); | ||
| 56 | |||
| 57 | defmt::info!("LPUART Ring Buffer DMA example starting..."); | ||
| 58 | |||
| 59 | // Enable DMA0 clock and release reset | ||
| 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 { | ||
| 74 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0); | ||
| 75 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); | ||
| 76 | } | ||
| 77 | |||
| 78 | // Create UART configuration | ||
| 79 | let config = Config { | ||
| 80 | baudrate_bps: 115_200, | ||
| 81 | enable_tx: true, | ||
| 82 | enable_rx: true, | ||
| 83 | ..Default::default() | ||
| 84 | }; | ||
| 85 | |||
| 86 | // Create blocking UART for TX (we'll use DMA for RX only) | ||
| 87 | let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); | ||
| 88 | let (mut tx, _rx) = lpuart.split(); | ||
| 89 | |||
| 90 | tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap(); | ||
| 91 | tx.blocking_write(b"==============================\r\n\r\n").unwrap(); | ||
| 92 | |||
| 93 | // Get LPUART2 RX data register address for DMA | ||
| 94 | let lpuart2 = unsafe { &*pac::Lpuart2::ptr() }; | ||
| 95 | let rx_data_addr = lpuart2.data().as_ptr() as *const u8; | ||
| 96 | |||
| 97 | // Enable RX DMA request in LPUART | ||
| 98 | lpuart2.baud().modify(|_, w| w.rdmae().enabled()); | ||
| 99 | |||
| 100 | // Create DMA channel for RX | ||
| 101 | let dma_ch_rx = DmaChannel::new(p.DMA_CH0); | ||
| 102 | let edma = dma::edma_tcd(); | ||
| 103 | |||
| 104 | // Configure the DMA mux for LPUART2 RX | ||
| 105 | unsafe { | ||
| 106 | dma_ch_rx.set_request_source(edma, DMA_REQ_LPUART2_RX); | ||
| 107 | } | ||
| 108 | |||
| 109 | tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap(); | ||
| 110 | |||
| 111 | // Set up the ring buffer with circular DMA | ||
| 112 | // This configures the DMA for continuous reception | ||
| 113 | let ring_buf = unsafe { | ||
| 114 | let buf = &mut *core::ptr::addr_of_mut!(RX_RING_BUFFER); | ||
| 115 | dma_ch_rx.setup_circular_read(rx_data_addr, buf) | ||
| 116 | }; | ||
| 117 | |||
| 118 | // Enable DMA requests to start continuous reception | ||
| 119 | unsafe { | ||
| 120 | dma_ch_rx.enable_request(edma); | ||
| 121 | } | ||
| 122 | |||
| 123 | tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap(); | ||
| 124 | tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n").unwrap(); | ||
| 125 | |||
| 126 | // Main loop: read from ring buffer and echo back | ||
| 127 | let mut read_buf = [0u8; 16]; | ||
| 128 | let mut total_received: usize = 0; | ||
| 129 | |||
| 130 | loop { | ||
| 131 | // Async read - waits until data is available | ||
| 132 | match ring_buf.read(&mut read_buf).await { | ||
| 133 | Ok(n) if n > 0 => { | ||
| 134 | total_received += n; | ||
| 135 | |||
| 136 | // Echo back what we received | ||
| 137 | tx.blocking_write(b"RX[").unwrap(); | ||
| 138 | for (i, &byte) in read_buf.iter().enumerate().take(n) { | ||
| 139 | write_hex(&mut tx, byte); | ||
| 140 | if i < n - 1 { | ||
| 141 | tx.blocking_write(b" ").unwrap(); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | tx.blocking_write(b"]: ").unwrap(); | ||
| 145 | tx.blocking_write(&read_buf[..n]).unwrap(); | ||
| 146 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 147 | |||
| 148 | defmt::info!("Received {} bytes, total: {}", n, total_received); | ||
| 149 | } | ||
| 150 | Ok(_) => { | ||
| 151 | // No data, shouldn't happen with async read | ||
| 152 | } | ||
| 153 | Err(_) => { | ||
| 154 | // Overrun detected | ||
| 155 | tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap(); | ||
| 156 | defmt::error!("Ring buffer overrun!"); | ||
| 157 | ring_buf.clear(); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
