dma Rx working
This commit is contained in:
@@ -8,18 +8,20 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use dma_gpio::dma_timer::init_tim6_for_uart;
|
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::Input;
|
||||||
use embassy_stm32::gpio::Pull;
|
use embassy_stm32::gpio::Pull;
|
||||||
use dma_gpio::gpio_uart_rx::GpioUartRx;
|
use dma_gpio::gpio_dma_uart_rx::GpioDmaRx;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
use embassy_stm32::{
|
use embassy_stm32::{
|
||||||
gpio::{Level, Output, Speed},
|
gpio::{Level, Output, Speed},
|
||||||
peripherals::GPDMA1_CH0,
|
peripherals::GPDMA1_CH0,
|
||||||
|
peripherals::GPDMA1_CH1,
|
||||||
};
|
};
|
||||||
use embassy_stm32::Peri;
|
use embassy_stm32::Peri;
|
||||||
|
|
||||||
use dma_gpio::gpio_dma_uart::{
|
use dma_gpio::gpio_dma_uart_tx::{
|
||||||
write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig,
|
write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,13 +31,15 @@ static PIPE_RX: Pipe<CriticalSectionRawMutex, 256> = Pipe::new();
|
|||||||
// Baud rate: one TIM6 update equals one UART bit-time
|
// 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 OVERSAMPLE: u16 = 16;
|
const TX_OVERSAMPLE: u16 = 1;
|
||||||
|
const RX_OVERSAMPLE: u16 = 16;
|
||||||
|
|
||||||
const UART_CFG: UartConfig = UartConfig {
|
const UART_CFG: UartConfig = UartConfig {
|
||||||
data_bits: 8,
|
data_bits: 8,
|
||||||
parity: Parity::None,
|
parity: Parity::None,
|
||||||
stop_bits: StopBits::One,
|
stop_bits: StopBits::One,
|
||||||
};
|
};
|
||||||
|
static mut RX_DMA_BUF: [u32; 512] = [0; 512];
|
||||||
|
|
||||||
fn dump_tim6_regs() {
|
fn dump_tim6_regs() {
|
||||||
// PAC path for STM32U5: module `tim6`, type `Tim6` with ptr()
|
// PAC path for STM32U5: module `tim6`, type `Tim6` with ptr()
|
||||||
@@ -80,41 +84,45 @@ fn dump_dma_ch0_regs() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn rx_task(pin: Input<'static>) {
|
|
||||||
let mut rx = GpioUartRx::new(pin, BAUD, UART_CFG, &PIPE_RX);
|
|
||||||
rx.run().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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!("DMA Pipe -> GPIO UART-like TX");
|
info!("Hehe");
|
||||||
|
|
||||||
|
// PA3 as Rx "wire"
|
||||||
let pa3_rx = Input::new(p.PA3, Pull::Up);
|
let pa3_rx = Input::new(p.PA3, Pull::Up);
|
||||||
spawner.spawn(rx_task(pa3_rx)).unwrap();
|
|
||||||
|
|
||||||
// PA2 is the TX "wire"
|
// PA2 is the TX "wire"
|
||||||
let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh);
|
let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh);
|
||||||
// drop(_pa2);
|
// drop(_pa2);
|
||||||
init_tim6_for_uart(p.TIM6, BAUD, OVERSAMPLE);
|
init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE); // TX
|
||||||
|
init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE); // RX
|
||||||
dump_tim6_regs();
|
dump_tim6_regs();
|
||||||
|
|
||||||
// Start DMA consumer task
|
// Start DMA consumer task
|
||||||
spawner.spawn(dma_tx_task(p.GPDMA1_CH0)).unwrap();
|
spawner.spawn(dma_tx_task(p.GPDMA1_CH0)).unwrap();
|
||||||
|
spawner.spawn(rx_dma_task(p.GPDMA1_CH1)).unwrap();
|
||||||
|
|
||||||
// Example: transmit a string as UART frames via the Pipe
|
// Example: transmit a string as UART frames via the Pipe
|
||||||
loop {
|
loop {
|
||||||
write_uart_frames_to_pipe(
|
write_uart_frames_to_pipe(
|
||||||
&PIPE_TX,
|
&PIPE_TX,
|
||||||
TX_PIN_BIT,
|
TX_PIN_BIT,
|
||||||
b"Hello, DMA UART (configurable)!\r\n",
|
b"Hello marshmallow\r\n",
|
||||||
&UART_CFG,
|
&UART_CFG,
|
||||||
).await;
|
).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>) {
|
||||||
|
let buf_ptr = core::ptr::addr_of_mut!(RX_DMA_BUF);
|
||||||
|
let buf = unsafe { &mut *buf_ptr };
|
||||||
|
let mut rx = GpioDmaRx::new(ch, 3 /*PA3 bit*/, buf, &PIPE_RX);
|
||||||
|
rx.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn dma_tx_task(ch: Peri<'static, GPDMA1_CH0>) {
|
async fn dma_tx_task(ch: Peri<'static, GPDMA1_CH0>) {
|
||||||
let mut tx = GpioDmaBsrrTx::new(ch);
|
let mut tx = GpioDmaBsrrTx::new(ch);
|
||||||
|
|||||||
@@ -1,51 +1,58 @@
|
|||||||
// src/dma_timer.rs
|
// src/dma_timer.rs
|
||||||
|
|
||||||
use embassy_stm32::{
|
use embassy_stm32::{
|
||||||
peripherals::TIM6,
|
peripherals::{TIM6, TIM7},
|
||||||
rcc,
|
rcc,
|
||||||
timer::low_level::Timer,
|
timer::low_level::Timer,
|
||||||
Peri,
|
Peri,
|
||||||
};
|
};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
use embassy_stm32::timer::BasicInstance;
|
||||||
use embassy_stm32::pac::timer::vals::Urs;
|
use embassy_stm32::pac::timer::vals::Urs;
|
||||||
|
|
||||||
/// Initializes TIM6 to tick at `baud * oversample` frequency.
|
/// Initializes TIM6 to tick at `baud * oversample` frequency.
|
||||||
/// Each TIM6 update event triggers one DMA beat.
|
/// Each TIM6 update event triggers one DMA beat.
|
||||||
pub fn init_tim6_for_uart<'d>(tim6: Peri<'d, TIM6>, baud: u32, oversample: u16) {
|
pub fn init_tim6_for_uart<'d>(tim6: Peri<'d, TIM6>, baud: u32, oversample: u16) {
|
||||||
|
|
||||||
rcc::enable_and_reset::<TIM6>();
|
rcc::enable_and_reset::<TIM6>();
|
||||||
let ll = Timer::new(tim6);
|
let ll = Timer::new(tim6);
|
||||||
|
configure_basic_timer(&ll, baud, oversample);
|
||||||
|
mem::forget(ll);
|
||||||
|
}
|
||||||
|
|
||||||
let f_tim6 = rcc::frequency::<TIM6>().0;
|
/// Initializes TIM7 to tick at `baud * oversample` frequency.
|
||||||
|
/// Each TIM7 update event triggers one DMA beat.
|
||||||
|
pub fn init_tim7_for_uart<'d>(tim7: Peri<'d, TIM7>, baud: u32, oversample: u16) {
|
||||||
|
rcc::enable_and_reset::<TIM7>();
|
||||||
|
let ll = Timer::new(tim7);
|
||||||
|
configure_basic_timer(&ll, baud, oversample);
|
||||||
|
mem::forget(ll);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared internal helper — identical CR1/ARR setup
|
||||||
|
fn configure_basic_timer<T: BasicInstance>(ll: &Timer<'_, T>, baud: u32, oversample: u16) {
|
||||||
|
let f_timer = rcc::frequency::<T>().0;
|
||||||
let target = baud.saturating_mul(oversample.max(1) as u32).max(1);
|
let target = baud.saturating_mul(oversample.max(1) as u32).max(1);
|
||||||
|
|
||||||
// Compute ARR (prescaler=0)
|
// Compute ARR (prescaler = 0)
|
||||||
let mut arr = (f_tim6 / target).saturating_sub(1) as u16;
|
let mut arr = (f_timer / target).saturating_sub(1) as u16;
|
||||||
if arr == 0 {
|
if arr == 0 { arr = 1; }
|
||||||
arr = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ll.regs_basic().cr1().write(|w| {
|
ll.regs_basic().cr1().write(|w| {
|
||||||
w.set_cen(false);
|
w.set_cen(false);
|
||||||
w.set_opm(false);
|
w.set_opm(false);
|
||||||
w.set_udis(false); // boolean field: false = allow UEV
|
w.set_udis(false);
|
||||||
w.set_urs(Urs::ANY_EVENT); // enum field: DMA+interrupts on any event
|
w.set_urs(Urs::ANY_EVENT);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Write prescaler directly (simple u16 register)
|
|
||||||
ll.regs_basic().psc().write_value(0u16);
|
ll.regs_basic().psc().write_value(0u16);
|
||||||
|
|
||||||
// Set ARR, enable DMA request, issue first update
|
|
||||||
ll.regs_basic().arr().write(|w| w.set_arr(arr));
|
ll.regs_basic().arr().write(|w| w.set_arr(arr));
|
||||||
ll.regs_basic().dier().modify(|w| w.set_ude(true));
|
ll.regs_basic().dier().modify(|w| w.set_ude(true));
|
||||||
ll.regs_basic().egr().write(|w| w.set_ug(true));
|
ll.regs_basic().egr().write(|w| w.set_ug(true));
|
||||||
|
|
||||||
// Start timer
|
|
||||||
ll.regs_basic().cr1().write(|w| {
|
ll.regs_basic().cr1().write(|w| {
|
||||||
w.set_opm(false);
|
w.set_opm(false);
|
||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
w.set_udis(false);
|
w.set_udis(false);
|
||||||
w.set_urs(Urs::ANY_EVENT);
|
w.set_urs(Urs::ANY_EVENT);
|
||||||
});
|
});
|
||||||
mem::forget(ll); // KEEP THE TIMER ALIVE
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
dma_gpio/src/gpio_dma_uart_rx.rs
Normal file
57
dma_gpio/src/gpio_dma_uart_rx.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// src/gpio_dma_uart_rx.rs
|
||||||
|
use embassy_stm32::{
|
||||||
|
dma::{Request, Transfer, TransferOptions},
|
||||||
|
peripherals::GPDMA1_CH1,
|
||||||
|
Peri,
|
||||||
|
};
|
||||||
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||||
|
|
||||||
|
// RM0456 tabulka 137
|
||||||
|
pub const TIM7_UP_REQ: Request = 5;
|
||||||
|
|
||||||
|
pub struct GpioDmaRx<'d, const N: usize> {
|
||||||
|
ch: Peri<'d, GPDMA1_CH1>,
|
||||||
|
pin_bit: u8,
|
||||||
|
buf: &'d mut [u32; N],
|
||||||
|
opts: TransferOptions,
|
||||||
|
pipe_rx: &'d Pipe<CriticalSectionRawMutex, 256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, const N: usize> GpioDmaRx<'d, N> {
|
||||||
|
pub fn new(
|
||||||
|
ch: Peri<'d, GPDMA1_CH1>,
|
||||||
|
pin_bit: u8,
|
||||||
|
buf: &'d mut [u32; N],
|
||||||
|
pipe_rx: &'d Pipe<CriticalSectionRawMutex, 256>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ch,
|
||||||
|
pin_bit,
|
||||||
|
buf,
|
||||||
|
opts: TransferOptions::default(),
|
||||||
|
pipe_rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self) -> ! {
|
||||||
|
loop {
|
||||||
|
let gpioa_idr_addr = embassy_stm32::pac::GPIOA.as_ptr() as *mut u32;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Transfer::new_read(
|
||||||
|
self.ch.reborrow(),
|
||||||
|
TIM7_UP_REQ,
|
||||||
|
gpioa_idr_addr,
|
||||||
|
&mut self.buf[..],
|
||||||
|
self.opts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
|
||||||
|
for &word in self.buf.iter() {
|
||||||
|
let bit_high = ((word >> self.pin_bit) & 1) as u8;
|
||||||
|
self.pipe_rx.write(&[bit_high]).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use embassy_stm32::{
|
|||||||
};
|
};
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||||
|
|
||||||
|
// kapitola 17.4.11 - 2 casovace pre 2 DMA
|
||||||
pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687 STM32U5xx datasheet
|
pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687 STM32U5xx datasheet
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// src/gpio_uart_rx.rs
|
|
||||||
|
|
||||||
use embassy_stm32::gpio::{Input, Level, Pull};
|
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
|
||||||
use embassy_time::{Timer, Duration};
|
|
||||||
use crate::gpio_dma_uart::UartConfig;
|
|
||||||
|
|
||||||
/// Simplified bit-banged UART RX using a timer for sampling.
|
|
||||||
/// Sample pin every bit-time once start bit is detected.
|
|
||||||
pub struct GpioUartRx<'d, const N: usize> {
|
|
||||||
pin: Input<'d>,
|
|
||||||
baud: u32,
|
|
||||||
cfg: UartConfig,
|
|
||||||
pipe: &'d Pipe<CriticalSectionRawMutex, N>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, const N: usize> GpioUartRx<'d, N> {
|
|
||||||
pub fn new(pin: Input<'d>, baud: u32, cfg: UartConfig, pipe: &'d Pipe<CriticalSectionRawMutex, N>) -> Self {
|
|
||||||
Self { pin, baud, cfg, pipe }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Async read loop: waits for start bit, samples bits, pushes received byte into pipe.
|
|
||||||
pub async fn run(&mut self) -> ! {
|
|
||||||
let bit_time = Duration::from_hz(self.baud.into());
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Wait for start bit edge
|
|
||||||
while self.pin.get_level() == Level::High {
|
|
||||||
Timer::after_micros(1).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip only 1 bit-time since you *don’t* want 1.5 bit delay
|
|
||||||
Timer::after(bit_time).await;
|
|
||||||
|
|
||||||
let nbits = self.cfg.data_bits.clamp(5, 8);
|
|
||||||
let mut data = 0u8;
|
|
||||||
for i in 0..nbits {
|
|
||||||
if self.pin.get_level() == Level::High {
|
|
||||||
data |= 1 << i;
|
|
||||||
}
|
|
||||||
Timer::after(bit_time).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push into Pipe as soon as byte is complete
|
|
||||||
self.pipe.write(&[data]).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub mod gpio_dma_uart;
|
pub mod gpio_dma_uart_tx;
|
||||||
pub use gpio_dma_uart::*;
|
pub mod gpio_dma_uart_rx;
|
||||||
|
|
||||||
pub mod dma_timer;
|
pub mod dma_timer;
|
||||||
|
|
||||||
|
pub use gpio_dma_uart_tx::*;
|
||||||
|
pub use gpio_dma_uart_rx::*;
|
||||||
pub use dma_timer::*;
|
pub use dma_timer::*;
|
||||||
pub mod gpio_uart_rx;
|
|
||||||
|
|||||||
Reference in New Issue
Block a user