Files
IR-protocol/IR_DecoderRaw.h
2026-04-02 17:25:10 +03:00

201 lines
9.9 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"
#include "RingBuffer.h"
class Print;
#define IRDEBUG
#ifdef IRDEBUG
#define wrHigh 255 // Запись HIGH инициирована // green
#define wrLow 255 // Запись LOW инициирована // blue
#define writeOp 255 // Операция записи, 1 пульс для 0 и 2 для 1 // orange
// Исправленные ошибки // purle
// 1 пульс: fix
#define errOut 255
#define up 255
#define down 255
#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
constexpr uint16_t IR_ResponseDelay = ((uint16_t)(((bitTime+riseTolerance) * (8 + syncBits + 1))*2.7735))/1000;
class IR_Encoder;
class IR_DecoderRaw : virtual public IR_FOX
{
friend IR_Encoder;
protected:
PackInfo packInfo;
uint8_t msgTypeReceive = 0;
IR_Encoder *encoder; // Указатель на парный передатчик
bool availableRaw();
public:
//////////////////////////////////////////////////////////////////////////
/// @brief Конструктор
/// @param pin Номер вывода прерывания/данных от приёмника (2 или 3 для atmega 328p)
/// @param addr Адрес приёмника
/// @param encPair Указатель на передатчик, работающий в паре
IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair = nullptr);
void isr(); // Функция прерывания
void tick(); // Обработка приёмника, необходима для работы
inline bool isOverflow() { return isBufferOverflow; }; // Буффер переполнился
bool isSubOverflow();
volatile inline bool isReciving() { return isRecive; }; // Возвращает true, если происходит приём пакета
#if defined(IR_EDGE_TRACE)
void edgeTraceClear();
bool edgeTraceOverflow() const { return edgeTrace_overflow; }
uint16_t edgeTracePendingCount() const;
/** При непустом кольце: перевод строки + @IRF1v1: + hex; в tick() сброс на Serial автоматически. См. ref/IR_EDGE_TRACE_FORMAT.md */
uint16_t edgeTraceFlushChunk(Print &out, uint16_t maxRec = 48);
#endif
/// Кадр собран по длине из заголовка, но CRC не сошёлся — один раз можно прочитать копию сырых байтов.
bool availableReject();
uint8_t getRejectSize() const { return rejectPackSize; }
const uint8_t* getRejectBuffer() const { return rejectBuffer; }
//////////////////////////////////////////////////////////////////////////
private:
bool isRejectAvailable = false;
uint8_t rejectPackSize = 0;
uint8_t rejectBuffer[dataByteSizeMax]{};
ErrorsStruct errors;
bool isAvailable = false;
uint16_t packSize;
uint16_t crcValue;
volatile uint16_t isPairSending = 0; // Флаг передачи парного передатчика
volatile bool isRecive = false; // Флаг приёма
volatile bool isPreamb = false; // флаг начальной последовости
volatile bool isSubBufferOverflow = false;
bool isBufferOverflow = false; // Флаг переполнения буффера данных
bool isWrongPack = false; // Флаг битого пакета
uint16_t riseSyncTime = bitTime; // Подстраиваемое время бита в мкс
volatile uint32_t lastEdgeTime = 0; // время последнего фронта
////////////////////////////////////////////////////////////////////////
volatile uint32_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]; // вспомогательный буфер для хранения необработанных фронтов/спадов
RingBuffer<FrontStorage, subBufferSize> subBuffer;
#if defined(IR_EDGE_TRACE)
struct IrEdgeTraceRec
{
uint32_t t_us;
uint8_t level;
uint8_t flags;
};
void edgeTracePush(uint32_t t_us, uint8_t level, uint8_t flags);
IrEdgeTraceRec edgeTrace_buf[IR_EDGE_TRACE_CAPACITY]{};
volatile uint16_t edgeTrace_w = 0;
volatile uint16_t edgeTrace_r = 0;
volatile bool edgeTrace_overflow = false;
#endif
////////////////////////////////////////////////////////////////////////
uint8_t dataBuffer[dataByteSizeMax]{0}; // Буффер данных
volatile uint32_t prevRise, prevPrevRise, prevFall, prevPrevFall; // Время предыдущих фронтов/спадов
volatile uint32_t risePeriod;
volatile uint32_t highTime;
volatile uint32_t lowTime;
uint32_t oldTime;
uint16_t wrongCounter;
int8_t highCount;
int8_t lowCount;
int8_t allCount;
uint16_t errorCounter = 0; // Счётчик ошибок
int8_t preambFrontCounter = 0; // Счётчик __/``` ↑ преамбулы
int16_t bufBitPos = 0; // Позиция для записи бита в буффер
private:
bool isReciveRaw;
void listenStart();
void checkTimeout(); //
/// @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 packTraceInvertFix если true — в IRDEBUG_SERIAL_PACK бит в трассе пишется как `0`/`1` (исправление по фронтам)
void writeToBuffer(bool bit, bool packTraceInvertFix = false);
////////////////////////////////////////////////////////////////////////
void firstRX(); /// @brief Установка и сброс начальных значений и флагов в готовность к приёму данных
/// @brief Целочисленное деление с округлением вверх
/// @param val Значение
/// @param divider Делитель
/// @return Результат
uint16_t ceil_div(uint16_t val, uint16_t divider);
#ifdef IRDEBUG
uint32_t wrCounter;
inline void errPulse(uint8_t pin, uint8_t count);
inline void infoPulse(uint8_t pin, uint8_t count);
#endif
#if defined(IRDEBUG_SERIAL_PACK)
static constexpr uint16_t kPackTraceBufCap =
uint16_t(dataByteSizeMax) * (uint16_t(bitPerByte) + uint16_t(syncBits)) + 48u;
void packTraceResetFrame();
void packTracePushBit(bool bit);
void packTracePushChar(char c);
/** Помечает в packTraceBitBuf бит (после BRUTEFORCE_CHECK) обёрткой `0`/`1` по финальному значению в dataBuffer. */
void packTraceWrapDataBitInBackticks(uint16_t byteIndex, uint8_t bitInByte);
/** IR hex: все байты dataBuffer[0 .. byteCount-1] в hex. */
void packTraceEmitHex(uint8_t byteCount) const;
/** IR raw: биты и синхра; тройной пробел между блоками msg/from/to/data/CRC; первый байт 3+пробел+5. endWithNewline — перевод строки после сырой строки. */
void packTraceEmitRawBitsLine(bool endWithNewline = true) const;
void packTraceEmitErrorFlash(const __FlashStringHelper *msg);
void packTraceEmitEndOk(uint8_t packSize);
void packTraceEmitEndBadCrc(uint8_t packSize);
void packTraceOnTimeoutOrAbort(bool fromListenStart);
void packTraceForceEndSyncPhase();
bool packTraceSoftReject() const;
bool packTraceOpen = false;
bool packTraceHadWrongSync = false;
char packTraceBitBuf[kPackTraceBufCap]{};
uint16_t packTraceLen = 0;
#endif
};