mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-18 19:13:58 +00:00
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:
@ -276,10 +276,8 @@ void IR_DecoderRaw::isr()
|
|||||||
// Интервалы между соседними фронтами считаются как (uint32_t)(t1 - t0) — корректно при
|
// Интервалы между соседними фронтами считаются как (uint32_t)(t1 - t0) — корректно при
|
||||||
// паузе < ~35 мин между фронтами; условие «тишина > longSilence» в preambleProcessEdge
|
// паузе < ~35 мин между фронтами; условие «тишина > longSilence» в preambleProcessEdge
|
||||||
// переписано без front.time > prevRise (оно ломается при wrap).
|
// переписано без front.time > prevRise (оно ломается при wrap).
|
||||||
uint32_t t;
|
// B6: micros() в ISR не требует маскировки; прежний interrupts() посреди ISR снимал PRIMASK.
|
||||||
noInterrupts();
|
const uint32_t t = micros();
|
||||||
t = micros();
|
|
||||||
interrupts();
|
|
||||||
|
|
||||||
FrontStorage edge;
|
FrontStorage edge;
|
||||||
edge.dir = port->IDR & mask;
|
edge.dir = port->IDR & mask;
|
||||||
@ -403,16 +401,8 @@ void IR_DecoderRaw::tick()
|
|||||||
// с метками ISR из очереди → ложные TIMEOUT (bits=0) каждый пакет.
|
// с метками ISR из очереди → ложные TIMEOUT (bits=0) каждый пакет.
|
||||||
|
|
||||||
FrontStorage rawFront;
|
FrontStorage rawFront;
|
||||||
bool hasRawFront = false;
|
|
||||||
bool processedFront = false;
|
bool processedFront = false;
|
||||||
noInterrupts();
|
const bool hasRawFront = subBuffer.pop(rawFront); // B5: копия под одной критсекцией (без торн-рида)
|
||||||
FrontStorage *rawPtr = subBuffer.pop();
|
|
||||||
if (rawPtr != nullptr)
|
|
||||||
{
|
|
||||||
rawFront = *rawPtr;
|
|
||||||
hasRawFront = true;
|
|
||||||
}
|
|
||||||
interrupts();
|
|
||||||
|
|
||||||
if (IR_INPUT_MIN_PULSE_US > 0U)
|
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, poly1) << 8) & ~((crc_t)0xFF);
|
||||||
crc |= crc8(dataBuffer, 0, len + 1, poly2) & (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))
|
dataBuffer[len + 1] == (crc & 0xFF))
|
||||||
{
|
{
|
||||||
crcOK = true;
|
crcOK = true;
|
||||||
@ -984,6 +974,7 @@ bool IR_DecoderRaw::availableReject()
|
|||||||
|
|
||||||
uint16_t IR_DecoderRaw::ceil_div(uint16_t val, uint16_t divider)
|
uint16_t IR_DecoderRaw::ceil_div(uint16_t val, uint16_t divider)
|
||||||
{
|
{
|
||||||
|
if (divider == 0) return 0; // B10: защита от деления на 0 (актуально при freeFrec — сейчас выкл, не включаем)
|
||||||
int ret = val / divider;
|
int ret = val / divider;
|
||||||
if ((val << 4) / divider - (ret << 4) >= 8)
|
if ((val << 4) / divider - (ret << 4) >= 8)
|
||||||
ret++;
|
ret++;
|
||||||
|
|||||||
@ -688,7 +688,10 @@ IR_SendResult IR_Encoder::sendData(uint16_t addrTo, uint8_t *data, uint8_t len,
|
|||||||
|
|
||||||
IR_SendResult IR_Encoder::sendDataFULL(uint16_t addrFrom, uint16_t addrTo, uint8_t *data, uint8_t len, bool needAccept)
|
IR_SendResult IR_Encoder::sendDataFULL(uint16_t addrFrom, uint16_t addrTo, uint8_t *data, uint8_t len, bool needAccept)
|
||||||
{
|
{
|
||||||
if (len > bytePerPack)
|
// 5-битное поле длины => ВЕСЬ кадр ≤31 байт (для Data payload ≤24). Было `len > bytePerPack(31)` —
|
||||||
|
// неверно: packSize=7+len оборачивался в заголовке (packSize & 0x1F) при len 25..31 → кадр молча
|
||||||
|
// терялся, а send возвращал успех. Проверяем полный packSize в широком типе (uint8_t 7+len мог переполниться).
|
||||||
|
if (((uint16_t)msgBytes + addrBytes + addrBytes + len + crcBytes) > IR_MASK_MSG_INFO)
|
||||||
{
|
{
|
||||||
Serial.println("IR Pack to big");
|
Serial.println("IR Pack to big");
|
||||||
return IR_SendResult(false, 0, IR_SendStatus::PayloadTooLarge);
|
return IR_SendResult(false, 0, IR_SendStatus::PayloadTooLarge);
|
||||||
|
|||||||
15
RingBuffer.h
15
RingBuffer.h
@ -36,6 +36,21 @@ public:
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// B5: безопасный pop — копирует элемент под ОДНОЙ критсекцией и отдаёт по значению.
|
||||||
|
// (T* pop() отдаёт указатель во внутренний слот; его внутренний interrupts() снимает внешнюю
|
||||||
|
// защиту вызывающего ДО чтения *ptr → торн-рид, если кольцо переполнится в этом окне.)
|
||||||
|
bool pop(T &out) {
|
||||||
|
bool popped = false;
|
||||||
|
noInterrupts();
|
||||||
|
if (!isEmpty()) {
|
||||||
|
out = data[start];
|
||||||
|
start = (start + 1) % BufferSize;
|
||||||
|
popped = true;
|
||||||
|
}
|
||||||
|
interrupts();
|
||||||
|
return popped;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T data[BufferSize];
|
T data[BufferSize];
|
||||||
unsigned int start, end;
|
unsigned int start, end;
|
||||||
|
|||||||
Reference in New Issue
Block a user