diff --git a/dma_gpio/src/bin/main.rs b/dma_gpio/src/bin/main.rs index 3cb6da5..6688d8f 100644 --- a/dma_gpio/src/bin/main.rs +++ b/dma_gpio/src/bin/main.rs @@ -9,9 +9,8 @@ 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, - }, + 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, }; diff --git a/dma_gpio/src/software_uart/gpio_dma_uart_tx.rs b/dma_gpio/src/software_uart/gpio_dma_uart_tx.rs index 69adcd2..a1f1e9e 100644 --- a/dma_gpio/src/software_uart/gpio_dma_uart_tx.rs +++ b/dma_gpio/src/software_uart/gpio_dma_uart_tx.rs @@ -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, diff --git a/dma_gpio/src/software_uart/mod.rs b/dma_gpio/src/software_uart/mod.rs index 3ef2c36..3ecae27 100644 --- a/dma_gpio/src/software_uart/mod.rs +++ b/dma_gpio/src/software_uart/mod.rs @@ -3,9 +3,11 @@ pub mod gpio_dma_uart_tx; pub mod gpio_dma_uart_rx; pub mod dma_timer; +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 uart_emulation::*; pub use debug::*; diff --git a/dma_gpio/src/software_uart/uart_emulation.rs b/dma_gpio/src/software_uart/uart_emulation.rs new file mode 100644 index 0000000..edd2fd5 --- /dev/null +++ b/dma_gpio/src/software_uart/uart_emulation.rs @@ -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 +}