improved parsing of software uart
This commit is contained in:
@@ -115,53 +115,85 @@ pub fn decode_uart_samples(
|
||||
let frame_bits = 1 + nbits + parity_bits + stop_bits_count;
|
||||
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
|
||||
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 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: is the middle of the start bit actually 0?
|
||||
if samples.get(scan_idx).copied().unwrap_or(1) != 0 {
|
||||
idx += 1;
|
||||
// 1. Validate Start Bit (Must be 0)
|
||||
if get_bit(scan_idx) != 0 {
|
||||
idx += 1; // False start (noise), move on
|
||||
continue;
|
||||
}
|
||||
|
||||
// Move to center of first data bit
|
||||
scan_idx += ovs;
|
||||
|
||||
// Sample data bits
|
||||
// 2. Read Data Bits
|
||||
let mut data: u8 = 0;
|
||||
for bit in 0..nbits {
|
||||
let bit_val = samples
|
||||
.get(scan_idx)
|
||||
.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;
|
||||
if get_bit(scan_idx) == 1 {
|
||||
data |= 1 << bit;
|
||||
}
|
||||
scan_idx += ovs;
|
||||
}
|
||||
|
||||
// Parity: skip
|
||||
match cfg.parity {
|
||||
Parity::None => {}
|
||||
Parity::Even | Parity::Odd => {
|
||||
scan_idx += ovs;
|
||||
}
|
||||
// 3. Skip Parity (if any)
|
||||
if cfg.parity != Parity::None {
|
||||
scan_idx += ovs;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// If overflow happens in push (unlikely given 256 limit), ignore
|
||||
// 5. Byte is valid
|
||||
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 {
|
||||
// No start bit detected here, move to next sample
|
||||
idx += 1;
|
||||
|
||||
Reference in New Issue
Block a user