working
This commit is contained in:
46
dma_gpio/Cargo.lock
generated
46
dma_gpio/Cargo.lock
generated
@@ -229,6 +229,29 @@ dependencies = [
|
||||
"defmt 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dma_gpio"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"defmt 1.0.1",
|
||||
"defmt-rtt",
|
||||
"embassy-executor",
|
||||
"embassy-futures",
|
||||
"embassy-stm32",
|
||||
"embassy-sync",
|
||||
"embassy-time",
|
||||
"embassy-usb",
|
||||
"embedded-graphics",
|
||||
"embedded-hal 1.0.0",
|
||||
"heapless 0.9.1",
|
||||
"micromath",
|
||||
"panic-halt",
|
||||
"panic-probe",
|
||||
"tinybmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.11"
|
||||
@@ -616,29 +639,6 @@ dependencies = [
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hal_rng"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"defmt 1.0.1",
|
||||
"defmt-rtt",
|
||||
"embassy-executor",
|
||||
"embassy-futures",
|
||||
"embassy-stm32",
|
||||
"embassy-sync",
|
||||
"embassy-time",
|
||||
"embassy-usb",
|
||||
"embedded-graphics",
|
||||
"embedded-hal 1.0.0",
|
||||
"heapless 0.9.1",
|
||||
"micromath",
|
||||
"panic-halt",
|
||||
"panic-probe",
|
||||
"tinybmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
authors = ["Priec <filippriec@gmail.com>"]
|
||||
name = "hal_rng"
|
||||
name = "dma_gpio"
|
||||
edition = "2024"
|
||||
version = "0.1.0"
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use embassy_time::{Duration, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
use embassy_stm32::{
|
||||
dma::{Request, Transfer, TransferOptions},
|
||||
dma::Request,
|
||||
gpio::{Level, Output, Speed},
|
||||
peripherals::{GPDMA1_CH0, TIM6},
|
||||
rcc,
|
||||
@@ -18,26 +18,27 @@ use embassy_stm32::{
|
||||
};
|
||||
use embassy_stm32::Peri;
|
||||
|
||||
static PIPE: Pipe<CriticalSectionRawMutex, 64> = Pipe::new();
|
||||
use dma_gpio::gpio_dma_uart::{encode_uart_byte, write_uart_frames_to_pipe, GpioDmaBsrrTx, TIM6_UP_REQ};
|
||||
|
||||
// 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;
|
||||
static PIPE: Pipe<CriticalSectionRawMutex, 256> = Pipe::new();
|
||||
|
||||
// Baud rate: one TIM6 update equals one UART bit-time
|
||||
const BAUD: u32 = 115_200;
|
||||
const TX_PIN_BIT: u8 = 2; // PA2
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
info!("DMA Pipe -> GPIO (single pin) using GPDMA HAL");
|
||||
info!("DMA Pipe -> GPIO UART-like TX (safe wrapper)");
|
||||
|
||||
// Single pin: PA2 as output (DMA will hit PA2->BSRR)
|
||||
let _pa2 = Output::new(p.PA2, Level::Low, Speed::VeryHigh);
|
||||
// PA2 is the TX "wire"
|
||||
let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh);
|
||||
drop(_pa2);
|
||||
|
||||
// TIM6: enable Update DMA request at RATE_HZ
|
||||
// TIM6 generates one DMA request per bit-time
|
||||
let tim6 = LlTimer::new(p.TIM6);
|
||||
let f_tim6 = rcc::frequency::<TIM6>().0;
|
||||
let arr = (f_tim6 / RATE_HZ).saturating_sub(1) as u16;
|
||||
let arr = (f_tim6 / BAUD).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| {
|
||||
@@ -45,68 +46,32 @@ async fn main(spawner: Spawner) {
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
// DMA worker: reads u32 words (as 4 bytes) and writes to GPIOA BSRR
|
||||
// Start DMA consumer task
|
||||
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];
|
||||
|
||||
// Example: transmit a string as UART frames via the Pipe
|
||||
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;
|
||||
}
|
||||
write_uart_frames_to_pipe(&PIPE, TX_PIN_BIT, b"Hello, DMA UART!\r\n").await;
|
||||
Timer::after(Duration::from_secs(2)).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");
|
||||
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];
|
||||
|
||||
// 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.");
|
||||
// Log + send via DMA (timer-paced, 1 beat per bit time)
|
||||
info!("DMA write 0x{:08X} -> GPIOA.BSRR", w);
|
||||
tx.write_word(w).await;
|
||||
}
|
||||
}
|
||||
|
||||
83
dma_gpio/src/gpio_dma_uart.rs
Normal file
83
dma_gpio/src/gpio_dma_uart.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
// src/gpio_dma_uart.rs
|
||||
use embassy_stm32::{
|
||||
dma::{Request, Transfer, TransferOptions},
|
||||
peripherals::GPDMA1_CH0,
|
||||
Peri,
|
||||
};
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||
|
||||
pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687
|
||||
|
||||
pub struct GpioDmaBsrrTx<'d> {
|
||||
ch: Peri<'d, GPDMA1_CH0>,
|
||||
bsrr: *mut u32,
|
||||
opts: TransferOptions,
|
||||
}
|
||||
|
||||
impl<'d> GpioDmaBsrrTx<'d> {
|
||||
// Constructor. Hides the raw register pointer internally.
|
||||
pub fn new(ch: Peri<'d, GPDMA1_CH0>) -> Self {
|
||||
let bsrr = embassy_stm32::pac::GPIOA.bsrr().as_ptr() as *mut u32;
|
||||
Self {
|
||||
ch,
|
||||
bsrr,
|
||||
opts: TransferOptions::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// Safe API: perform one timer-paced DMA write of a single 32-bit BSRR word.
|
||||
pub async fn write_word(&mut self, word: u32) {
|
||||
let buf = [word];
|
||||
// Safety: bsrr is a valid 32-bit aligned register, buf lives until DMA completes,
|
||||
// request selects TIM6_UP, which paces one beat per update.
|
||||
unsafe {
|
||||
Transfer::new_write(
|
||||
self.ch.reborrow(),
|
||||
TIM6_UP_REQ,
|
||||
&buf,
|
||||
self.bsrr,
|
||||
self.opts,
|
||||
)
|
||||
}
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
// Build 10 BSRR words for one UART frame (8N1) on a given GPIO bit.
|
||||
// start bit, 8 data bits LSB-first, stop bit
|
||||
// BSRR je safe atomic write only shortcut
|
||||
pub fn encode_uart_byte(pin_bit: u8, data: u8) -> [u32; 10] {
|
||||
let mut words = [0u32; 10];
|
||||
|
||||
// Dokumentacia strana 636 13.4.7
|
||||
// set bit - HIGH, reset bit - LOW
|
||||
let set_high = |bit: u8| -> u32 { 1u32 << bit };
|
||||
let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) };
|
||||
|
||||
// start bit LOW
|
||||
words[0] = set_low(pin_bit);
|
||||
|
||||
// data bits, LSB first
|
||||
for i in 0..8 {
|
||||
let one = (data >> i) & 1 != 0;
|
||||
words[1 + i] = if one { set_high(pin_bit) } else { set_low(pin_bit) };
|
||||
}
|
||||
|
||||
// stop bit HIGH
|
||||
words[9] = set_high(pin_bit);
|
||||
words
|
||||
}
|
||||
|
||||
// Convenience: push UART frames for a whole byte slice into a Pipe.
|
||||
pub async fn write_uart_frames_to_pipe<const N: usize>(
|
||||
pipe: &Pipe<CriticalSectionRawMutex, N>,
|
||||
pin_bit: u8,
|
||||
bytes: &[u8],
|
||||
) {
|
||||
for &b in bytes {
|
||||
let frames = encode_uart_byte(pin_bit, b);
|
||||
for w in frames {
|
||||
pipe.write(&w.to_le_bytes()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
#![no_std]
|
||||
pub mod gpio_dma_uart;
|
||||
pub use gpio_dma_uart::*;
|
||||
|
||||
Reference in New Issue
Block a user