mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-18 19:13:58 +00:00
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
138 lines
5.2 KiB
C++
138 lines
5.2 KiB
C++
#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;
|
||
};
|