#pragma once #include #include // #define IRDEBUG_INFO /** Число потоков DMA-TX задаётся шаблоном: IrDmaTxStm32<2>, см. IrDmaTxStm32.h и irproto::kDefaultDmaTxMaxStreams. */ namespace irproto { constexpr size_t kDefaultDmaTxMaxStreams = 4U; /** Кольцевой буфер BSRR-слов для ISR-TX (как у DMA: два полублока). Чётное число. */ constexpr uint16_t kIsrTxBsrrWordCount = 256U; /** Максимум RLE-сегментов для buildGateRuns при ISR-TX. */ constexpr size_t kIsrTxMaxGateRuns = 512U; static_assert((kIsrTxBsrrWordCount & 1U) == 0U, "kIsrTxBsrrWordCount must be even"); } // Пошаговый разбор кадра на Serial (по умолчанию выключено). Пульсы IRDEBUG на пинах не меняют. // #define IRDEBUG_SERIAL_PACK // Не обрывать приём сразу при накопленной sync-ошибке — «дописывать» до таймаута (только вместе с IRDEBUG_SERIAL_PACK). // #define IRDEBUG_SERIAL_SOFT_REJECT // Краткий лог причин, почему физический сигнал не дошёл до распознанного пакета. // Формат и коды: ref/IR_RX_BRIEF_LOG.md #ifndef IR_RX_BRIEF_LOG #define IR_RX_BRIEF_LOG 0 #endif // 1: печатать только отклонённые/ошибочные события; успехи и шумовые PREAMB скрыть. #ifndef IR_RX_BRIEF_LOG_REJECT_ONLY #define IR_RX_BRIEF_LOG_REJECT_ONLY 1 #endif // Журнал фронтов ИК в ISR; сброс строк @IRF1v1: в IR_DecoderRaw::tick(). См. ref/IR_EDGE_TRACE_FORMAT.md // Расход RAM ≈ IR_EDGE_TRACE_CAPACITY * 6 байт на декодер. Выключить — закомментировать: // #define IR_EDGE_TRACE #if defined(IR_EDGE_TRACE) #ifndef IR_EDGE_TRACE_CAPACITY #define IR_EDGE_TRACE_CAPACITY 512u #endif /** Запись в edgeTrace: фронт не передан в decode (isPairSending). */ #define IR_EDGE_TRACE_F_SKIP_DECODE 0x01u #endif // Пример в скетче: void irPackTracePrintOkCommand(const uint8_t* b, uint8_t n) { Serial.print(F("CarCmd::...")); } #if defined(IRDEBUG_SERIAL_PACK) /** Слабая реализация в IR_DecoderRaw.cpp; в скетче определите свою для вывода вроде CarCmd::... */ void irPackTracePrintOkCommand(const uint8_t *buf, uint8_t packSize); #endif /*////////////////////////////////////////////////////////////////////////////////////// Для работы в паре положить декодер в энкодер */ // Адресация с 1 до 65 499 #define IR_Broadcast 65000 // 65 500 ~ 65 535 - широковещательные пакеты (всем) /* *Адресное пространство: Адрес 0 запрещен и зарезервирован под NULL, либо тесты IR_MSG_ACCEPT с адреса 0 воспринимается всеми устройствами */ //**** Контрольные точки ****** #define IR_MAX_ADDR_CPU 63999 #define IR_MIN_ADDR_CPU 32000 // //***** Группы машинок ******** // #define IR_MAX_CAR_GROUP 31999 // #define IR_MIN_CAR_GROUP 30000 // //********** FREE ************* // #define IR_MAX_FREE 31999 // #define IR_MIN_FREE 2000 //********* Машинки *********** #define IR_MAX_CAR 31999 #define IR_MIN_CAR 1 //***** Пульты управления ***** #define IR_MAX_CONTROLLER 64999 #define IR_MIN_CONTROLLER 64000 /* /```````````````````````````````````````````````` data pack `````````````````````````````````````````````\                                                                                                                                             {``````````} [````````````````````````] [````````````````````] [````````````````````````] [``````````````] { msg type } [ addr_from uint16_t ] [ addr_to uint16_t ] [====== data bytes ======] [ CRC Bytes ] {..........} [........................] [....................] [........................] [..............]                                                                                                            { aka size } [addr_from_H][addr_from_L] [addr_to_H][addr_to_L] [data_H][data_n..][data_L] [ crc1 ][ crc2 ] |     0           1            2              3         4          5                         |       |     \____________________________________________________________________________________________/       |     |                                                                                                    |     \____________________________________________________________________________________________________/     msg type:                                 //  __________                                 // | 01234567 |                                 //  ----------                                 // | xxx..... | = тип сообщения (биты 7..5)                                 // | ...xxxxx | = полная длина кадра в байтах (5 бит, 0..31, IR_MASK_MSG_INFO), не «31 бит» и не отдельный лимит «24 байта»                                 // Полезная нагрузка в data pack: до bytePerPack байт (см. #define bytePerPack).                                 //  ---------- */ #define IR_MSG_BACK 0U // | 000...... | = Задний сигнал машинки #define IR_MSG_ACCEPT 1U // | 001..... | = подтверждение #define IR_MSG_REQUEST 2U // | 010..... | = запрос // #define IR_MSG_ 3U // | 011..... | = ?? #define IR_MSG_BACK_TO 4U // | 100..... | = Задний сигнал машинки c адресацией // #define IR_MSG_ 5U // | 101..... | = ?? #define IR_MSG_DATA_NOACCEPT 6U // | 110..... | = данные, не требующие подтверждения #define IR_MSG_DATA_ACCEPT 7U // | 111..... | = данные требующие подтверждения ; /*   // ---------- /``````````````````````````````` подтверждение `````````````````````````````\      /``````````````````````````````````````` запрос ``````````````````````````````````\                                                                                                                        {``````````} [````````````````````````] [``````````````````] [``````````````]      {``````````} [````````````````````````] [````````````````````````] [``````````````] { msg type } [ addr_from uint16_t ] [=== customByte ===] [ CRC Bytes ]      { msg type } [ addr_from uint16_t ] [ addr_to uint16_t ] [ CRC Bytes ] {..........} [........................] [..................] [..............]      {..........} [........................] [........................] [..............]                                                                                                                                                              { 001..... } [addr_from_H][addr_from_L] [=== customByte ===] [ crc1 ][ crc2 ]      { 010..... } [addr_from_H][addr_from_L] [addr_from_H][addr_from_L] [ crc1 ][ crc2 ] |     0            1           2                  3              4       5          |     0            1           2              3           4           5       6     \________________________________________________________________/       |          \_____________________________________________________________________/       |     |                                                                        |          |                                                                             |     \________________________________________________________________________/          \_____________________________________________________________________________/     customByte - контрольная сумма принятых данных по poly1 /`````````````````````` Задний сигнал машинки без адресации ``````````````````````\         // Первый байт: (IR_MSG_BACK<<5) | (packSize & IR_MASK_MSG_INFO) — как у data pack (тип + длина 0..31).                                                                                             {``````````} [````````````````````````] [````````````````````````] [``````````````]         { msg type } [ addr_from uint16_t ] [====== data bytes ======] [ CRC Bytes ]         {..........} [........................] [........................] [..............]                                                                                                     { xxx..|..xxxxx } [addr_from_H][addr_from_L] [data_H][data_n..][data_L] [ crc1 ][ crc2 ]         |     0           1            2            3                         |       |             \_____________________________________________________________________/       |             |                                                                             |             \_____________________________________________________________________________/             /```````````````````````````````````` Задний сигнал машинки с адресацией ````````````````````````````````````\  // Первый байт: (IR_MSG_BACK_TO<<5) | (packSize & IR_MASK_MSG_INFO) — IR_MSG_BACK_TO в битах 7..5, длина 0..31.                                                                                            {``````````} [````````````````````````] [````````````````````````] [````````````````````````] [``````````````]  { msg type } [ addr_from uint16_t ] [ addr_to uint16_t ] [====== data bytes ======] [ CRC Bytes ]  {..........} [........................] [........................] [........................] [..............]                                                                                                                   { xxx..|..xxxxx } [addr_from_H][addr_from_L] [addr_to_H][addr_to_L] [data_H][data_n..][data_L] [ crc1 ][ crc2 ]  |     0           1            2              3           4            5                         |       |      \________________________________________________________________________________________________/       |      |                                                                                                        |      \________________________________________________________________________________________________________/      */ #define IR_MASK_MSG_TYPE 0b00000111 #define IR_MASK_MSG_INFO 0b00011111 /* /////////////////////////////////////////////////////////////////////////////////////*/ typedef uint16_t crc_t; // #define BRUTEFORCE_CHECK // Перепроверяет пакет на 1 битные ошибки //TODO: зависает #define bytePerPack (31) // колличество байтов в пакете #ifndef freeFrec #define freeFrec false #endif #ifndef subBufferSize #define subBufferSize 250 // Буфер для складирования фронтов, пока их не обработают (передатчик) #endif /** Максимальное число передатчиков, способных временно заглушить один декодер. */ #ifndef IR_PAIR_MUTE_MAX_ENCODERS #define IR_PAIR_MUTE_MAX_ENCODERS 8U #endif /** Минимальная длительность удержания уровня (мкс): короче — импульс/пара фронтов выкидывается до tick() * (иголки на плато, дребезг). 0 — фильтр выключен, фронты идут в декодер как с ISR. */ #ifndef IR_INPUT_MIN_PULSE_US #define IR_INPUT_MIN_PULSE_US 0 #endif /** Сколько подтверждённых фронтов держать перед выпуском в декодер (потоковая задержка). */ #ifndef IR_INPUT_FILTER_HOLDBACK_EDGES #define IR_INPUT_FILTER_HOLDBACK_EDGES 3U #endif /** Если новых фронтов нет, через minPulse*mult держатель принудительно сбрасывается в декодер. */ #ifndef IR_INPUT_FILTER_TIMEOUT_MULT #define IR_INPUT_FILTER_TIMEOUT_MULT 5U #endif /** Синхронно с IrFoxProtocolConstants.h / IrFoxDecoder (плагин Saleae). */ #ifndef IR_SHORT_LOW_GLITCH_REJECT #define IR_SHORT_LOW_GLITCH_REJECT 1 #endif #ifndef IR_GLITCH_REJECT_PHASE_NUDGE #define IR_GLITCH_REJECT_PHASE_NUDGE 1 #endif #ifndef IR_MICRO_GAP_RISE_REJECT #define IR_MICRO_GAP_RISE_REJECT 1 #endif /** Лок преамбулы: сколько одинаковых подряд периодов подъёма нужно для старта кадра. */ #ifndef IR_PREAMBLE_LOCK_RISE_PERIODS #define IR_PREAMBLE_LOCK_RISE_PERIODS 2U #endif /** Допуск одинаковости периода преамбулы (проценты) + минимальная абсолютная полка в мкс. */ #ifndef IR_PREAMBLE_JITTER_PCT #define IR_PREAMBLE_JITTER_PCT 18U #endif #ifndef IR_PREAMBLE_JITTER_US_MIN #define IR_PREAMBLE_JITTER_US_MIN 80U #endif /** Грубое окно валидности периода преамбулы RISE->RISE (в процентах от bitTime). * Для текущего протокола преамбула заметно длиннее обычного битового периода. */ #ifndef IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT #define IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT 220U #endif #ifndef IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT #define IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT 340U #endif /** Таймаут окна кандидата преамбулы: IR_timeout * mult. */ #ifndef IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT #define IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT 3U #endif #define preambPulse 3 ///////////////////////////////////////////////////////////////////////////////////// #define bitPerByte 8U // Колличество бит в байте #define addrBytes 2 #define msgBytes 1 #define crcBytes 2 #define poly1 0x31 #define poly2 0x8C #define syncBits 3U // количество битов синхронизации #define dataByteSizeMax (msgBytes + addrBytes + addrBytes + bytePerPack + crcBytes) #define preambFronts (preambPulse * 2) // количество фронтов преамбулы (Приём) #define preambToggle ((bitPauseTakts * 2 + bitActiveTakts) * 2 - 1) // колличество переключений преамбулы (Передача) #define carrierFrec 38000U // частота несущей (Приём/Передача) #define carrierPeriod (1000000U / carrierFrec) // период несущей в us (Приём) // В процессе работы значения будут отклонятся в соответствии с предыдущим битом #define bitActiveTakts 25U // длительность высокого уровня в тактах #define bitPauseTakts 12U // длительность низкого уровня в тактах #define bitTakts (bitActiveTakts + bitPauseTakts) // Общая длительность бита в тактах #define bitTime (bitTakts * carrierPeriod) // Общая длительность бита #define tolerance 300U constexpr uint16_t test_all_Time = bitTime; constexpr uint16_t test_all_Takts = bitTakts * 2; constexpr uint16_t test_hi = ((bitPauseTakts) * 2 - 0) + ((bitActiveTakts) * 2 - 0); constexpr uint16_t test_low = ((bitPauseTakts / 2 + bitActiveTakts) * 2 - 0) + ((bitPauseTakts)-0); class IR_FOX { public: struct PackOffsets { uint8_t msgOffset; uint8_t addrFromOffset; uint8_t addrToOffset; uint8_t dataOffset; uint8_t crcOffset; }; struct ErrorsStruct { uint8_t lowSignal = 0; uint8_t highSignal = 0; uint8_t other = 0; void reset() { lowSignal = 0; highSignal = 0; other = 0; } uint16_t all() { return lowSignal + highSignal + other; } }; struct PackInfo { uint8_t *buffer = nullptr; uint8_t packSize = 0; uint16_t crc = 0; ErrorsStruct err; uint16_t rTime = 0; }; inline uint16_t getId() const { return id; } inline void setId(uint16_t id) { this->id = id; } static void checkAddressRuleApply(uint16_t address, uint16_t id, bool &flag); void setPin(uint8_t pin); inline uint8_t getPin() { return pin; }; inline GPIO_TypeDef *getPort() const { return port; } inline uint16_t getPinMask() const { return mask; } protected: uint16_t id; uint8_t pin; GPIO_TypeDef *port; uint16_t mask; ErrorsStruct errors; uint8_t crc8(uint8_t *data, uint8_t start, uint8_t end, uint8_t poly); };