mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
794 lines
22 KiB
C++
794 lines
22 KiB
C++
#include "IR_Encoder.h"
|
|
#include "IR_DecoderRaw.h"
|
|
#include <string.h>
|
|
|
|
#define LoopOut 12
|
|
#define ISR_Out 10
|
|
#define TestOut 13
|
|
|
|
IR_Encoder *IR_Encoder::head = nullptr;
|
|
IR_Encoder *IR_Encoder::last = nullptr;
|
|
volatile bool IR_Encoder::carrierStopPending = false;
|
|
|
|
IR_Encoder::IR_Encoder(uint8_t pin, uint16_t addr, IR_DecoderRaw *decPair, bool autoHandle)
|
|
{
|
|
setPin(pin);
|
|
id = addr;
|
|
this->decPair = decPair;
|
|
signal = noSignal;
|
|
isSending = false;
|
|
#if disablePairDec
|
|
if (decPair != nullptr)
|
|
{
|
|
blindDecoders = new IR_DecoderRaw *[1]{decPair};
|
|
decodersCount = 1;
|
|
}
|
|
#endif
|
|
if (decPair != nullptr)
|
|
{
|
|
decPair->encoder = this;
|
|
}
|
|
|
|
if (autoHandle)
|
|
{
|
|
if (IR_Encoder::head == nullptr)
|
|
{
|
|
IR_Encoder::head = this;
|
|
}
|
|
if (last != nullptr)
|
|
{
|
|
last->next = this;
|
|
}
|
|
last = this;
|
|
|
|
pinMode(pin, OUTPUT);
|
|
}
|
|
};
|
|
|
|
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::txAdvanceBoundary(TxFsmState &st, const uint8_t *sendBufferLocal)
|
|
{
|
|
while (true)
|
|
{
|
|
switch (st.signal)
|
|
{
|
|
case noSignal:
|
|
st.signal = preamb;
|
|
return false;
|
|
|
|
case preamb:
|
|
if (st.preambFrontCounter)
|
|
{
|
|
st.preambFrontCounter--;
|
|
st.toggleCounter = preambToggle;
|
|
st.state = !st.state;
|
|
return true;
|
|
}
|
|
st.signal = data;
|
|
st.state = !LOW;
|
|
continue;
|
|
|
|
case data:
|
|
if (st.dataSequenceCounter)
|
|
{
|
|
if (!(st.dataSequenceCounter & 1U))
|
|
{
|
|
st.currentBitSequence =
|
|
((sendBufferLocal[st.dataByteCounter] >> st.dataBitCounter) & 1U) ? bitHigh : bitLow;
|
|
st.dataBitCounter--;
|
|
}
|
|
st.toggleCounter = st.currentBitSequence[!st.state];
|
|
st.dataSequenceCounter--;
|
|
st.state = !st.state;
|
|
return true;
|
|
}
|
|
st.syncLastBit = ((sendBufferLocal[st.dataByteCounter]) & 1U);
|
|
st.dataByteCounter++;
|
|
st.dataBitCounter = bitPerByte - 1;
|
|
st.dataSequenceCounter = bitPerByte * 2;
|
|
st.signal = sync;
|
|
continue;
|
|
|
|
case sync:
|
|
if (st.syncSequenceCounter)
|
|
{
|
|
if (!(st.syncSequenceCounter & 1U))
|
|
{
|
|
if (st.syncSequenceCounter == 2)
|
|
{
|
|
st.currentBitSequence = ((sendBufferLocal[st.dataByteCounter]) & 0b10000000) ? bitLow : bitHigh;
|
|
}
|
|
else
|
|
{
|
|
st.currentBitSequence = st.syncLastBit ? bitLow : bitHigh;
|
|
st.syncLastBit = !st.syncLastBit;
|
|
}
|
|
}
|
|
st.toggleCounter = st.currentBitSequence[!st.state];
|
|
st.syncSequenceCounter--;
|
|
st.state = !st.state;
|
|
return true;
|
|
}
|
|
st.signal = data;
|
|
st.syncSequenceCounter = syncBits * 2;
|
|
if (st.dataByteCounter >= st.sendLen)
|
|
{
|
|
st.signal = noSignal;
|
|
}
|
|
continue;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IR_Encoder::txAdvanceAfterOutput(TxFsmState &st, const uint8_t *sendBufferLocal)
|
|
{
|
|
if (st.toggleCounter)
|
|
{
|
|
st.toggleCounter--;
|
|
return true;
|
|
}
|
|
return txAdvanceBoundary(st, sendBufferLocal);
|
|
}
|
|
|
|
bool IR_Encoder::txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool &gateOut)
|
|
{
|
|
gateOut = st.state;
|
|
return txAdvanceAfterOutput(st, sendBufferLocal);
|
|
}
|
|
|
|
void IR_Encoder::loadTxFsmFromMembers(TxFsmState &st) const
|
|
{
|
|
st.sendLen = sendLen;
|
|
st.toggleCounter = toggleCounter;
|
|
st.dataBitCounter = dataBitCounter;
|
|
st.dataByteCounter = dataByteCounter;
|
|
st.preambFrontCounter = preambFrontCounter;
|
|
st.dataSequenceCounter = dataSequenceCounter;
|
|
st.syncSequenceCounter = syncSequenceCounter;
|
|
st.syncLastBit = syncLastBit;
|
|
st.state = state;
|
|
st.currentBitSequence = currentBitSequence;
|
|
st.signal = signal;
|
|
}
|
|
|
|
void IR_Encoder::storeTxFsmToMembers(const TxFsmState &st)
|
|
{
|
|
sendLen = st.sendLen;
|
|
toggleCounter = st.toggleCounter;
|
|
dataBitCounter = st.dataBitCounter;
|
|
dataByteCounter = st.dataByteCounter;
|
|
preambFrontCounter = st.preambFrontCounter;
|
|
dataSequenceCounter = st.dataSequenceCounter;
|
|
syncSequenceCounter = st.syncSequenceCounter;
|
|
syncLastBit = st.syncLastBit;
|
|
state = st.state;
|
|
currentBitSequence = st.currentBitSequence;
|
|
signal = st.signal;
|
|
}
|
|
|
|
inline HardwareTimer* IR_Encoder::get_IR_Timer(){return IR_Encoder::IR_Timer;}
|
|
|
|
void IR_Encoder::carrierResume() {
|
|
if (IR_Timer != nullptr)
|
|
IR_Timer->resume();
|
|
}
|
|
|
|
void IR_Encoder::carrierPauseIfIdle() {
|
|
for (IR_Encoder *p = head; p != nullptr; p = p->next)
|
|
if (p->isSending)
|
|
return;
|
|
if (IR_Timer != nullptr)
|
|
IR_Timer->pause();
|
|
}
|
|
|
|
void IR_Encoder::tick() {
|
|
if (!carrierStopPending)
|
|
return;
|
|
carrierStopPending = false;
|
|
carrierPauseIfIdle();
|
|
}
|
|
|
|
void IR_Encoder::begin(HardwareTimer* timer, uint8_t channel, IRQn_Type IRQn, uint8_t priority, void(*isrCallback)()){
|
|
IR_Timer = timer;
|
|
if(IR_Timer == nullptr) return;
|
|
IR_Timer->pause();
|
|
IR_Timer->setOverflow(carrierFrec * 2, HERTZ_FORMAT);
|
|
IR_Timer->attachInterrupt(channel, (isrCallback == nullptr ? IR_Encoder::isr : isrCallback));
|
|
NVIC_SetPriority(IRQn, priority);
|
|
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);
|
|
|
|
TxFsmState st{};
|
|
st.sendLen = len;
|
|
st.toggleCounter = preambToggle;
|
|
st.dataBitCounter = bitPerByte - 1;
|
|
st.dataByteCounter = 0;
|
|
st.preambFrontCounter = preambPulse * 2 - 1;
|
|
st.dataSequenceCounter = bitPerByte * 2;
|
|
st.syncSequenceCounter = syncBits * 2;
|
|
st.syncLastBit = false;
|
|
st.signal = preamb;
|
|
st.state = HIGH;
|
|
st.currentBitSequence = bitHigh;
|
|
|
|
size_t runCount = 0;
|
|
bool isActive = true;
|
|
while (isActive)
|
|
{
|
|
bool gate = false;
|
|
isActive = txEmitTick(st, sendBufferLocal, gate);
|
|
|
|
if (runCount > 0 && outRuns[runCount - 1].gate == gate)
|
|
{
|
|
outRuns[runCount - 1].lenTicks = (uint16_t)(outRuns[runCount - 1].lenTicks + 1U);
|
|
}
|
|
else
|
|
{
|
|
if (runCount >= maxRuns)
|
|
{
|
|
return 0;
|
|
}
|
|
outRuns[runCount].gate = gate;
|
|
outRuns[runCount].lenTicks = 1U;
|
|
runCount++;
|
|
}
|
|
}
|
|
return runCount;
|
|
}
|
|
|
|
|
|
void IR_Encoder::enable()
|
|
{
|
|
bool exist = false;
|
|
IR_Encoder *current = IR_Encoder::head;
|
|
while (current != nullptr)
|
|
{
|
|
exist = (current == this);
|
|
if (exist) break;
|
|
current = current->next;
|
|
}
|
|
if (!exist)
|
|
{
|
|
if (IR_Encoder::head == nullptr)
|
|
{
|
|
IR_Encoder::head = this;
|
|
last = this;
|
|
}
|
|
else
|
|
{
|
|
last->next = this;
|
|
last = this;
|
|
}
|
|
this->next = nullptr; // Указываем, что следующий за этим элементом — nullptr
|
|
}
|
|
pinMode(pin, OUTPUT);
|
|
}
|
|
|
|
void IR_Encoder::disable()
|
|
{
|
|
IR_Encoder *current = IR_Encoder::head;
|
|
IR_Encoder *prev = nullptr;
|
|
|
|
while (current != nullptr)
|
|
{
|
|
if (current == this) break;
|
|
prev = current;
|
|
current = current->next;
|
|
}
|
|
|
|
if (current != nullptr) // Элемент найден в списке
|
|
{
|
|
if (prev != nullptr)
|
|
{
|
|
prev->next = current->next; // Убираем текущий элемент из списка
|
|
}
|
|
else
|
|
{
|
|
IR_Encoder::head = current->next; // Удаляемый элемент был первым
|
|
}
|
|
|
|
if (current == last)
|
|
{
|
|
last = prev; // Если удаляется последний элемент, обновляем last
|
|
}
|
|
}
|
|
|
|
pinMode(pin, INPUT);
|
|
}
|
|
|
|
void IR_Encoder::setBlindDecoders(IR_DecoderRaw *decoders[], uint8_t count)
|
|
{
|
|
#if disablePairDec
|
|
if (blindDecoders != nullptr)
|
|
delete[] blindDecoders;
|
|
#endif
|
|
decodersCount = count;
|
|
blindDecoders = decoders;
|
|
}
|
|
|
|
IR_Encoder::~IR_Encoder(){};
|
|
|
|
IR_SendResult IR_Encoder::sendData(uint16_t addrTo, uint8_t dataByte, bool needAccept)
|
|
{
|
|
return sendData(addrTo, &dataByte, 1, needAccept);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::sendData(uint16_t addrTo, uint8_t *data, uint8_t len, bool needAccept){
|
|
return sendDataFULL(id, addrTo, data, len, needAccept);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::sendDataFULL(uint16_t addrFrom, uint16_t addrTo, uint8_t *data, uint8_t len, bool needAccept)
|
|
{
|
|
if (len > bytePerPack)
|
|
{
|
|
Serial.println("IR Pack to big");
|
|
return IR_SendResult(false, 0);
|
|
}
|
|
constexpr uint8_t dataStart = msgBytes + addrBytes + addrBytes;
|
|
memset(sendBuffer, 0x00, dataByteSizeMax);
|
|
uint8_t packSize = msgBytes + addrBytes + addrBytes + len + crcBytes;
|
|
uint8_t msgType =
|
|
((needAccept ? IR_MSG_DATA_ACCEPT : IR_MSG_DATA_NOACCEPT) << 5) | (packSize & IR_MASK_MSG_INFO);
|
|
|
|
// формирование массива
|
|
// msg_type
|
|
sendBuffer[0] = msgType;
|
|
|
|
// addr_self
|
|
sendBuffer[1] = addrFrom >> 8 & 0xFF;
|
|
sendBuffer[2] = addrFrom & 0xFF;
|
|
|
|
// addr_to
|
|
sendBuffer[3] = addrTo >> 8 & 0xFF;
|
|
sendBuffer[4] = addrTo & 0xFF;
|
|
|
|
for (uint16_t i = dataStart; (i < dataStart + len) && (data != nullptr); i++)
|
|
{
|
|
sendBuffer[i] = ((uint8_t *)data)[i - dataStart];
|
|
}
|
|
|
|
// data crc
|
|
sendBuffer[packSize - crcBytes] = crc8(sendBuffer, 0, packSize - crcBytes, poly1) & 0xFF;
|
|
sendBuffer[packSize - crcBytes + 1] = crc8(sendBuffer, 0, packSize - crcBytes + 1, poly2) & 0xFF;
|
|
|
|
//* вывод итогового буфера
|
|
// Serial.print("IR SEND [len=");
|
|
// Serial.print(packSize);
|
|
// Serial.print("] : ");
|
|
// for (uint8_t i = 0; i < packSize; i++)
|
|
// {
|
|
// if (sendBuffer[i] < 0x10)
|
|
// Serial.print('0');
|
|
// Serial.print(sendBuffer[i], HEX);
|
|
// Serial.print(' ');
|
|
// }
|
|
// Serial.println();
|
|
|
|
// if (decPair != nullptr) {
|
|
// decPair->isWaitingAccept = ((msgType >> 5) & IR_MASK_MSG_TYPE == IR_MSG_DATA_ACCEPT);
|
|
// if (decPair->isWaitingAccept) {
|
|
// decPair->addrWaitingFrom = addrTo;
|
|
// }
|
|
// }
|
|
|
|
// отправка
|
|
rawSend(sendBuffer, packSize);
|
|
|
|
// Возвращаем результат отправки
|
|
uint32_t sendTime = calculateSendTime(packSize);
|
|
return IR_SendResult(true, sendTime);
|
|
}
|
|
|
|
|
|
IR_SendResult IR_Encoder::sendAccept(uint16_t addrTo, uint8_t customByte)
|
|
{
|
|
constexpr uint8_t packsize = msgBytes + addrBytes + 1U + crcBytes;
|
|
memset(sendBuffer, 0x00, dataByteSizeMax);
|
|
sendBuffer[0] = IR_MSG_ACCEPT << 5;
|
|
sendBuffer[0] |= packsize & IR_MASK_MSG_INFO; // размер пакета
|
|
|
|
// addr_self
|
|
sendBuffer[1] = id >> 8 & 0xFF;
|
|
sendBuffer[2] = id & 0xFF;
|
|
|
|
// Serial.print("\nRAW Accept to ");
|
|
// Serial.println(addrTo);
|
|
|
|
sendBuffer[3] = customByte;
|
|
|
|
// data crc
|
|
sendBuffer[4] = crc8(sendBuffer, 0, 4, poly1) & 0xFF;
|
|
sendBuffer[5] = crc8(sendBuffer, 0, 5, poly2) & 0xFF;
|
|
|
|
rawSend(sendBuffer, packsize);
|
|
|
|
// Возвращаем результат отправки
|
|
uint32_t sendTime = calculateSendTime(packsize);
|
|
return IR_SendResult(true, sendTime);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::sendRequest(uint16_t addrTo)
|
|
{
|
|
constexpr uint8_t packsize = msgBytes + addrBytes + addrBytes + crcBytes;
|
|
memset(sendBuffer, 0x00, dataByteSizeMax);
|
|
sendBuffer[0] = IR_MSG_REQUEST << 5;
|
|
sendBuffer[0] |= packsize & IR_MASK_MSG_INFO;
|
|
|
|
// addr_self
|
|
sendBuffer[1] = id >> 8 & 0xFF;
|
|
sendBuffer[2] = id & 0xFF;
|
|
|
|
// addr_to
|
|
sendBuffer[3] = addrTo >> 8 & 0xFF;
|
|
sendBuffer[4] = addrTo & 0xFF;
|
|
|
|
// data crc
|
|
sendBuffer[5] = crc8(sendBuffer, 0, 5, poly1) & 0xFF;
|
|
sendBuffer[6] = crc8(sendBuffer, 0, 6, poly2) & 0xFF;
|
|
|
|
rawSend(sendBuffer, packsize);
|
|
|
|
// Возвращаем результат отправки
|
|
uint32_t sendTime = calculateSendTime(packsize);
|
|
return IR_SendResult(true, sendTime);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::sendBack(uint8_t data)
|
|
{
|
|
return _sendBack(false, 0, &data, 1);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::sendBack(uint8_t *data, uint8_t len)
|
|
{
|
|
return _sendBack(false, 0, data, len);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::sendBackTo(uint16_t addrTo, uint8_t *data, uint8_t len)
|
|
{
|
|
return _sendBack(true, addrTo, data, len);
|
|
}
|
|
|
|
IR_SendResult IR_Encoder::_sendBack(bool isAdressed, uint16_t addrTo, uint8_t *data, uint8_t len)
|
|
{
|
|
if (len > bytePerPack)
|
|
{
|
|
return IR_SendResult(false, 0);
|
|
}
|
|
memset(sendBuffer, 0x00, dataByteSizeMax);
|
|
uint8_t dataStart = msgBytes + addrBytes + (isAdressed ? addrBytes : 0);
|
|
|
|
uint8_t packSize = msgBytes + addrBytes + (isAdressed ? addrBytes : 0) + min(uint8_t(1), len) + crcBytes;
|
|
uint8_t msgType =
|
|
((isAdressed ? IR_MSG_BACK_TO : IR_MSG_BACK) << 5) | ((packSize) & IR_MASK_MSG_INFO);
|
|
|
|
// формирование массива
|
|
// msg_type
|
|
sendBuffer[0] = msgType;
|
|
|
|
// addr_from or data
|
|
sendBuffer[1] = id >> 8 & 0xFF;
|
|
sendBuffer[2] = id & 0xFF;
|
|
|
|
// addr_to
|
|
sendBuffer[3] = addrTo >> 8 & 0xFF;
|
|
sendBuffer[4] = addrTo & 0xFF;
|
|
|
|
for (uint16_t i = dataStart; i < dataStart + len; i++)
|
|
{
|
|
sendBuffer[i] = ((uint8_t *)data)[i - dataStart];
|
|
}
|
|
|
|
// data crc
|
|
sendBuffer[packSize - crcBytes] = crc8(sendBuffer, 0, packSize - crcBytes, poly1) & 0xFF;
|
|
sendBuffer[packSize - crcBytes + 1] = crc8(sendBuffer, 0, packSize - crcBytes + 1, poly2) & 0xFF;
|
|
|
|
// отправка
|
|
rawSend(sendBuffer, packSize);
|
|
|
|
// Возвращаем результат отправки
|
|
uint32_t sendTime = calculateSendTime(packSize);
|
|
return IR_SendResult(true, sendTime);
|
|
}
|
|
|
|
void IR_Encoder::setDecoder_isSending()
|
|
{
|
|
if (decodersCount)
|
|
{
|
|
for (uint8_t i = 0; i < decodersCount; i++)
|
|
{
|
|
blindDecoders[i]->isPairSending ^= id;
|
|
// Serial.print("setDecoder_isSending() id = ");
|
|
// Serial.print(id);
|
|
// Serial.print(" isPairSending = ");
|
|
// Serial.println(blindDecoders[i]->isPairSending);
|
|
}
|
|
}
|
|
}
|
|
|
|
void IR_Encoder::rawSend(uint8_t *ptr, uint8_t len)
|
|
{
|
|
if (isSending)
|
|
{
|
|
// TODO: Обработка повторной отправки
|
|
return;
|
|
}
|
|
|
|
// Проверка на переполнение буфера
|
|
if (len > dataByteSizeMax)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Serial.print("IR tx hex: ");
|
|
for (uint8_t i = 0; i < len; i++)
|
|
{
|
|
if (ptr[i] < 0x10) Serial.print("0");
|
|
Serial.print(ptr[i], HEX);
|
|
}
|
|
Serial.println();
|
|
|
|
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();
|
|
|
|
// noInterrupts();
|
|
sendLen = len;
|
|
toggleCounter = preambToggle; // Первая генерация для первого signal
|
|
|
|
dataBitCounter = bitPerByte - 1;
|
|
dataByteCounter = 0;
|
|
|
|
preambFrontCounter = preambPulse * 2 - 1; // -1 за счёт генерации уже на этапе сразу после инициализации
|
|
dataSequenceCounter = bitPerByte * 2;
|
|
syncSequenceCounter = syncBits * 2;
|
|
|
|
signal = preamb;
|
|
isSending = true;
|
|
state = HIGH;
|
|
|
|
currentBitSequence = bitHigh;
|
|
// interrupts();
|
|
}
|
|
|
|
void IR_Encoder::isr()
|
|
{
|
|
IR_Encoder *current = IR_Encoder::head;
|
|
while (current != nullptr)
|
|
{
|
|
current->_isr();
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
void IR_Encoder::_isr()
|
|
{
|
|
if (!isSending)
|
|
return;
|
|
|
|
ir_out_virtual = !ir_out_virtual && state;
|
|
|
|
port->ODR &= ~(mask);
|
|
port->ODR |= mask & (ir_out_virtual ? (uint16_t)0xFFFF : (uint16_t)0x0000);
|
|
|
|
TxFsmState st{};
|
|
loadTxFsmFromMembers(st);
|
|
const bool active = txAdvanceAfterOutput(st, sendBuffer);
|
|
storeTxFsmToMembers(st);
|
|
|
|
if (!active)
|
|
{
|
|
isSending = false;
|
|
setDecoder_isSending();
|
|
carrierStopPending = true;
|
|
}
|
|
}
|
|
|
|
void IR_Encoder::sendByte(uint8_t byte, bool *prev, bool LOW_FIRST)
|
|
{
|
|
uint8_t mask = LOW_FIRST ? 0b00000001 : 0b10000000;
|
|
for (uint8_t bitShift = 8; bitShift; bitShift--)
|
|
{
|
|
// digitalWrite(9, HIGH);
|
|
// digitalWrite(9, LOW);
|
|
byte &mask ? send_HIGH(prev) : send_LOW();
|
|
*prev = byte & mask;
|
|
LOW_FIRST ? mask <<= 1 : mask >>= 1;
|
|
// digitalWrite(9, HIGH);
|
|
// digitalWrite(9, LOW);
|
|
}
|
|
}
|
|
|
|
void IR_Encoder::addSync(bool *prev, bool *next)
|
|
{
|
|
switch (syncBits)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
*prev ? send_LOW() : send_HIGH();
|
|
*prev = !*prev;
|
|
break;
|
|
default:
|
|
for (int16_t i = 0; i < syncBits - 1; i++)
|
|
{
|
|
*prev ? send_LOW() : send_HIGH();
|
|
*prev = !*prev;
|
|
}
|
|
*next ? send_LOW() : send_HIGH(0);
|
|
*prev = !*next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t IR_Encoder::bitHigh[2] = {
|
|
(bitPauseTakts) * 2 - 1,
|
|
(bitActiveTakts) * 2 - 1};
|
|
uint8_t IR_Encoder::bitLow[2] = {
|
|
(bitPauseTakts / 2 + bitActiveTakts) * 2 - 1,
|
|
(bitPauseTakts)-1};
|
|
|
|
uint32_t IR_Encoder::calculateSendTime(uint8_t packSize) const
|
|
{
|
|
// Расчет времени отправки пакета в миллисекундах
|
|
|
|
// Время преамбулы: preambPulse * 2 фронта * bitTakts тактов
|
|
uint32_t preambTime = preambPulse * 2 * bitTakts;
|
|
|
|
// Время данных: количество бит * bitTakts тактов
|
|
uint32_t dataTime = packSize * 8 * bitTakts;
|
|
|
|
// Время синхронизации: syncBits * 2 фронта * bitTakts тактов
|
|
uint32_t syncTime = syncBits * 2 * bitTakts;
|
|
|
|
// Общее время в тактах
|
|
uint32_t totalTakts = preambTime + dataTime + syncTime;
|
|
|
|
// Конвертируем в миллисекунды
|
|
// carrierPeriod - период несущей в микросекундах
|
|
// totalTakts * carrierPeriod / 1000 = время в миллисекундах
|
|
uint32_t sendTimeMs = (totalTakts * carrierPeriod) / 1000;
|
|
|
|
return sendTimeMs;
|
|
}
|
|
|
|
// Функции для тестирования времени отправки без фактической отправки
|
|
|
|
uint32_t IR_Encoder::testSendTime(uint16_t addrTo, uint8_t dataByte, bool needAccept) const
|
|
{
|
|
return testSendTime(addrTo, &dataByte, 1, needAccept);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendTime(uint16_t addrTo, uint8_t *data, uint8_t len, bool needAccept) const
|
|
{
|
|
return testSendTimeFULL(id, addrTo, data, len, needAccept);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendTimeFULL(uint16_t addrFrom, uint16_t addrTo, uint8_t *data, uint8_t len, bool needAccept) const
|
|
{
|
|
if (len > bytePerPack)
|
|
{
|
|
return 0; // Возвращаем 0 для недопустимого размера
|
|
}
|
|
|
|
uint8_t packSize = msgBytes + addrBytes + addrBytes + len + crcBytes;
|
|
return calculateSendTime(packSize);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendAccept(uint16_t addrTo, uint8_t customByte) const
|
|
{
|
|
constexpr uint8_t packsize = msgBytes + addrBytes + 1U + crcBytes;
|
|
return calculateSendTime(packsize);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendRequest(uint16_t addrTo) const
|
|
{
|
|
constexpr uint8_t packsize = msgBytes + addrBytes + addrBytes + crcBytes;
|
|
return calculateSendTime(packsize);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendBack(uint8_t data) const
|
|
{
|
|
return testSendBack(false, 0, &data, 1);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendBack(uint8_t *data, uint8_t len) const
|
|
{
|
|
return testSendBack(false, 0, data, len);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendBackTo(uint16_t addrTo, uint8_t *data, uint8_t len) const
|
|
{
|
|
return testSendBack(true, addrTo, data, len);
|
|
}
|
|
|
|
uint32_t IR_Encoder::testSendBack(bool isAdressed, uint16_t addrTo, uint8_t *data, uint8_t len) const
|
|
{
|
|
if (len > bytePerPack)
|
|
{
|
|
return 0; // Возвращаем 0 для недопустимого размера
|
|
}
|
|
|
|
uint8_t packSize = msgBytes + addrBytes + (isAdressed ? addrBytes : 0) + min(uint8_t(1), len) + crcBytes;
|
|
return calculateSendTime(packSize);
|
|
}
|
|
|
|
// uint8_t* IR_Encoder::bitHigh = new uint8_t[2]{
|
|
// (bitPauseTakts) * 2 - 0,
|
|
// (bitActiveTakts) * 2 - 0};
|
|
// uint8_t* IR_Encoder::bitLow = new uint8_t[2]{
|
|
// (bitPauseTakts/2 + bitActiveTakts) * 2 - 0,
|
|
// (bitPauseTakts) - 0};
|