archive: freeze IR-protocol WIP before stepwise integration

This commit is contained in:
2026-08-28 13:27:46 +03:00
parent 96ffb91b97
commit 57db9c35b8
18 changed files with 1848 additions and 391 deletions

View File

@ -1,5 +1,6 @@
#include "IR_DecoderRaw.h"
#include "IR_Encoder.h"
#include "IrInterruptGuard.h"
#include <cstdio>
#include <cstring>
@ -53,9 +54,8 @@ IR_DecoderRaw::IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPa
bool IR_DecoderRaw::isSubOverflow()
{
noInterrupts();
volatile bool ret = isSubBufferOverflow;
interrupts();
IrInterruptGuard guard;
const bool ret = isSubBufferOverflow;
return ret;
}
@ -103,7 +103,7 @@ void IR_DecoderRaw::refreshPairMuteState()
++active;
}
const uint32_t nowUs = micros();
noInterrupts();
IrInterruptGuard guard;
const bool wasActive = (isPairSending != 0);
isPairSending = active;
#if IR_RX_BRIEF_LOG
@ -121,7 +121,6 @@ void IR_DecoderRaw::refreshPairMuteState()
rxBriefMuteBlockedEdges = 0;
}
#endif
interrupts();
}
#if IR_RX_BRIEF_LOG
@ -142,6 +141,7 @@ const __FlashStringHelper *IR_DecoderRaw::rxBriefReasonTag(RxBriefReason reason)
case RxBriefReason::Timeout: return F("TIMEOUT");
case RxBriefReason::Crc: return F("CRC");
case RxBriefReason::Ok: return F("OK");
case RxBriefReason::Count: return F("UNK");
default: return F("UNK");
}
}
@ -152,7 +152,7 @@ const __FlashStringHelper *IR_DecoderRaw::rxBriefReasonTag(RxBriefReason reason)
void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uint32_t tUs)
{
const uint8_t ri = (uint8_t)reason;
if (ri < 14U)
if (ri < kRxBriefReasonCount)
rxReasonCnt[ri]++;
#if !IR_RX_BRIEF_LOG
(void)a; (void)b; (void)tUs;
@ -228,6 +228,8 @@ void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uin
Serial.print(b);
}
break;
case RxBriefReason::Count:
break;
}
Serial.println();
#endif // IR_RX_BRIEF_LOG (печать)
@ -235,11 +237,14 @@ void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uin
void IR_DecoderRaw::printRxReasonStats(Print &out) const
{
static const char *const kTags[14] = {"?", "MUTEB", "MUTEE", "QRAW", "QFLT", "HOLD",
"GLITCH", "TIME", "PREAMB", "SYNC", "BUF",
"TIMEOUT", "CRC", "OK"};
static const char *const kTags[] = {"?", "MUTEB", "MUTEE", "QRAW", "QFLT", "HOLD",
"GLITCH", "TIME", "PREAMB", "SYNC", "BUF",
"TIMEOUT", "CRC", "OK"};
static_assert(sizeof(kTags) / sizeof(kTags[0]) == kRxBriefReasonCount,
"RX reason tag table must match RxBriefReason::Count");
out.print(F("RXSTAT"));
for (uint8_t i = 1; i < 14U; i++)
for (uint8_t i = static_cast<uint8_t>(RxBriefReason::MuteBegin);
i < kRxBriefReasonCount; ++i)
{
out.print(',');
out.print(kTags[i]);
@ -273,22 +278,23 @@ void IR_DecoderRaw::rxBriefFlushDeferredIsrLogs()
uint16_t muteEndCnt = 0;
uint16_t rawCnt = 0;
uint32_t rawLastUs = 0;
noInterrupts();
muteBeginPending = rxBriefMuteBeginPending;
muteBeginUs = rxBriefMuteBeginUs;
rxBriefMuteBeginPending = false;
rxBriefMuteBeginUs = 0;
muteEndPending = rxBriefMuteEndPending;
muteEndUs = rxBriefMuteEndUs;
muteEndCnt = rxBriefMuteEndCount;
rxBriefMuteEndPending = false;
rxBriefMuteEndUs = 0;
rxBriefMuteEndCount = 0;
rawCnt = rxBriefRawOverflowDrops;
rawLastUs = rxBriefRawOverflowLastUs;
rxBriefRawOverflowDrops = 0;
rxBriefRawOverflowLastUs = 0;
interrupts();
{
IrInterruptGuard guard;
muteBeginPending = rxBriefMuteBeginPending;
muteBeginUs = rxBriefMuteBeginUs;
rxBriefMuteBeginPending = false;
rxBriefMuteBeginUs = 0;
muteEndPending = rxBriefMuteEndPending;
muteEndUs = rxBriefMuteEndUs;
muteEndCnt = rxBriefMuteEndCount;
rxBriefMuteEndPending = false;
rxBriefMuteEndUs = 0;
rxBriefMuteEndCount = 0;
rawCnt = rxBriefRawOverflowDrops;
rawLastUs = rxBriefRawOverflowLastUs;
rxBriefRawOverflowDrops = 0;
rxBriefRawOverflowLastUs = 0;
}
if (muteBeginPending)
rxBriefLog(RxBriefReason::MuteBegin, 0, 0, muteBeginUs);
if (muteEndPending)
@ -359,7 +365,7 @@ void IR_DecoderRaw::firstRX()
#ifdef IRDEBUG
wrCounter = 0;
#endif
memset(dataBuffer, 0x00, dataByteSizeMax);
memset(dataBuffer, 0x00, irproto::kMaxWireFrameBytes);
pulseFilterReset();
preambleResetToIdle();
}
@ -368,9 +374,8 @@ bool IR_DecoderRaw::rxTimeoutPipelineBusy() const
{
if (pulseFilterHoldCount != 0U)
return true;
noInterrupts();
IrInterruptGuard guard;
const bool busy = !subBuffer.isEmpty();
interrupts();
return busy;
}
@ -378,7 +383,7 @@ void IR_DecoderRaw::listenStart()
{
if (rxTimeoutPipelineBusy())
return;
if (isReciveRaw && ((micros() - lastEdgeTime) > IR_timeout * 2U))
if (isReciveRaw && ((micros() - lastEdgeTime) > receiveSilenceTimeoutUs()))
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceOnTimeoutOrAbort(true);
@ -396,7 +401,7 @@ inline void IR_DecoderRaw::checkTimeout()
if (rxTimeoutPipelineBusy())
return;
if (micros() - lastEdgeTime > IR_timeout * 2U)
if (micros() - lastEdgeTime > receiveSilenceTimeoutUs())
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceOnTimeoutOrAbort(false);
@ -767,8 +772,8 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
#if !defined(IRDEBUG_SERIAL_PACK)
(void)packTraceInvertFix;
#endif
if (i_dataBuffer >= dataByteSizeMax * 8)
{ // проверка переполнения (>=: иначе при i_dataBuffer==dataByteSizeMax*8 запись dataBuffer[38] за границей массива — B3)
if (i_dataBuffer >= irproto::kMaxWireFrameBytes * 8U)
{ // >=: не даёт записать бит за пределом 5-битной wire-длины.
isBufferOverflow = true;
rxBriefLog(RxBriefReason::BufferOverflow, i_dataBuffer, 0, micros());
#if defined(IRDEBUG_SERIAL_PACK)
@ -876,7 +881,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
{ // Ппервый байт
packSize = dataBuffer[0] & IR_MASK_MSG_INFO;
// B1: под-минимальная длина (1..2) физически не несёт CRC (min кадр = msg+crc = 3 байта) → шум/битьё.
// Без отсева packSize==1 даёт crcCheck(1-2) → len=255 → OOB-чтение dataBuffer[0..256] (массив 38).
// Без отсева packSize==1 даёт crcCheck(1-2) → len=255 → OOB-чтение wire-буфера.
// packSize>=3 (в т.ч. будущие компактные кадры) обрабатываются как обычно.
if (packSize != 0 && packSize < msgBytes + crcBytes)
isWrongPack = true;
@ -910,7 +915,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
uint8_t packTraceBfBit = 0;
bool packTraceBfMark = false;
if (!isAvailable) // Исправление первого бита // Очень большая затычка...
for (size_t i = 0; i < min(uint16_t(packSize - crcBytes * 2U), uint16_t(dataByteSizeMax)); ++i)
for (size_t i = 0; i < min(uint16_t(packSize - crcBytes * 2U), uint16_t(irproto::kMaxWireFrameBytes)); ++i)
{
for (int j = 0; j < 8; ++j)
{
@ -918,7 +923,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
dataBuffer[i] ^= 1 << j;
isAvailable =
crcCheck(min(uint16_t(packSize - crcBytes), uint16_t(dataByteSizeMax - 1U)), crcValue);
crcCheck(min(uint16_t(packSize - crcBytes), uint16_t(irproto::kMaxWireFrameBytes - 1U)), crcValue);
// обратно инвертируем бит в исходное состояние
if (isAvailable)
@ -953,7 +958,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
rxBriefLog(RxBriefReason::Ok, packSize, errSum, micros());
else
rxBriefLog(RxBriefReason::Crc, packSize, errSum, micros());
if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) {
if (!isAvailable && packSize > 0 && packSize <= irproto::kMaxWireFrameBytes) {
memcpy(rejectBuffer, dataBuffer, packSize);
rejectPackSize = static_cast<uint8_t>(packSize);
isRejectAvailable = true;
@ -1007,38 +1012,34 @@ uint16_t IR_DecoderRaw::ceil_div(uint16_t val, uint16_t divider)
void IR_DecoderRaw::edgeTracePush(uint32_t t_us, uint8_t level, uint8_t flags)
{
const uint16_t cap = static_cast<uint16_t>(IR_EDGE_TRACE_CAPACITY);
noInterrupts();
IrInterruptGuard guard;
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
const uint16_t next = static_cast<uint16_t>((w + 1u) % cap);
if (next == r)
{
edgeTrace_overflow = true;
interrupts();
return;
}
edgeTrace_buf[w].t_us = t_us;
edgeTrace_buf[w].level = level ? 1u : 0u;
edgeTrace_buf[w].flags = flags;
edgeTrace_w = next;
interrupts();
}
void IR_DecoderRaw::edgeTraceClear()
{
noInterrupts();
IrInterruptGuard guard;
edgeTrace_w = 0;
edgeTrace_r = 0;
edgeTrace_overflow = false;
interrupts();
}
uint16_t IR_DecoderRaw::edgeTracePendingCount() const
{
noInterrupts();
IrInterruptGuard guard;
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
interrupts();
const uint16_t cap = static_cast<uint16_t>(IR_EDGE_TRACE_CAPACITY);
if (w >= r)
return static_cast<uint16_t>(w - r);
@ -1054,27 +1055,28 @@ uint16_t IR_DecoderRaw::edgeTraceFlushChunk(Print &out, uint16_t maxRec)
maxRec = kStackCap;
const uint16_t cap = static_cast<uint16_t>(IR_EDGE_TRACE_CAPACITY);
noInterrupts();
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
uint16_t avail = (w >= r) ? static_cast<uint16_t>(w - r) : static_cast<uint16_t>(cap - r + w);
uint16_t toCopy = (avail > maxRec) ? maxRec : avail;
const bool truncated = (avail > toCopy);
if (toCopy == 0)
{
interrupts();
return 0;
}
uint8_t tmp[kStackCap * 6];
for (uint16_t i = 0; i < toCopy; ++i)
uint16_t toCopy = 0U;
bool truncated = false;
bool ovf = false;
{
const uint16_t idx = static_cast<uint16_t>((r + i) % cap);
memcpy(tmp + i * 6u, &edgeTrace_buf[idx], 6u);
IrInterruptGuard guard;
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
const uint16_t avail = (w >= r) ? static_cast<uint16_t>(w - r)
: static_cast<uint16_t>(cap - r + w);
toCopy = (avail > maxRec) ? maxRec : avail;
truncated = (avail > toCopy);
if (toCopy == 0U)
return 0U;
for (uint16_t i = 0; i < toCopy; ++i)
{
const uint16_t idx = static_cast<uint16_t>((r + i) % cap);
memcpy(tmp + i * 6u, &edgeTrace_buf[idx], 6u);
}
edgeTrace_r = static_cast<uint16_t>((r + toCopy) % cap);
ovf = edgeTrace_overflow;
}
edgeTrace_r = static_cast<uint16_t>((r + toCopy) % cap);
const bool ovf = edgeTrace_overflow;
interrupts();
uint8_t meta = 0;
if (ovf)
@ -1304,7 +1306,7 @@ void IR_DecoderRaw::packTraceForceEndSyncPhase()
void IR_DecoderRaw::packTraceEmitHex(uint8_t byteCount) const
{
Serial.print(F("IR hex:"));
for (uint8_t i = 0; i < byteCount && i < dataByteSizeMax; i++)
for (uint8_t i = 0; i < byteCount && i < irproto::kMaxWireFrameBytes; i++)
{
Serial.print(' ');
ptPrintHexU8(dataBuffer[i]);
@ -1402,8 +1404,8 @@ void IR_DecoderRaw::packTraceEmitErrorFlash(const __FlashStringHelper *msg)
Serial.println(msg);
{
uint16_t nb = i_dataBuffer / 8u;
if (nb > dataByteSizeMax)
nb = dataByteSizeMax;
if (nb > irproto::kMaxWireFrameBytes)
nb = irproto::kMaxWireFrameBytes;
packTraceEmitHex(static_cast<uint8_t>(nb));
}
packTraceResetFrame();
@ -1436,8 +1438,8 @@ void IR_DecoderRaw::packTraceOnTimeoutOrAbort(bool fromListenStart)
return;
const uint16_t expected = (i_dataBuffer >= 8) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0;
uint16_t gotBytes = i_dataBuffer / 8;
if (gotBytes > dataByteSizeMax)
gotBytes = dataByteSizeMax;
if (gotBytes > irproto::kMaxWireFrameBytes)
gotBytes = irproto::kMaxWireFrameBytes;
Serial.println();
packTraceEmitRawBitsLine(false);
Serial.print(F(" => ERROR: TIMEOUT, rx_data_size = "));
@ -1609,7 +1611,7 @@ void IR_DecoderRaw::preambleStartCandidate(const FrontStorage &front)
bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
{
const uint32_t longSilence = IR_timeout * 2U;
const uint32_t longSilence = receiveSilenceTimeoutUs();
const uint32_t candTimeout = IR_timeout * (uint32_t)IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT;
if (preambleState == PreambleState::Idle)
@ -1696,7 +1698,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
err_syncBit = 0;
isWrongPack = false;
msgTypeReceive = 0;
memset(dataBuffer, 0x00, dataByteSizeMax);
memset(dataBuffer, 0x00, irproto::kMaxWireFrameBytes);
preambleState = PreambleState::Locked;
isPreamb = false;