IR-protocol/IR_Decoder.h

320 lines
15 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.

#pragma once
#include "IR_config.h"
//#define IRDEBUG
#ifdef IRDEBUG
#define wrHigh A3 // Запись HIGH инициирована // green
#define wrLow A3 // Запись LOW инициирована // blue
#define writeOp 13 // Операция записи, 1 пульс для 0 и 2 для 1 // orange
// Исправленные ошибки // purle
// 1 пульс: fix
#define errOut A3
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////
#define riseTime riseSyncTime //* bitTime */ 893U // TODO: Должно высчитываться медианой
#define riseTolerance tolerance /* 250U */ // погрешность
#define riseTimeMax (riseTime + riseTolerance)
#define riseTimeMin (riseTime - riseTolerance)
#define aroundRise(t) (riseTimeMin < t && t < riseTimeMax)
#define IR_timeout (riseTimeMax * (8 + syncBits +1)) // us // таймаут в 8 data + 3 sync + 1
class IR_Encoder;
class IR_Decoder : private IR_FOX {
friend IR_Encoder;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class IDataPack : protected IR_FOX {
friend IR_Decoder;
public:
IDataPack(IR_Decoder* dec) : dec(dec) {};
public:
bool avaliable() { if (isAvaliable) { isAvaliable = false; return true; } else { return isAvaliable; }; };
virtual uint8_t getMsgInfo() { return msgPtr[0] & IR_MASK_MSG_INFO; };
virtual uint8_t getMsgType() { return (msgPtr[0] >> 5) & IR_MASK_MSG_TYPE; };
virtual uint8_t getMsgRAW() { return msgPtr[0]; };
virtual uint16_t getErrorCount() { return err.all(); };
virtual uint8_t getErrorLowSignal() { return err.lowSignal; };
virtual uint8_t getErrorHighSignal() { return err.highSignal; };
virtual uint8_t getErrorOther() { return err.other; };
// uint16_t getCrcIN() { return crcPtr[0] << 8 | crcPtr[1]; };
// uint16_t getCrcCALC() { return crcCalcVal; };
virtual uint16_t getTunerTime() { return bitPeriod; };
void resetAvaliable() { isAvaliable = false; };
protected:
inline static uint8_t data[dataByteSizeMax] { 0 };
inline static uint8_t* msgPtr;
inline static uint8_t* addrFromPtr;
inline static uint8_t* addrToPtr;
inline static uint8_t* dataPtr;
inline static uint8_t* crcPtr;
inline static ErrorsStruct err;
inline static uint8_t packRawSize;
inline static uint16_t bitPeriod;
inline static uint16_t crcCalcVal;
IR_Decoder* dec;
bool isAvaliable = false;
virtual bool checkAddress(PackOutInfo* packInfo) {
bool ret;
uint8_t targetAddrOffset = packInfo->offsets.addrToOffset;
uint16_t address = (packInfo->ptr[targetAddrOffset] << 8) | (packInfo->ptr[targetAddrOffset + 1]);
checkaddressRuleApply(address, dec->id, ret);
return ret;
};
virtual void onPackAddressCorrect(PackOutInfo* packInfo) {
memset(IDataPack::data, 0, dataByteSizeMax);
memcpy(data, packInfo->ptr, min(packInfo->packSize, dataByteSizeMax));
err = packInfo->err;
bitPeriod = packInfo->rTime;
crcCalcVal = packInfo->crc;
packRawSize = packInfo->packSize;
msgPtr = (data + packInfo->offsets.msgOffset);
addrFromPtr = (data + packInfo->offsets.addrFromOffset);
addrToPtr = (data + packInfo->offsets.addrToOffset);
dataPtr = (data + packInfo->offsets.dataOffset);
crcPtr = (data + packInfo->offsets.crcOffset);
}
virtual void onPackAddressIncorrect(PackOutInfo* packInfo) {}
virtual void afterSet() {}
void set(PackOutInfo packInfo, bool isNeedAddressCheck) {
isAvaliable = false;
if (!isNeedAddressCheck || checkAddress(&packInfo)) {
onPackAddressCorrect(&packInfo);
#ifdef IRDEBUG_INFO
Serial.println(" OK ");
#endif
isAvaliable = true;
} else {
onPackAddressIncorrect(&packInfo);
#ifdef IRDEBUG_INFO
Serial.println(" NOT MY ");
#endif
}
afterSet();
}
};
public:
///////////////////////////////////////////////////////////////////////////////////////////////////////////
class HasAddrFrom {
public:
virtual uint16_t getAddrFrom() { return IDataPack::addrFromPtr[0] << 8 | IDataPack::addrFromPtr[1]; };
};
class HasAddrTo {
public:
virtual uint16_t getAddrTo() { return IDataPack::addrToPtr[0] << 8 | IDataPack::addrToPtr[1]; };
};
class HasData {
public:
virtual uint8_t getDataSize() { return (uint8_t)(IDataPack::crcPtr - IDataPack::dataPtr); };
virtual uint8_t* getDataPrt() { return IDataPack::dataPtr; };
virtual uint8_t getDataRawSize() { return IDataPack::packRawSize; };
virtual uint8_t* getDataRawPtr() { return IDataPack::data; };
};
class IAcceptable {
public:
virtual bool isNeedAccept();
};
class AnyData : public IDataPack, public HasAddrFrom, public HasAddrTo, public HasData {
using IDataPack::IDataPack;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////
class Data : public AnyData, public IAcceptable {
using AnyData::AnyData;
public:
bool isNeedAccept() override { return ((IDataPack::msgPtr[0] >> 5) & IR_MASK_MSG_TYPE) == IR_MSG_DATA_ACCEPT; };
private:
void afterSet() override {
if (Data::IDataPack::isAvaliable && isNeedAccept()) {
dec->isWaitingAcceptSend = true;
dec->addrWaitingSendgTo = getAddrFrom();
Serial.print("\n Need to accept to ");
Serial.println(dec->addrWaitingSendgTo);
}
}
};
class BackData : public AnyData {
using AnyData::AnyData;
public:
bool isAddressed() { return IDataPack::msgPtr[0] & 0b00010000; };
uint8_t getMsgInfo() { return IDataPack::msgPtr[0] & (IR_MASK_MSG_INFO >> 1); };
uint16_t getAddrTo() override {
if (isAddressed()) {
return IDataPack::addrToPtr[0] << 8 | IDataPack::addrToPtr[1];
} else {
return IR_Broadcast;
}
};
};
class Accept : public IDataPack, public HasAddrFrom {
using IDataPack::IDataPack;
public:
private:
bool checkAddress(PackOutInfo* packInfo) override {
bool ret;
uint8_t targetAddrOffset = packInfo->offsets.addrFromOffset;
uint16_t address = (packInfo->ptr[targetAddrOffset] << 8) | (packInfo->ptr[targetAddrOffset + 1]);
ret = address == dec->addrWaitingFrom;
return ret;
}
};
class Request : public IDataPack, public HasAddrFrom, public HasAddrTo {
using IDataPack::IDataPack;
public:
private:
// void afterSet() override {
// if (isAvaliable) {
// dec->isWaitingAcceptSend = true;
// dec->addrWaitingSendgTo = getAddrFrom();
// Serial.print("\n Need to accept to ");
// Serial.println(dec->addrWaitingSendgTo);
// }
// }
};
class RawTune {
friend IR_Decoder;
public:
bool avaliable() { return isAvaliable; };
uint16_t getTunerTime() { return bitPeriod; };
void resetAvaliable() { isAvaliable = false; };
protected:
bool isAvaliable;
uint16_t bitPeriod;
void set(uint16_t time) {
bitPeriod = time;
isAvaliable = true;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public:
uint16_t id;
Data gotData = Data(this); /// @brief Контейнер с данными
BackData gotBackData = BackData(this);
Accept gotAccept = Accept(this); /// @brief Контейнер с подтверждением
Request gotRequest = Request(this); /// @brief Контейнер с запросом
RawTune gotTune; /// @brief Контейнер с информацией подстройки
const uint8_t isrPin; // Пин прерывания
//////////////////////////////////////////////////////////////////////////
/// @brief Конструктор
/// @param isrPin Номер вывода прерывания/данных от приёмника (2 или 3 для atmega 328p)
/// @param addr Адрес приёмника
/// @param encPair Указатель на передатчик, работающий в паре
IR_Decoder(const uint8_t isrPin, uint16_t addr, IR_Encoder* encPair = nullptr);
~IR_Decoder();
void isr(); ///@brief Функция прерывания
void tick(); /// @brief Обработка приёмника, необходима для работы
// @return Буффер переполнился
bool isOverflow() { return isBufferOverflow; };
/// @brief Флаг приёма
/// @return Возвращает true, если происходит приём пакета
// bool isReciving() { return isRecive; }; //TODO: Некорректно работает
// @brief Слушатель для работы isReciving()
void listen();
//////////////////////////////////////////////////////////////////////////
bool isWaitingAccept = false; // Флаг ожидания подтверждения
bool isWaitingAcceptSend = false; // Флаг ожидания отправки подтверждения
uint16_t addrWaitingFrom = 0; // Адрес, от кого ожидается подтверждение
uint16_t addrWaitingSendgTo = 0; // Адрес, кому нужно отправить подтверждение
private:
IR_Encoder* encoder; // Указатель на парный передатчик
volatile uint16_t isPairSending = 0; // Флаг передачи парного передатчика
volatile bool isRecive = false; // Флаг приёма
volatile bool isPreamb = false; // флаг начальной последовости
bool isCrcCorrect = false; // Флаг корректности crc
bool isBufferOverflow = false; // Флаг переполнения буффера данных
bool isWrongPack = false; // Флаг битого пакета
uint16_t riseSyncTime = bitTime; // Подстраиваемое время бита в мкс
////////////////////////////////////////////////////////////////////////
volatile uint8_t currentSubBufferIndex; // Счетчик текущей позиции во вспомогательном буфере фронтов/спадов
struct FrontStorage { // Структура для хранения времени и направления фронта/спада
volatile uint32_t time = 0; // Время
volatile bool dir = false; // Направление (true = ↑; false = ↓)
volatile FrontStorage* next = nullptr; // Указатель на следующий связанный фронт/спад, или nullptr если конец
};
volatile FrontStorage* lastFront = nullptr; // Указатель последнего фронта/спада
volatile FrontStorage* firstUnHandledFront = nullptr; // Указатель первого необработанного фронта/спада
volatile FrontStorage subBuffer[subBufferSize]; // вспомогательный буфер для хранения необработанных фронтов/спадов
////////////////////////////////////////////////////////////////////////
uint8_t* dataBuffer = nullptr; // Указатель на буффер данных
uint32_t prevRise, prevPrevRise, prevFall, prevPrevFall; // Время предыдущих фронтов/спадов
uint16_t errorCounter = 0; // Счётчик ошибок
int8_t preambFrontCounter = 0; // Счётчик __/``` ↑ преамбулы
int16_t bufBitPos = 0; // Позиция для записи бита в буффер
private:
/// @brief Проверка CRC. Проверяет len байт со значением crc, пришедшим в пакете
/// @param len Длина в байтах проверяемых данных
/// @param crc Результат рассчёта crc (Выходной параметр)
/// @return true если crc верно
bool crcCheck(uint8_t len, uint16_t& crc);
////////////////////////////////////////////////////////////////////////
bool isData = true; // Флаг относится ли бит к данным, или битам синхронизации
uint16_t i_dataBuffer; // Счётчик буфера данных
uint8_t nextControlBit = bitPerByte; // Метка для смены флага isData
uint8_t i_syncBit; // Счётчик битов синхронизации
uint8_t err_syncBit; // Счётчик ошибок синхронизации
/// @brief Запиь бита в буффер, а так же проверка битов синхранизации и их фильтрация
/// @param Бит данных
void writeToBuffer(bool);
void packToOutClass(uint8_t packSize, IDataPack& obj, PackOffsets offsets, bool isNeedAddressCheck = true);
////////////////////////////////////////////////////////////////////////
/// @brief Установка и сброс начальных значений и флагов в готовность к приёму данных
void firstRX();
/// @brief Целочисленное деление с округлением вверх
/// @param val Значение
/// @param divider Делитель
/// @return Результат
uint16_t ceil_div(uint16_t val, uint16_t divider);
#ifdef IRDEBUG
inline void errPulse(uint8_t pin, uint8_t count);
inline void infoPulse(uint8_t pin, uint8_t count);
#endif
};