aboutsummaryrefslogtreecommitdiff
path: root/examples/src/bin/lpuart_ring_buffer.rs
blob: bc666560c7c7facfbd60fd24053fa11f7e5f489b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! LPUART Ring Buffer DMA example for MCXA276.
//!
//! This example demonstrates using the new `RingBuffer` API for continuous
//! circular DMA reception from a UART peripheral.
//!
//! # Features demonstrated:
//! - `setup_circular_read()` for continuous peripheral-to-memory DMA
//! - `RingBuffer` for async reading of received data
//! - Handling of potential overrun conditions
//! - Half-transfer and complete-transfer interrupts for timely wakeups
//!
//! # How it works:
//! 1. Set up a circular DMA transfer from LPUART RX to a ring buffer
//! 2. DMA continuously writes received bytes into the buffer, wrapping around
//! 3. Application asynchronously reads data as it arrives
//! 4. Both half-transfer and complete-transfer interrupts wake the reader

#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_mcxa::clocks::config::Div8;
use embassy_mcxa::clocks::Gate;
use embassy_mcxa::dma::{self, DmaChannel, DmaCh0InterruptHandler, DmaCh1InterruptHandler, DMA_REQ_LPUART2_RX};
use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx};
use embassy_mcxa::{bind_interrupts, pac};
use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};

// Bind DMA channel interrupts
bind_interrupts!(struct Irqs {
    DMA_CH0 => DmaCh0InterruptHandler;
    DMA_CH1 => DmaCh1InterruptHandler;
});

// Ring buffer for RX - power of 2 is ideal for modulo efficiency
static mut RX_RING_BUFFER: [u8; 64] = [0; 64];

/// Helper to write a byte as hex to UART
fn write_hex(tx: &mut LpuartTx<'_, Blocking>, byte: u8) {
    const HEX: &[u8; 16] = b"0123456789ABCDEF";
    let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]];
    tx.blocking_write(&buf).ok();
}

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    // Small delay to allow probe-rs to attach after reset
    for _ in 0..100_000 {
        cortex_m::asm::nop();
    }

    let mut cfg = hal::config::Config::default();
    cfg.clock_cfg.sirc.fro_12m_enabled = true;
    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());
    let p = hal::init(cfg);

    defmt::info!("LPUART Ring Buffer DMA example starting...");

    // Enable DMA0 clock and release reset
    unsafe {
        hal::peripherals::DMA0::enable_clock();
        hal::peripherals::DMA0::release_reset();
    }

    let pac_periphs = unsafe { pac::Peripherals::steal() };

    // Initialize DMA
    unsafe {
        dma::init(&pac_periphs);
    }

    // Enable DMA interrupts
    unsafe {
        cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH0);
        cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1);
    }

    // Create UART configuration
    let config = Config {
        baudrate_bps: 115_200,
        enable_tx: true,
        enable_rx: true,
        ..Default::default()
    };

    // Create blocking UART for TX (we'll use DMA for RX only)
    let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap();
    let (mut tx, _rx) = lpuart.split();

    tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap();
    tx.blocking_write(b"==============================\r\n\r\n").unwrap();

    // Get LPUART2 RX data register address for DMA
    let lpuart2 = unsafe { &*pac::Lpuart2::ptr() };
    let rx_data_addr = lpuart2.data().as_ptr() as *const u8;

    // Enable RX DMA request in LPUART
    lpuart2.baud().modify(|_, w| w.rdmae().enabled());

    // Create DMA channel for RX
    let dma_ch_rx = DmaChannel::new(p.DMA_CH0);
    let edma = dma::edma_tcd();

    // Configure the DMA mux for LPUART2 RX
    unsafe {
        dma_ch_rx.set_request_source(edma, DMA_REQ_LPUART2_RX);
    }

    tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n").unwrap();

    // Set up the ring buffer with circular DMA
    // This configures the DMA for continuous reception
    let ring_buf = unsafe {
        let buf = &mut *core::ptr::addr_of_mut!(RX_RING_BUFFER);
        dma_ch_rx.setup_circular_read(rx_data_addr, buf)
    };

    // Enable DMA requests to start continuous reception
    unsafe {
        dma_ch_rx.enable_request(edma);
    }

    tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n").unwrap();
    tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n").unwrap();

    // Main loop: read from ring buffer and echo back
    let mut read_buf = [0u8; 16];
    let mut total_received: usize = 0;

    loop {
        // Async read - waits until data is available
        match ring_buf.read(&mut read_buf).await {
            Ok(n) if n > 0 => {
                total_received += n;

                // Echo back what we received
                tx.blocking_write(b"RX[").unwrap();
                for (i, &byte) in read_buf.iter().enumerate().take(n) {
                    write_hex(&mut tx, byte);
                    if i < n - 1 {
                        tx.blocking_write(b" ").unwrap();
                    }
                }
                tx.blocking_write(b"]: ").unwrap();
                tx.blocking_write(&read_buf[..n]).unwrap();
                tx.blocking_write(b"\r\n").unwrap();

                defmt::info!("Received {} bytes, total: {}", n, total_received);
            }
            Ok(_) => {
                // No data, shouldn't happen with async read
            }
            Err(_) => {
                // Overrun detected
                tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap();
                defmt::error!("Ring buffer overrun!");
                ring_buf.clear();
            }
        }
    }
}