#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, если происходит приём пакета uint32_t pulseFilterDroppedByFilteredOverflow() const { return pulseFilterDropFilteredOverflow; } uint32_t pulseFilterDroppedByHoldOverflow() const { return pulseFilterDropHoldOverflow; } uint32_t pulseFilterDroppedGlitchPairs() const { return pulseFilterDropGlitchPairs; } void pulseFilterResetStats(); #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 subBuffer; /** Очередь фронтов после потокового анти-глитча; tick() читает из неё при включённом фильтре. */ RingBuffer filteredSubBuffer; static constexpr uint8_t kPulseFilterHoldCap = 6; FrontStorage pulseFilterHoldEdges[kPulseFilterHoldCap]{}; uint8_t pulseFilterHoldCount = 0; bool pulseFilterLastRawValid = false; uint32_t pulseFilterLastRawTime = 0; uint32_t pulseFilterDropFilteredOverflow = 0; uint32_t pulseFilterDropHoldOverflow = 0; uint32_t pulseFilterDropGlitchPairs = 0; static constexpr uint8_t kPreambleLockNeed = (uint8_t)IR_PREAMBLE_LOCK_RISE_PERIODS; enum class PreambleState : uint8_t { Idle = 0, Candidate = 1, Locked = 2 }; PreambleState preambleState = PreambleState::Idle; uint8_t preambleGoodPeriods = 0; uint16_t preambleMeanPeriod = 0; uint32_t preambleCandidateLastEdgeTime = 0; uint32_t preambleCandidateFirstRiseTime = 0; bool preambleCandidateFirstRiseValid = false; #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(); // /** Один сырой фронт из subBuffer -> потоковый holdback-антиглитч. */ void pulseFilterFeedOneRaw(const FrontStorage &e); void pulseFilterFlushTimeout(uint32_t nowUs); bool pulseFilterEmit(const FrontStorage &e); void pulseFilterShiftLeft(uint8_t n); void pulseFilterReset(); static uint32_t absDiffU32(uint32_t a, uint32_t b); uint32_t preambleJitterTolUs(uint32_t baselineUs) const; bool preambleRisePeriodCoarseOk(uint32_t periodUs) const; void preambleResetToIdle(); void preambleStartCandidate(const FrontStorage &front); bool preambleProcessEdge(const FrontStorage &front); /// @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 };