mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-03-13 04:44:44 +00:00
184
IR_Encoder.cpp
184
IR_Encoder.cpp
@ -1,5 +1,6 @@
|
|||||||
#include "IR_Encoder.h"
|
#include "IR_Encoder.h"
|
||||||
#include "IR_DecoderRaw.h"
|
#include "IR_DecoderRaw.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define LoopOut 12
|
#define LoopOut 12
|
||||||
#define ISR_Out 10
|
#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;
|
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;}
|
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();
|
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()
|
void IR_Encoder::enable()
|
||||||
{
|
{
|
||||||
@ -360,6 +522,27 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
{
|
{
|
||||||
return;
|
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();
|
IR_Encoder::carrierResume();
|
||||||
// Serial.println("START");
|
// Serial.println("START");
|
||||||
setDecoder_isSending();
|
setDecoder_isSending();
|
||||||
@ -380,7 +563,6 @@ void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|||||||
state = HIGH;
|
state = HIGH;
|
||||||
|
|
||||||
currentBitSequence = bitHigh;
|
currentBitSequence = bitHigh;
|
||||||
isSending = true;
|
|
||||||
// interrupts();
|
// interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
IR_Encoder.h
24
IR_Encoder.h
@ -21,6 +21,14 @@ class IR_Encoder : public IR_FOX
|
|||||||
IR_Encoder *next;
|
IR_Encoder *next;
|
||||||
public:
|
public:
|
||||||
static HardwareTimer* IR_Timer;
|
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:
|
private:
|
||||||
// uint16_t id; /// @brief Адрес передатчика
|
// uint16_t id; /// @brief Адрес передатчика
|
||||||
public:
|
public:
|
||||||
@ -31,10 +39,21 @@ public:
|
|||||||
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);
|
||||||
|
/** Configure timer frequency for TX clock (carrierFrec*2) without attaching ISR. */
|
||||||
|
static void beginClockOnly(HardwareTimer *timer);
|
||||||
static HardwareTimer* get_IR_Timer();
|
static HardwareTimer* get_IR_Timer();
|
||||||
/** Call from main loop/tick: if ISR requested carrier stop, pause timer here (not in ISR). */
|
/** Call from main loop/tick: if ISR requested carrier stop, pause timer here (not in ISR). */
|
||||||
static void tick();
|
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 enable();
|
||||||
void disable();
|
void disable();
|
||||||
|
|
||||||
@ -74,6 +93,10 @@ private:
|
|||||||
static volatile bool carrierStopPending;
|
static volatile bool carrierStopPending;
|
||||||
static void carrierResume();
|
static void carrierResume();
|
||||||
static void carrierPauseIfIdle();
|
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);
|
IR_SendResult _sendBack(bool isAdressed, uint16_t addrTo, uint8_t *data, uint8_t len);
|
||||||
|
|
||||||
void setDecoder_isSending();
|
void setDecoder_isSending();
|
||||||
@ -123,4 +146,3 @@ private:
|
|||||||
uint8_t *currentBitSequence = bitLow;
|
uint8_t *currentBitSequence = bitLow;
|
||||||
volatile SignalPart signal;
|
volatile SignalPart signal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -201,6 +201,8 @@ public:
|
|||||||
static void checkAddressRuleApply(uint16_t address, uint16_t id, bool &flag);
|
static void checkAddressRuleApply(uint16_t address, uint16_t id, bool &flag);
|
||||||
void setPin(uint8_t pin);
|
void setPin(uint8_t pin);
|
||||||
inline uint8_t getPin() { return pin; };
|
inline uint8_t getPin() { return pin; };
|
||||||
|
inline GPIO_TypeDef *getPort() const { return port; }
|
||||||
|
inline uint16_t getPinMask() const { return mask; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
|
|||||||
Reference in New Issue
Block a user