// 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_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::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_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 const BAUD: u32 = 115_200; const TX_PIN_BIT: u8 = 2; // PA2 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() 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"); // PA3 as Rx "wire" let pa3_rx = Input::new(p.PA3, Pull::Up); // 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(); // 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 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); 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(); } } } }