From 883c0b00cfaf349966a35175d7d7fe5c4fe3fc8f Mon Sep 17 00:00:00 2001 From: DashyFox Date: Mon, 7 Sep 2026 17:56:23 +0300 Subject: [PATCH] =?UTF-8?q?perf(tx):=20=D0=B7=D0=B0=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20DMA-=D0=B1=D1=83=D1=84=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=20=D0=BF=D0=BE=20=D1=80=D0=B0=D0=BD=D0=B0=D0=BC=20?= =?UTF-8?q?=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D0=BF=D0=BE=D1=81=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B0=D0=B2=D1=82=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IrTxBsrrWave::fill: пауза (gate=0) — один цикл записи resetWord, несущая — копия шаблона периода (powerN слов setWord + остальные resetWord); состояние переносится через границы порций как у nextWord(). На 12 МГц предзаполнение 4096 слов перед стартом DMA и ISR-дозаполнение половин (2048 слов каждые ~8.9 мс кадра — было ~88% CPU) укорачиваются в разы: старт несущей после вызова send 15.7 → ~5 мс (с построением ранов). Эквивалентность проверена на хосте: 17856 случаев (len 1..31, multiply 2..8, powerN 1..multiply/2, порции 2048/случайные/по 1 слову), 0 расхождений на 719 млн слов. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq --- IrTxBsrrWave.h | 56 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/IrTxBsrrWave.h b/IrTxBsrrWave.h index 31115ff..f5178f4 100644 --- a/IrTxBsrrWave.h +++ b/IrTxBsrrWave.h @@ -65,13 +65,63 @@ public: 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; } - do { - *dst++ = nextWord(); - } while (--count != 0); + 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: