aboutsummaryrefslogtreecommitdiff
path: root/examples/src/bin/lpuart_ring_buffer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/src/bin/lpuart_ring_buffer.rs')
-rw-r--r--examples/src/bin/lpuart_ring_buffer.rs162
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
21use embassy_executor::Spawner;
22use embassy_mcxa::clocks::config::Div8;
23use embassy_mcxa::clocks::Gate;
24use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX};
25use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
26use embassy_mcxa::{bind_interrupts, pac};
27use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
28
29// Bind DMA channel interrupts
30bind_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
36static mut RX_RING_BUFFER: [u8; 64] = [0; 64];
37
38/// Helper to write a byte as hex to UART
39fn 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]
46async 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