#pragma once #include "IrTxGateTypes.h" #include #include #if defined(__GNUC__) #define IR_TX_BSRR_WAVE_HOT __attribute__((always_inline)) inline #else #define IR_TX_BSRR_WAVE_HOT inline #endif /** * Генерация потока 32-бит слов для GPIO BSRR из RLE-сегментов IrTxGateRun. * За один период несущей — multiply физических тиков; при gate — powerN из них HIGH (N ≤ multiply/2). */ class IrTxBsrrWave { public: void configure(uint32_t setW, uint32_t resetW, IrTxGateRun* r, size_t n, uint16_t multiply, uint16_t powerN) { setWord = setW; resetWord = resetW; runs = r; runCount = n; multiply_ = multiply < 2 ? 2 : multiply; const uint16_t cap = static_cast(multiply_ / 2U); powerN_ = (powerN > cap) ? cap : powerN; resetWave(); } void resetWave() { runIndex_ = 0; slotInPeriod_ = 0; ticksLeftInRun_ = 0; if (runCount > 0U && runs != nullptr) { ticksLeftInRun_ = runs[0].lenTicks; } } IR_TX_BSRR_WAVE_HOT uint32_t nextWord() { if (runIndex_ >= runCount) { return resetWord; } const bool gate = runs[runIndex_].gate; uint32_t out; if (!gate) { slotInPeriod_ = 0; out = resetWord; } else { out = (slotInPeriod_ < powerN_) ? setWord : resetWord; slotInPeriod_++; if (slotInPeriod_ >= multiply_) { slotInPeriod_ = 0; } } if (ticksLeftInRun_ > 0) { ticksLeftInRun_--; } if (ticksLeftInRun_ == 0) { runIndex_++; if (runIndex_ < runCount) { ticksLeftInRun_ = runs[runIndex_].lenTicks; } } return out; } // Заполнение по ранам, а не по словам: тот же поток слов, что даёт nextWord() (состояние // runIndex_/ticksLeftInRun_/slotInPeriod_ переносится через границы порций), но пауза (gate=0) // пишется одним циклом записи, а несущая — копией готового шаблона периода. На 12 МГц это // ~1 мс на 4096 слов вместо ~16 (пословный автомат) — и в предзаполнении перед стартом DMA, // и в ISR-дозаполнении половин буфера во время передачи. IR_TX_BSRR_WAVE_HOT void fill(uint32_t* dst, uint16_t count) { if (dst == nullptr || count == 0) { return; } while (count != 0) { if (runIndex_ >= runCount) { do { *dst++ = resetWord; } while (--count != 0); return; } const bool gate = runs[runIndex_].gate; uint16_t n = ticksLeftInRun_; // слов до конца текущего рана if (n == 0) n = 1; // ран нулевой длины: nextWord() выдаёт одно слово и переходит дальше if (n > count) n = count; if (!gate) { slotInPeriod_ = 0; uint16_t k = n; do { *dst++ = resetWord; } while (--k != 0); } else { uint16_t k = n; // добить текущий период до слота 0 (если ран начался посреди периода на границе порции) while (k != 0 && slotInPeriod_ != 0) { *dst++ = (slotInPeriod_ < powerN_) ? setWord : resetWord; if (++slotInPeriod_ >= multiply_) slotInPeriod_ = 0; k--; } // целые периоды: powerN_ слов setWord, остальные resetWord while (k >= multiply_) { uint16_t i = 0; for (; i < powerN_; ++i) *dst++ = setWord; for (; i < multiply_; ++i) *dst++ = resetWord; k = (uint16_t)(k - multiply_); } // хвост неполного периода while (k != 0) { *dst++ = (slotInPeriod_ < powerN_) ? setWord : resetWord; if (++slotInPeriod_ >= multiply_) slotInPeriod_ = 0; k--; } } count = (uint16_t)(count - n); if (ticksLeftInRun_ > n) { ticksLeftInRun_ = (uint16_t)(ticksLeftInRun_ - n); } else { ticksLeftInRun_ = 0; } if (ticksLeftInRun_ == 0) { runIndex_++; if (runIndex_ < runCount) { ticksLeftInRun_ = runs[runIndex_].lenTicks; } } } } private: uint32_t setWord = 0; uint32_t resetWord = 0; IrTxGateRun* runs = nullptr; size_t runCount = 0; uint16_t multiply_ = 2; uint16_t powerN_ = 1; size_t runIndex_ = 0; uint16_t ticksLeftInRun_ = 0; uint16_t slotInPeriod_ = 0; };