From 9451bc5ae9de8679f2edbaee56519acfc135d0bc Mon Sep 17 00:00:00 2001 From: Priec Date: Sun, 23 Nov 2025 21:44:01 +0100 Subject: [PATCH] timer HAL --- .../software_uart/src/dma_timer.rs | 44 ++--------- .../software_uart/src/gpio_dma_uart_rx.rs | 17 ++-- .../software_uart/src/gpio_dma_uart_tx.rs | 9 +-- .../software_uart/src/uart_emulation.rs | 78 +++++++++++-------- 4 files changed, 61 insertions(+), 87 deletions(-) diff --git a/semestralka_1_final_crate/software_uart/src/dma_timer.rs b/semestralka_1_final_crate/software_uart/src/dma_timer.rs index e38be3f..a0ccec6 100644 --- a/semestralka_1_final_crate/software_uart/src/dma_timer.rs +++ b/semestralka_1_final_crate/software_uart/src/dma_timer.rs @@ -2,18 +2,16 @@ use embassy_stm32::{ peripherals::{TIM6, TIM7}, - rcc, timer::low_level::Timer, Peri, + time::Hertz, }; use core::mem; use embassy_stm32::timer::BasicInstance; -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) { - rcc::enable_and_reset::(); let ll = Timer::new(tim6); configure_basic_timer(&ll, baud, oversample); mem::forget(ll); @@ -22,46 +20,20 @@ pub fn init_tim6_for_uart<'d>(tim6: Peri<'d, TIM6>, baud: u32, oversample: u16) /// 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::(); let ll = Timer::new(tim7); configure_basic_timer(&ll, baud, oversample); - // Enable Update Interrupt (UIE) - ll.regs_basic().dier().modify(|w| { - w.set_ude(true); - w.set_uie(false); - }); + ll.enable_update_interrupt(false); //Disable CPU interrupts mem::forget(ll); } -// Shared internal helper — identical CR1/ARR setup fn configure_basic_timer(ll: &Timer<'_, T>, baud: u32, oversample: u16) { - let f_timer = rcc::frequency::().0; let target = baud.saturating_mul(oversample.max(1) as u32).max(1); + let target_freq = Hertz(target); - // Compute ARR (prescaler = 0) - // let mut arr = (f_timer / target).saturating_sub(1) as u16; - let mut arr = ((f_timer + target / 2) / target).saturating_sub(1) as u16; - if arr == 0 { arr = 1; } + ll.stop(); + ll.set_frequency(target_freq); + ll.enable_update_dma(true); - ll.regs_basic().cr1().write(|w| { - w.set_cen(false); - w.set_opm(false); - w.set_udis(false); - w.set_urs(Urs::ANY_EVENT); - }); - - ll.regs_basic().psc().write_value(0u16); - 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)); - - // Clear spurious UIF from UG trigger - ll.regs_basic().sr().modify(|w| w.set_uif(false)); - - ll.regs_basic().cr1().write(|w| { - w.set_opm(false); - w.set_cen(true); - w.set_udis(false); - w.set_urs(Urs::ANY_EVENT); - }); + ll.clear_update_interrupt(); + ll.start(); } diff --git a/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_rx.rs b/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_rx.rs index e27a4aa..326a0d3 100644 --- a/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_rx.rs +++ b/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_rx.rs @@ -1,4 +1,4 @@ -// src/runtime.rs +// src/gpio_dma_uart_rx.rs use embassy_executor::task; use embassy_stm32::{ @@ -46,12 +46,10 @@ pub async fn rx_dma_task( }; rx.start(); - // We read into the second half of a buffer, keeping "leftovers" in the first half. const CHUNK_SIZE: usize = 4096; const HISTORY_SIZE: usize = 512; const TOTAL_BUF_SIZE: usize = HISTORY_SIZE + CHUNK_SIZE; - // Logic level buffer let mut level_buf = [0u8; TOTAL_BUF_SIZE]; let mut valid_len = 0usize; @@ -60,6 +58,7 @@ pub async fn rx_dma_task( loop { let _ = rx.read_exact(&mut raw_chunk).await; + // Extract Rx pin value from IDR for (i, b) in raw_chunk.iter().enumerate() { level_buf[valid_len + i] = ((*b >> rx_pin_bit) & 1) as u8; } @@ -74,27 +73,23 @@ pub async fn rx_dma_task( if !decoded.is_empty() { pipe_rx.write(decoded.as_slice()).await; - for byte in decoded.as_slice() { + // for byte in decoded.as_slice() { // info!("DMA BUFFER CHAR: {} (ASCII: {})", *byte, *byte as char); - } + // } } // Shift remaining data to front // We processed 'consumed' samples. - // We keep everything from 'consumed' up to 'current_end'. + // Keeping the rest of the data let remaining = current_end - consumed; - // SAFETY if remaining > HISTORY_SIZE, we are in trouble (buffer too small / decoder stuck). if remaining > 0 { level_buf.copy_within(consumed..current_end, 0); } valid_len = remaining; - // If valid_len grows too large (decoder not consuming), we must discard to avoid panic on next write + // SAFETY if decoder is stuck and buffer is filling up, discard old data if valid_len >= HISTORY_SIZE { - // Discard oldest to make space - // logic: we move the last (HISTORY_SIZE/2) to 0. - // This effectively "skips" garbage data. let keep = HISTORY_SIZE / 2; level_buf.copy_within(valid_len - keep..valid_len, 0); valid_len = keep; diff --git a/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_tx.rs b/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_tx.rs index e259eb1..9aa84c2 100644 --- a/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_tx.rs +++ b/semestralka_1_final_crate/software_uart/src/gpio_dma_uart_tx.rs @@ -19,10 +19,8 @@ pub async fn encode_uart_frames<'a>( pin_bit: u8, bytes: &[u8], out_buf: &'a mut [u32], - uart_cfg: &mut UartConfig, + uart_cfg: &UartConfig, ) -> usize { - uart_cfg.validity_check(); // clamping to min 5 bits - let mut offset = 0; for &b in bytes { let mut frame = [0u32; 12]; @@ -44,13 +42,12 @@ pub async fn encode_uart_frames<'a>( pub async fn tx_dma_task( mut ch: Peri<'static, GPDMA1_CH0>, register: *mut u32, // GPIOx_BSRR - _tx_ring_mem: &'static mut [u32], + tx_ring_mem: &'static mut [u32], pipe_rx: &'static Pipe, tx_pin_bit: u8, - uart_cfg: &'static mut UartConfig, + uart_cfg: &'static UartConfig, ) { info!("TX DMA task ready (One‑shot)"); - let mut frame_buf = [0u32; 4096]; let mut rx_buf = [0u8; 256]; let tim6 = embassy_stm32::pac::TIM6; diff --git a/semestralka_1_final_crate/software_uart/src/uart_emulation.rs b/semestralka_1_final_crate/software_uart/src/uart_emulation.rs index 36eb5c3..5c65314 100644 --- a/semestralka_1_final_crate/software_uart/src/uart_emulation.rs +++ b/semestralka_1_final_crate/software_uart/src/uart_emulation.rs @@ -1,4 +1,5 @@ // src/uart_emulation.rs + use heapless::Vec; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -31,18 +32,6 @@ impl Default for UartConfig { } } -impl UartConfig { - pub fn validity_check(&mut self) { - if self.data_bits < 5 { - defmt::warn!( - "Invalid UART data_bits={} – clamping to minimum 5 bits.", - self.data_bits - ); - self.data_bits = 5; - } - } -} - /// Encodes one byte into a sequence of GPIO BSRR words pub fn encode_uart_byte_cfg( pin_bit: u8, @@ -148,14 +137,11 @@ pub fn decode_uart_samples( } }; - // Loop while we have enough remaining samples for a full frame + // Decode while remaining samples for a full frame while idx + frame_len <= samples.len() { - // Wait for falling edge (High -> Low) - // samples[idx] == 1 (Idle/Stop) && samples[idx+1] == 0 (Start) + // Start - idle HIGH to start LOW if samples[idx] != 0 && samples[idx + 1] == 0 { - // Align to center of START bit - // Start bit begins at idx+1. Center is at idx + 1 + (ovs/2) - let center_offset = 1 + (ovs / 2); + let center_offset = ovs / 2; let mut scan_idx = idx + center_offset; // Validate Start Bit @@ -176,33 +162,39 @@ pub fn decode_uart_samples( scan_idx += ovs; } - // Skip Parity + let mut error_data = false; if cfg.parity != Parity::None { + let expected_parity = calculate_parity(data, cfg.parity, cfg.data_bits); + let actual_parity = get_bit(scan_idx); + if expected_parity != actual_parity { + // Parity error + error_data = true; + } scan_idx += ovs; } - // Validate Stop Bit (Must be 1) - // If stop bit is 0, it's a framing error. We reject the whole byte. - if get_bit(scan_idx) == 0 { - idx += 1; // Next sample - continue; + for _ in 0..stop_bits_count { + if get_bit(scan_idx) == 0 { + // Framing error + error_data = true; + break; + } + scan_idx += ovs; + } + if error_data { + idx += 1; + continue; // Skip this frame completely } - // Byte is valid let _ = out.push(data); - - // Active Resync: Fast-forward through the stop bit(s) and idle time - // scan_idx is currently at the center of the Stop bit. idx = scan_idx; - // Advance while we are reading High (1). - // As soon as we see Low (0), we stop. That 0 is the beginning of the NEXT start bit. - // The outer loop expects `idx` to be the High *before* the start bit, so we will handle that. + + // Next startbit reach while idx < samples.len() && samples[idx] != 0 { idx += 1; } - // Back up one step. - // The outer loop logic is: `if samples[idx] != 0 && samples[idx+1] == 0`. - // If we stopped at `idx` because it was 0, then `idx-1` was the last 1 (Idle). + + // Mensi hack if idx > 0 { idx -= 1; } @@ -214,3 +206,21 @@ pub fn decode_uart_samples( (out, idx) } + +/// Calculate the expected parity bit (0 or 1) for the given data and parity mode +fn calculate_parity(data: u8, parity: Parity, data_bits: u8) -> u8 { + match parity { + Parity::None => 0, + Parity::Even | Parity::Odd => { + // Mask to only count bits that are part of the data + let mask: u8 = if data_bits == 8 { 0xFF } else { ((1u16 << data_bits) - 1) as u8 }; + + let ones = (data & mask).count_ones() & 1; + match parity { + Parity::Even => ones as u8, // If ones=1 (odd), emit 1 to make even + Parity::Odd => (ones ^ 1) as u8, // XOR - If ones=1 (odd), emit 0 to keep odd + _ => 0, + } + } + } +}