diff --git a/dma_gpio/src/bin/main.rs b/dma_gpio/src/bin/main.rs index ff06329..00afd74 100644 --- a/dma_gpio/src/bin/main.rs +++ b/dma_gpio/src/bin/main.rs @@ -10,7 +10,6 @@ use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::{ - dma::Request, gpio::{Level, Output, Speed}, peripherals::{GPDMA1_CH0, TIM6}, rcc, @@ -18,7 +17,9 @@ use embassy_stm32::{ }; use embassy_stm32::Peri; -use dma_gpio::gpio_dma_uart::{encode_uart_byte, write_uart_frames_to_pipe, GpioDmaBsrrTx, TIM6_UP_REQ}; +use dma_gpio::gpio_dma_uart::{ + write_uart_frames_to_pipe, GpioDmaBsrrTx, Parity, StopBits, UartConfig, TIM6_UP_REQ, +}; static PIPE: Pipe = Pipe::new(); @@ -26,10 +27,16 @@ static PIPE: Pipe = Pipe::new(); const BAUD: u32 = 115_200; const TX_PIN_BIT: u8 = 2; // PA2 +const UART_CFG: UartConfig = UartConfig { + data_bits: 8, + parity: Parity::None, + stop_bits: StopBits::One, +}; + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - info!("DMA Pipe -> GPIO UART-like TX (safe wrapper)"); + info!("DMA Pipe -> GPIO UART-like TX"); // PA2 is the TX "wire" let _pa2 = Output::new(p.PA2, Level::High, Speed::VeryHigh); @@ -51,7 +58,12 @@ async fn main(spawner: Spawner) { // Example: transmit a string as UART frames via the Pipe loop { - write_uart_frames_to_pipe(&PIPE, TX_PIN_BIT, b"Hello, DMA UART!\r\n").await; + write_uart_frames_to_pipe( + &PIPE, + TX_PIN_BIT, + b"Hello, DMA UART (configurable)!\r\n", + &UART_CFG, + ).await; Timer::after(Duration::from_secs(2)).await; } } @@ -70,7 +82,6 @@ async fn dma_tx_task(ch: Peri<'static, GPDMA1_CH0>) { } let w = u32::from_le_bytes(b); - // Log + send via DMA (timer-paced, 1 beat per bit time) info!("DMA write 0x{:08X} -> GPIOA.BSRR", w); tx.write_word(w).await; } diff --git a/dma_gpio/src/gpio_dma_uart.rs b/dma_gpio/src/gpio_dma_uart.rs index a0c18c6..f592f72 100644 --- a/dma_gpio/src/gpio_dma_uart.rs +++ b/dma_gpio/src/gpio_dma_uart.rs @@ -6,7 +6,37 @@ use embassy_stm32::{ }; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; -pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687 +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>, @@ -43,41 +73,95 @@ impl<'d> GpioDmaBsrrTx<'d> { } } -// Build 10 BSRR words for one UART frame (8N1) on a given GPIO bit. -// start bit, 8 data bits LSB-first, stop bit +// 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(pin_bit: u8, data: u8) -> [u32; 10] { - let mut words = [0u32; 10]; - +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 + // 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) }; - // start bit LOW - words[0] = set_low(pin_bit); + let mut idx = 0usize; - // data bits, LSB first - for i in 0..8 { - let one = (data >> i) & 1 != 0; - words[1 + i] = if one { set_high(pin_bit) } else { set_low(pin_bit) }; + // 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; } - // stop bit HIGH - words[9] = set_high(pin_bit); - words + // 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 } -// Convenience: push UART frames for a whole byte slice into a Pipe. +// Push UART frames for a whole byte slice into a Pipe. pub async fn write_uart_frames_to_pipe( pipe: &Pipe, pin_bit: u8, bytes: &[u8], + cfg: &UartConfig, ) { for &b in bytes { - let frames = encode_uart_byte(pin_bit, b); - for w in frames { + let mut frame = [0u32; 12]; + let used = encode_uart_byte_cfg(pin_bit, b, cfg, &mut frame); + for w in &frame[..used] { pipe.write(&w.to_le_bytes()).await; } } } + +// Optional: emit a BREAK (line LOW for 'bits' bit-times). +pub async fn write_break_to_pipe( + pipe: &Pipe, + pin_bit: u8, + bits: usize, +) { + let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) }; + let word = set_low(pin_bit); + for _ in 0..bits { + pipe.write(&word.to_le_bytes()).await; + } +}