From 0cd40eb5e28c2b9e6fb66dc037ee40dbc0d92fb0 Mon Sep 17 00:00:00 2001 From: Priec Date: Wed, 19 Nov 2025 15:59:14 +0100 Subject: [PATCH] uart rx working fully --- semestralka_1d_rx_bez_dma/src/bin/main.rs | 43 ++++++++-- .../src/software_uart/gpio_dma_uart_rx.rs | 4 +- .../src/software_uart/runtime.rs | 81 +++++++++++++++++++ .../src/software_uart/uart_emulation.rs | 67 +++++++++------ 4 files changed, 165 insertions(+), 30 deletions(-) create mode 100644 semestralka_1d_rx_bez_dma/src/software_uart/runtime.rs diff --git a/semestralka_1d_rx_bez_dma/src/bin/main.rs b/semestralka_1d_rx_bez_dma/src/bin/main.rs index 640a105..1897a0e 100644 --- a/semestralka_1d_rx_bez_dma/src/bin/main.rs +++ b/semestralka_1d_rx_bez_dma/src/bin/main.rs @@ -26,6 +26,8 @@ use dma_gpio::config::{PIPE_HW_TX, PIPE_HW_RX, PIPE_SW_TX, PIPE_SW_RX}; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; use dma_gpio::hw_uart_internal::usart2; use dma_gpio::hw_uart_internal::driver::uart_task as uart_task_internal; +use dma_gpio::software_uart::decode_uart_samples; +use dma_gpio::config::UART_CFG; use dma_gpio::config::{PIPE_INT_TX, PIPE_INT_RX}; use embassy_time::{Duration, Timer}; use embassy_stm32::pac; @@ -120,13 +122,44 @@ async fn main(spawner: Spawner) { // EDN OF SOFTWARE UART unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::TIM7); } - let mut last_bit: Option = None; + + let mut rx_samples = [1u8; 1024]; + let mut rx_count = 0usize; loop { + // 1. Drain the channel into the local buffer while let Ok(bit) = PD6_BITS.try_receive() { - if Some(bit) != last_bit { - // PD6 level changed since the last sample → print one 'c' - info!("c"); - last_bit = Some(bit); + if rx_count < rx_samples.len() { + rx_samples[rx_count] = bit; + rx_count += 1; + } else { + // Buffer full: discarding oldest data by rotating + // Simple naive strategy: just clear half + // Ideally we should prevent this by processing faster + warn!("RX Buffer overflow, resetting"); + rx_count = 0; + } + } + + // 2. Try to decode UART frames from the buffer + if rx_count > 0 { + // Try to decode + let (decoded, consumed) = decode_uart_samples( + &rx_samples[..rx_count], + RX_OVERSAMPLE, + &UART_CFG + ); + + // Print received characters + for b in decoded { + info!("Received: {}", b as char); + } + + // 3. Remove processed samples from the buffer + if consumed > 0 { + // Shift remaining data to the front + // copy_within is efficient for slices + rx_samples.copy_within(consumed..rx_count, 0); + rx_count -= consumed; } } yield_now().await; diff --git a/semestralka_1d_rx_bez_dma/src/software_uart/gpio_dma_uart_rx.rs b/semestralka_1d_rx_bez_dma/src/software_uart/gpio_dma_uart_rx.rs index 887add3..49f9949 100644 --- a/semestralka_1d_rx_bez_dma/src/software_uart/gpio_dma_uart_rx.rs +++ b/semestralka_1d_rx_bez_dma/src/software_uart/gpio_dma_uart_rx.rs @@ -69,10 +69,10 @@ pub async fn rx_dma_task( levels[i] = ((*b >> RX_PIN_BIT) & 1) as u8; } - let decoded = decode_uart_samples(&levels, RX_OVERSAMPLE, &UART_CFG); + let (decoded, consumed) = decode_uart_samples(&levels, RX_OVERSAMPLE, &UART_CFG); if !decoded.is_empty() { info!("SW RX decoded {:a}", decoded.as_slice()); - pipe_rx.write(&decoded).await; + pipe_rx.write(decoded.as_slice()).await; } yield_now().await; diff --git a/semestralka_1d_rx_bez_dma/src/software_uart/runtime.rs b/semestralka_1d_rx_bez_dma/src/software_uart/runtime.rs new file mode 100644 index 0000000..cfce386 --- /dev/null +++ b/semestralka_1d_rx_bez_dma/src/software_uart/runtime.rs @@ -0,0 +1,81 @@ +// src/software_uart/runtime.rs +use embassy_executor::task; +use embassy_stm32::{ + dma::Request, + peripherals::GPDMA1_CH1, + Peri, +}; +use crate::config::RX_PIN_BIT; +use embassy_stm32::dma::{ + ReadableRingBuffer, + TransferOptions, +}; +use crate::config::{RX_OVERSAMPLE, UART_CFG}; +use crate::software_uart::decode_uart_samples; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; +use embassy_futures::yield_now; +use defmt::info; + +// datasheet tabulka 137 +pub const TIM7_UP_REQ: Request = 5; + +/// 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>, + register: *mut u8, + ring: &'static mut [u8], + pipe_rx: &'static Pipe, +) { + 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 { ReadableRingBuffer::new(ch, TIM7_UP_REQ, register, ring, opts) }; + rx.start(); + + let mut raw_chunk = [0u8; 256]; + let mut levels = [0u8; 256]; + let mut last_bytes: [u8; 16] = [0; 16]; // remember previous 16 samples + let mut last_value: u8 = 0; // remember previous single IDR sample (optional) + + loop { + let _ = rx.read_exact(&mut raw_chunk).await; + + // If nothing has changed (all bytes same as previous), skip logging + if raw_chunk[..16] == last_bytes[..] { + continue; + } + + // Save for next comparison + last_bytes.copy_from_slice(&raw_chunk[..16]); + let current_avg = raw_chunk[0]; + + // Optionally only if single sample changed + if current_avg != last_value { + last_value = current_avg; + + info!( + "DMA change detected: IDR[0..16]={=[u8]:02X} bit{}={}", + &raw_chunk[..16], + RX_PIN_BIT, + (current_avg >> RX_PIN_BIT) & 1 + ); + } + + // Extract logic levels as before + for (i, b) in raw_chunk.iter().enumerate() { + levels[i] = ((*b >> RX_PIN_BIT) & 1) as u8; + } + + // Updated to handle tuple return (bytes, consumed_count) + let (decoded, _consumed) = decode_uart_samples(&levels, RX_OVERSAMPLE, &UART_CFG); + if !decoded.is_empty() { + info!("SW RX decoded {:a}", decoded.as_slice()); + pipe_rx.write(&decoded).await; + } + + yield_now().await; + } +} diff --git a/semestralka_1d_rx_bez_dma/src/software_uart/uart_emulation.rs b/semestralka_1d_rx_bez_dma/src/software_uart/uart_emulation.rs index d463849..8e46e74 100644 --- a/semestralka_1d_rx_bez_dma/src/software_uart/uart_emulation.rs +++ b/semestralka_1d_rx_bez_dma/src/software_uart/uart_emulation.rs @@ -91,61 +91,82 @@ pub fn encode_uart_byte_cfg( } /// Decode an oversampled stream of logic levels into UART bytes. +/// Returns (decoded bytes, number of samples consumed/processed). pub fn decode_uart_samples( samples: &[u8], oversample: u16, cfg: &UartConfig, -) -> heapless::Vec { - +) -> (heapless::Vec, usize) { let mut out = Vec::::new(); let mut idx = 0usize; let nbits = cfg.data_bits as usize; + let ovs = oversample as usize; - while idx + (oversample as usize * (nbits + 3)) < samples.len() { - // Wait for start bit (falling edge: high -> low) + // Calculate total frame width in samples to ensure we have enough data + // 1 start + n data + parity? + stops + let parity_bits = match cfg.parity { + Parity::None => 0, + _ => 1, + }; + let stop_bits_count = match cfg.stop_bits { + StopBits::One => 1, + StopBits::Two => 2, + }; + let frame_bits = 1 + nbits + parity_bits + stop_bits_count; + let frame_len = frame_bits * ovs; + + // We loop while we have enough 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) if samples[idx] != 0 && samples[idx + 1] == 0 { - // Align to middle of start bit - idx += (oversample / 2) as usize; + + // 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 mut scan_idx = idx + center_offset; - // Sanity check start bit really low - if samples.get(idx).copied().unwrap_or(1) != 0 { + // Sanity check: is the middle of the start bit actually 0? + if samples.get(scan_idx).copied().unwrap_or(1) != 0 { idx += 1; continue; } + // Move to center of first data bit + scan_idx += ovs; + // Sample data bits let mut data: u8 = 0; for bit in 0..nbits { - idx += oversample as usize; let bit_val = samples - .get(idx) + .get(scan_idx) .map(|&b| if b != 0 { 1u8 } else { 0u8 }) - .unwrap_or(1); + .unwrap_or(1); // Default to high if out of bounds (shouldn't happen) data |= bit_val << bit; + scan_idx += ovs; } - // Parity: skip / verify + // Parity: skip match cfg.parity { Parity::None => {} Parity::Even | Parity::Odd => { - idx += oversample as usize; - // You can optionally add parity check here if needed + scan_idx += ovs; } } - // Move past stop bits - let stop_skip = match cfg.stop_bits { - StopBits::One => oversample as usize, - StopBits::Two => (oversample * 2) as usize, - }; - idx += stop_skip; - - // Push decoded byte + // We successfully read a byte. + // Advance main `idx` past this frame. + // We processed `frame_len` samples starting from `idx` (which was the idle bit before start). + // Actually, to be precise, we should advance by roughly the frame length. + idx += frame_len; + + // If overflow happens in push (unlikely given 256 limit), ignore let _ = out.push(data); } else { + // No start bit detected here, move to next sample idx += 1; } } - out + (out, idx) }