From 4365c72688329c1a7efd1b6d8fe665c2e05115b3 Mon Sep 17 00:00:00 2001 From: Priec Date: Sat, 1 Nov 2025 23:35:27 +0100 Subject: [PATCH] complete movement, some parts are destroyed and not moved yet --- dma_gpio/src/bin/main.rs | 186 ++++-------------- dma_gpio/src/lib.rs | 8 +- dma_gpio/src/software_uart/debug.rs | 43 ++++ dma_gpio/src/{ => software_uart}/dma_timer.rs | 0 .../{ => software_uart}/gpio_dma_uart_rx.rs | 0 .../{ => software_uart}/gpio_dma_uart_tx.rs | 0 dma_gpio/src/software_uart/mod.rs | 13 ++ dma_gpio/src/software_uart/runtime.rs | 61 ++++++ 8 files changed, 153 insertions(+), 158 deletions(-) create mode 100644 dma_gpio/src/software_uart/debug.rs rename dma_gpio/src/{ => software_uart}/dma_timer.rs (100%) rename dma_gpio/src/{ => software_uart}/gpio_dma_uart_rx.rs (100%) rename dma_gpio/src/{ => software_uart}/gpio_dma_uart_tx.rs (100%) create mode 100644 dma_gpio/src/software_uart/mod.rs create mode 100644 dma_gpio/src/software_uart/runtime.rs diff --git a/dma_gpio/src/bin/main.rs b/dma_gpio/src/bin/main.rs index 9331f45..3b0bd86 100644 --- a/dma_gpio/src/bin/main.rs +++ b/dma_gpio/src/bin/main.rs @@ -1,178 +1,62 @@ // src/bin/main.rs - #![no_std] #![no_main] use defmt::*; use embassy_executor::Spawner; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; +use embassy_stm32::gpio::{Input, Output, Level, Pull, Speed}; use embassy_time::{Duration, Timer}; -use dma_gpio::dma_timer::init_tim6_for_uart; -use dma_gpio::dma_timer::init_tim7_for_uart; -use embassy_stm32::gpio::Input; -use embassy_stm32::gpio::Pull; -use dma_gpio::gpio_dma_uart_rx::TIM7_UP_REQ; -use embassy_stm32::dma::{ReadableRingBuffer as DmaRingRx, TransferOptions}; +use dma_gpio::software_uart::{ + dma_timer::{init_tim6_for_uart, init_tim7_for_uart}, + gpio_dma_uart_tx::{write_uart_frames_to_pipe, UartConfig, Parity, StopBits}, + runtime::{rx_dma_task, tx_dma_task}, + debug::dump_tim6_regs, +}; use {defmt_rtt as _, panic_probe as _}; -use embassy_stm32::{ - gpio::{Level, Output, Speed}, - peripherals::GPDMA1_CH0, - peripherals::GPDMA1_CH1, -}; -use embassy_stm32::Peri; - -use dma_gpio::gpio_dma_uart_tx::{ - write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig, -}; - -static PIPE_TX: Pipe = Pipe::new(); -static PIPE_RX: Pipe = Pipe::new(); - -// Baud rate: one TIM6 update equals one UART bit-time +/// SOFTWARE UART CONFIGURATION const BAUD: u32 = 115_200; const TX_PIN_BIT: u8 = 2; // PA2 const TX_OVERSAMPLE: u16 = 1; const RX_OVERSAMPLE: u16 = 16; +const PIPE_TX_SIZE: usize = 256; +const PIPE_RX_SIZE: usize = 256; const RX_RING_BYTES: usize = 4096; -const UART_CFG: UartConfig = UartConfig { - data_bits: 8, - parity: Parity::None, - stop_bits: StopBits::One, -}; +static PIPE_TX: Pipe = Pipe::new(); +static PIPE_RX: Pipe = Pipe::new(); +// Large hardware RX DMA circular buffer, scales with oversampling rate static mut RX_RING: [u8; RX_RING_BYTES] = [0; RX_RING_BYTES]; -fn dump_tim6_regs() { - // PAC path for STM32U5: module `tim6`, type `Tim6` with ptr() - use embassy_stm32::pac::timer::TimBasic; - let tim = unsafe { TimBasic::from_ptr(0x4000_1000usize as _) }; - let sr = tim.sr().read(); - let dier = tim.dier().read(); - let cr1 = tim.cr1().read(); - let arr = tim.arr().read().arr(); - let psc = tim.psc().read(); - info!( - "TIM6: CR1.CEN={} DIER.UDE={} SR.UIF={} PSC={} ARR={}", - cr1.cen(), - dier.ude(), - sr.uif(), - psc, - arr - ); -} - -fn dump_dma_ch0_regs() { - // PAC path for GPDMA1: module `gpdma1`, type `Gpdma1` - use embassy_stm32::pac::gpdma::Gpdma; - let dma = unsafe { Gpdma::from_ptr(0x4002_0000usize as _) }; - let ch = dma.ch(0); - let cr = ch.cr().read(); - let tr1 = ch.tr1().read(); - let tr2 = ch.tr2().read(); - let br1 = ch.br1().read(); - info!( - "GPDMA1_CH0: EN={} PRIO={} SDW={} DDW={} SINC={} DINC={} REQSEL={} SWREQ={} DREQ={} BNDT={}", - cr.en(), - cr.prio(), - tr1.sdw(), - tr1.ddw(), - tr1.sinc(), - tr1.dinc(), - tr2.reqsel(), - tr2.swreq(), - tr2.dreq(), - br1.bndt() - ); -} - #[embassy_executor::main] async fn main(spawner: Spawner) { - let p = embassy_stm32::init(Default::default()); - info!("Hehe"); + // Initialize peripheral access + let mut p = embassy_stm32::init(Default::default()); + info!("UART DMA demo starting..."); - // PA3 as Rx "wire" - let pa3_rx = Input::new(p.PA3, Pull::Up); + // GPIO setup + let rx = Input::new(p.PA3, Pull::Up); + let _tx = Output::new(p.PA2, Level::High, Speed::VeryHigh); - // PA2 is the TX "wire" - let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh); - // drop(_pa2); - init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE); // TX - init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE); // RX - dump_tim6_regs(); + // Timer setup + init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE); + init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE); + dump_tim6_regs(); // debug - // Start DMA consumer task - spawner.spawn(tx_dma_task(p.GPDMA1_CH0).unwrap()); - spawner.spawn(rx_dma_task(p.GPDMA1_CH1).unwrap()); + // Spawn DMA tasks + spawner.spawn(tx_dma_task(p.GPDMA1_CH0)).unwrap(); + spawner.spawn(rx_dma_task(p.GPDMA1_CH1)).unwrap(); - // Example: transmit a string as UART frames via the Pipe + // UART config (8N1 default) + let uart_cfg = UartConfig { + data_bits: 8, + parity: Parity::None, + stop_bits: StopBits::One, + }; + + // Main loop: send data periodically loop { - write_uart_frames_to_pipe( - &PIPE_TX, - TX_PIN_BIT, - b"Hello marshmallow\r\n", - &UART_CFG, - ).await; + write_uart_frames_to_pipe(&PIPE_TX, TX_PIN_BIT, b"Hello marshmallow\r\n", &uart_cfg).await; Timer::after(Duration::from_secs(2)).await; } } - -#[embassy_executor::task] -async fn rx_dma_task(ch: Peri<'static, GPDMA1_CH1>) { - // SAFETY Own the static RX buffer exclusively. - let ring_ptr = core::ptr::addr_of_mut!(RX_RING); - let ring = unsafe { &mut *ring_ptr }; - - // DMA will read samples from GPIOA input register. - // Each sample is one byte (PA0..PA7). PA3 lives in bit 3. - let gpioa_idr = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8; - - let mut opts = TransferOptions::default(); - opts.half_transfer_ir = true; // wake at half buffer - opts.complete_transfer_ir = true; // wake at full buffer - - // Start hardware-linked circular DMA into ring. - let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) }; - rx.start(); - - // Drain in fixed chunks when CPU has time - let mut chunk = [0u8; 256]; - loop { - // Wait for a full chunk - // Wakes on half/full buffer events - let _ = rx.read_exact(&mut chunk).await; - // Forward raw samples to PIPE_RX (decode later) - PIPE_RX.write(&chunk).await; - } -} - -#[embassy_executor::task] -async fn tx_dma_task(ch: Peri<'static, GPDMA1_CH0>) { - let mut tx = GpioDmaBsrrTx::new(ch); - - info!("DMA task started, waiting for frames..."); - loop { - // Read one 32-bit BSRR word (4 bytes) from the Pipe - let mut b = [0u8; 4]; - let n = PIPE_TX.read(&mut b).await; - if n != 4 { - continue; - } - let w = u32::from_le_bytes(b); - - info!("DMA write 0x{:08X} -> GPIOA.BSRR", w); - match embassy_time::with_timeout( - Duration::from_millis(20), - tx.write_word(w), - ) - .await - { - Ok(()) => {} - Err(_) => { - warn!("DMA timeout: no TIM6 request"); - dump_tim6_regs(); - dump_dma_ch0_regs(); - } - } - } -} diff --git a/dma_gpio/src/lib.rs b/dma_gpio/src/lib.rs index 1076c81..869e157 100644 --- a/dma_gpio/src/lib.rs +++ b/dma_gpio/src/lib.rs @@ -1,9 +1,3 @@ #![no_std] -pub mod gpio_dma_uart_tx; -pub mod gpio_dma_uart_rx; -pub mod dma_timer; - -pub use gpio_dma_uart_tx::*; -pub use gpio_dma_uart_rx::*; -pub use dma_timer::*; +pub mod software_uart; diff --git a/dma_gpio/src/software_uart/debug.rs b/dma_gpio/src/software_uart/debug.rs new file mode 100644 index 0000000..ae6a256 --- /dev/null +++ b/dma_gpio/src/software_uart/debug.rs @@ -0,0 +1,43 @@ +// src/software_uart/debug.rs +use defmt::info; + +pub fn dump_tim6_regs() { + use embassy_stm32::pac::timer::TimBasic; + let tim = unsafe { TimBasic::from_ptr(0x4000_1000usize as _) }; + let sr = tim.sr().read(); + let dier = tim.dier().read(); + let cr1 = tim.cr1().read(); + let arr = tim.arr().read().arr(); + let psc = tim.psc().read(); + info!( + "TIM6: CR1.CEN={} DIER.UDE={} SR.UIF={} PSC={} ARR={}", + cr1.cen(), + dier.ude(), + sr.uif(), + psc, + arr + ); +} + +pub fn dump_dma_ch0_regs() { + use embassy_stm32::pac::gpdma::Gpdma; + let dma = unsafe { Gpdma::from_ptr(0x4002_0000usize as _) }; + let ch = dma.ch(0); + let cr = ch.cr().read(); + let tr1 = ch.tr1().read(); + let tr2 = ch.tr2().read(); + let br1 = ch.br1().read(); + info!( + "GPDMA1_CH0: EN={} PRIO={} SDW={} DDW={} SINC={} DINC={} REQSEL={} SWREQ={} DREQ={} BNDT={}", + cr.en(), + cr.prio(), + tr1.sdw(), + tr1.ddw(), + tr1.sinc(), + tr1.dinc(), + tr2.reqsel(), + tr2.swreq(), + tr2.dreq(), + br1.bndt() + ); +} diff --git a/dma_gpio/src/dma_timer.rs b/dma_gpio/src/software_uart/dma_timer.rs similarity index 100% rename from dma_gpio/src/dma_timer.rs rename to dma_gpio/src/software_uart/dma_timer.rs diff --git a/dma_gpio/src/gpio_dma_uart_rx.rs b/dma_gpio/src/software_uart/gpio_dma_uart_rx.rs similarity index 100% rename from dma_gpio/src/gpio_dma_uart_rx.rs rename to dma_gpio/src/software_uart/gpio_dma_uart_rx.rs diff --git a/dma_gpio/src/gpio_dma_uart_tx.rs b/dma_gpio/src/software_uart/gpio_dma_uart_tx.rs similarity index 100% rename from dma_gpio/src/gpio_dma_uart_tx.rs rename to dma_gpio/src/software_uart/gpio_dma_uart_tx.rs diff --git a/dma_gpio/src/software_uart/mod.rs b/dma_gpio/src/software_uart/mod.rs new file mode 100644 index 0000000..55a845d --- /dev/null +++ b/dma_gpio/src/software_uart/mod.rs @@ -0,0 +1,13 @@ +// src/software_uart/mod.rs + +pub mod gpio_dma_uart_tx; +pub mod gpio_dma_uart_rx; +pub mod dma_timer; +pub mod runtime; +pub mod debug; + +pub use gpio_dma_uart_tx::*; +pub use gpio_dma_uart_rx::*; +pub use dma_timer::*; +pub use runtime::*; +pub use debug::*; diff --git a/dma_gpio/src/software_uart/runtime.rs b/dma_gpio/src/software_uart/runtime.rs new file mode 100644 index 0000000..c111745 --- /dev/null +++ b/dma_gpio/src/software_uart/runtime.rs @@ -0,0 +1,61 @@ +// src/software_uart/runtime.rs +use defmt::{info, warn}; +use embassy_executor::task; +use embassy_stm32::{ + dma::{ReadableRingBuffer as DmaRingRx, TransferOptions}, + peripherals::{GPDMA1_CH0, GPDMA1_CH1}, + Peri, +}; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; +use embassy_time::Duration; +use crate::software_uart::{ + gpio_dma_uart_rx::TIM7_UP_REQ, gpio_dma_uart_tx::GpioDmaBsrrTx, + debug::{dump_dma_ch0_regs, dump_tim6_regs}, +}; + +/// RX DMA task: reads GPIO samples paced by TIM7 and fills PIPE_RX +#[task] +pub async fn rx_dma_task(ch: Peri<'static, GPDMA1_CH1>) { + let ring = unsafe { &mut RX_RING }; + let gpioa_idr = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8; + + let mut opts = TransferOptions::default(); + opts.half_transfer_ir = true; + opts.complete_transfer_ir = true; + + let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) }; + rx.start(); + + let mut chunk = [0u8; 256]; + loop { + let _ = rx.read_exact(&mut chunk).await; + PIPE_RX.write(&chunk).await; + } +} + +/// TX DMA task: dequeues prebuilt frames from PIPE_TX and writes to GPIOA.BSRR +#[task] +pub async fn tx_dma_task(ch: Peri<'static, GPDMA1_CH0>) { + let mut tx = GpioDmaBsrrTx::new(ch); + info!("DMA TX task started"); + + loop { + let mut b = [0u8; 4]; + let n = PIPE_TX.read(&mut b).await; + if n != 4 { + continue; + } + + let w = u32::from_le_bytes(b); + info!("DMA write 0x{:08X} -> GPIOA.BSRR", w); + + match embassy_time::with_timeout(Duration::from_millis(20), tx.write_word(w)).await { + Ok(()) => {} + Err(_) => { + warn!("DMA timeout: no TIM6 request"); + dump_tim6_regs(); + dump_dma_ch0_regs(); + } + } + } +}