uart rx working fully
This commit is contained in:
@@ -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 embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||||
use dma_gpio::hw_uart_internal::usart2;
|
use dma_gpio::hw_uart_internal::usart2;
|
||||||
use dma_gpio::hw_uart_internal::driver::uart_task as uart_task_internal;
|
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 dma_gpio::config::{PIPE_INT_TX, PIPE_INT_RX};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use embassy_stm32::pac;
|
use embassy_stm32::pac;
|
||||||
@@ -120,13 +122,44 @@ async fn main(spawner: Spawner) {
|
|||||||
// EDN OF SOFTWARE UART
|
// EDN OF SOFTWARE UART
|
||||||
|
|
||||||
unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::TIM7); }
|
unsafe { cortex_m::peripheral::NVIC::unmask(pac::Interrupt::TIM7); }
|
||||||
let mut last_bit: Option<u8> = None;
|
|
||||||
|
let mut rx_samples = [1u8; 1024];
|
||||||
|
let mut rx_count = 0usize;
|
||||||
loop {
|
loop {
|
||||||
|
// 1. Drain the channel into the local buffer
|
||||||
while let Ok(bit) = PD6_BITS.try_receive() {
|
while let Ok(bit) = PD6_BITS.try_receive() {
|
||||||
if Some(bit) != last_bit {
|
if rx_count < rx_samples.len() {
|
||||||
// PD6 level changed since the last sample → print one 'c'
|
rx_samples[rx_count] = bit;
|
||||||
info!("c");
|
rx_count += 1;
|
||||||
last_bit = Some(bit);
|
} 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;
|
yield_now().await;
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ pub async fn rx_dma_task(
|
|||||||
levels[i] = ((*b >> RX_PIN_BIT) & 1) as u8;
|
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() {
|
if !decoded.is_empty() {
|
||||||
info!("SW RX decoded {:a}", decoded.as_slice());
|
info!("SW RX decoded {:a}", decoded.as_slice());
|
||||||
pipe_rx.write(&decoded).await;
|
pipe_rx.write(decoded.as_slice()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
|
|||||||
81
semestralka_1d_rx_bez_dma/src/software_uart/runtime.rs
Normal file
81
semestralka_1d_rx_bez_dma/src/software_uart/runtime.rs
Normal file
@@ -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<CriticalSectionRawMutex, 1024>,
|
||||||
|
) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,61 +91,82 @@ pub fn encode_uart_byte_cfg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decode an oversampled stream of logic levels into UART bytes.
|
/// Decode an oversampled stream of logic levels into UART bytes.
|
||||||
|
/// Returns (decoded bytes, number of samples consumed/processed).
|
||||||
pub fn decode_uart_samples(
|
pub fn decode_uart_samples(
|
||||||
samples: &[u8],
|
samples: &[u8],
|
||||||
oversample: u16,
|
oversample: u16,
|
||||||
cfg: &UartConfig,
|
cfg: &UartConfig,
|
||||||
) -> heapless::Vec<u8, 256> {
|
) -> (heapless::Vec<u8, 256>, usize) {
|
||||||
|
|
||||||
let mut out = Vec::<u8, 256>::new();
|
let mut out = Vec::<u8, 256>::new();
|
||||||
let mut idx = 0usize;
|
let mut idx = 0usize;
|
||||||
let nbits = cfg.data_bits as usize;
|
let nbits = cfg.data_bits as usize;
|
||||||
|
let ovs = oversample as usize;
|
||||||
|
|
||||||
while idx + (oversample as usize * (nbits + 3)) < samples.len() {
|
// Calculate total frame width in samples to ensure we have enough data
|
||||||
// Wait for start bit (falling edge: high -> low)
|
// 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 {
|
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
|
// Sanity check: is the middle of the start bit actually 0?
|
||||||
if samples.get(idx).copied().unwrap_or(1) != 0 {
|
if samples.get(scan_idx).copied().unwrap_or(1) != 0 {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move to center of first data bit
|
||||||
|
scan_idx += ovs;
|
||||||
|
|
||||||
// Sample data bits
|
// Sample data bits
|
||||||
let mut data: u8 = 0;
|
let mut data: u8 = 0;
|
||||||
for bit in 0..nbits {
|
for bit in 0..nbits {
|
||||||
idx += oversample as usize;
|
|
||||||
let bit_val = samples
|
let bit_val = samples
|
||||||
.get(idx)
|
.get(scan_idx)
|
||||||
.map(|&b| if b != 0 { 1u8 } else { 0u8 })
|
.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;
|
data |= bit_val << bit;
|
||||||
|
scan_idx += ovs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parity: skip / verify
|
// Parity: skip
|
||||||
match cfg.parity {
|
match cfg.parity {
|
||||||
Parity::None => {}
|
Parity::None => {}
|
||||||
Parity::Even | Parity::Odd => {
|
Parity::Even | Parity::Odd => {
|
||||||
idx += oversample as usize;
|
scan_idx += ovs;
|
||||||
// You can optionally add parity check here if needed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move past stop bits
|
// We successfully read a byte.
|
||||||
let stop_skip = match cfg.stop_bits {
|
// Advance main `idx` past this frame.
|
||||||
StopBits::One => oversample as usize,
|
// We processed `frame_len` samples starting from `idx` (which was the idle bit before start).
|
||||||
StopBits::Two => (oversample * 2) as usize,
|
// Actually, to be precise, we should advance by roughly the frame length.
|
||||||
};
|
idx += frame_len;
|
||||||
idx += stop_skip;
|
|
||||||
|
// If overflow happens in push (unlikely given 256 limit), ignore
|
||||||
// Push decoded byte
|
|
||||||
let _ = out.push(data);
|
let _ = out.push(data);
|
||||||
} else {
|
} else {
|
||||||
|
// No start bit detected here, move to next sample
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
(out, idx)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user