Files
IR-protocol/IrTxBsrrWave.h
DashyFox 883c0b00cf perf(tx): заполнение DMA-буфера по ранам вместо пословного автомата
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-07 17:56:23 +03:00

138 lines
5.2 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 "IrTxGateTypes.h"
#include <cstddef>
#include <cstdint>
#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<uint16_t>(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;
};