Compare commits
2 Commits
f2b6590473
...
172dd899f9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
172dd899f9 | ||
|
|
f56fe0561b |
@@ -7,18 +7,17 @@ use defmt::*;
|
|||||||
use embassy_executor::Spawner;
|
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 {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, TIM6},
|
peripherals::GPDMA1_CH0,
|
||||||
rcc,
|
|
||||||
timer::low_level::Timer as LlTimer,
|
|
||||||
};
|
};
|
||||||
use embassy_stm32::Peri;
|
use embassy_stm32::Peri;
|
||||||
|
|
||||||
use dma_gpio::gpio_dma_uart::{
|
use dma_gpio::gpio_dma_uart::{
|
||||||
write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig, TIM6_UP_REQ,
|
write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
static PIPE: Pipe<CriticalSectionRawMutex, 256> = Pipe::new();
|
static PIPE: Pipe<CriticalSectionRawMutex, 256> = Pipe::new();
|
||||||
@@ -26,6 +25,7 @@ static PIPE: 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 = 6;
|
||||||
|
|
||||||
const UART_CFG: UartConfig = UartConfig {
|
const UART_CFG: UartConfig = UartConfig {
|
||||||
data_bits: 8,
|
data_bits: 8,
|
||||||
@@ -33,6 +33,49 @@ const UART_CFG: UartConfig = UartConfig {
|
|||||||
stop_bits: StopBits::One,
|
stop_bits: StopBits::One,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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());
|
let p = embassy_stm32::init(Default::default());
|
||||||
@@ -40,18 +83,9 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// 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);
|
||||||
// TIM6 generates one DMA request per bit-time
|
dump_tim6_regs();
|
||||||
let tim6 = LlTimer::new(p.TIM6);
|
|
||||||
let f_tim6 = rcc::frequency::<TIM6>().0;
|
|
||||||
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| {
|
|
||||||
w.set_opm(false);
|
|
||||||
w.set_cen(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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();
|
||||||
@@ -83,6 +117,18 @@ async fn dma_tx_task(ch: Peri<'static, GPDMA1_CH0>) {
|
|||||||
let w = u32::from_le_bytes(b);
|
let w = u32::from_le_bytes(b);
|
||||||
|
|
||||||
info!("DMA write 0x{:08X} -> GPIOA.BSRR", w);
|
info!("DMA write 0x{:08X} -> GPIOA.BSRR", w);
|
||||||
tx.write_word(w).await;
|
match embassy_time::with_timeout(
|
||||||
|
Duration::from_millis(20),
|
||||||
|
tx.write_word(w),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(_) => {
|
||||||
|
warn!("DMA timeout: no TIM6 request (wrong DMAMUX req?)");
|
||||||
|
dump_tim6_regs();
|
||||||
|
dump_dma_ch0_regs();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
48
dma_gpio/src/dma_timer.rs
Normal file
48
dma_gpio/src/dma_timer.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// src/dma_timer.rs
|
||||||
|
|
||||||
|
use embassy_stm32::{
|
||||||
|
peripherals::TIM6,
|
||||||
|
rcc,
|
||||||
|
timer::low_level::Timer,
|
||||||
|
Peri,
|
||||||
|
};
|
||||||
|
use embassy_stm32::pac::timer::vals::Urs;
|
||||||
|
|
||||||
|
/// Initializes TIM6 to tick at `baud * oversample` frequency.
|
||||||
|
/// Each TIM6 update event triggers one DMA beat.
|
||||||
|
pub fn init_tim6_for_uart<'d>(tim6: Peri<'d, TIM6>, baud: u32, oversample: u16) {
|
||||||
|
|
||||||
|
let ll = Timer::new(tim6);
|
||||||
|
|
||||||
|
let f_tim6 = rcc::frequency::<TIM6>().0;
|
||||||
|
let target = baud.saturating_mul(oversample.max(1) as u32).max(1);
|
||||||
|
|
||||||
|
// Compute ARR (prescaler=0)
|
||||||
|
let mut arr = (f_tim6 / target).saturating_sub(1) as u16;
|
||||||
|
if arr == 0 {
|
||||||
|
arr = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ll.regs_basic().cr1().write(|w| {
|
||||||
|
w.set_cen(false);
|
||||||
|
w.set_opm(false);
|
||||||
|
w.set_udis(false); // boolean field: false = allow UEV
|
||||||
|
w.set_urs(Urs::ANY_EVENT); // enum field: DMA+interrupts on any event
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write prescaler directly (simple u16 register)
|
||||||
|
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().dier().modify(|w| w.set_ude(true));
|
||||||
|
ll.regs_basic().egr().write(|w| w.set_ug(true));
|
||||||
|
|
||||||
|
// Start timer
|
||||||
|
ll.regs_basic().cr1().write(|w| {
|
||||||
|
w.set_opm(false);
|
||||||
|
w.set_cen(true);
|
||||||
|
w.set_udis(false);
|
||||||
|
w.set_urs(Urs::ANY_EVENT);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub mod gpio_dma_uart;
|
pub mod gpio_dma_uart;
|
||||||
pub use gpio_dma_uart::*;
|
pub use gpio_dma_uart::*;
|
||||||
|
|
||||||
|
pub mod dma_timer;
|
||||||
|
pub use dma_timer::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user