3 Commits

Author SHA1 Message Date
Priec
15b3b96b68 compiled and working 2025-11-01 23:47:15 +01:00
Priec
4365c72688 complete movement, some parts are destroyed and not moved yet 2025-11-01 23:35:27 +01:00
Priec
63c353faac adjusted comments 2025-11-01 14:08:27 +01:00
10 changed files with 168 additions and 155 deletions

10
dma_gpio/Cargo.lock generated
View File

@@ -279,6 +279,7 @@ dependencies = [
"micromath",
"panic-halt",
"panic-probe",
"static_cell",
"tinybmp",
]
@@ -1033,6 +1034,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_cell"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
dependencies = [
"portable-atomic",
]
[[package]]
name = "stm32-fmc"
version = "0.3.2"

View File

@@ -26,3 +26,4 @@ tinybmp = "0.6.0"
panic-probe = { version = "1.0.0", features = ["defmt"] }
defmt-rtt = "1.1.0"
defmt = "1.0.1"
static_cell = "2.1.1"

View File

@@ -1,178 +1,61 @@
// src/bin/main.rs
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Input, Output, Level, Pull, Speed};
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::TIM7_UP_REQ;
use embassy_stm32::dma::{ReadableRingBuffer as DmaRingRx, TransferOptions};
use dma_gpio::software_uart::{
dma_timer::{init_tim6_for_uart, init_tim7_for_uart},
gpio_dma_uart_tx::{write_uart_frames_to_pipe, UartConfig, Parity, StopBits},
runtime::{rx_dma_task, tx_dma_task},
debug::dump_tim6_regs,
};
use static_cell::StaticCell;
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<CriticalSectionRawMutex, 256> = Pipe::new();
static PIPE_RX: Pipe<CriticalSectionRawMutex, 256> = Pipe::new();
// Baud rate: one TIM6 update equals one UART bit-time
/// SOFTWARE UART CONFIGURATION
const BAUD: u32 = 115_200;
const TX_PIN_BIT: u8 = 2; // PA2
const TX_OVERSAMPLE: u16 = 1;
const RX_OVERSAMPLE: u16 = 16;
const PIPE_TX_SIZE: usize = 256;
const PIPE_RX_SIZE: usize = 256;
const RX_RING_BYTES: usize = 4096;
const UART_CFG: UartConfig = UartConfig {
data_bits: 8,
parity: Parity::None,
stop_bits: StopBits::One,
};
static mut RX_RING: [u8; RX_RING_BYTES] = [0; RX_RING_BYTES];
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()
);
}
static PIPE_TX: Pipe<CriticalSectionRawMutex, PIPE_TX_SIZE> = Pipe::new();
static PIPE_RX: Pipe<CriticalSectionRawMutex, PIPE_RX_SIZE> = Pipe::new();
static RX_RING: StaticCell<[u8; RX_RING_BYTES]> = StaticCell::new();
#[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);
let _rx = Input::new(p.PA3, Pull::Up);
let _tx = Output::new(p.PA2, Level::High, Speed::VeryHigh);
// 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
init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE);
init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE);
dump_tim6_regs();
// Start DMA consumer task
spawner.spawn(tx_dma_task(p.GPDMA1_CH0).unwrap());
spawner.spawn(rx_dma_task(p.GPDMA1_CH1).unwrap());
// Safe one-time init from StaticCell
let ring: &mut [u8; RX_RING_BYTES] = RX_RING.init([0; RX_RING_BYTES]);
// Spawn tasks
spawner.spawn(tx_dma_task(p.GPDMA1_CH0, &PIPE_TX).unwrap());
spawner.spawn(rx_dma_task(p.GPDMA1_CH1, &PIPE_RX, ring).unwrap());
let uart_cfg = UartConfig {
data_bits: 8,
parity: Parity::None,
stop_bits: StopBits::One,
};
// 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;
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>) {
// SAFETY: this task owns the ring buffer exclusively (Rust 2024: avoid &mut static directly).
let ring_ptr = core::ptr::addr_of_mut!(RX_RING);
let ring = unsafe { &mut *ring_ptr };
// Sample the low byte of GPIOA.IDR (PA0..PA7). PA3 is bit 3, so that's within this byte.
let idr_low_byte = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8;
let mut opts = TransferOptions::default();
// Enable HT/TC IRQ so read_exact can await naturally.
opts.half_transfer_ir = true;
opts.complete_transfer_ir = true;
// Start hardware-linked circular DMA into ring.
let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, idr_low_byte, ring, opts) };
rx.start();
// Drain in fixed chunks when CPU has time; DMA keeps running regardless.
let mut chunk = [0u8; 256];
loop {
// Wait for a full chunk; wakes on half/full buffer events
let _ = rx.read_exact(&mut chunk).await;
// Forward raw samples to PIPE_RX (decode later if desired)
PIPE_RX.write(&chunk).await;
}
}
#[embassy_executor::task]
async fn tx_dma_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();
}
}
}
}

View File

@@ -1,9 +1,3 @@
#![no_std]
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 software_uart;

View File

@@ -0,0 +1,43 @@
// src/software_uart/debug.rs
use defmt::info;
pub fn dump_tim6_regs() {
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
);
}
pub fn dump_dma_ch0_regs() {
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()
);
}

View File

@@ -0,0 +1,13 @@
// src/software_uart/mod.rs
pub mod gpio_dma_uart_tx;
pub mod gpio_dma_uart_rx;
pub mod dma_timer;
pub mod runtime;
pub mod debug;
pub use gpio_dma_uart_tx::*;
pub use gpio_dma_uart_rx::*;
pub use dma_timer::*;
pub use runtime::*;
pub use debug::*;

View File

@@ -0,0 +1,69 @@
// src/software_uart/runtime.rs
use defmt::{info, warn};
use embassy_executor::task;
use embassy_stm32::{
dma::{ReadableRingBuffer as DmaRingRx, TransferOptions},
peripherals::{GPDMA1_CH0, GPDMA1_CH1},
Peri,
};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
use embassy_time::Duration;
use crate::software_uart::{
gpio_dma_uart_rx::TIM7_UP_REQ,
gpio_dma_uart_tx::GpioDmaBsrrTx,
debug::{dump_dma_ch0_regs, dump_tim6_regs},
};
/// RX DMA task: reads GPIO samples paced by TIM7 and fills PIPE_RX
#[task]
pub async fn rx_dma_task(
ch: Peri<'static, GPDMA1_CH1>,
pipe_rx: &'static Pipe<CriticalSectionRawMutex, 256>,
ring: &'static mut [u8],
) {
let gpioa_idr = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8;
let mut opts = TransferOptions::default();
opts.half_transfer_ir = true;
opts.complete_transfer_ir = true;
// SAFETY: ring is exclusive to this task
let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) };
rx.start();
let mut chunk = [0u8; 256];
loop {
let _ = rx.read_exact(&mut chunk).await;
pipe_rx.write(&chunk).await;
}
}
/// TX DMA task: dequeues prebuilt frames from PIPE_TX and writes to GPIOA.BSRR
#[task]
pub async fn tx_dma_task(
ch: Peri<'static, GPDMA1_CH0>,
pipe_tx: &'static Pipe<CriticalSectionRawMutex, 256>,
) {
let mut tx = GpioDmaBsrrTx::new(ch);
info!("DMA TX task started");
loop {
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();
}
}
}
}