mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
fix overflow and mute
This commit is contained in:
@ -129,7 +129,7 @@ void IrFoxDecoder::write_to_buffer(bool bit, bool pack_trace_invert_fix, uint64_
|
|||||||
|
|
||||||
if (buf_bit_pos == next_control_bit)
|
if (buf_bit_pos == next_control_bit)
|
||||||
{
|
{
|
||||||
next_control_bit = static_cast<uint8_t>(next_control_bit + (is_data ? irfox::kSyncBits : irfox::kBitPerByte));
|
next_control_bit = next_control_bit + (is_data ? irfox::kSyncBits : irfox::kBitPerByte);
|
||||||
is_data = !is_data;
|
is_data = !is_data;
|
||||||
i_sync_bit = 0;
|
i_sync_bit = 0;
|
||||||
err_sync_bit = 0;
|
err_sync_bit = 0;
|
||||||
|
|||||||
@ -127,7 +127,7 @@ private:
|
|||||||
int16_t buf_bit_pos = 0;
|
int16_t buf_bit_pos = 0;
|
||||||
bool is_data = true;
|
bool is_data = true;
|
||||||
uint16_t i_data_buffer = 0;
|
uint16_t i_data_buffer = 0;
|
||||||
uint8_t next_control_bit = irfox::kBitPerByte;
|
uint16_t next_control_bit = irfox::kBitPerByte;
|
||||||
uint8_t i_sync_bit = 0;
|
uint8_t i_sync_bit = 0;
|
||||||
uint8_t err_sync_bit = 0;
|
uint8_t err_sync_bit = 0;
|
||||||
uint16_t error_counter = 0;
|
uint16_t error_counter = 0;
|
||||||
|
|||||||
@ -26,6 +26,11 @@ static inline uint8_t irPulseFilterHoldbackEdges()
|
|||||||
return hb;
|
return hb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint16_t irClampU16(uint32_t v)
|
||||||
|
{
|
||||||
|
return (v > 0xFFFFU) ? 0xFFFFU : (uint16_t)v;
|
||||||
|
}
|
||||||
|
|
||||||
IR_DecoderRaw::IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair) : encoder(encPair)
|
IR_DecoderRaw::IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair) : encoder(encPair)
|
||||||
{
|
{
|
||||||
setPin(pin);
|
setPin(pin);
|
||||||
@ -74,6 +79,198 @@ void IR_DecoderRaw::pulseFilterResetStats()
|
|||||||
pulseFilterDropGlitchPairs = 0;
|
pulseFilterDropGlitchPairs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IR_DecoderRaw::registerPairMuteEncoder(IR_Encoder *enc)
|
||||||
|
{
|
||||||
|
if (enc == nullptr)
|
||||||
|
return false;
|
||||||
|
for (uint8_t i = 0; i < pairMuteEncoderCount; ++i)
|
||||||
|
{
|
||||||
|
if (pairMuteEncoders[i] == enc)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pairMuteEncoderCount >= IR_PAIR_MUTE_MAX_ENCODERS)
|
||||||
|
return false;
|
||||||
|
pairMuteEncoders[pairMuteEncoderCount++] = enc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_DecoderRaw::refreshPairMuteState()
|
||||||
|
{
|
||||||
|
uint16_t active = 0;
|
||||||
|
for (uint8_t i = 0; i < pairMuteEncoderCount; ++i)
|
||||||
|
{
|
||||||
|
IR_Encoder *enc = pairMuteEncoders[i];
|
||||||
|
if (enc != nullptr && enc->isBusy())
|
||||||
|
++active;
|
||||||
|
}
|
||||||
|
const uint32_t nowUs = micros();
|
||||||
|
noInterrupts();
|
||||||
|
const bool wasActive = (isPairSending != 0);
|
||||||
|
isPairSending = active;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
if (!wasActive && active != 0)
|
||||||
|
{
|
||||||
|
rxBriefMuteBlockedEdges = 0;
|
||||||
|
rxBriefMuteBeginPending = true;
|
||||||
|
rxBriefMuteBeginUs = nowUs;
|
||||||
|
}
|
||||||
|
else if (wasActive && active == 0)
|
||||||
|
{
|
||||||
|
rxBriefMuteEndPending = true;
|
||||||
|
rxBriefMuteEndUs = nowUs;
|
||||||
|
rxBriefMuteEndCount = rxBriefMuteBlockedEdges;
|
||||||
|
rxBriefMuteBlockedEdges = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
const __FlashStringHelper *IR_DecoderRaw::rxBriefReasonTag(RxBriefReason reason)
|
||||||
|
{
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case RxBriefReason::MuteBegin: return F("MUTE_BEGIN");
|
||||||
|
case RxBriefReason::MuteEnd: return F("MUTE_END");
|
||||||
|
case RxBriefReason::RawOverflow: return F("QRAW");
|
||||||
|
case RxBriefReason::FilterOverflow: return F("QFLT");
|
||||||
|
case RxBriefReason::HoldOverflow: return F("HOLD");
|
||||||
|
case RxBriefReason::Glitch: return F("GLITCH");
|
||||||
|
case RxBriefReason::Timing: return F("TIME");
|
||||||
|
case RxBriefReason::Preamble: return F("PREAMB");
|
||||||
|
case RxBriefReason::Sync: return F("SYNC");
|
||||||
|
case RxBriefReason::BufferOverflow: return F("BUF");
|
||||||
|
case RxBriefReason::Timeout: return F("TIMEOUT");
|
||||||
|
case RxBriefReason::Crc: return F("CRC");
|
||||||
|
case RxBriefReason::Ok: return F("OK");
|
||||||
|
default: return F("UNK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uint32_t tUs)
|
||||||
|
{
|
||||||
|
#if IR_RX_BRIEF_LOG_REJECT_ONLY
|
||||||
|
if (reason == RxBriefReason::Ok || reason == RxBriefReason::Preamble)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
if (tUs == 0U)
|
||||||
|
tUs = micros();
|
||||||
|
Serial.print(F("IRRX t="));
|
||||||
|
Serial.print((unsigned long)tUs);
|
||||||
|
Serial.print(F(" rsn="));
|
||||||
|
Serial.print(rxBriefReasonTag(reason));
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case RxBriefReason::MuteBegin:
|
||||||
|
break;
|
||||||
|
case RxBriefReason::MuteEnd:
|
||||||
|
case RxBriefReason::RawOverflow:
|
||||||
|
Serial.print(F(" cnt="));
|
||||||
|
Serial.print(a);
|
||||||
|
break;
|
||||||
|
case RxBriefReason::FilterOverflow:
|
||||||
|
case RxBriefReason::HoldOverflow:
|
||||||
|
case RxBriefReason::Glitch:
|
||||||
|
Serial.print(F(" total="));
|
||||||
|
Serial.print(a);
|
||||||
|
break;
|
||||||
|
case RxBriefReason::Timing:
|
||||||
|
Serial.print(F(" rp="));
|
||||||
|
Serial.print(a);
|
||||||
|
if (b)
|
||||||
|
{
|
||||||
|
Serial.print(F(" hp="));
|
||||||
|
Serial.print(b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RxBriefReason::Preamble:
|
||||||
|
Serial.print(F(" good="));
|
||||||
|
Serial.print(a);
|
||||||
|
if (b)
|
||||||
|
{
|
||||||
|
Serial.print(F(" per="));
|
||||||
|
Serial.print(b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RxBriefReason::Sync:
|
||||||
|
Serial.print(F(" err="));
|
||||||
|
Serial.print(a);
|
||||||
|
break;
|
||||||
|
case RxBriefReason::BufferOverflow:
|
||||||
|
Serial.print(F(" bits="));
|
||||||
|
Serial.print(a);
|
||||||
|
break;
|
||||||
|
case RxBriefReason::Timeout:
|
||||||
|
Serial.print(F(" bits="));
|
||||||
|
Serial.print(a);
|
||||||
|
if (b)
|
||||||
|
{
|
||||||
|
Serial.print(F(" exp="));
|
||||||
|
Serial.print(b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RxBriefReason::Crc:
|
||||||
|
case RxBriefReason::Ok:
|
||||||
|
Serial.print(F(" len="));
|
||||||
|
Serial.print(a);
|
||||||
|
if (b)
|
||||||
|
{
|
||||||
|
Serial.print(F(" err="));
|
||||||
|
Serial.print(b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_DecoderRaw::rxBriefNoteMuteBlockedIsr(uint32_t tUs)
|
||||||
|
{
|
||||||
|
(void)tUs;
|
||||||
|
if (rxBriefMuteBlockedEdges != UINT16_MAX)
|
||||||
|
++rxBriefMuteBlockedEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_DecoderRaw::rxBriefNoteRawOverflowIsr(uint32_t tUs)
|
||||||
|
{
|
||||||
|
if (rxBriefRawOverflowDrops != UINT16_MAX)
|
||||||
|
++rxBriefRawOverflowDrops;
|
||||||
|
rxBriefRawOverflowLastUs = tUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_DecoderRaw::rxBriefFlushDeferredIsrLogs()
|
||||||
|
{
|
||||||
|
bool muteBeginPending = false;
|
||||||
|
uint32_t muteBeginUs = 0;
|
||||||
|
bool muteEndPending = false;
|
||||||
|
uint32_t muteEndUs = 0;
|
||||||
|
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();
|
||||||
|
if (muteBeginPending)
|
||||||
|
rxBriefLog(RxBriefReason::MuteBegin, 0, 0, muteBeginUs);
|
||||||
|
if (muteEndPending)
|
||||||
|
rxBriefLog(RxBriefReason::MuteEnd, muteEndCnt, 0, muteEndUs);
|
||||||
|
if (rawCnt)
|
||||||
|
rxBriefLog(RxBriefReason::RawOverflow, rawCnt, 0, rawLastUs);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//////////////////////////////////// isr ///////////////////////////////////////////
|
//////////////////////////////////// isr ///////////////////////////////////////////
|
||||||
volatile uint32_t time_;
|
volatile uint32_t time_;
|
||||||
|
|
||||||
@ -99,10 +296,19 @@ void IR_DecoderRaw::isr()
|
|||||||
|
|
||||||
if (isPairSending)
|
if (isPairSending)
|
||||||
{
|
{
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefNoteMuteBlockedIsr(edge.time);
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
subBuffer.push(edge);
|
if (!subBuffer.push(edge))
|
||||||
|
{
|
||||||
|
isSubBufferOverflow = true;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefNoteRawOverflowIsr(edge.time);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -123,6 +329,7 @@ void IR_DecoderRaw::firstRX()
|
|||||||
i_dataBuffer = 0;
|
i_dataBuffer = 0;
|
||||||
nextControlBit = bitPerByte;
|
nextControlBit = bitPerByte;
|
||||||
i_syncBit = 0;
|
i_syncBit = 0;
|
||||||
|
err_syncBit = 0;
|
||||||
isWrongPack = false;
|
isWrongPack = false;
|
||||||
|
|
||||||
isPreamb = true;
|
isPreamb = true;
|
||||||
@ -157,6 +364,10 @@ inline void IR_DecoderRaw::checkTimeout()
|
|||||||
{
|
{
|
||||||
#if defined(IRDEBUG_SERIAL_PACK)
|
#if defined(IRDEBUG_SERIAL_PACK)
|
||||||
packTraceOnTimeoutOrAbort(false);
|
packTraceOnTimeoutOrAbort(false);
|
||||||
|
#endif
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
const uint16_t expected = (i_dataBuffer >= 8U) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0U;
|
||||||
|
rxBriefLog(RxBriefReason::Timeout, i_dataBuffer, expected, micros());
|
||||||
#endif
|
#endif
|
||||||
isRecive = false; // приём завершён
|
isRecive = false; // приём завершён
|
||||||
msgTypeReceive = 0;
|
msgTypeReceive = 0;
|
||||||
@ -168,6 +379,9 @@ inline void IR_DecoderRaw::checkTimeout()
|
|||||||
|
|
||||||
void IR_DecoderRaw::tick()
|
void IR_DecoderRaw::tick()
|
||||||
{
|
{
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefFlushDeferredIsrLogs();
|
||||||
|
#endif
|
||||||
FrontStorage currentFront;
|
FrontStorage currentFront;
|
||||||
bool hasCurrentFront = false;
|
bool hasCurrentFront = false;
|
||||||
FrontStorage rawFront;
|
FrontStorage rawFront;
|
||||||
@ -250,6 +464,9 @@ void IR_DecoderRaw::tick()
|
|||||||
if (short_low_glitch)
|
if (short_low_glitch)
|
||||||
{
|
{
|
||||||
errors.other++;
|
errors.other++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
|
||||||
|
#endif
|
||||||
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
||||||
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
||||||
#endif
|
#endif
|
||||||
@ -264,6 +481,9 @@ void IR_DecoderRaw::tick()
|
|||||||
if (micro_gap_rise)
|
if (micro_gap_rise)
|
||||||
{
|
{
|
||||||
errors.other++;
|
errors.other++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
|
||||||
|
#endif
|
||||||
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
||||||
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
||||||
#endif
|
#endif
|
||||||
@ -273,6 +493,9 @@ void IR_DecoderRaw::tick()
|
|||||||
if (candRp <= riseTimeMax / 4U && !highCount && !lowCount)
|
if (candRp <= riseTimeMax / 4U && !highCount && !lowCount)
|
||||||
{
|
{
|
||||||
errors.other++;
|
errors.other++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Timing, irClampU16(candRp), 0, currentFront.time);
|
||||||
|
#endif
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,6 +559,11 @@ void IR_DecoderRaw::tick()
|
|||||||
if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack)
|
if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack)
|
||||||
// ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
|
// ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
|
||||||
{
|
{
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
if (!isBufferOverflow && !isWrongPack)
|
||||||
|
rxBriefLog(RxBriefReason::Timing, irClampU16((uint32_t)risePeriod),
|
||||||
|
irClampU16((uint32_t)highTime), currentFront.time);
|
||||||
|
#endif
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,6 +722,9 @@ void IR_DecoderRaw::tick()
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
END:;
|
END:;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefFlushDeferredIsrLogs();
|
||||||
|
#endif
|
||||||
#if defined(IR_EDGE_TRACE)
|
#if defined(IR_EDGE_TRACE)
|
||||||
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
|
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
|
||||||
#endif
|
#endif
|
||||||
@ -507,6 +738,9 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
|||||||
if (i_dataBuffer > dataByteSizeMax * 8)
|
if (i_dataBuffer > dataByteSizeMax * 8)
|
||||||
{ // проверка переполнения
|
{ // проверка переполнения
|
||||||
isBufferOverflow = true;
|
isBufferOverflow = true;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::BufferOverflow, i_dataBuffer, 0, micros());
|
||||||
|
#endif
|
||||||
#if defined(IRDEBUG_SERIAL_PACK)
|
#if defined(IRDEBUG_SERIAL_PACK)
|
||||||
if (packTraceOpen)
|
if (packTraceOpen)
|
||||||
packTraceEmitErrorFlash(F("ERROR: buffer overflow"));
|
packTraceEmitErrorFlash(F("ERROR: buffer overflow"));
|
||||||
@ -581,6 +815,9 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
isWrongPack = true;
|
isWrongPack = true;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Sync, err_syncBit, 0, micros());
|
||||||
|
#endif
|
||||||
#if defined(IRDEBUG_SERIAL_PACK)
|
#if defined(IRDEBUG_SERIAL_PACK)
|
||||||
packTraceEmitErrorFlash(F("ERROR: Wrong sync bit"));
|
packTraceEmitErrorFlash(F("ERROR: Wrong sync bit"));
|
||||||
#endif
|
#endif
|
||||||
@ -674,6 +911,14 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
|||||||
packTraceEmitEndOk(static_cast<uint8_t>(packSize));
|
packTraceEmitEndOk(static_cast<uint8_t>(packSize));
|
||||||
else
|
else
|
||||||
packTraceEmitEndBadCrc(static_cast<uint8_t>(packSize));
|
packTraceEmitEndBadCrc(static_cast<uint8_t>(packSize));
|
||||||
|
#endif
|
||||||
|
const uint16_t errSum =
|
||||||
|
uint16_t(errors.lowSignal) + uint16_t(errors.highSignal) + uint16_t(errors.other);
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
if (isAvailable)
|
||||||
|
rxBriefLog(RxBriefReason::Ok, packSize, errSum, micros());
|
||||||
|
else
|
||||||
|
rxBriefLog(RxBriefReason::Crc, packSize, errSum, micros());
|
||||||
#endif
|
#endif
|
||||||
if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) {
|
if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) {
|
||||||
memcpy(rejectBuffer, dataBuffer, packSize);
|
memcpy(rejectBuffer, dataBuffer, packSize);
|
||||||
@ -1203,6 +1448,9 @@ bool IR_DecoderRaw::pulseFilterEmit(const FrontStorage &e)
|
|||||||
if (filteredSubBuffer.isFull())
|
if (filteredSubBuffer.isFull())
|
||||||
{
|
{
|
||||||
pulseFilterDropFilteredOverflow++;
|
pulseFilterDropFilteredOverflow++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::FilterOverflow, irClampU16(pulseFilterDropFilteredOverflow), 0, e.time);
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
filteredSubBuffer.push(e);
|
filteredSubBuffer.push(e);
|
||||||
@ -1232,6 +1480,9 @@ void IR_DecoderRaw::pulseFilterFeedOneRaw(const FrontStorage &e)
|
|||||||
if (pulseFilterHoldCount >= kPulseFilterHoldCap)
|
if (pulseFilterHoldCount >= kPulseFilterHoldCap)
|
||||||
{
|
{
|
||||||
pulseFilterDropHoldOverflow++;
|
pulseFilterDropHoldOverflow++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::HoldOverflow, irClampU16(pulseFilterDropHoldOverflow), 0, e.time);
|
||||||
|
#endif
|
||||||
pulseFilterEmit(pulseFilterHoldEdges[0]);
|
pulseFilterEmit(pulseFilterHoldEdges[0]);
|
||||||
pulseFilterShiftLeft(1);
|
pulseFilterShiftLeft(1);
|
||||||
}
|
}
|
||||||
@ -1248,6 +1499,9 @@ void IR_DecoderRaw::pulseFilterFeedOneRaw(const FrontStorage &e)
|
|||||||
if (dt < minUs)
|
if (dt < minUs)
|
||||||
{
|
{
|
||||||
pulseFilterDropGlitchPairs++;
|
pulseFilterDropGlitchPairs++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, e.time);
|
||||||
|
#endif
|
||||||
pulseFilterShiftLeft(2);
|
pulseFilterShiftLeft(2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1276,6 +1530,9 @@ void IR_DecoderRaw::pulseFilterFlushTimeout(uint32_t nowUs)
|
|||||||
if (dt < IR_INPUT_MIN_PULSE_US)
|
if (dt < IR_INPUT_MIN_PULSE_US)
|
||||||
{
|
{
|
||||||
pulseFilterDropGlitchPairs++;
|
pulseFilterDropGlitchPairs++;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, nowUs);
|
||||||
|
#endif
|
||||||
pulseFilterShiftLeft(2);
|
pulseFilterShiftLeft(2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1343,7 +1600,12 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
|||||||
if (preambleState == PreambleState::Candidate)
|
if (preambleState == PreambleState::Candidate)
|
||||||
{
|
{
|
||||||
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
|
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
|
||||||
|
{
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, 0, front.time);
|
||||||
|
#endif
|
||||||
preambleStartCandidate(front);
|
preambleStartCandidate(front);
|
||||||
|
}
|
||||||
|
|
||||||
preambleCandidateLastEdgeTime = front.time;
|
preambleCandidateLastEdgeTime = front.time;
|
||||||
if (!front.dir)
|
if (!front.dir)
|
||||||
@ -1362,6 +1624,9 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
|||||||
{
|
{
|
||||||
preambleGoodPeriods = 0;
|
preambleGoodPeriods = 0;
|
||||||
preambleMeanPeriod = 0;
|
preambleMeanPeriod = 0;
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Preamble, 0, irClampU16(period), front.time);
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1382,6 +1647,9 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, irClampU16(period), front.time);
|
||||||
|
#endif
|
||||||
preambleGoodPeriods = 1;
|
preambleGoodPeriods = 1;
|
||||||
preambleMeanPeriod = (uint16_t)period;
|
preambleMeanPeriod = (uint16_t)period;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,15 +72,32 @@ public:
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
private:
|
private:
|
||||||
|
enum class RxBriefReason : uint8_t
|
||||||
|
{
|
||||||
|
MuteBegin = 1,
|
||||||
|
MuteEnd = 2,
|
||||||
|
RawOverflow = 3,
|
||||||
|
FilterOverflow = 4,
|
||||||
|
HoldOverflow = 5,
|
||||||
|
Glitch = 6,
|
||||||
|
Timing = 7,
|
||||||
|
Preamble = 8,
|
||||||
|
Sync = 9,
|
||||||
|
BufferOverflow = 10,
|
||||||
|
Timeout = 11,
|
||||||
|
Crc = 12,
|
||||||
|
Ok = 13
|
||||||
|
};
|
||||||
|
|
||||||
bool isRejectAvailable = false;
|
bool isRejectAvailable = false;
|
||||||
uint8_t rejectPackSize = 0;
|
uint8_t rejectPackSize = 0;
|
||||||
uint8_t rejectBuffer[dataByteSizeMax]{};
|
uint8_t rejectBuffer[dataByteSizeMax]{};
|
||||||
|
|
||||||
ErrorsStruct errors;
|
ErrorsStruct errors;
|
||||||
bool isAvailable = false;
|
bool isAvailable = false;
|
||||||
uint16_t packSize;
|
uint16_t packSize = 0;
|
||||||
uint16_t crcValue;
|
uint16_t crcValue = 0;
|
||||||
volatile uint16_t isPairSending = 0; // Флаг передачи парного передатчика
|
volatile uint16_t isPairSending = 0; // Число активных TX, временно глушащих этот RX.
|
||||||
volatile bool isRecive = false; // Флаг приёма
|
volatile bool isRecive = false; // Флаг приёма
|
||||||
volatile bool isPreamb = false; // флаг начальной последовости
|
volatile bool isPreamb = false; // флаг начальной последовости
|
||||||
volatile bool isSubBufferOverflow = false;
|
volatile bool isSubBufferOverflow = false;
|
||||||
@ -107,6 +124,8 @@ private:
|
|||||||
RingBuffer<FrontStorage, subBufferSize> subBuffer;
|
RingBuffer<FrontStorage, subBufferSize> subBuffer;
|
||||||
/** Очередь фронтов после потокового анти-глитча; tick() читает из неё при включённом фильтре. */
|
/** Очередь фронтов после потокового анти-глитча; tick() читает из неё при включённом фильтре. */
|
||||||
RingBuffer<FrontStorage, subBufferSize> filteredSubBuffer;
|
RingBuffer<FrontStorage, subBufferSize> filteredSubBuffer;
|
||||||
|
IR_Encoder *pairMuteEncoders[IR_PAIR_MUTE_MAX_ENCODERS]{};
|
||||||
|
uint8_t pairMuteEncoderCount = 0;
|
||||||
static constexpr uint8_t kPulseFilterHoldCap = 6;
|
static constexpr uint8_t kPulseFilterHoldCap = 6;
|
||||||
FrontStorage pulseFilterHoldEdges[kPulseFilterHoldCap]{};
|
FrontStorage pulseFilterHoldEdges[kPulseFilterHoldCap]{};
|
||||||
uint8_t pulseFilterHoldCount = 0;
|
uint8_t pulseFilterHoldCount = 0;
|
||||||
@ -143,6 +162,17 @@ private:
|
|||||||
volatile bool edgeTrace_overflow = false;
|
volatile bool edgeTrace_overflow = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
volatile bool rxBriefMuteBeginPending = false;
|
||||||
|
volatile uint32_t rxBriefMuteBeginUs = 0;
|
||||||
|
volatile bool rxBriefMuteEndPending = false;
|
||||||
|
volatile uint32_t rxBriefMuteEndUs = 0;
|
||||||
|
volatile uint16_t rxBriefMuteEndCount = 0;
|
||||||
|
volatile uint16_t rxBriefMuteBlockedEdges = 0;
|
||||||
|
volatile uint16_t rxBriefRawOverflowDrops = 0;
|
||||||
|
volatile uint32_t rxBriefRawOverflowLastUs = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
uint8_t dataBuffer[dataByteSizeMax]{0}; // Буффер данных
|
uint8_t dataBuffer[dataByteSizeMax]{0}; // Буффер данных
|
||||||
volatile uint32_t prevRise, prevPrevRise, prevFall, prevPrevFall; // Время предыдущих фронтов/спадов
|
volatile uint32_t prevRise, prevPrevRise, prevFall, prevPrevFall; // Время предыдущих фронтов/спадов
|
||||||
@ -163,7 +193,7 @@ private:
|
|||||||
int16_t bufBitPos = 0; // Позиция для записи бита в буффер
|
int16_t bufBitPos = 0; // Позиция для записи бита в буффер
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isReciveRaw;
|
bool isReciveRaw = false;
|
||||||
void listenStart();
|
void listenStart();
|
||||||
void checkTimeout(); //
|
void checkTimeout(); //
|
||||||
/** Один сырой фронт из subBuffer -> потоковый holdback-антиглитч. */
|
/** Один сырой фронт из subBuffer -> потоковый holdback-антиглитч. */
|
||||||
@ -173,6 +203,8 @@ bool isReciveRaw;
|
|||||||
void pulseFilterShiftLeft(uint8_t n);
|
void pulseFilterShiftLeft(uint8_t n);
|
||||||
void pulseFilterReset();
|
void pulseFilterReset();
|
||||||
static uint32_t absDiffU32(uint32_t a, uint32_t b);
|
static uint32_t absDiffU32(uint32_t a, uint32_t b);
|
||||||
|
bool registerPairMuteEncoder(IR_Encoder *enc);
|
||||||
|
void refreshPairMuteState();
|
||||||
uint32_t preambleJitterTolUs(uint32_t baselineUs) const;
|
uint32_t preambleJitterTolUs(uint32_t baselineUs) const;
|
||||||
bool preambleRisePeriodCoarseOk(uint32_t periodUs) const;
|
bool preambleRisePeriodCoarseOk(uint32_t periodUs) const;
|
||||||
void preambleResetToIdle();
|
void preambleResetToIdle();
|
||||||
@ -187,10 +219,10 @@ bool isReciveRaw;
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
bool isData = true; // Флаг относится ли бит к данным, или битам синхронизации
|
bool isData = true; // Флаг относится ли бит к данным, или битам синхронизации
|
||||||
uint16_t i_dataBuffer; // Счётчик буфера данных
|
uint16_t i_dataBuffer = 0; // Счётчик буфера данных
|
||||||
uint16_t nextControlBit = bitPerByte; // Метка для смены флага isData; uint16_t нужен для длинных кадров (>24 байт total)
|
uint16_t nextControlBit = bitPerByte; // Метка для смены флага isData; uint16_t нужен для длинных кадров (>24 байт total)
|
||||||
uint8_t i_syncBit; // Счётчик битов синхронизации
|
uint8_t i_syncBit = 0; // Счётчик битов синхронизации
|
||||||
uint8_t err_syncBit; // Счётчик ошибок синхронизации
|
uint8_t err_syncBit = 0; // Счётчик ошибок синхронизации
|
||||||
|
|
||||||
/// @brief Запиь бита в буффер, а так же проверка битов синхранизации и их фильтрация
|
/// @brief Запиь бита в буффер, а так же проверка битов синхранизации и их фильтрация
|
||||||
/// @param packTraceInvertFix если true — в IRDEBUG_SERIAL_PACK бит в трассе пишется как `0`/`1` (исправление по фронтам)
|
/// @param packTraceInvertFix если true — в IRDEBUG_SERIAL_PACK бит в трассе пишется как `0`/`1` (исправление по фронтам)
|
||||||
@ -205,6 +237,14 @@ bool isReciveRaw;
|
|||||||
/// @return Результат
|
/// @return Результат
|
||||||
uint16_t ceil_div(uint16_t val, uint16_t divider);
|
uint16_t ceil_div(uint16_t val, uint16_t divider);
|
||||||
|
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
|
static const __FlashStringHelper *rxBriefReasonTag(RxBriefReason reason);
|
||||||
|
void rxBriefLog(RxBriefReason reason, uint16_t a = 0, uint16_t b = 0, uint32_t tUs = 0);
|
||||||
|
void rxBriefNoteMuteBlockedIsr(uint32_t tUs);
|
||||||
|
void rxBriefNoteRawOverflowIsr(uint32_t tUs);
|
||||||
|
void rxBriefFlushDeferredIsrLogs();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef IRDEBUG
|
#ifdef IRDEBUG
|
||||||
uint32_t wrCounter;
|
uint32_t wrCounter;
|
||||||
inline void errPulse(uint8_t pin, uint8_t count);
|
inline void errPulse(uint8_t pin, uint8_t count);
|
||||||
|
|||||||
@ -15,19 +15,14 @@ IR_Encoder::IR_Encoder(uint8_t pin, uint16_t addr, IR_DecoderRaw *decPair, bool
|
|||||||
setPin(pin);
|
setPin(pin);
|
||||||
id = addr;
|
id = addr;
|
||||||
this->decPair = decPair;
|
this->decPair = decPair;
|
||||||
signal = noSignal;
|
|
||||||
isSending = false;
|
|
||||||
#if disablePairDec
|
|
||||||
if (decPair != nullptr)
|
if (decPair != nullptr)
|
||||||
{
|
{
|
||||||
blindDecoders = new IR_DecoderRaw *[1]{decPair};
|
singleBlindDecoder = decPair;
|
||||||
|
blindDecoders = &singleBlindDecoder;
|
||||||
decodersCount = 1;
|
decodersCount = 1;
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (decPair != nullptr)
|
|
||||||
{
|
|
||||||
decPair->encoder = this;
|
decPair->encoder = this;
|
||||||
}
|
}
|
||||||
|
registerWithBlindDecoders();
|
||||||
|
|
||||||
if (autoHandle)
|
if (autoHandle)
|
||||||
{
|
{
|
||||||
@ -233,7 +228,7 @@ void IR_Encoder::externalFinishSend()
|
|||||||
}
|
}
|
||||||
|
|
||||||
isSending = false;
|
isSending = false;
|
||||||
setDecoder_isSending();
|
refreshBlindDecoderMuteState();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns)
|
size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns)
|
||||||
@ -351,12 +346,16 @@ void IR_Encoder::disable()
|
|||||||
|
|
||||||
void IR_Encoder::setBlindDecoders(IR_DecoderRaw *decoders[], uint8_t count)
|
void IR_Encoder::setBlindDecoders(IR_DecoderRaw *decoders[], uint8_t count)
|
||||||
{
|
{
|
||||||
#if disablePairDec
|
if (count > IR_PAIR_MUTE_MAX_ENCODERS)
|
||||||
if (blindDecoders != nullptr)
|
{
|
||||||
delete[] blindDecoders;
|
decodersCount = 0;
|
||||||
#endif
|
blindDecoders = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
decodersCount = count;
|
decodersCount = count;
|
||||||
blindDecoders = decoders;
|
blindDecoders = decoders;
|
||||||
|
registerWithBlindDecoders();
|
||||||
|
refreshBlindDecoderMuteState();
|
||||||
}
|
}
|
||||||
|
|
||||||
IR_Encoder::~IR_Encoder(){};
|
IR_Encoder::~IR_Encoder(){};
|
||||||
@ -543,19 +542,28 @@ IR_SendResult IR_Encoder::_sendBack(bool isAdressed, uint16_t addrTo, uint8_t *d
|
|||||||
return IR_SendResult(true, sendTime);
|
return IR_SendResult(true, sendTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IR_Encoder::setDecoder_isSending()
|
void IR_Encoder::registerWithBlindDecoders()
|
||||||
{
|
|
||||||
if (decodersCount)
|
|
||||||
{
|
{
|
||||||
|
if (!decodersCount || blindDecoders == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < decodersCount; i++)
|
for (uint8_t i = 0; i < decodersCount; i++)
|
||||||
{
|
{
|
||||||
blindDecoders[i]->isPairSending ^= id;
|
if (blindDecoders[i] != nullptr)
|
||||||
// Serial.print("setDecoder_isSending() id = ");
|
blindDecoders[i]->registerPairMuteEncoder(this);
|
||||||
// Serial.print(id);
|
|
||||||
// Serial.print(" isPairSending = ");
|
|
||||||
// Serial.println(blindDecoders[i]->isPairSending);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IR_Encoder::refreshBlindDecoderMuteState()
|
||||||
|
{
|
||||||
|
if (!decodersCount || blindDecoders == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < decodersCount; i++)
|
||||||
|
{
|
||||||
|
if (blindDecoders[i] != nullptr)
|
||||||
|
blindDecoders[i]->refreshPairMuteState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
||||||
@ -572,13 +580,13 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.print("IR tx hex: ");
|
// Serial.print("IR tx hex: ");
|
||||||
for (uint8_t i = 0; i < len; i++)
|
// for (uint8_t i = 0; i < len; i++)
|
||||||
{
|
// {
|
||||||
if (ptr[i] < 0x10) Serial.print("0");
|
// if (ptr[i] < 0x10) Serial.print("0");
|
||||||
Serial.print(ptr[i], HEX);
|
// Serial.print(ptr[i], HEX);
|
||||||
}
|
// }
|
||||||
Serial.println();
|
// Serial.println();
|
||||||
|
|
||||||
if (externalTxStartFn != nullptr)
|
if (externalTxStartFn != nullptr)
|
||||||
{
|
{
|
||||||
@ -587,24 +595,18 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark as sending and delegate actual signal output to external backend.
|
|
||||||
setDecoder_isSending();
|
|
||||||
sendLen = len;
|
sendLen = len;
|
||||||
isSending = true;
|
isSending = true;
|
||||||
|
refreshBlindDecoderMuteState();
|
||||||
|
|
||||||
const bool ok = externalTxStartFn(externalTxCtx, this, ptr, len);
|
const bool ok = externalTxStartFn(externalTxCtx, this, ptr, len);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
isSending = false;
|
isSending = false;
|
||||||
setDecoder_isSending();
|
refreshBlindDecoderMuteState();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IR_Encoder::carrierResume();
|
|
||||||
// Serial.println("START");
|
|
||||||
setDecoder_isSending();
|
|
||||||
|
|
||||||
// noInterrupts();
|
|
||||||
sendLen = len;
|
sendLen = len;
|
||||||
toggleCounter = preambToggle; // Первая генерация для первого signal
|
toggleCounter = preambToggle; // Первая генерация для первого signal
|
||||||
|
|
||||||
@ -618,8 +620,9 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
signal = preamb;
|
signal = preamb;
|
||||||
isSending = true;
|
isSending = true;
|
||||||
state = HIGH;
|
state = HIGH;
|
||||||
|
|
||||||
currentBitSequence = bitHigh;
|
currentBitSequence = bitHigh;
|
||||||
|
refreshBlindDecoderMuteState();
|
||||||
|
IR_Encoder::carrierResume();
|
||||||
// interrupts();
|
// interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,7 +654,7 @@ void IR_Encoder::_isr()
|
|||||||
if (!active)
|
if (!active)
|
||||||
{
|
{
|
||||||
isSending = false;
|
isSending = false;
|
||||||
setDecoder_isSending();
|
refreshBlindDecoderMuteState();
|
||||||
carrierStopPending = true;
|
carrierStopPending = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
IR_Encoder.h
42
IR_Encoder.h
@ -35,7 +35,8 @@ public:
|
|||||||
/// @brief Класс передатчика
|
/// @brief Класс передатчика
|
||||||
/// @param addr Адрес передатчика
|
/// @param addr Адрес передатчика
|
||||||
/// @param pin Вывод передатчика
|
/// @param pin Вывод передатчика
|
||||||
/// @param decPair Приёмник, для которого отключается приём в момент передачи передатчиком
|
/// @param decPair Если задан, конструктор регистрирует этот один приёмник как blind-decoder
|
||||||
|
/// (аналог setBlindDecoders() для одного RX).
|
||||||
IR_Encoder(uint8_t pin, uint16_t addr = 0, IR_DecoderRaw *decPair = nullptr, bool autoHandle = true);
|
IR_Encoder(uint8_t pin, uint16_t addr = 0, IR_DecoderRaw *decPair = nullptr, bool autoHandle = true);
|
||||||
static void isr();
|
static void isr();
|
||||||
static void begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, uint8_t priority, void(*isrCallback)() = nullptr);
|
static void begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, uint8_t priority, void(*isrCallback)() = nullptr);
|
||||||
@ -58,6 +59,13 @@ public:
|
|||||||
void disable();
|
void disable();
|
||||||
|
|
||||||
void setBlindDecoders(IR_DecoderRaw *decoders[], uint8_t count);
|
void setBlindDecoders(IR_DecoderRaw *decoders[], uint8_t count);
|
||||||
|
template <size_t N>
|
||||||
|
void setBlindDecoders(IR_DecoderRaw *(&decoders)[N])
|
||||||
|
{
|
||||||
|
static_assert(N <= IR_PAIR_MUTE_MAX_ENCODERS,
|
||||||
|
"IR_Encoder::setBlindDecoders: array size exceeds IR_PAIR_MUTE_MAX_ENCODERS");
|
||||||
|
setBlindDecoders(decoders, static_cast<uint8_t>(N));
|
||||||
|
}
|
||||||
void rawSend(uint8_t *ptr, uint8_t len);
|
void rawSend(uint8_t *ptr, uint8_t len);
|
||||||
|
|
||||||
IR_SendResult sendData(uint16_t addrTo, uint8_t dataByte, bool needAccept = false);
|
IR_SendResult sendData(uint16_t addrTo, uint8_t dataByte, bool needAccept = false);
|
||||||
@ -99,7 +107,8 @@ private:
|
|||||||
static void *externalTxCtx;
|
static void *externalTxCtx;
|
||||||
IR_SendResult _sendBack(bool isAdressed, uint16_t addrTo, uint8_t *data, uint8_t len);
|
IR_SendResult _sendBack(bool isAdressed, uint16_t addrTo, uint8_t *data, uint8_t len);
|
||||||
|
|
||||||
void setDecoder_isSending();
|
void refreshBlindDecoderMuteState();
|
||||||
|
void registerWithBlindDecoders();
|
||||||
void sendByte(uint8_t byte, bool *prev, bool LOW_FIRST);
|
void sendByte(uint8_t byte, bool *prev, bool LOW_FIRST);
|
||||||
void addSync(bool *prev, bool *next);
|
void addSync(bool *prev, bool *next);
|
||||||
uint32_t calculateSendTime(uint8_t packSize) const;
|
uint32_t calculateSendTime(uint8_t packSize) const;
|
||||||
@ -137,25 +146,26 @@ private:
|
|||||||
void loadTxFsmFromMembers(TxFsmState &st) const;
|
void loadTxFsmFromMembers(TxFsmState &st) const;
|
||||||
void storeTxFsmToMembers(const TxFsmState &st);
|
void storeTxFsmToMembers(const TxFsmState &st);
|
||||||
|
|
||||||
IR_DecoderRaw *decPair;
|
IR_DecoderRaw *decPair = nullptr;
|
||||||
IR_DecoderRaw **blindDecoders;
|
IR_DecoderRaw *singleBlindDecoder = nullptr;
|
||||||
uint8_t decodersCount;
|
IR_DecoderRaw **blindDecoders = nullptr;
|
||||||
|
uint8_t decodersCount = 0;
|
||||||
|
|
||||||
uint8_t sendLen;
|
uint8_t sendLen = 0;
|
||||||
uint8_t sendBuffer[dataByteSizeMax]{0}; /// @brief Буффер данных для отправки
|
uint8_t sendBuffer[dataByteSizeMax]{0}; /// @brief Буффер данных для отправки
|
||||||
|
|
||||||
volatile bool isSending;
|
volatile bool isSending = false;
|
||||||
volatile bool state; /// @brief Текущий уровень генерации
|
volatile bool state = LOW; /// @brief Текущий уровень генерации
|
||||||
|
|
||||||
volatile uint8_t dataByteCounter;
|
volatile uint8_t dataByteCounter = 0;
|
||||||
|
|
||||||
volatile uint8_t toggleCounter; /// @brief Счётчик переключений
|
volatile uint8_t toggleCounter = 0; /// @brief Счётчик переключений
|
||||||
volatile uint8_t dataBitCounter;
|
volatile uint8_t dataBitCounter = 0;
|
||||||
|
|
||||||
volatile uint8_t preambFrontCounter;
|
volatile uint8_t preambFrontCounter = 0;
|
||||||
volatile uint8_t dataSequenceCounter;
|
volatile uint8_t dataSequenceCounter = 0;
|
||||||
volatile uint8_t syncSequenceCounter;
|
volatile uint8_t syncSequenceCounter = 0;
|
||||||
volatile bool syncLastBit;
|
volatile bool syncLastBit = false;
|
||||||
|
|
||||||
struct BitSequence
|
struct BitSequence
|
||||||
{
|
{
|
||||||
@ -165,5 +175,5 @@ private:
|
|||||||
static uint8_t bitHigh[2];
|
static uint8_t bitHigh[2];
|
||||||
static uint8_t bitLow[2];
|
static uint8_t bitLow[2];
|
||||||
uint8_t *currentBitSequence = bitLow;
|
uint8_t *currentBitSequence = bitLow;
|
||||||
volatile SignalPart signal;
|
volatile SignalPart signal = noSignal;
|
||||||
};
|
};
|
||||||
|
|||||||
17
IR_config.h
17
IR_config.h
@ -13,6 +13,16 @@ constexpr size_t kDefaultDmaTxMaxStreams = 4U;
|
|||||||
// Не обрывать приём сразу при накопленной sync-ошибке — «дописывать» до таймаута (только вместе с IRDEBUG_SERIAL_PACK).
|
// Не обрывать приём сразу при накопленной sync-ошибке — «дописывать» до таймаута (только вместе с IRDEBUG_SERIAL_PACK).
|
||||||
// #define IRDEBUG_SERIAL_SOFT_REJECT
|
// #define IRDEBUG_SERIAL_SOFT_REJECT
|
||||||
|
|
||||||
|
// Краткий лог причин, почему физический сигнал не дошёл до распознанного пакета.
|
||||||
|
// Формат и коды: ref/IR_RX_BRIEF_LOG.md
|
||||||
|
#ifndef IR_RX_BRIEF_LOG
|
||||||
|
#define IR_RX_BRIEF_LOG 1
|
||||||
|
#endif
|
||||||
|
// 1: печатать только отклонённые/ошибочные события; успехи и шумовые PREAMB скрыть.
|
||||||
|
#ifndef IR_RX_BRIEF_LOG_REJECT_ONLY
|
||||||
|
#define IR_RX_BRIEF_LOG_REJECT_ONLY 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// Журнал фронтов ИК в ISR; сброс строк @IRF1v1: в IR_DecoderRaw::tick(). См. ref/IR_EDGE_TRACE_FORMAT.md
|
// Журнал фронтов ИК в ISR; сброс строк @IRF1v1: в IR_DecoderRaw::tick(). См. ref/IR_EDGE_TRACE_FORMAT.md
|
||||||
// Расход RAM ≈ IR_EDGE_TRACE_CAPACITY * 6 байт на декодер. Выключить — закомментировать:
|
// Расход RAM ≈ IR_EDGE_TRACE_CAPACITY * 6 байт на декодер. Выключить — закомментировать:
|
||||||
// #define IR_EDGE_TRACE
|
// #define IR_EDGE_TRACE
|
||||||
@ -154,6 +164,11 @@ typedef uint16_t crc_t;
|
|||||||
#define subBufferSize 250 // Буфер для складирования фронтов, пока их не обработают (передатчик)
|
#define subBufferSize 250 // Буфер для складирования фронтов, пока их не обработают (передатчик)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** Максимальное число передатчиков, способных временно заглушить один декодер. */
|
||||||
|
#ifndef IR_PAIR_MUTE_MAX_ENCODERS
|
||||||
|
#define IR_PAIR_MUTE_MAX_ENCODERS 8U
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Минимальная длительность удержания уровня (мкс): короче — импульс/пара фронтов выкидывается до tick()
|
/** Минимальная длительность удержания уровня (мкс): короче — импульс/пара фронтов выкидывается до tick()
|
||||||
* (иголки на плато, дребезг). 0 — фильтр выключен, фронты идут в декодер как с ISR. */
|
* (иголки на плато, дребезг). 0 — фильтр выключен, фронты идут в декодер как с ISR. */
|
||||||
#ifndef IR_INPUT_MIN_PULSE_US
|
#ifndef IR_INPUT_MIN_PULSE_US
|
||||||
@ -204,8 +219,6 @@ typedef uint16_t crc_t;
|
|||||||
|
|
||||||
#define preambPulse 3
|
#define preambPulse 3
|
||||||
|
|
||||||
#define disablePairDec false // Отключать парный приёмник, возможны баги, используйте setBlindDecoders()
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define bitPerByte 8U // Колличество бит в байте
|
#define bitPerByte 8U // Колличество бит в байте
|
||||||
|
|||||||
@ -13,13 +13,16 @@ public:
|
|||||||
return start == end;
|
return start == end;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(T element) {
|
bool push(T element) {
|
||||||
|
bool pushed = false;
|
||||||
noInterrupts();
|
noInterrupts();
|
||||||
if (!isFull()) {
|
if (!isFull()) {
|
||||||
data[end] = element;
|
data[end] = element;
|
||||||
end = (end + 1) % BufferSize;
|
end = (end + 1) % BufferSize;
|
||||||
|
pushed = true;
|
||||||
}
|
}
|
||||||
interrupts();
|
interrupts();
|
||||||
|
return pushed;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* pop() {
|
T* pop() {
|
||||||
|
|||||||
60
ref/IR_RX_BRIEF_LOG.md
Normal file
60
ref/IR_RX_BRIEF_LOG.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# IR RX Brief Log
|
||||||
|
|
||||||
|
Краткий лог включается через:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define IR_RX_BRIEF_LOG 1
|
||||||
|
#define IR_RX_BRIEF_LOG_REJECT_ONLY 1 // только отклонённые/ошибочные события
|
||||||
|
```
|
||||||
|
|
||||||
|
Лог печатается короткими строками вида:
|
||||||
|
|
||||||
|
```text
|
||||||
|
IRRX t=1234567 rsn=CRC len=25 err=3
|
||||||
|
IRRX t=1234000 rsn=MUTE_BEGIN
|
||||||
|
IRRX t=1234988 rsn=MUTE_END cnt=42
|
||||||
|
```
|
||||||
|
|
||||||
|
Где:
|
||||||
|
|
||||||
|
- `t` — uptime МК в `micros()`
|
||||||
|
- `rsn` — краткий код причины
|
||||||
|
- остальные поля зависят от причины
|
||||||
|
|
||||||
|
## Коды `rsn`
|
||||||
|
|
||||||
|
| Код | Смысл | Типичные поля |
|
||||||
|
|-----|-------|---------------|
|
||||||
|
| `MUTE_BEGIN` | Началось окно mute: RX временно игнорирует вход, пока активен связанный TX | - |
|
||||||
|
| `MUTE_END` | Окно mute завершилось; `cnt` показывает число заблокированных фронтов за всё окно | `cnt` |
|
||||||
|
| `QRAW` | Потеря фронтов из-за переполнения сырой очереди `subBuffer` | `cnt` |
|
||||||
|
| `QFLT` | Потеря фронтов из-за переполнения очереди после входного фильтра | `total` |
|
||||||
|
| `HOLD` | Переполнен holdback фильтра до выпуска фронтов | `total` |
|
||||||
|
| `GLITCH` | Фронт/пара фронтов отброшены как глитч | `total` |
|
||||||
|
| `TIME` | Плохой тайминг фронтов/битов, кадр не может нормально разбираться | `rp`, `hp` |
|
||||||
|
| `PREAMB` | Кандидат преамбулы не залочился или был перезапущен | `good`, `per` |
|
||||||
|
| `SYNC` | Ошибка sync-бита привела к reject кадра | `err` |
|
||||||
|
| `BUF` | Переполнен битовый буфер кадра | `bits` |
|
||||||
|
| `TIMEOUT` | Кадр оборвался по таймауту до завершения | `bits`, `exp` |
|
||||||
|
| `CRC` | Кадр дошёл до конца по длине, но CRC не сошёлся | `len`, `err` |
|
||||||
|
| `OK` | Кадр успешно распознан | `len`, `err` |
|
||||||
|
|
||||||
|
## Поля
|
||||||
|
|
||||||
|
- `cnt` — число событий/фронтов, накопленных за окно или пакетную группу
|
||||||
|
- `total` — накопленный счётчик отбраковок данного типа
|
||||||
|
- `rp` — `risePeriod`
|
||||||
|
- `hp` — `highTime`
|
||||||
|
- `good` — число подряд подходящих периодов преамбулы перед срывом
|
||||||
|
- `per` — период преамбулы/кандидата
|
||||||
|
- `err` — суммарные ошибки `lowSignal + highSignal + other` либо счётчик sync-ошибок
|
||||||
|
- `bits` — сколько data-бит успело накопиться
|
||||||
|
- `exp` — ожидаемая длина кадра из первого байта, если уже известна
|
||||||
|
- `len` — полная длина кадра в байтах
|
||||||
|
|
||||||
|
## Когда смотреть подробный debug
|
||||||
|
|
||||||
|
- Если нужен полный поток битов и sync: включать `IRDEBUG_SERIAL_PACK`
|
||||||
|
- Если нужно понять, какие именно фронты пришли в ISR: включать `IR_EDGE_TRACE`
|
||||||
|
- `IR_RX_BRIEF_LOG` нужен как короткий always-on-ish индикатор сути проблемы, без длинного дампа
|
||||||
|
- `IR_RX_BRIEF_LOG_REJECT_ONLY=1` скрывает `OK` и `PREAMB`, оставляя только отклонения/ошибки
|
||||||
Reference in New Issue
Block a user