2 Commits

Author SHA1 Message Date
Priec
92e27ad076 Last moves, comments and file organization 2025-11-03 23:26:39 +01:00
Priec
93c43dee11 redesigned, removed redundancy 2025-11-03 22:41:16 +01:00
6 changed files with 124 additions and 223 deletions

View File

@@ -9,10 +9,9 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
use embassy_time::{Duration, Timer};
use dma_gpio::software_uart::{
dma_timer::{init_tim6_for_uart, init_tim7_for_uart},
gpio_dma_uart_tx::{
write_uart_frames_to_ring, Parity, StopBits, UartConfig, TIM6_UP_REQ,
},
runtime::rx_dma_task,
uart_emulation::{Parity, StopBits, UartConfig},
gpio_dma_uart_tx::{write_uart_frames_to_ring, TIM6_UP_REQ},
gpio_dma_uart_rx::rx_dma_task,
debug::dump_tim6_regs,
};
use embassy_stm32::dma::{TransferOptions, WritableRingBuffer};

View File

@@ -1,57 +1,39 @@
// src/gpio_dma_uart_rx.rs
// src/software_uart/runtime.rs
use embassy_executor::task;
use embassy_stm32::{
dma::{Request, Transfer, TransferOptions},
dma::Request,
peripherals::GPDMA1_CH1,
Peri,
};
use embassy_stm32::dma::{
ReadableRingBuffer as DmaRingRx,
TransferOptions,
};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
// RM0456 tabulka 137
// datasheet 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>,
}
/// 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;
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,
}
}
let mut opts = TransferOptions::default();
opts.half_transfer_ir = true;
opts.complete_transfer_ir = true;
pub async fn run(&mut self) -> ! {
loop {
let gpioa_idr_addr = embassy_stm32::pac::GPIOA.as_ptr() as *mut u32;
// SAFETY: ring is exclusive to this task
let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) };
rx.start();
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;
}
}
let mut chunk = [0u8; 256];
loop {
let _ = rx.read_exact(&mut chunk).await;
pipe_rx.write(&chunk).await;
}
}

View File

@@ -1,145 +1,12 @@
// src/gpio_dma_uart.rs
use embassy_stm32::{
dma::{Request, Transfer, TransferOptions},
peripherals::GPDMA1_CH0,
Peri,
};
// src/software_uart/gpio_dma_uart_tx.rs
use embassy_stm32::dma::Request;
use embassy_stm32::dma::WritableRingBuffer;
use crate::software_uart::uart_emulation::{UartConfig, encode_uart_byte_cfg};
// 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
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Parity {
None,
Even,
Odd,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StopBits {
One,
Two,
}
#[derive(Clone, Copy, Debug)]
pub struct UartConfig {
pub data_bits: u8, // 5..=8 bitov strana 16 TI_uart
pub parity: Parity,
pub stop_bits: StopBits,
}
impl Default for UartConfig {
fn default() -> Self {
Self {
data_bits: 8,
parity: Parity::None,
stop_bits: StopBits::One,
}
}
}
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 up to 12 BSRR words for one UART frame on a given GPIO bit.
// Format: 1 START (low), N data (LSB first), optional PARITY, STOP(1/2 -> here 1 or 2 ticks).
// BSRR je safe atomic write only shortcut
pub fn encode_uart_byte_cfg(
pin_bit: u8,
data: u8,
cfg: &UartConfig,
out: &mut [u32; 12],
) -> usize {
// Dokumentacia strana 636 13.4.7
// set bit - HIGH, reset bit - LOW (BSRR)
let set_high = |bit: u8| -> u32 { 1u32 << bit };
let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) };
let mut idx = 0usize;
// START bit (LOW)
out[idx] = set_low(pin_bit);
idx += 1;
// Data bits, LSB first (5..=8)
let nbits = cfg.data_bits.clamp(5, 8);
for i in 0..nbits {
let one = ((data >> i) & 1) != 0;
out[idx] = if one { set_high(pin_bit) } else { set_low(pin_bit) };
idx += 1;
}
// Optional parity
match cfg.parity {
Parity::None => {}
Parity::Even | Parity::Odd => {
// Count ones
let mask: u8 = if nbits == 8 { 0xFF } else { (1u16 << nbits) as u8 - 1 };
let ones = (data & mask).count_ones() & 1; // 0=even, 1=odd
let par_bit_is_one = match cfg.parity {
Parity::Even => ones == 1, // make total ones even
Parity::Odd => ones == 0, // make total ones odd
_ => false,
};
out[idx] = if par_bit_is_one {
set_high(pin_bit)
} else {
set_low(pin_bit)
};
idx += 1;
}
}
// STOP bits (HIGH)
// - STB=0 => 1 stop bit
// - STB=1 => 2 stop bits
let stop_ticks = match cfg.stop_bits {
StopBits::One => 1usize,
StopBits::Two => 2usize,
};
for _ in 0..stop_ticks {
out[idx] = set_high(pin_bit);
idx += 1;
}
idx
}
/// Push UART frames into the DMA-backed TX ring non-blockingly.
/// Automatically waits for free space when ring is full.
/// Push UART frames into the DMA-backed TX ring
pub async fn write_uart_frames_to_ring(
ring: &mut WritableRingBuffer<'static, u32>,
pin_bit: u8,

View File

@@ -3,11 +3,11 @@
pub mod gpio_dma_uart_tx;
pub mod gpio_dma_uart_rx;
pub mod dma_timer;
pub mod runtime;
pub mod uart_emulation;
pub mod debug;
pub use gpio_dma_uart_tx::*;
pub use gpio_dma_uart_rx::*;
pub use dma_timer::*;
pub use runtime::*;
pub use uart_emulation::*;
pub use debug::*;

View File

@@ -1,36 +0,0 @@
// src/software_uart/runtime.rs
use embassy_executor::task;
use embassy_stm32::{
peripherals::GPDMA1_CH1,
Peri,
};
use embassy_stm32::dma::{
ReadableRingBuffer as DmaRingRx,
TransferOptions,
};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
use crate::software_uart::gpio_dma_uart_rx::TIM7_UP_REQ;
/// 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;
}
}

View File

@@ -0,0 +1,89 @@
// src/software_uart/uart_emulation.rs
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Parity {
None,
Even,
Odd,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StopBits {
One,
Two,
}
#[derive(Clone, Copy, Debug)]
pub struct UartConfig {
pub data_bits: u8,
pub parity: Parity,
pub stop_bits: StopBits,
}
impl Default for UartConfig {
fn default() -> Self {
Self {
data_bits: 8,
parity: Parity::None,
stop_bits: StopBits::One,
}
}
}
/// Encodes one byte into a sequence of GPIO BSRR words
pub fn encode_uart_byte_cfg(
pin_bit: u8,
data: u8,
cfg: &UartConfig,
out: &mut [u32; 12],
) -> usize {
// GPIOx_BSRR register str. 636 kap. 13.4.7
let set_high = |bit: u8| -> u32 { 1u32 << bit };
let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) };
let mut idx = 0usize;
// START bit (LOW)
out[idx] = set_low(pin_bit);
idx += 1;
// Data bits, LSB-first
let nbits = cfg.data_bits.clamp(5, 8);
for i in 0..nbits {
let one = ((data >> i) & 1) != 0;
out[idx] = if one { set_high(pin_bit) } else { set_low(pin_bit) };
idx += 1;
}
// Parity
match cfg.parity {
Parity::None => {}
Parity::Even | Parity::Odd => {
let mask: u8 = if nbits == 8 { 0xFF } else { (1u16 << nbits) as u8 - 1 };
let ones = (data & mask).count_ones() & 1;
let par_bit_is_one = match cfg.parity {
Parity::Even => ones == 1,
Parity::Odd => ones == 0,
_ => false,
};
out[idx] = if par_bit_is_one {
set_high(pin_bit)
} else {
set_low(pin_bit)
};
idx += 1;
}
}
// STOP bits (HIGH)
let stop_ticks = match cfg.stop_bits {
StopBits::One => 1usize,
StopBits::Two => 2usize,
};
for _ in 0..stop_ticks {
out[idx] = set_high(pin_bit);
idx += 1;
}
idx
}