From fc1a3bacef62d70adfc8d6ca2ebb91c14e008423 Mon Sep 17 00:00:00 2001 From: DashyFox Date: Wed, 11 Mar 2026 16:57:55 +0300 Subject: [PATCH] upd --- IR_Encoder.cpp | 184 ++++++++++++++++++++++++++++++++++++++++++++++++- IR_Encoder.h | 24 ++++++- IR_config.h | 2 + 3 files changed, 208 insertions(+), 2 deletions(-) diff --git a/IR_Encoder.cpp b/IR_Encoder.cpp index 357dc60..ba663ae 100644 --- a/IR_Encoder.cpp +++ b/IR_Encoder.cpp @@ -1,5 +1,6 @@ #include "IR_Encoder.h" #include "IR_DecoderRaw.h" +#include #define LoopOut 12 #define ISR_Out 10 @@ -45,6 +46,9 @@ IR_Encoder::IR_Encoder(uint8_t pin, uint16_t addr, IR_DecoderRaw *decPair, bool }; 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; inline HardwareTimer* IR_Encoder::get_IR_Timer(){return IR_Encoder::IR_Timer;} @@ -78,6 +82,164 @@ void IR_Encoder::begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, ui IR_Timer->pause(); } +void IR_Encoder::beginClockOnly(HardwareTimer *timer) +{ + IR_Timer = timer; + if (IR_Timer == nullptr) + return; + IR_Timer->pause(); + IR_Timer->setOverflow(carrierFrec * 2, HERTZ_FORMAT); + IR_Timer->pause(); +} + +void IR_Encoder::setExternalTxBackend(ExternalTxStartFn startFn, ExternalTxBusyFn busyFn, void *ctx) +{ + externalTxStartFn = startFn; + externalTxBusyFn = busyFn; + externalTxCtx = ctx; +} + +void IR_Encoder::externalFinishSend() +{ + if (!isSending) + return; + + // Force output low. + if (port != nullptr) { + port->BSRR = ((uint32_t)mask) << 16; + } + + isSending = false; + setDecoder_isSending(); +} + +size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns) +{ + if (packet == nullptr || outRuns == nullptr || maxRuns == 0) + { + return 0; + } + if (len == 0 || len > dataByteSizeMax) + { + return 0; + } + + // Copy into fixed-size buffer to match original encoder behavior (safe reads past sendLen). + uint8_t sendBufferLocal[dataByteSizeMax] = {0}; + memcpy(sendBufferLocal, packet, len); + + uint8_t sendLenLocal = len; + uint8_t toggleCounterLocal = preambToggle; + uint8_t dataBitCounterLocal = bitPerByte - 1; + uint8_t dataByteCounterLocal = 0; + uint8_t preambFrontCounterLocal = preambPulse * 2 - 1; + uint8_t dataSequenceCounterLocal = bitPerByte * 2; + uint8_t syncSequenceCounterLocal = syncBits * 2; + bool syncLastBitLocal = false; + SignalPart signalLocal = preamb; + bool stateLocal = HIGH; + uint8_t *currentBitSequenceLocal = bitHigh; + + size_t runCount = 0; + + while (true) + { + const bool gate = stateLocal; + const uint16_t runLenTicks = (uint16_t)toggleCounterLocal + 1U; + + if (runCount > 0 && outRuns[runCount - 1].gate == gate) + { + outRuns[runCount - 1].lenTicks = (uint16_t)(outRuns[runCount - 1].lenTicks + runLenTicks); + } + else + { + if (runCount >= maxRuns) + { + return 0; + } + outRuns[runCount].gate = gate; + outRuns[runCount].lenTicks = runLenTicks; + runCount++; + } + + // Advance state to the next run boundary (equivalent to ISR iteration when toggleCounter == 0). + while (true) + { + switch (signalLocal) + { + case noSignal: + return runCount; + + case preamb: + if (preambFrontCounterLocal) + { + preambFrontCounterLocal--; + toggleCounterLocal = preambToggle; + break; + } + // End of preamble. + signalLocal = data; + stateLocal = !LOW; + continue; + + case data: + if (dataSequenceCounterLocal) + { + if (!(dataSequenceCounterLocal & 1U)) + { + currentBitSequenceLocal = ((sendBufferLocal[dataByteCounterLocal] >> dataBitCounterLocal) & 1U) ? bitHigh : bitLow; + dataBitCounterLocal--; + } + toggleCounterLocal = currentBitSequenceLocal[!stateLocal]; + dataSequenceCounterLocal--; + break; + } + // End of data byte. + syncLastBitLocal = ((sendBufferLocal[dataByteCounterLocal]) & 1U); + dataByteCounterLocal++; + dataBitCounterLocal = bitPerByte - 1; + dataSequenceCounterLocal = bitPerByte * 2; + signalLocal = sync; + continue; + + case sync: + if (syncSequenceCounterLocal) + { + if (!(syncSequenceCounterLocal & 1U)) + { + if (syncSequenceCounterLocal == 2) + { + currentBitSequenceLocal = ((sendBufferLocal[dataByteCounterLocal]) & 0b10000000) ? bitLow : bitHigh; + } + else + { + currentBitSequenceLocal = syncLastBitLocal ? bitLow : bitHigh; + syncLastBitLocal = !syncLastBitLocal; + } + } + toggleCounterLocal = currentBitSequenceLocal[!stateLocal]; + syncSequenceCounterLocal--; + break; + } + // End of sync. + signalLocal = data; + syncSequenceCounterLocal = syncBits * 2; + if (dataByteCounterLocal >= sendLenLocal) + { + signalLocal = noSignal; + } + continue; + + default: + return 0; + } + + stateLocal = !stateLocal; + break; + } + } +} + void IR_Encoder::enable() { @@ -360,6 +522,27 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len) { return; } + + if (externalTxStartFn != nullptr) + { + if (externalTxBusyFn != nullptr && externalTxBusyFn(externalTxCtx)) + { + return; + } + + // Mark as sending and delegate actual signal output to external backend. + setDecoder_isSending(); + sendLen = len; + isSending = true; + + const bool ok = externalTxStartFn(externalTxCtx, this, ptr, len); + if (!ok) + { + isSending = false; + setDecoder_isSending(); + } + return; + } IR_Encoder::carrierResume(); // Serial.println("START"); setDecoder_isSending(); @@ -380,7 +563,6 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len) state = HIGH; currentBitSequence = bitHigh; - isSending = true; // interrupts(); } diff --git a/IR_Encoder.h b/IR_Encoder.h index a2ca8ac..f1013de 100644 --- a/IR_Encoder.h +++ b/IR_Encoder.h @@ -21,6 +21,14 @@ class IR_Encoder : public IR_FOX IR_Encoder *next; public: static HardwareTimer* IR_Timer; + + struct IR_TxGateRun { + uint16_t lenTicks; // number of timer ticks at carrierFrec*2 + bool gate; // true: carrier enabled (output toggles), false: silent (output forced low) + }; + + using ExternalTxBusyFn = bool (*)(void *ctx); + using ExternalTxStartFn = bool (*)(void *ctx, IR_Encoder *enc, const uint8_t *packet, uint8_t len); private: // uint16_t id; /// @brief Адрес передатчика public: @@ -31,10 +39,21 @@ public: IR_Encoder(uint8_t pin, uint16_t addr = 0, IR_DecoderRaw *decPair = nullptr, bool autoHandle = true); static void isr(); static void begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, uint8_t priority, void(*isrCallback)() = nullptr); + /** Configure timer frequency for TX clock (carrierFrec*2) without attaching ISR. */ + static void beginClockOnly(HardwareTimer *timer); static HardwareTimer* get_IR_Timer(); /** Call from main loop/tick: if ISR requested carrier stop, pause timer here (not in ISR). */ static void tick(); + /** Optional: register external TX backend (e.g. DMA driver). */ + static void setExternalTxBackend(ExternalTxStartFn startFn, ExternalTxBusyFn busyFn, void *ctx); + + /** Called by external TX backend on actual end of transmission. */ + void externalFinishSend(); + + /** Build RLE runs of carrier gate for a packet (no HW access). */ + static size_t buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns); + void enable(); void disable(); @@ -74,6 +93,10 @@ private: static volatile bool carrierStopPending; static void carrierResume(); static void carrierPauseIfIdle(); + + static ExternalTxStartFn externalTxStartFn; + static ExternalTxBusyFn externalTxBusyFn; + static void *externalTxCtx; IR_SendResult _sendBack(bool isAdressed, uint16_t addrTo, uint8_t *data, uint8_t len); void setDecoder_isSending(); @@ -123,4 +146,3 @@ private: uint8_t *currentBitSequence = bitLow; volatile SignalPart signal; }; - diff --git a/IR_config.h b/IR_config.h index 4149f4a..48ca2aa 100644 --- a/IR_config.h +++ b/IR_config.h @@ -201,6 +201,8 @@ public: static void checkAddressRuleApply(uint16_t address, uint16_t id, bool &flag); void setPin(uint8_t pin); inline uint8_t getPin() { return pin; }; + inline GPIO_TypeDef *getPort() const { return port; } + inline uint16_t getPinMask() const { return mask; } protected: uint16_t id;