improved parsing of software uart

This commit is contained in:
Priec
2025-11-19 20:40:13 +01:00
parent de4e04787b
commit ef56483016

View File

@@ -115,53 +115,85 @@ pub fn decode_uart_samples(
let frame_bits = 1 + nbits + parity_bits + stop_bits_count; let frame_bits = 1 + nbits + parity_bits + stop_bits_count;
let frame_len = frame_bits * ovs; let frame_len = frame_bits * ovs;
// Helper: Majority vote over 3 samples centered at `i`
let get_bit = |i: usize| -> u8 {
let mut votes = 0;
// Check i-1, i, i+1. Saturating sub/add handles boundaries roughly.
if i > 0 && samples.get(i - 1).map_or(true, |&x| x != 0) {
votes += 1;
}
if samples.get(i).map_or(true, |&x| x != 0) {
votes += 1;
}
if samples.get(i + 1).map_or(true, |&x| x != 0) {
votes += 1;
}
if votes >= 2 {
1
} else {
0
}
};
// We loop while we have enough remaining samples for a full frame // We loop while we have enough remaining samples for a full frame
while idx + frame_len <= samples.len() { while idx + frame_len <= samples.len() {
// Wait for falling edge (High -> Low) // Wait for falling edge (High -> Low)
// samples[idx] == 1 (Idle/Stop) && samples[idx+1] == 0 (Start) // 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 center of START bit // Align to center of START bit
// Start bit begins at idx+1. Center is at idx + 1 + (ovs/2) // Start bit begins at idx+1. Center is at idx + 1 + (ovs/2)
let center_offset = 1 + (ovs / 2); let center_offset = 1 + (ovs / 2);
let mut scan_idx = idx + center_offset; let mut scan_idx = idx + center_offset;
// Sanity check: is the middle of the start bit actually 0? // 1. Validate Start Bit (Must be 0)
if samples.get(scan_idx).copied().unwrap_or(1) != 0 { if get_bit(scan_idx) != 0 {
idx += 1; idx += 1; // False start (noise), move on
continue; continue;
} }
// Move to center of first data bit // Move to center of first data bit
scan_idx += ovs; scan_idx += ovs;
// Sample data bits // 2. Read Data Bits
let mut data: u8 = 0; let mut data: u8 = 0;
for bit in 0..nbits { for bit in 0..nbits {
let bit_val = samples if get_bit(scan_idx) == 1 {
.get(scan_idx) data |= 1 << bit;
.map(|&b| if b != 0 { 1u8 } else { 0u8 }) }
.unwrap_or(1); // Default to high if out of bounds (shouldn't happen)
data |= bit_val << bit;
scan_idx += ovs; scan_idx += ovs;
} }
// Parity: skip // 3. Skip Parity (if any)
match cfg.parity { if cfg.parity != Parity::None {
Parity::None => {}
Parity::Even | Parity::Odd => {
scan_idx += ovs; scan_idx += ovs;
} }
// 4. 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; // Try to find a real start bit on the next sample
continue;
} }
// We successfully read a byte. // 5. Byte is valid
// 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); let _ = out.push(data);
// 6. 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.
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).
if idx > 0 {
idx -= 1;
}
} else { } else {
// No start bit detected here, move to next sample // No start bit detected here, move to next sample
idx += 1; idx += 1;