Files
IR-protocol/IR_Encoder.cpp

616 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "IR_Encoder.h"
#include "IR_DecoderRaw.h"
#define LoopOut 12
#define ISR_Out 10
#define TestOut 13
IR_Encoder *IR_Encoder::head = nullptr;
IR_Encoder *IR_Encoder::last = nullptr;
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;
inline HardwareTimer* IR_Encoder::get_IR_Timer(){return IR_Encoder::IR_Timer;}
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->setOverflow(carrierFrec * 2, HERTZ_FORMAT);
IR_Timer->attachInterrupt(channel, (isrCallback == nullptr ? IR_Encoder::isr : isrCallback));
NVIC_SetPriority(IRQn, priority);
IR_Timer->resume();
}
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 >> 1));
// формирование массива
// 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.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;
isSending = true;
// 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);
if (toggleCounter)
{
toggleCounter--;
}
else
{
IsrStart:
switch (signal)
{
case noSignal:
signal = preamb;
// сброс счетчиков
// ...
isSending = false;
// Serial.println("STOP");
setDecoder_isSending();
// Serial.println();
return;
break;
case preamb:
if (preambFrontCounter)
{
preambFrontCounter--;
toggleCounter = preambToggle; // Вторая и последующие генерации для этого signal
}
else
{ // Конец преамбулы, переход на следующий signal
signal = data;
state = !LOW; // Инверсное состояние первой генерации следующего signal
goto IsrStart; // Применение новых параметров в этй же итерации прерывания
}
break;
case data:
if (dataSequenceCounter)
{
if (!(dataSequenceCounter & 1U))
{ // если чётный - смена бита
currentBitSequence = ((sendBuffer[dataByteCounter] >> dataBitCounter) & 1U) ? bitHigh : bitLow; // определение текущего бита
dataBitCounter--;
}
toggleCounter = currentBitSequence[!state];
dataSequenceCounter--;
}
else
{ // Конец data, переход на следующий signal
syncLastBit = ((sendBuffer[dataByteCounter]) & 1U);
dataByteCounter++;
dataBitCounter = bitPerByte - 1;
dataSequenceCounter = bitPerByte * 2;
signal = sync;
goto IsrStart; // Применение новых параметров в этй же итерации прерывания
}
break;
case sync:
if (syncSequenceCounter)
{
if (!(syncSequenceCounter & 1U))
{ // если чётный - смена бита
if (syncSequenceCounter == 2)
{ // Если последний бит
currentBitSequence = ((sendBuffer[dataByteCounter]) & 0b10000000) ? bitLow : bitHigh;
}
else
{
currentBitSequence = syncLastBit ? bitLow : bitHigh; // определение текущего бита
syncLastBit = !syncLastBit;
}
}
toggleCounter = currentBitSequence[!state];
syncSequenceCounter--;
}
else
{ // Конец sync, переход на следующий signal
signal = data;
syncSequenceCounter = syncBits * 2;
if (dataByteCounter >= sendLen)
{ // определение конца данных
signal = noSignal;
}
goto IsrStart; // Применение новых параметров в этй же итерации прерывания
}
break;
default:
return;
break;
}
state = !state;
}
}
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};