DMA working
This commit is contained in:
@@ -5,49 +5,108 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_sync::{
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||||
blocking_mutex::raw::CriticalSectionRawMutex,
|
|
||||||
pipe::Pipe,
|
|
||||||
};
|
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
use embassy_stm32::{
|
use embassy_stm32::{
|
||||||
|
dma::{Request, Transfer, TransferOptions},
|
||||||
gpio::{Level, Output, Speed},
|
gpio::{Level, Output, Speed},
|
||||||
pac::{self, GPDMA1, GPIOB, dmamux},
|
peripherals::{GPDMA1_CH0, TIM6},
|
||||||
peripherals::TIM6,
|
|
||||||
rcc,
|
rcc,
|
||||||
timer::low_level::Timer as LlTimer,
|
timer::low_level::Timer as LlTimer,
|
||||||
};
|
};
|
||||||
|
use embassy_stm32::Peri;
|
||||||
|
|
||||||
static PIPE: Pipe<CriticalSectionRawMutex, 64> = Pipe::new();
|
static PIPE: Pipe<CriticalSectionRawMutex, 64> = 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]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
info!("Hello World!");
|
info!("DMA Pipe -> GPIO (single pin) using GPDMA HAL");
|
||||||
|
|
||||||
spawner.spawn(pipe_consumer_task()).unwrap();
|
// 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::<TIM6>().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];
|
||||||
|
|
||||||
let mut counter: u32 = 0;
|
|
||||||
loop {
|
loop {
|
||||||
let bytes = counter.to_le_bytes(); // convert 4 bytes
|
for chunk in pattern {
|
||||||
PIPE.write(&bytes).await;
|
PIPE.write(&chunk).await;
|
||||||
info!("Producer pushed value {}", counter);
|
info!("Producer queued pattern word: [{:02X} {:02X} {:02X} {:02X}]",
|
||||||
|
chunk[0], chunk[1], chunk[2], chunk[3]);
|
||||||
counter = counter.wrapping_add(1);
|
// No long wait — the timer pacing handles timing.
|
||||||
Timer::after(Duration::from_secs(2)).await;
|
// 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]
|
#[embassy_executor::task]
|
||||||
async fn pipe_consumer_task() {
|
async fn dma_tx_task(mut ch: Peri<'static, GPDMA1_CH0>) {
|
||||||
let mut buf = [0u8; 4];
|
// 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 {
|
loop {
|
||||||
let n = PIPE.read(&mut buf).await;
|
let mut b = [0u8; 4];
|
||||||
if n == 4 {
|
|
||||||
let val = u32::from_le_bytes(buf);
|
// Wait until the producer pushes 4 bytes into the Pipe.
|
||||||
info!("Consumer read {}", val);
|
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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
pinout1.png
Normal file
BIN
pinout1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 497 KiB |
BIN
pinout2.png
Normal file
BIN
pinout2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 417 KiB |
Reference in New Issue
Block a user