// 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 {defmt_rtt as _, panic_probe as _}; use embassy_stm32::{ dma::{Request, Transfer, TransferOptions}, gpio::{Level, Output, Speed}, peripherals::{GPDMA1_CH0, TIM6}, rcc, timer::low_level::Timer as LlTimer, }; use embassy_stm32::Peri; static PIPE: Pipe = Pipe::new(); // One DMA beat per TIM6 update const RATE_HZ: u32 = 100; // GPDMA REQSEL for TIM6 update (RM0456 Table 137 -> 4) const TIM6_UP_REQ: Request = 4; #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("DMA Pipe -> GPIO (single pin) using GPDMA HAL"); // Single pin: PA2 as output (DMA will hit PA2->BSRR) let _pa2 = Output::new(p.PA2, Level::Low, Speed::VeryHigh); drop(_pa2); // TIM6: enable Update DMA request at RATE_HZ let tim6 = LlTimer::new(p.TIM6); let f_tim6 = rcc::frequency::().0; let arr = (f_tim6 / RATE_HZ).saturating_sub(1) as u16; tim6.regs_basic().arr().modify(|w| w.set_arr(arr)); tim6.regs_basic().dier().modify(|w| w.set_ude(true)); tim6.regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); // DMA worker: reads u32 words (as 4 bytes) and writes to GPIOA BSRR spawner.spawn(dma_tx_task(p.GPDMA1_CH0)).unwrap(); // Producer: PA2 logic every second // The pattern repeats every 4 timer ticks: HIGH, HIGH, HIGH, LOW. let high = (1u32 << 2).to_le_bytes(); // PA2 high let low = (1u32 << (2 + 16)).to_le_bytes(); // PA2 low let pattern = [high, high, high, low]; loop { for chunk in pattern { PIPE.write(&chunk).await; info!("Producer queued pattern word: [{:02X} {:02X} {:02X} {:02X}]", chunk[0], chunk[1], chunk[2], chunk[3]); // No long wait — the timer pacing handles timing. // Small pause just to keep logging readable. Timer::after_millis(200).await; } } } // DMA task: for each 4 bytes from PIPE, submit one 32-bit write to GPIOA->BSRR, // paced by TIM6 update requests (REQ=4). #[embassy_executor::task] async fn dma_tx_task(mut ch: Peri<'static, GPDMA1_CH0>) { // Raw destination address (GPIOA->BSRR register) let bsrr_ptr = embassy_stm32::pac::GPIOA.bsrr().as_ptr() as *mut u32; let opts = TransferOptions::default(); static mut WORD: [u32; 1] = [0; 1]; info!("DMA task started, waiting for data"); loop { let mut b = [0u8; 4]; // Wait until the producer pushes 4 bytes into the Pipe. let n = PIPE.read(&mut b).await; if n != 4 { warn!("Received {} bytes (expected 4), skip.", n); continue; } let w = u32::from_le_bytes(b); unsafe { WORD[0] = w; } // Log the value that is about to be written by DMA. info!("DMA starting transfer 0x{:08X} to GPIOA->BSRR", w); // Perform the single-word DMA transfer paced by TIM6 update request. let transfer = unsafe { Transfer::new_write( ch.reborrow(), TIM6_UP_REQ, // request = 4 (TIM6_UPD_DMA) core::slice::from_ref(&WORD[0]), bsrr_ptr, opts, ) }; transfer.await; info!("DMA transfer complete."); } }