diff --git a/dma_gpio/src/bin/main.rs b/dma_gpio/src/bin/main.rs index 6ef40d3..3bb955f 100644 --- a/dma_gpio/src/bin/main.rs +++ b/dma_gpio/src/bin/main.rs @@ -8,18 +8,20 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; 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_uart_rx::GpioUartRx; +use dma_gpio::gpio_dma_uart_rx::GpioDmaRx; 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::{ +use dma_gpio::gpio_dma_uart_tx::{ write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig, }; @@ -29,13 +31,15 @@ static PIPE_RX: Pipe = Pipe::new(); // Baud rate: one TIM6 update equals one UART bit-time const BAUD: u32 = 115_200; const TX_PIN_BIT: u8 = 2; // PA2 -const OVERSAMPLE: u16 = 16; +const TX_OVERSAMPLE: u16 = 1; +const RX_OVERSAMPLE: u16 = 16; const UART_CFG: UartConfig = UartConfig { data_bits: 8, parity: Parity::None, stop_bits: StopBits::One, }; +static mut RX_DMA_BUF: [u32; 512] = [0; 512]; fn dump_tim6_regs() { // PAC path for STM32U5: module `tim6`, type `Tim6` with ptr() @@ -80,41 +84,45 @@ fn dump_dma_ch0_regs() { ); } -#[embassy_executor::task] -async fn rx_task(pin: Input<'static>) { - let mut rx = GpioUartRx::new(pin, BAUD, UART_CFG, &PIPE_RX); - rx.run().await; -} - #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - info!("DMA Pipe -> GPIO UART-like TX"); + info!("Hehe"); + // PA3 as Rx "wire" let pa3_rx = Input::new(p.PA3, Pull::Up); - spawner.spawn(rx_task(pa3_rx)).unwrap(); // PA2 is the TX "wire" let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh); // drop(_pa2); - init_tim6_for_uart(p.TIM6, BAUD, OVERSAMPLE); + init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE); // TX + init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE); // RX dump_tim6_regs(); // Start DMA consumer task spawner.spawn(dma_tx_task(p.GPDMA1_CH0)).unwrap(); + spawner.spawn(rx_dma_task(p.GPDMA1_CH1)).unwrap(); // Example: transmit a string as UART frames via the Pipe loop { write_uart_frames_to_pipe( &PIPE_TX, TX_PIN_BIT, - b"Hello, DMA UART (configurable)!\r\n", + 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>) { + let buf_ptr = core::ptr::addr_of_mut!(RX_DMA_BUF); + let buf = unsafe { &mut *buf_ptr }; + let mut rx = GpioDmaRx::new(ch, 3 /*PA3 bit*/, buf, &PIPE_RX); + rx.run().await; +} + #[embassy_executor::task] async fn dma_tx_task(ch: Peri<'static, GPDMA1_CH0>) { let mut tx = GpioDmaBsrrTx::new(ch); diff --git a/dma_gpio/src/dma_timer.rs b/dma_gpio/src/dma_timer.rs index b897dbc..6979383 100644 --- a/dma_gpio/src/dma_timer.rs +++ b/dma_gpio/src/dma_timer.rs @@ -1,51 +1,58 @@ // src/dma_timer.rs use embassy_stm32::{ - peripherals::TIM6, + peripherals::{TIM6, TIM7}, rcc, timer::low_level::Timer, Peri, }; use core::mem; +use embassy_stm32::timer::BasicInstance; use embassy_stm32::pac::timer::vals::Urs; /// Initializes TIM6 to tick at `baud * oversample` frequency. /// Each TIM6 update event triggers one DMA beat. pub fn init_tim6_for_uart<'d>(tim6: Peri<'d, TIM6>, baud: u32, oversample: u16) { - rcc::enable_and_reset::(); let ll = Timer::new(tim6); + configure_basic_timer(&ll, baud, oversample); + mem::forget(ll); +} - let f_tim6 = rcc::frequency::().0; +/// Initializes TIM7 to tick at `baud * oversample` frequency. +/// Each TIM7 update event triggers one DMA beat. +pub fn init_tim7_for_uart<'d>(tim7: Peri<'d, TIM7>, baud: u32, oversample: u16) { + rcc::enable_and_reset::(); + let ll = Timer::new(tim7); + configure_basic_timer(&ll, baud, oversample); + mem::forget(ll); +} + +// Shared internal helper — identical CR1/ARR setup +fn configure_basic_timer(ll: &Timer<'_, T>, baud: u32, oversample: u16) { + let f_timer = rcc::frequency::().0; let target = baud.saturating_mul(oversample.max(1) as u32).max(1); - // Compute ARR (prescaler=0) - let mut arr = (f_tim6 / target).saturating_sub(1) as u16; - if arr == 0 { - arr = 1; - } + // Compute ARR (prescaler = 0) + let mut arr = (f_timer / target).saturating_sub(1) as u16; + if arr == 0 { arr = 1; } ll.regs_basic().cr1().write(|w| { w.set_cen(false); w.set_opm(false); - w.set_udis(false); // boolean field: false = allow UEV - w.set_urs(Urs::ANY_EVENT); // enum field: DMA+interrupts on any event + w.set_udis(false); + w.set_urs(Urs::ANY_EVENT); }); - // Write prescaler directly (simple u16 register) ll.regs_basic().psc().write_value(0u16); - - // Set ARR, enable DMA request, issue first update ll.regs_basic().arr().write(|w| w.set_arr(arr)); ll.regs_basic().dier().modify(|w| w.set_ude(true)); ll.regs_basic().egr().write(|w| w.set_ug(true)); - // Start timer ll.regs_basic().cr1().write(|w| { w.set_opm(false); w.set_cen(true); w.set_udis(false); w.set_urs(Urs::ANY_EVENT); }); - mem::forget(ll); // KEEP THE TIMER ALIVE } diff --git a/dma_gpio/src/gpio_dma_uart_rx.rs b/dma_gpio/src/gpio_dma_uart_rx.rs new file mode 100644 index 0000000..cf100c6 --- /dev/null +++ b/dma_gpio/src/gpio_dma_uart_rx.rs @@ -0,0 +1,57 @@ +// src/gpio_dma_uart_rx.rs +use embassy_stm32::{ + dma::{Request, Transfer, TransferOptions}, + peripherals::GPDMA1_CH1, + Peri, +}; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; + +// RM0456 tabulka 137 +pub const TIM7_UP_REQ: Request = 5; + +pub struct GpioDmaRx<'d, const N: usize> { + ch: Peri<'d, GPDMA1_CH1>, + pin_bit: u8, + buf: &'d mut [u32; N], + opts: TransferOptions, + pipe_rx: &'d Pipe, +} + +impl<'d, const N: usize> GpioDmaRx<'d, N> { + pub fn new( + ch: Peri<'d, GPDMA1_CH1>, + pin_bit: u8, + buf: &'d mut [u32; N], + pipe_rx: &'d Pipe, + ) -> Self { + Self { + ch, + pin_bit, + buf, + opts: TransferOptions::default(), + pipe_rx, + } + } + + pub async fn run(&mut self) -> ! { + loop { + let gpioa_idr_addr = embassy_stm32::pac::GPIOA.as_ptr() as *mut u32; + + unsafe { + Transfer::new_read( + self.ch.reborrow(), + TIM7_UP_REQ, + gpioa_idr_addr, + &mut self.buf[..], + self.opts, + ) + } + .await; + + for &word in self.buf.iter() { + let bit_high = ((word >> self.pin_bit) & 1) as u8; + self.pipe_rx.write(&[bit_high]).await; + } + } + } +} diff --git a/dma_gpio/src/gpio_dma_uart.rs b/dma_gpio/src/gpio_dma_uart_tx.rs similarity index 99% rename from dma_gpio/src/gpio_dma_uart.rs rename to dma_gpio/src/gpio_dma_uart_tx.rs index f592f72..f1b6fc2 100644 --- a/dma_gpio/src/gpio_dma_uart.rs +++ b/dma_gpio/src/gpio_dma_uart_tx.rs @@ -6,6 +6,7 @@ use embassy_stm32::{ }; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; +// kapitola 17.4.11 - 2 casovace pre 2 DMA pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687 STM32U5xx datasheet #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/dma_gpio/src/gpio_uart_rx.rs b/dma_gpio/src/gpio_uart_rx.rs deleted file mode 100644 index a72da8e..0000000 --- a/dma_gpio/src/gpio_uart_rx.rs +++ /dev/null @@ -1,48 +0,0 @@ -// src/gpio_uart_rx.rs - -use embassy_stm32::gpio::{Input, Level, Pull}; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; -use embassy_time::{Timer, Duration}; -use crate::gpio_dma_uart::UartConfig; - -/// Simplified bit-banged UART RX using a timer for sampling. -/// Sample pin every bit-time once start bit is detected. -pub struct GpioUartRx<'d, const N: usize> { - pin: Input<'d>, - baud: u32, - cfg: UartConfig, - pipe: &'d Pipe, -} - -impl<'d, const N: usize> GpioUartRx<'d, N> { - pub fn new(pin: Input<'d>, baud: u32, cfg: UartConfig, pipe: &'d Pipe) -> Self { - Self { pin, baud, cfg, pipe } - } - - /// Async read loop: waits for start bit, samples bits, pushes received byte into pipe. - pub async fn run(&mut self) -> ! { - let bit_time = Duration::from_hz(self.baud.into()); - - loop { - // Wait for start bit edge - while self.pin.get_level() == Level::High { - Timer::after_micros(1).await; - } - - // Skip only 1 bit-time since you *don’t* want 1.5 bit delay - Timer::after(bit_time).await; - - let nbits = self.cfg.data_bits.clamp(5, 8); - let mut data = 0u8; - for i in 0..nbits { - if self.pin.get_level() == Level::High { - data |= 1 << i; - } - Timer::after(bit_time).await; - } - - // push into Pipe as soon as byte is complete - self.pipe.write(&[data]).await; - } - } -} diff --git a/dma_gpio/src/lib.rs b/dma_gpio/src/lib.rs index 92f6125..1076c81 100644 --- a/dma_gpio/src/lib.rs +++ b/dma_gpio/src/lib.rs @@ -1,8 +1,9 @@ #![no_std] -pub mod gpio_dma_uart; -pub use gpio_dma_uart::*; - +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 gpio_uart_rx;