complete movement, some parts are destroyed and not moved yet
This commit is contained in:
@@ -1,178 +1,62 @@
|
|||||||
// src/bin/main.rs
|
// src/bin/main.rs
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
use embassy_stm32::gpio::{Input, Output, Level, Pull, Speed};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use dma_gpio::dma_timer::init_tim6_for_uart;
|
use dma_gpio::software_uart::{
|
||||||
use dma_gpio::dma_timer::init_tim7_for_uart;
|
dma_timer::{init_tim6_for_uart, init_tim7_for_uart},
|
||||||
use embassy_stm32::gpio::Input;
|
gpio_dma_uart_tx::{write_uart_frames_to_pipe, UartConfig, Parity, StopBits},
|
||||||
use embassy_stm32::gpio::Pull;
|
runtime::{rx_dma_task, tx_dma_task},
|
||||||
use dma_gpio::gpio_dma_uart_rx::TIM7_UP_REQ;
|
debug::dump_tim6_regs,
|
||||||
use embassy_stm32::dma::{ReadableRingBuffer as DmaRingRx, TransferOptions};
|
};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
use embassy_stm32::{
|
/// SOFTWARE UART CONFIGURATION
|
||||||
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
|
|
||||||
const BAUD: u32 = 115_200;
|
const BAUD: u32 = 115_200;
|
||||||
const TX_PIN_BIT: u8 = 2; // PA2
|
const TX_PIN_BIT: u8 = 2; // PA2
|
||||||
const TX_OVERSAMPLE: u16 = 1;
|
const TX_OVERSAMPLE: u16 = 1;
|
||||||
const RX_OVERSAMPLE: u16 = 16;
|
const RX_OVERSAMPLE: u16 = 16;
|
||||||
|
const PIPE_TX_SIZE: usize = 256;
|
||||||
|
const PIPE_RX_SIZE: usize = 256;
|
||||||
const RX_RING_BYTES: usize = 4096;
|
const RX_RING_BYTES: usize = 4096;
|
||||||
|
|
||||||
const UART_CFG: UartConfig = UartConfig {
|
static PIPE_TX: Pipe<CriticalSectionRawMutex, PIPE_TX_SIZE> = Pipe::new();
|
||||||
data_bits: 8,
|
static PIPE_RX: Pipe<CriticalSectionRawMutex, PIPE_RX_SIZE> = Pipe::new();
|
||||||
parity: Parity::None,
|
// Large hardware RX DMA circular buffer, scales with oversampling rate
|
||||||
stop_bits: StopBits::One,
|
|
||||||
};
|
|
||||||
static mut RX_RING: [u8; RX_RING_BYTES] = [0; RX_RING_BYTES];
|
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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
let p = embassy_stm32::init(Default::default());
|
// Initialize peripheral access
|
||||||
info!("Hehe");
|
let mut p = embassy_stm32::init(Default::default());
|
||||||
|
info!("UART DMA demo starting...");
|
||||||
|
|
||||||
// PA3 as Rx "wire"
|
// GPIO setup
|
||||||
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"
|
// Timer setup
|
||||||
let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh);
|
init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE);
|
||||||
// drop(_pa2);
|
init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE);
|
||||||
init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE); // TX
|
dump_tim6_regs(); // debug
|
||||||
init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE); // RX
|
|
||||||
dump_tim6_regs();
|
|
||||||
|
|
||||||
// Start DMA consumer task
|
// Spawn DMA tasks
|
||||||
spawner.spawn(tx_dma_task(p.GPDMA1_CH0).unwrap());
|
spawner.spawn(tx_dma_task(p.GPDMA1_CH0)).unwrap();
|
||||||
spawner.spawn(rx_dma_task(p.GPDMA1_CH1).unwrap());
|
spawner.spawn(rx_dma_task(p.GPDMA1_CH1)).unwrap();
|
||||||
|
|
||||||
// Example: transmit a string as UART frames via the Pipe
|
// UART config (8N1 default)
|
||||||
|
let uart_cfg = UartConfig {
|
||||||
|
data_bits: 8,
|
||||||
|
parity: Parity::None,
|
||||||
|
stop_bits: StopBits::One,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main loop: send data periodically
|
||||||
loop {
|
loop {
|
||||||
write_uart_frames_to_pipe(
|
write_uart_frames_to_pipe(&PIPE_TX, TX_PIN_BIT, b"Hello marshmallow\r\n", &uart_cfg).await;
|
||||||
&PIPE_TX,
|
|
||||||
TX_PIN_BIT,
|
|
||||||
b"Hello marshmallow\r\n",
|
|
||||||
&UART_CFG,
|
|
||||||
).await;
|
|
||||||
Timer::after(Duration::from_secs(2)).await;
|
Timer::after(Duration::from_secs(2)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn rx_dma_task(ch: Peri<'static, GPDMA1_CH1>) {
|
|
||||||
// SAFETY Own the static RX buffer exclusively.
|
|
||||||
let ring_ptr = core::ptr::addr_of_mut!(RX_RING);
|
|
||||||
let ring = unsafe { &mut *ring_ptr };
|
|
||||||
|
|
||||||
// DMA will read samples from GPIOA input register.
|
|
||||||
// Each sample is one byte (PA0..PA7). PA3 lives in bit 3.
|
|
||||||
let gpioa_idr = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8;
|
|
||||||
|
|
||||||
let mut opts = TransferOptions::default();
|
|
||||||
opts.half_transfer_ir = true; // wake at half buffer
|
|
||||||
opts.complete_transfer_ir = true; // wake at full buffer
|
|
||||||
|
|
||||||
// Start hardware-linked circular DMA into ring.
|
|
||||||
let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) };
|
|
||||||
rx.start();
|
|
||||||
|
|
||||||
// Drain in fixed chunks when CPU has time
|
|
||||||
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)
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub mod gpio_dma_uart_tx;
|
pub mod software_uart;
|
||||||
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::*;
|
|
||||||
|
|||||||
43
dma_gpio/src/software_uart/debug.rs
Normal file
43
dma_gpio/src/software_uart/debug.rs
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
13
dma_gpio/src/software_uart/mod.rs
Normal file
13
dma_gpio/src/software_uart/mod.rs
Normal 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::*;
|
||||||
61
dma_gpio/src/software_uart/runtime.rs
Normal file
61
dma_gpio/src/software_uart/runtime.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// 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>) {
|
||||||
|
let ring = unsafe { &mut RX_RING };
|
||||||
|
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;
|
||||||
|
|
||||||
|
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>) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user