From d103d2a3ae5c7197bd9a6da3b54cf83bf2b863e7 Mon Sep 17 00:00:00 2001 From: DashyFox Date: Mon, 7 Sep 2026 17:33:20 +0300 Subject: [PATCH] =?UTF-8?q?perf(tx):=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D1=86=D0=B0=20=D1=80=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=BA=D0=B0?= =?UTF-8?q?=D0=B4=D1=80=D0=B0=20=D1=81=D1=82=D1=80=D0=BE=D0=B8=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=20=D0=B3=D1=80=D0=B0=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D0=B0=D0=BC=20=D1=80=D0=B0=D0=BD=D0=BE=D0=B2,=20=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D0=BF=D0=BE=20=D1=82=D0=B0=D0=BA=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=20=D0=BD=D0=B5=D1=81=D1=83=D1=89=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildGateRuns/buildPhysicalGateRuns крутили автомат передачи по одному такту (пинг 10 байт = 8728 тактов) — на 12 МГц это ~30 мс между вызовом send и стартом DMA, на каждой передаче (задний ~20 мс). Обход по границам (txWalkRuns: ран = toggleCounter+1 тактов уровня state) даёт ту же последовательность за число шагов = число ранов (225). Эквивалентность проверена на хосте: 12480 случаев, len 1..38, множитель 2..8, логические и физические раны совпадают бит-в-бит (len=39 — чтение за буфером в sync-фазе автомата у обеих версий, на эфире ≤31). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq --- IR_Encoder.cpp | 93 ++++++++++++++++++++++++++++---------------------- IR_Encoder.h | 2 ++ 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/IR_Encoder.cpp b/IR_Encoder.cpp index 8663ccb..62ec179 100644 --- a/IR_Encoder.cpp +++ b/IR_Encoder.cpp @@ -352,6 +352,29 @@ bool IR_Encoder::txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool return txAdvanceAfterOutput(st, sendBufferLocal); } +// Обход кадра по границам ранов: между границами автомат выдаёт st.toggleCounter+1 тактов +// уровня st.state (txAdvanceAfterOutput считает toggleCounter до нуля, затем txAdvanceBoundary +// открывает следующий ран). Даёт ту же последовательность тактов, что потиковый обход, но за +// число шагов = число ранов (пинг: ~230 вместо ~8700 тактов — на 12 МГц это ~30 мс перед стартом DMA). +template +bool IR_Encoder::txWalkRuns(TxFsmState &st, const uint8_t *sendBufferLocal, Emit emit) +{ + for (;;) + { + const bool gate = st.state; + const uint32_t lenTicks = (uint32_t)st.toggleCounter + 1U; + if (!emit(gate, lenTicks)) + { + return false; + } + st.toggleCounter = 0; + if (!txAdvanceBoundary(st, sendBufferLocal)) + { + return true; + } + } +} + void IR_Encoder::loadTxFsmFromMembers(TxFsmState &st) const { st.sendLen = sendLen; @@ -476,28 +499,27 @@ size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRu st.currentBitSequence = bitHigh; size_t runCount = 0; - bool isActive = true; - while (isActive) - { - bool gate = false; - isActive = txEmitTick(st, sendBufferLocal, gate); - + const bool ok = txWalkRuns(st, sendBufferLocal, [&](bool gate, uint32_t lenTicks) -> bool { if (runCount > 0 && outRuns[runCount - 1].gate == gate) { - outRuns[runCount - 1].lenTicks = (uint16_t)(outRuns[runCount - 1].lenTicks + 1U); - } - else - { - if (runCount >= maxRuns) + const uint32_t merged = (uint32_t)outRuns[runCount - 1].lenTicks + lenTicks; + if (merged > 65535U) { - return 0; + return false; } - outRuns[runCount].gate = gate; - outRuns[runCount].lenTicks = 1U; - runCount++; + outRuns[runCount - 1].lenTicks = (uint16_t)merged; + return true; } - } - return runCount; + if (runCount >= maxRuns || lenTicks > 65535U) + { + return false; + } + outRuns[runCount].gate = gate; + outRuns[runCount].lenTicks = (uint16_t)lenTicks; + runCount++; + return true; + }); + return ok ? runCount : 0; } size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns, uint16_t multiply) @@ -564,40 +586,29 @@ size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_ bool currentGate = false; uint32_t currentLogicalLen = 0; bool havePendingRun = false; - bool isActive = true; - while (isActive) - { - bool gate = false; - isActive = txEmitTick(st, sendBufferLocal, gate); - - if (!havePendingRun) + const bool ok = txWalkRuns(st, sendBufferLocal, [&](bool gate, uint32_t lenTicks) -> bool { + if (havePendingRun && currentGate == gate) { - currentGate = gate; - currentLogicalLen = 1U; - havePendingRun = true; - continue; + currentLogicalLen += lenTicks; + return true; } - - if (currentGate == gate) + if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount)) { - currentLogicalLen++; - continue; + return false; } - - if (!appendPhysicalRun(currentGate, currentLogicalLen, runCount)) - { - return 0; - } - currentGate = gate; - currentLogicalLen = 1U; + currentLogicalLen = lenTicks; + havePendingRun = true; + return true; + }); + if (!ok) + { + return 0; } - if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount)) { return 0; } - return runCount; } diff --git a/IR_Encoder.h b/IR_Encoder.h index af097cf..df55d86 100644 --- a/IR_Encoder.h +++ b/IR_Encoder.h @@ -214,6 +214,8 @@ private: static bool txAdvanceBoundary(TxFsmState &st, const uint8_t *sendBufferLocal); static bool txAdvanceAfterOutput(TxFsmState &st, const uint8_t *sendBufferLocal); static bool txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool &gateOut); + template + static bool txWalkRuns(TxFsmState &st, const uint8_t *sendBufferLocal, Emit emit); void loadTxFsmFromMembers(TxFsmState &st) const; void storeTxFsmToMembers(const TxFsmState &st); bool shouldUseBufferedIsr() const;