From a99fe56aae96b58ff313c43bab99b0c87288aed8 Mon Sep 17 00:00:00 2001 From: Allen Hill Date: Tue, 21 Jul 2026 13:22:19 -0700 Subject: [PATCH] Fix intermittent, early boot short start bit errors --- src/avclan/avclan.h | 3 +- src/avclan/bus.cc | 7 +++-- src/avclan/target/avr-attiny3216/phy_avr.c | 36 ++++++++++++++++++---- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/avclan/avclan.h b/src/avclan/avclan.h index 303b16a..a64fe15 100644 --- a/src/avclan/avclan.h +++ b/src/avclan/avclan.h @@ -143,7 +143,8 @@ struct Error { BAD_CONTROLLER_PARITY, BAD_CONTROL_PARITY, BAD_PARITY, // generic bad parity has max severity - STARTBIT_TOO_SHORT, + STARTBIT_MISSED, + STARTBIT_MALFORMED, STARTBIT_TOO_LONG, BAD_STARTBIT, POOL_EMPTY, // non-bus error diff --git a/src/avclan/bus.cc b/src/avclan/bus.cc index 47b4a1d..aaa11c5 100644 --- a/src/avclan/bus.cc +++ b/src/avclan/bus.cc @@ -289,7 +289,10 @@ auto Bus::read(uint16_t address, Frame::Print print) switch (err.errno) { case POOL_EMPTY: puts("failed Frame alloc"); break; case BAD_STARTBIT: fputs("bad start bit (other)", stdout); break; - case STARTBIT_TOO_SHORT: fputs("bad start bit (short)", stdout); break; + case STARTBIT_MISSED: fputs("missed start bit", stdout); break; + case STARTBIT_MALFORMED: + fputs("malformed start bit (external cause)", stdout); + break; case STARTBIT_TOO_LONG: fputs("bad start bit (long)", stdout); break; case BAD_CONTROLLER_PARITY: fputs("reading controller addr.", stdout); @@ -312,7 +315,7 @@ auto Bus::read(uint16_t address, Frame::Print print) } // Only print if some data has been correctly received - if (print.print && (err.errno < STARTBIT_TOO_SHORT)) { + if (print.print && (err.errno < STARTBIT_MISSED)) { if (err.errno > BAD_DATA_PARITY) in->length = 0; in->print(print); diff --git a/src/avclan/target/avr-attiny3216/phy_avr.c b/src/avclan/target/avr-attiny3216/phy_avr.c index 9e79abc..cef4e8d 100644 --- a/src/avclan/target/avr-attiny3216/phy_avr.c +++ b/src/avclan/target/avr-attiny3216/phy_avr.c @@ -344,6 +344,14 @@ void phy_init() { // error reporting; no printing happens here. Read phy_read_startbit() { uint16_t startbitlen = TCB1.CNT = 0; + + // Reset the ~atomic `pulsewidth` variable to detect the post-pulse update + // from the TCB0_INT_vect ISR + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (!BUS_IS_IDLE) // Only reset if bus is actively driven (i.e. current + pulsewidth = 0; // value is stale/already been used) + } + while (!BUS_IS_IDLE) { startbitlen = TCB1.CNT; if (startbitlen > (uint16_t)AVCLAN_STARTBIT_LOGIC_0 * 1.2) { @@ -367,18 +375,34 @@ Read phy_read_startbit() { return result; } } + + // `pulsewidth` updates once the TCB0_INT_vect ISR runs for this pulse. + TCB1.CNT = 0; + do { + if (TCB1.CNT > (uint16_t)AVCLAN_BIT0_LOGIC_1) // Wait a max of ~6μs for ISR + return BAD_STARTBIT; // ISR/other implementation bug; abort + + // Read ~atomically, to prevent torn reads + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { startbitlen = pulsewidth; } + } while (startbitlen == 0); + if (startbitlen < (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 0.8)) { - // We missed the beginning of this message; wait for it to finish (bus - // continuously idle for >1 bit length) before returning, so we don't have - // multiple false-starts while the in-progress message keeps sending more - // bits. + // Not a start bit; wait for the message to finish (bus continuously idle + // for >1 bit length) before returning, so we only report one error (instead + // of e.g. repeated "bad (short) start bit" errors) TCB1.CNT = 0; while (TCB1.CNT < (uint16_t)(AVCLAN_BIT_LENGTH_MAX * 1.2)) { if (!BUS_IS_IDLE) - TCB1.CNT = 0; + TCB1.CNT = 0; // Reset counter after each bit pulse } - return STARTBIT_TOO_SHORT; + // A pulse no wider than a normal bit means we merely tuned in mid-frame and + // this was a data bit; a wider-but-still-sub-start pulse means some other + // device emitted a wonky pulse. + return (startbitlen < (uint16_t)AVCLAN_BIT_LENGTH_MAX) ? STARTBIT_MISSED + : STARTBIT_MALFORMED; } + if (startbitlen > (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 1.2)) + return STARTBIT_TOO_LONG; return (Read)0; // that was a start bit }