Refactor IR decoder and encoder for improved pulse filtering and ISR handling. Removed unused filtered sub-buffer, updated pulse filter methods, and added support for buffered ISR storage in the encoder. Enhanced documentation for clarity on DMA TX backend and ISR modes.

This commit is contained in:
2026-04-20 14:48:45 +03:00
parent 01a34ed3f7
commit 31ac7a3625
8 changed files with 291 additions and 105 deletions

View File

@ -1,7 +1,24 @@
#include "IR_Encoder.h"
#include "IR_DecoderRaw.h"
#include "IrTxIsrBufferedStorage.h"
#include <string.h>
#if defined(_MSC_VER)
#define IRPROTO_PRAGMA_MESSAGE(text) __pragma(message(text))
#else
#define IRPROTO_PRAGMA_MESSAGE(text) _Pragma(#text)
#endif
#if defined(ARDUINO_ARCH_STM32)
#if defined(STM32G4xx)
IRPROTO_PRAGMA_MESSAGE(message("[IR-protocol] TX backends: ISR + built-in DMA"))
#elif defined(STM32F4xx)
IRPROTO_PRAGMA_MESSAGE(message("[IR-protocol] TX backends: ISR only"))
#else
IRPROTO_PRAGMA_MESSAGE(message("[IR-protocol] TX backends: ISR"))
#endif
#endif
#define LoopOut 12
#define ISR_Out 10
#define TestOut 13
@ -14,6 +31,7 @@ IR_Encoder::IR_Encoder(uint8_t pin, uint16_t addr, IR_DecoderRaw *decPair, bool
{
setPin(pin);
id = addr;
txIsrMode_ = txIsrLegacyMode_ ? TxIsrMode::Legacy : TxIsrMode::Buffered;
this->decPair = decPair;
if (decPair != nullptr)
{
@ -45,7 +63,7 @@ HardwareTimer* IR_Encoder::IR_Timer = nullptr;
IR_Encoder::ExternalTxStartFn IR_Encoder::externalTxStartFn = nullptr;
IR_Encoder::ExternalTxBusyFn IR_Encoder::externalTxBusyFn = nullptr;
void *IR_Encoder::externalTxCtx = nullptr;
bool IR_Encoder::txIsrLegacyMode_ = false;
bool IR_Encoder::txIsrLegacyMode_ = true;
uint16_t IR_Encoder::s_carrierMultiply = 2;
void IR_Encoder::setCarrierMultiply(uint16_t multiply)
@ -146,6 +164,11 @@ bool IR_Encoder::scaleGateRunsToPhysical(IR_TxGateRun* runs, size_t* ioCount, si
void IR_Encoder::setTxIsrLegacyMode(bool legacy)
{
txIsrLegacyMode_ = legacy;
const TxIsrMode mode = legacy ? TxIsrMode::Legacy : TxIsrMode::Buffered;
for (IR_Encoder *p = head; p != nullptr; p = p->next)
{
p->txIsrMode_ = mode;
}
}
bool IR_Encoder::txIsrLegacyMode()
@ -153,6 +176,54 @@ bool IR_Encoder::txIsrLegacyMode()
return txIsrLegacyMode_;
}
void IR_Encoder::attachBufferedIsrStorage(IrTxIsrBufferedStorageBase& storage)
{
txBufferedCtx_ = &storage;
}
void IR_Encoder::detachBufferedIsrStorage()
{
txBufferedCtx_ = nullptr;
if (!isSending)
{
txActiveBufferedCtx_ = nullptr;
txUseBufferedIsr_ = false;
}
}
bool IR_Encoder::hasBufferedIsrStorage() const
{
return txBufferedCtx_ != nullptr && txBufferedCtx_->isValid();
}
void IR_Encoder::enableBufferedIsr(IrTxIsrBufferedStorageBase& storage)
{
attachBufferedIsrStorage(storage);
txIsrMode_ = TxIsrMode::Buffered;
}
void IR_Encoder::disableBufferedIsr()
{
txIsrMode_ = TxIsrMode::Legacy;
if (!isSending)
{
txActiveBufferedCtx_ = nullptr;
txUseBufferedIsr_ = false;
}
}
IR_Encoder::TxIsrMode IR_Encoder::txIsrMode() const
{
return txIsrMode_;
}
bool IR_Encoder::shouldUseBufferedIsr() const
{
return txIsrMode_ == TxIsrMode::Buffered &&
txBufferedCtx_ != nullptr &&
txBufferedCtx_->isValid();
}
bool IR_Encoder::txAdvanceBoundary(TxFsmState &st, const uint8_t *sendBufferLocal)
{
while (true)
@ -336,6 +407,8 @@ void IR_Encoder::externalFinishSend()
}
isSending = false;
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
refreshBlindDecoderMuteState();
}
@ -704,6 +777,8 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
}
sendLen = len;
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
isSending = true;
refreshBlindDecoderMuteState();
@ -727,7 +802,11 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
}
sendLen = len;
if (txIsrLegacyMode_)
const bool useBufferedIsr = shouldUseBufferedIsr();
txUseBufferedIsr_ = useBufferedIsr;
txActiveBufferedCtx_ = useBufferedIsr ? txBufferedCtx_ : nullptr;
if (!useBufferedIsr)
{
toggleCounter = preambToggle;
dataBitCounter = bitPerByte - 1;
@ -756,22 +835,36 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
return;
}
size_t nRuns = buildGateRuns(sendBuffer, len, txGateRuns_, irproto::kIsrTxMaxGateRuns);
if (nRuns == 0U)
IrTxIsrBufferedStorageBase* buf = txActiveBufferedCtx_;
if (buf == nullptr || !buf->isValid())
{
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
return;
}
if (!scaleGateRunsToPhysical(txGateRuns_, &nRuns, irproto::kIsrTxMaxGateRuns, carrierMultiply()))
buf->resetRuntimeState();
size_t nRuns = buildGateRuns(sendBuffer, len, buf->gateRuns, buf->maxGateRuns);
if (nRuns == 0U)
{
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
return;
}
if (!scaleGateRunsToPhysical(buf->gateRuns, &nRuns, buf->maxGateRuns, carrierMultiply()))
{
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
return;
}
uint32_t total = 0;
for (size_t i = 0; i < nRuns; i++)
{
total += txGateRuns_[i].lenTicks;
total += buf->gateRuns[i].lenTicks;
}
txBsrrTotalTicks_ = total;
buf->totalTicks = total;
const uint32_t setW = (uint32_t)mask;
const uint32_t resetW = ((uint32_t)mask) << 16U;
@ -780,13 +873,8 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
const uint16_t cap = maxPowerNumerator();
txPowerSnap_ = (powerNumerator_ > cap) ? cap : powerNumerator_;
}
txBsrrWave_.configure(setW, resetW, txGateRuns_, nRuns, txMultiplySnap_, txPowerSnap_);
txBsrrHalfLen_ = (uint16_t)(irproto::kIsrTxBsrrWordCount / 2U);
txBsrrWave_.fill(txBsrrWords_, irproto::kIsrTxBsrrWordCount);
txBsrrReadIdx_ = 0;
txBsrrTicksSent_ = 0;
buf->wave.configure(setW, resetW, buf->gateRuns, nRuns, txMultiplySnap_, txPowerSnap_);
buf->wave.fill(buf->bsrrWords, buf->wordCount);
isSending = true;
refreshBlindDecoderMuteState();
@ -815,7 +903,7 @@ void IR_Encoder::_isr()
if (port == nullptr)
return;
if (txIsrLegacyMode_)
if (!txUseBufferedIsr_)
{
const uint32_t setW = (uint32_t)mask;
const uint32_t resetW = ((uint32_t)mask) << 16U;
@ -850,33 +938,49 @@ void IR_Encoder::_isr()
{
port->BSRR = resetW;
isSending = false;
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
refreshBlindDecoderMuteState();
carrierStopPending = true;
}
return;
}
port->BSRR = txBsrrWords_[txBsrrReadIdx_];
txBsrrReadIdx_++;
txBsrrTicksSent_++;
if (txBsrrTicksSent_ >= txBsrrTotalTicks_)
IrTxIsrBufferedStorageBase* buf = txActiveBufferedCtx_;
if (buf == nullptr || !buf->isValid())
{
port->BSRR = ((uint32_t)mask) << 16U;
isSending = false;
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
refreshBlindDecoderMuteState();
carrierStopPending = true;
return;
}
if (txBsrrReadIdx_ == txBsrrHalfLen_)
port->BSRR = buf->bsrrWords[buf->readIdx];
buf->readIdx++;
buf->ticksSent++;
if (buf->ticksSent >= buf->totalTicks)
{
txBsrrWave_.fill(&txBsrrWords_[0], txBsrrHalfLen_);
port->BSRR = ((uint32_t)mask) << 16U;
isSending = false;
txUseBufferedIsr_ = false;
txActiveBufferedCtx_ = nullptr;
refreshBlindDecoderMuteState();
carrierStopPending = true;
return;
}
else if (txBsrrReadIdx_ >= irproto::kIsrTxBsrrWordCount)
if (buf->readIdx == buf->halfLen)
{
txBsrrReadIdx_ = 0;
txBsrrWave_.fill(&txBsrrWords_[txBsrrHalfLen_], txBsrrHalfLen_);
buf->wave.fill(&buf->bsrrWords[0], buf->halfLen);
}
else if (buf->readIdx >= buf->wordCount)
{
buf->readIdx = 0;
buf->wave.fill(&buf->bsrrWords[buf->halfLen], buf->halfLen);
}
}