fix(rx/tx): B5 pop-by-value, B6 ISR micros mask, B7 crc parens, B10 div guard, send length validation

B5 (MEDIUM): RingBuffer::pop(T&) копирует под одной критсекцией; tick() перешёл на неё → нет торн-рида (внутренний interrupts() в T* pop снимал внешнюю защиту до чтения *ptr). T* pop() оставлен (не используется).
B6 (MEDIUM): убрана лишняя noInterrupts/interrupts вокруг micros() в EXTI-ISR (снимала PRIMASK посреди ISR). std::function-диспетчеризация attachInterrupt — структурна, не трогаю.
B7 (LOW): скобки в crcCheck (== & 0xFF по приоритету).
B10 (LOW): guard деления на 0 в ceil_div (актуально только при freeFrec — НЕ включаю).
send: sendDataFULL — валидация полного packSize<=31 (было len>bytePerPack=31, packSize=7+len оборачивался → тихая потеря Data payload 25..31).
B8 (isSubBufferOverflow) — уже volatile, потеря флага безвредна (диагностика), не трогаю. B9 (prevRise==0 при micros()==0) — уже обработан веткой в preambleProcessEdge, намеренно.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 13:38:51 +03:00
parent a8d32bbcef
commit bf14b3d7ad
3 changed files with 24 additions and 15 deletions

View File

@ -276,10 +276,8 @@ void IR_DecoderRaw::isr()
// Интервалы между соседними фронтами считаются как (uint32_t)(t1 - t0) — корректно при
// паузе < ~35 мин между фронтами; условие «тишина > longSilence» в preambleProcessEdge
// переписано без front.time > prevRise (оно ломается при wrap).
uint32_t t;
noInterrupts();
t = micros();
interrupts();
// B6: micros() в ISR не требует маскировки; прежний interrupts() посреди ISR снимал PRIMASK.
const uint32_t t = micros();
FrontStorage edge;
edge.dir = port->IDR & mask;
@ -403,16 +401,8 @@ void IR_DecoderRaw::tick()
// с метками ISR из очереди → ложные TIMEOUT (bits=0) каждый пакет.
FrontStorage rawFront;
bool hasRawFront = false;
bool processedFront = false;
noInterrupts();
FrontStorage *rawPtr = subBuffer.pop();
if (rawPtr != nullptr)
{
rawFront = *rawPtr;
hasRawFront = true;
}
interrupts();
const bool hasRawFront = subBuffer.pop(rawFront); // B5: копия под одной критсекцией (без торн-рида)
if (IR_INPUT_MIN_PULSE_US > 0U)
{
@ -961,7 +951,7 @@ bool IR_DecoderRaw::crcCheck(uint8_t len, crc_t &crc)
crc = (crc8(dataBuffer, 0, len, poly1) << 8) & ~((crc_t)0xFF);
crc |= crc8(dataBuffer, 0, len + 1, poly2) & (crc_t)0xFF;
if (dataBuffer[len] == (crc >> 8) & 0xFF &&
if (dataBuffer[len] == ((crc >> 8) & 0xFF) && // B7: скобки — иначе (== & 0xFF) по приоритету
dataBuffer[len + 1] == (crc & 0xFF))
{
crcOK = true;
@ -984,6 +974,7 @@ bool IR_DecoderRaw::availableReject()
uint16_t IR_DecoderRaw::ceil_div(uint16_t val, uint16_t divider)
{
if (divider == 0) return 0; // B10: защита от деления на 0 (актуально при freeFrec — сейчас выкл, не включаем)
int ret = val / divider;
if ((val << 4) / divider - (ret << 4) >= 8)
ret++;