IR-protocol/IR_Decoder.cpp
2024-01-25 15:46:11 +03:00

619 lines
23 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_Decoder.h"
#include "IR_Encoder.h"
#define checkAddr(h, l) (\
((uint16_t)((dataBuffer[h] << 8) | dataBuffer[l]) == addrSelf) || \
((uint16_t)((dataBuffer[h] << 8) | dataBuffer[l]) >= IR_Broadcast)\
)
IR_Decoder::IR_Decoder(const uint8_t isrPin, uint16_t addr, IR_Encoder* encPair = nullptr) : isrPin(isrPin), addrSelf(addr), encoder(encPair) {
// rawBuffer = new uint8_t[bufferRawSize] { 0 };
dataBuffer = new uint8_t[bufferDataSize] { 0 };
prevRise = prevFall = prevPrevFall = prevPrevRise = 0;
start_RX();
}
IR_Decoder::~IR_Decoder() {
delete dataBuffer;
}
void IR_Decoder::writeToBuffer(bool bit) {
if (i_dataBuffer >= bufferDataSize * 8 - 1) {// проверка переполнения
//TODO: Буффер переполнен!
isBufferOverflow = true;
}
if (isBufferOverflow || isPreamb || isWrongPack) return;
// Переключение флага, data или syncBit
if (bufBitPos == nextControlBit) {
nextControlBit += (isData ? syncBits : bitPerByte); // маркер следующего переключения
isData = !isData;
i_syncBit = 0; // сброс счетчика битов синхронизации
err_syncBit = 0; // сброс счетчика ошибок синхронизации
Serial.print(" ");
}
if (isData) { // Запись битов в dataBuffer
Serial.print(bit);
if (i_dataBuffer % 8 == 7) {
// Serial.print("+");
}
dataBuffer[(i_dataBuffer / 8)] |= bit << (7 - i_dataBuffer % 8);
i_dataBuffer++;
bufBitPos++;
} else {
//********************************* Проверка контрольных sync битов*******************************//
////////////////////// Исправление лишнего нуля ///////////////////////
if (i_syncBit == 0) { // Первый бит синхронизации
// Serial.print("~");
if (bit != (dataBuffer[((i_dataBuffer - 1) / 8)] >> (7 - (i_dataBuffer - 1) % 8) & 1)) {
bufBitPos++;
i_syncBit++;
} else {
i_syncBit = 0;
errorCounter++;
// Serial.print("E");
err_syncBit++;
// Serial.print("bit: "); Serial.println(bit);
// Serial.print("dataBuffer: "); Serial.println(dataBuffer[((i_dataBuffer - 1) / 8)] & 1 << (7 - ((i_dataBuffer - 1) & ~(~0 << 3))));
}
} else { // Последующие биты синхронизации
// Serial.print("`");
bufBitPos++;
i_syncBit++;
}
////////////////////// Проверка наличия битов синхранизации //////////////////////
isWrongPack = err_syncBit >= syncBits;
if (isWrongPack) Serial.print("****************");
}//**************************************************************************************************//
// Serial.print(bit);
#ifdef IRDEBUG
bit ? infoPulse(writeOp, 2) : infoPulse(writeOp, 1);
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//const auto testval = bufferBitSizeMax;
if ((i_dataBuffer >= (8 * msgBytes)) && !isMsgAvaliable) {
switch ((dataBuffer[0] >> 5) & IR_MASK_MSG_TYPE) {
case IR_MSG_ACCEPT:
if (i_dataBuffer >= ((msgBytes + addrBytes + crcBytes) * bitPerByte)) {
const uint8_t dataSize = msgBytes + addrBytes;
isRawAvaliable = true;
isMsgAvaliable = crcCheck(dataSize);
if (isMsgAvaliable && checkAddr(1, 2)) {
gotAccept._set(dataBuffer, msgBytes + addrBytes + crcBytes, crcValue, errorCounter, riseSyncTime);
gotAccept._isAvaliable = true;
}
}
break;
case IR_MSG_REQUEST:
if (i_dataBuffer >= ((msgBytes + addrBytes + addrBytes + crcBytes) * bitPerByte)) {
const uint8_t dataSize = msgBytes + addrBytes + addrBytes;
isRawAvaliable = true;
isMsgAvaliable = (crcCheck(dataSize));
if (isMsgAvaliable && checkAddr(3, 4)) {
gotRequest._isAvaliable = true;
gotRequest._set(dataBuffer, msgBytes + addrBytes + addrBytes + crcBytes, crcValue, errorCounter, riseSyncTime);
}
}
break;
case IR_MSG_DATA_ACCEPT:
case IR_MSG_DATA_NOACCEPT:
if (i_dataBuffer >= ((dataBuffer[0] & IR_MASK_MSG_INFO) + crcBytes) * bitPerByte) {
// Serial.println("OK");
const uint8_t dataSize = (dataBuffer[0] & IR_MASK_MSG_INFO);
isRawAvaliable = true;
isMsgAvaliable = crcCheck(dataSize);
if (isMsgAvaliable && checkAddr(3, 4)) {
gotData._isAvaliable = true;
gotData._set(dataBuffer, (dataSize)+crcBytes, crcValue, errorCounter, riseSyncTime);
} else {
gotRawData._isAvaliable = true;
gotRawData._set(dataBuffer, (dataSize)+crcBytes, crcValue, errorCounter, riseSyncTime);
}
}
break;
default:
break;
}
}/**/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
// uint8_t* IR_Decoder::getDataBuffer(bool reset = false) {
// if (!isRawAvaliable) { return nullptr; }
// if (dataBuffer != nullptr) { delete dataBuffer; dataBuffer = nullptr; } // устранение утечки памяти
// dataBuffer = new uint8_t[dataByteSizeMax] { 0 }; // Буффер по максимуму
// bool isData = true;
// bool controlCheckFirst = true;
// bool controlCheck;
// uint8_t nextControlBit = bitPerByte;
// uint16_t i_dataBuffer = 0;
// for (uint16_t i = 0; i < dataBitSize; i++) {
// if (i == nextControlBit) {
// controlCheckFirst = true;
// nextControlBit += (isData ? syncBits : bitPerByte);
// isData = !isData;
// }
// if (isData) {
// dataBuffer[i_dataBuffer / 8] |= (rawBuffer[(i / 8)] >> (7 - (i % 8)) & 1) << 7 - (i_dataBuffer % 8);
// i_dataBuffer++;
// } else { // Проверка контрольных sync битов
// if (controlCheckFirst) {
// controlCheck = (rawBuffer[(i / 8)] >> (7 - (i % 8)) & 1);
// controlCheckFirst = false;
// } else {
// controlCheck |= (rawBuffer[(i / 8)] >> (7 - (i % 8)) & 1);
// }
// }
// }
// isFilterBufferAvaliable = controlCheck;
// if (reset) { resetAvaliable(); }
// return dataBuffer;
// }
bool IR_Decoder::crcCheck(uint8_t len) {
bool crcOK = false;
crcValue = 0;
crcValue = (crc8(dataBuffer, 0, len, poly1) << 8) & ~((crc_t)0xFF);
crcValue |= crc8(dataBuffer, 0, len + 1, poly2) & (crc_t)0xFF;
if (
crcValue &&
dataBuffer[len] == (crcValue >> 8) & 0xFF &&
dataBuffer[len + 1] == (crcValue & 0xFF)
) {
crcOK = true;
} else { crcOK = false; }
return crcOK;
}
void IR_Decoder::start_RX() {
// Serial.println();
// Serial.println(printBytes(dataBuffer, dataByteSizeMax-1, BIN));
// Serial.println();
resetAvaliable();
isBufferOverflow = false;
memset(dataBuffer, 0x00, bufferDataSize);
bufBitPos = 0;
isData = true;
i_dataBuffer = 0;
nextControlBit = bitPerByte;
i_syncBit = 0;
isWrongPack = false;
}
void IR_Decoder::resetAvaliable() {
isRawAvaliable = false;
isMsgAvaliable = false;
isFilterBufferAvaliable = false;
}
uint16_t IR_Decoder::ceil_div(uint16_t val, uint16_t divider) {
int ret = val / divider;
if ((val << 4) / divider - (ret << 4) >= 8)
ret++;
return ret;
}
void IR_Decoder::listen() {
if (isRecive && micros() - prevRise > IR_timeout * 2) {
isRecive = false;
}
}
////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// isr ///////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
void IR_Decoder::tick() {
if (firstUnHandledFront == nullptr) return; //Если данных нет - ничего не делаем
FrontStorage currentFront;
//найти следующий необработанный фронт/спад
noInterrupts();
currentFront = *((FrontStorage*)firstUnHandledFront);
interrupts();
////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (currentFront.time - prevRise > IR_timeout) { // первый
// if (!currentFront.dir) { // ↓
errorCounter = 0;
isRecive = true;
isPreamb = true;
// frontCounter = preambFronts - 1U;
// } else { // ↑
riseSyncTime = bitTime /* 1100 */;
start_RX();
frontCounter = preambFronts - 1U;
// }
// Serial.println();
// Serial.print("currentFront.time: "); Serial.println(currentFront.time);
// Serial.print("currentFront.dir: "); Serial.println(currentFront.dir ? "UP" : "Down");
// Serial.print("prevRise: "); Serial.println(prevRise);
// Serial.print("frontCounter: "); Serial.println(frontCounter);
// prevRise = currentFront.time;
}
if (frontCounter > 0) { // в преамбуле
uint32_t risePeriod = currentFront.time - prevRise;
if (currentFront.dir && risePeriod < IR_timeout) { // __/``` ↑ и мы в внутри пакета
if (risePeriod < riseTimeMin << 1) { // fix рваной единицы
frontCounter += 2;
errorCounter++;
} else {
if (freeFrec) { riseSyncTime = (riseSyncTime + risePeriod / 2) / 2; } // tuner
}
} else { /* riseSyncTime = bitTime; */ } // сброс тюнера
frontCounter--;
// Serial.print("frontCounter: "); Serial.println(frontCounter);
} else {
if (isPreamb) {// первый фронт после
gotTune._set(riseSyncTime);
}
isPreamb = false;
}
// определить направление фронта
if (currentFront.dir) { // Если __/``` ↑
uint16_t risePeriod = currentFront.time - prevRise;
uint16_t highTime = currentFront.time - prevFall;
uint16_t lowTime = prevFall - prevRise;
int8_t highCount = 0;
int8_t lowCount = 0;
int8_t allCount = 0;
bool invertErr = false;
if (risePeriod < IR_timeout && !isBufferOverflow && risePeriod > riseTimeMin && !isWrongPack) {
// Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
if (aroundRise(risePeriod)) { // тактирование есть, сигнал хороший - без ошибок(?)
if (highTime > riseTimeMin >> 1) { // 1
#ifdef IRDEBUG
digitalWrite(wrHigh, 1);
#endif
writeToBuffer(HIGH);
} else { // 0
#ifdef IRDEBUG
digitalWrite(wrLow, 1);
#endif
writeToBuffer(LOW);
}
} else { // пропущены такты! сигнал средний // ошибка пропуска
highCount = ceil_div(highTime, riseTime); // предполагаемое колличество HIGH битов
lowCount = ceil_div(lowTime, riseTime); // предполагаемое колличество LOW битов
allCount = ceil_div(risePeriod, riseTime); // предполагаемое колличество всего битов
if (highCount == 0 && highTime > riseTime / 3) { // fix короткой единицы (?)после пропуска нулей(?)
highCount++;
errorCounter++;
#ifdef IRDEBUG
errPulse(errOut, 2);
#endif
}
if (lowCount + highCount > allCount) { // fix ошибочных сдвигов
if (lowCount > highCount) { // Лишние нули
lowCount = allCount - highCount;
#ifdef IRDEBUG
errPulse(errOut, 3);
#endif
} else if (lowCount < highCount) { // Лишние единицы
highCount = allCount - lowCount;
#ifdef IRDEBUG
errPulse(errOut, 4);
#endif
// неизвестный случай Инверсит след бит или соседние
// Очень редко
// TODO: Отловить проверить
} else if (lowCount == highCount) {
invertErr = true;
Serial.print("...");
}
errorCounter += allCount;
}
errorCounter += allCount;
#ifdef IRDEBUG
errPulse(errOut, 1);
#endif
for (int8_t i = 0; i < lowCount && 8 - i; i++) { // отправка LOW битов, если есть
if (i == lowCount - 1 && invertErr) {
invertErr = false;
writeToBuffer(!LOW);
#ifdef IRDEBUG
digitalWrite(wrLow, 1);
#endif
} else {
writeToBuffer(LOW);
#ifdef IRDEBUG
digitalWrite(wrLow, 1);
#endif
}
}
for (int8_t i = 0; i < highCount && 8 - i; i++) { // отправка HIGH битов, если есть
if (i == highCount - 1 && invertErr) {
invertErr = false;
writeToBuffer(!HIGH);
#ifdef IRDEBUG
digitalWrite(wrLow, 1);
#endif
} else {
writeToBuffer(HIGH);
#ifdef IRDEBUG
digitalWrite(wrHigh, 1);
#endif
}
}
}
#ifdef IRDEBUG
digitalWrite(wrHigh, 0);
digitalWrite(wrLow, 0);
#endif
}
if (risePeriod > riseTimeMax / 2 || highCount || lowCount) { // комплексный фикс рваной единицы
prevPrevRise = prevRise;
prevRise = currentFront.time;
} else {
errorCounter++;
#ifdef IRDEBUG
errPulse(errOut, 5);
#endif
}
} else { // Если ```\__ ↓
if (currentFront.time - prevFall > riseTimeMin) {
prevPrevFall = prevFall;
prevFall = currentFront.time;
} else {
#ifdef IRDEBUG
//errPulse(errOut, 5);
#endif
}
}
if (isPreamb && frontCounter <= 0) {
prevRise = currentFront.time + riseTime;
}
#ifdef IRDEBUG
digitalWrite(writeOp, isPreamb);
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////
firstUnHandledFront = firstUnHandledFront->next; //переместить флаг на следующий элемент для обработки (next or nullptr)
}
void IR_Decoder::isr() { // в прерывании вызываем isr()
if (isPairSending) return;
subBuffer[currentSubBufferIndex].next = nullptr;
subBuffer[currentSubBufferIndex].dir = (PIND >> isrPin) & 1;
subBuffer[currentSubBufferIndex].time = micros();
if (firstUnHandledFront == nullptr) {
firstUnHandledFront = &subBuffer[currentSubBufferIndex]; // Если нет необработанных данных - добавляем их
} else {
if (firstUnHandledFront == &subBuffer[currentSubBufferIndex]) { // Если контроллер не успел обработать новый сигнал, принудительно пропускаем его
firstUnHandledFront = firstUnHandledFront->next;
Serial.println();
Serial.println("ERROR");
Serial.println();
}
}
if (lastFront == nullptr) {
lastFront = &subBuffer[currentSubBufferIndex];
} else {
lastFront->next = &subBuffer[currentSubBufferIndex];
lastFront = &subBuffer[currentSubBufferIndex];
}
currentSubBufferIndex == (subBufferSize - 1) ? currentSubBufferIndex = 0 : currentSubBufferIndex++; // Закольцовка буффера
}
void IR_Decoder::noFunc() {
////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (micros() - prevRise > IR_timeout && (PIND >> isrPin) & 1) { // первый
isRecive = true;
isPreamb = true;
frontCounter = preambFronts - 1U;
errorCounter = 0;
riseSyncTime = bitTime /* 1100 */;
start_RX();
Serial.println("First!");
}
if (frontCounter > 0) { // в преамбуле
uint32_t risePeriod = micros() - prevRise;
if ((PIND >> isrPin) & 1 && risePeriod < IR_timeout) { // __/``` ↑ и мы в внутри пакета
if (risePeriod < riseTimeMin << 1) { // fix рваной единицы
frontCounter += 2;
errorCounter++;
} else {
if (freeFrec) { riseSyncTime = (riseSyncTime + risePeriod / 2) / 2; } // tuner
}
} else { /* riseSyncTime = bitTime; */ } // сброс тюнера
frontCounter--;
//Serial.println(frontCounter);
} else {
if (isPreamb) {// первый фронт после
gotTune._set(riseSyncTime);
}
isPreamb = false;
}
// определить направление фронта
if ((PIND >> isrPin) & 1) { // Если __/``` ↑
uint16_t risePeriod = micros() - prevRise;
uint16_t highTime = micros() - prevFall;
uint16_t lowTime = prevFall - prevRise;
int8_t highCount = 0;
int8_t lowCount = 0;
int8_t allCount = 0;
if (risePeriod < IR_timeout && !isBufferOverflow && risePeriod > riseTimeMin) {
// Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
if (aroundRise(risePeriod)) { // тактирование есть, сигнал хороший - без ошибок(?)
if (highTime > riseTimeMin >> 1) { // 1
#ifdef IRDEBUG
digitalWrite(wrHigh, 1);
#endif
writeToBuffer(HIGH);
} else { // 0
#ifdef IRDEBUG
digitalWrite(wrLow, 1);
#endif
writeToBuffer(LOW);
}
} else { // пропущены такты! сигнал средний // ошибка пропуска
highCount = ceil_div(highTime, riseTime); // предполагаемое колличество HIGH битов
lowCount = ceil_div(lowTime, riseTime); // предполагаемое колличество LOW битов
allCount = ceil_div(risePeriod, riseTime); // предполагаемое колличество всего битов
if (highCount == 0 && highTime > riseTime / 3) { // fix короткой единицы (?)после пропуска нулей(?)
highCount++;
errorCounter++;
#ifdef IRDEBUG
errPulse(errOut, 2);
#endif
}
if (lowCount + highCount > allCount) { // fix ошибочных сдвигов
if (lowCount > highCount) { // Лишние нули
lowCount = allCount - highCount;
#ifdef IRDEBUG
errPulse(errOut, 3);
#endif
} else if (lowCount < highCount) { // Лишние единицы
highCount = allCount - lowCount;
#ifdef IRDEBUG
errPulse(errOut, 4);
#endif
} else if (lowCount == highCount) {} // неизвестный случай
errorCounter += allCount;
}
errorCounter += allCount;
#ifdef IRDEBUG
errPulse(errOut, 1);
#endif
for (int8_t i = 0; i < lowCount && 8 - i; i++) { // отправка LOW битов, если есть
#ifdef IRDEBUG
digitalWrite(wrLow, 1);
#endif
writeToBuffer(LOW);
}
for (int8_t i = 0; i < highCount && 8 - i; i++) { // отправка HIGH битов, если есть
#ifdef IRDEBUG
digitalWrite(wrHigh, 1);
#endif
writeToBuffer(HIGH);
}
}
#ifdef IRDEBUG
digitalWrite(wrHigh, 0);
digitalWrite(wrLow, 0);
#endif
}
if (risePeriod > riseTimeMax >> 1 || highCount || lowCount) { // комплексный фикс рваной единицы
prevPrevRise = prevRise;
prevRise = micros();
} else {
errorCounter++;
#ifdef IRDEBUG
errPulse(errOut, 5);
#endif
}
} else { // Если ```\__ ↓
if (micros() - prevFall > riseTimeMin) {
prevPrevFall = prevFall;
prevFall = micros();
} else {
#ifdef IRDEBUG
//errPulse(errOut, 5);
#endif
}
}
if (isPreamb && frontCounter <= 0) {
prevRise = micros() + riseTime;
}
#ifdef IRDEBUG
digitalWrite(writeOp, isPreamb);
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
// IRDEBUG FUNC
#ifdef IRDEBUG
inline void IR_Decoder::errPulse(uint8_t pin, uint8_t count) {
for (size_t i = 0; i < count; i++) {
digitalWrite(pin, 1);
digitalWrite(pin, 0);
}
digitalWrite(pin, 0);
}
inline void IR_Decoder::infoPulse(uint8_t pin, uint8_t count) {
for (size_t i = 0; i < count; i++) {
digitalWrite(pin, 1);
digitalWrite(pin, 0);
}
}
#endif