Commit Graph

230 Commits

Author SHA1 Message Date
c01dfe16dd Merge pull request #13 from Show-maket/STM32
STM32 → main: платформенная ветка с ИК-циклом (после #12)
2026-09-09 17:02:34 +03:00
5759658d42 Merge pull request #12 from Show-maket/claude/rx-terminal-api
RX по состоянию, airtime формулами, DMA по ранам, гвард декодера после обрыва (для Car v4.3.11)
2026-09-09 17:02:16 +03:00
1b408b0de6 Merge branch 'STM32' into claude/rx-terminal-api 2026-09-09 17:01:57 +03:00
baac9fbf46 fix(rx): после обрыва кадра не требовать 30 мс тишины перед новым кандидатом преамбулы (releasePreambleGuard)
prevRise (последний декодированный фронт) не сбрасывался при abortFrame, а отброшенные гвардом фронты его не двигают —
валидный кадр, начавшийся через <30 мс после обрыва мусора, проглатывался целиком без счётчика. Стенд 09.09: КУ теряла
пинг машинки после обрывков чужого заднего и всплеска её дальномера за 24 мс до пинга (V-серия 75–83 %, после правки 100 %).
Хост-тест (настоящий IR_DecoderRaw.cpp + шим Arduino): после abort кадр через 5…28 мс — терялся, теперь принимается;
чистый конец кадра не тронут (мин. пауза 26.25 мс), хвост оборванного кадра ложных захватов не даёт, 200/200 валидных
среди мусора, стресс 166→176/200.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-09 16:09:55 +03:00
5b220dadd8 Revert "refactor(config): IR_ResponseDelay перенесён в IR_config.h"
This reverts commit b375aa169e.
2026-09-08 14:13:14 +03:00
b375aa169e refactor(config): IR_ResponseDelay перенесён в IR_config.h
Зависит только от конфигурационных констант (bitTime, tolerance, syncBits) — это таймаут байта
(irRxByteTimeoutUs) × 2.7735 = 42 мс; значение не изменилось. Потребителям достаточно IR_config.h.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-08 14:09:38 +03:00
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
d103d2a3ae perf(tx): таблица ранов кадра строится по границам ранов, а не по тактам несущей
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-07 17:33:20 +03:00
86956bcf99 fix(rx): таймаут кандидата преамбулы 3×→1× IR_timeout
Кандидат без фронтов дольше таймаута байта преамбулой быть не может. При 3× (45 мс)
rxLineActive() держал линию «занятой» после паразитных фронтов (засветка своим
дальномером) и откладывал пинг машинки на ~30 мс сверх заданного гэпа.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-07 17:07:22 +03:00
5b9f73ec7c feat(config): irRxByteTimeoutUs/irRxAbortSilenceUs — таймаут байта и тишина обрыва приёма формулой
Константы использует Car (IrCycle/Executer) вместо макроса IR_timeout,
который завязан на член класса декодера.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-07 16:30:44 +03:00
8f45f6e214 feat(rx): наблюдаемость приёма по состоянию + airtime кадра формулами
- IR_config.h: irFrameAirtimeUs/irFrameDecodeEndUs/irLockToDecodeEndUs/irLockLatencyUs,
  irMaxPackSize (31), irDataPackSize/irBackPackSize — все из констант FSM передатчика
  (преамбула 6×98 тактов, байт 11 бит × 74 такта, такт = полпериода несущей).
- IR_Encoder::calculateSendTime по той же формуле (раньше синхробиты считались один раз
  на кадр → занижение 21-26%, потребители компенсировали +30%).
- IR_DecoderRaw: rxLockSeq/rxLockTimeUs (лок преамбулы), rxMsgType, rxExpectedEndUs
  (по объявленной длине), rxLastEnd {seq, reason Ok/Crc/Timeout/Abort, msgType, packSize,
  tUs, expectedEndUs}. Терминалы: конец кадра, таймаут тишины, немедленный abort при
  sync-ошибке / длине <3 (в т.ч. 0) / переполнении — битый кадр больше не держит
  isReciving до конца чужой передачи + 30 мс.
- Порядок в tick: checkTimeout до listenStart (TIMEOUT-лог с реальной длиной), истечение
  кандидата преамбулы без фронтов, ложный PREAMB-инкремент на старте кандидата убран.
- rxMaxPackSize() = протокольные 31 (было 38 = размер буфера).

Совместимость: только добавления; существующие сигнатуры не тронуты. Собрано для G4 (Car)
и F4 (КУ).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
2026-09-07 12:22:22 +03:00
6c97d33c7c feat(rx): rxDeclaredPackSize/rxMaxPackSize + packAirtimeMs — бюджет удержания по размеру
Для гейта заднего у машинки (Даша 04.09): держать задний по СОСТОЯНИЮ приёма, но
не дольше airtime кадра — а если объявленный в 1-м байте размер МЕНЬШЕ протокольного
максимума, то ровно до его конца (усиление надёжности). rxDeclaredPackSize() =
packSize из 1-го байта, если валиден (не битый) и идёт приём, иначе 0 (потребитель
берёт rxMaxPackSize() — до CRC значение недоверенное). packAirtimeMs() — публичная
обёртка над calculateSendTime (чистая функция протокольных констант, годна и для RX).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3Ca6J2JJ1NrptqQjKs8BK
2026-09-04 15:51:59 +03:00
628c050702 feat(rx): rxLineActive() — признак «идёт валидный приём» по СОСТОЯНИЮ
Декодер уже знает состояние приёма (isReciving=локнут; preambleState Idle/
Candidate/Locked). rxLineActive() = локнут ИЛИ формируется ВАЛИДНАЯ преамбула
(>=1 совпавший по периоду фронт — отличает реальный кадр от одиночного шумового
фронта, который лишь заводит Candidate, но не набирает goodPeriods). Для гейта
заднего у машинки: не стрелять, пока на линии идёт/формируется ответ точки —
решение по состоянию, а не по хардкод-длительности (Даша 04.09). Аддитивно, const.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3Ca6J2JJ1NrptqQjKs8BK
2026-09-04 15:09:27 +03:00
a589416cfc reconstruct: restore brave-tape3 decoder stage 2026-08-28 14:35:19 +03:00
e25feb6824 reconstruct: restore brave-tape3 packet types stage 2026-08-28 14:30:44 +03:00
96ffb91b97 fix(tx): приоритет IRQ DMA-TX явно ниже SysTick/EXTI — millis() замедлялся под передачей
HAL_NVIC_EnableIRQ без SetPriority оставлял DMA-каналы TX на приоритете 0 (уровень
SysTick на G4). Длинный refill кольца BSRR (полубуфер 2048 слов = ~3.3мс при HCLK
12МГц) не вытеснялся, тики SysTick слипались: millis() замедлялся в 1.6x (один
излучатель) / 2.2x (два) на всё время передачи. На машинке это ~37% вранья часов
в каждый ИК-кадр: PID dt, планировщик, тайминги протокола.

Мерено на стенде TofSickHunt 2026-08-11 (Car raw/idle_cur_vs_coup*): прогоны «60с»
шли 85-95 реальных секунд. Фикс: irproto::kDmaTxIrqPriority=8 — ниже EXTI приёмника
(6), выше дна; SysTick честный. ISR-TX путь (F401/КУ) не подвержен: его refill на
TIM-приоритете 14. Верификация дилатации на стенде — раунд 4 (прерван, перезапустить).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:33:07 +03:00
08ea71eb5e feat(rx): always-on счётчики причин RX-событий (rxReasonCnt) + RXSTAT-сводка
rxBriefLog теперь безусловен: ВСЕГДА инкрементирует rxReasonCnt[reason]
(наблюдаемость по контракту живучести — работает в проде без печати),
печать события — только при IR_RX_BRIEF_LOG. 14 точек вызова развёрнуты
из-под #if (Glitch/Timing/Preamble/Sync/BufOverflow/Timeout/Crc/Ok +
pulse-filter пути); ISR-агрегатные MuteBegin/End/RawOverflow остаются
только при логе (их флаш живёт в brief-механике). Публичное API:
rxReasonCounters() / rxReasonCountersClear() / printRxReasonStats(Print&)
-> 'RXSTAT,GLITCH=..,TIME=..,...,OK=..'.
Компил-чек: LaserTestCheck (G4, флаг выкл) и Plan_B (F4, флаг вкл) — чисто.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 23:05:50 +03:00
5bcaf4092a perf(rx): батч-выемка фронтов в tick (IR_RX_TICK_BATCH=16) — приём развязан от частоты loop
Было: 1 сырой фронт за tick() -> RX-пропускная способность привязана к loop;
медленный loop (телеметрия/дисплей/КУ-печать) переполнял ISR-буфер 250 фронтов
(~120мс эфира) и кадры гибли молча (вероятная составляющая 'КУ отвечает не на
каждый пакет'). Стало: до IR_RX_TICK_BATCH фронтов за tick; idle-семантика
сохранена (flush пульс-фильтра только при пустом буфере; listenStart/
checkTimeout как раньше). Переопределяемо дефайном до include.
Компил-чек: LaserTestCheck (G491) собирается.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 18:22:44 +03:00
873277e59f docs 2026-07-01 15:33:49 +03:00
f93f4101c3 fix(tx): correct multi-byte BACK frames (packSize used min(1,len))
_sendBack и testSendBack считали packSize с min(uint8_t(1),len) → в кадр входил только 1 байт данных, остальные обрезались (и затирались CRC). Многобайтовый back (speed + customBackData/sendOrderedBack) слался битым. Теперь packSize = ...+len+crcBytes, плюс валидация полного packSize<=31 (IR_MASK_MSG_INFO). Для len=1 поведение не меняется.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:20:27 +03:00
bf14b3d7ad fix(rx/tx): B5 pop-by-value, B6 ISR micros mask, B7 crc parens, B10 div guard, send length validation
B5 (MEDIUM): RingBuffer::pop(T&) копирует под одной критсекцией; tick() перешёл на неё → нет торн-рида (внутренний interrupts() в T* pop снимал внешнюю защиту до чтения *ptr). T* pop() оставлен (не используется).
B6 (MEDIUM): убрана лишняя noInterrupts/interrupts вокруг micros() в EXTI-ISR (снимала PRIMASK посреди ISR). std::function-диспетчеризация attachInterrupt — структурна, не трогаю.
B7 (LOW): скобки в crcCheck (== & 0xFF по приоритету).
B10 (LOW): guard деления на 0 в ceil_div (актуально только при freeFrec — НЕ включаю).
send: sendDataFULL — валидация полного packSize<=31 (было len>bytePerPack=31, packSize=7+len оборачивался → тихая потеря Data payload 25..31).
B8 (isSubBufferOverflow) — уже volatile, потеря флага безвредна (диагностика), не трогаю. B9 (prevRise==0 при micros()==0) — уже обработан веткой в preambleProcessEdge, намеренно.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:38:51 +03:00
a8d32bbcef fix(rx): guard sub-minimum packSize before crcCheck (B1 OOB read)
packSize==1 давало crcCheck(1-crcBytes) → uint8 len=255 → crc8 читает dataBuffer[0..256] при массиве 38 (OOB-чтение ~217 байт). Триггер — лок преамбулы + первый байт с младшими 5 битами=1 (~1/32 ложных локов). Двойная защита: (1) ранний reject packSize 1..2 (< msg+crc) как битого; (2) конец кадра требует packSize>crcBytes, поэтому crcCheck(packSize-crcBytes) не уходит в underflow. Валидные кадры (packSize>=5) и будущие компактные (>=3) не затронуты.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:13:27 +03:00
fc3510c425 fix(rx): guard null encoder in auto-accept (B2), init accept fields (B4), fix dataBuffer OOB off-by-one (B3)
B2 (HIGH): IR_Decoder::_tick — encoder->sendAccept() без null-check; у Car decoder создан с encoder==nullptr → HardFault при приёме IR_MSG_DATA_ACCEPT. Добавлен guard.
B4 (MEDIUM): acceptSendTimer/isWaitingAcceptSend/addrAcceptSendTo/acceptCustomByte не инициализировались → мусор мог спонтанно дёрнуть sendAccept. Дефолты в .h.
B3 (HIGH): writeToBuffer — '>' вместо '>=' → при i_dataBuffer==dataByteSizeMax*8 (304) запись dataBuffer[38] за границей 38-байтового массива (packSize==0 runaway). Валидные кадры закрываются при <=248, не затронуты.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:34:12 +03:00
e5ed029cfd fix(ir-dma): __DSB() barriers before DMA arm and after circular refill (#8)
Перестраховка корректности (на M4F без D-кэша обычно не обязательно): завершить запись dmaBuf/BSRR до HAL_DMA_Start_IT и refill полубуфера до прохода циркулярного DMA.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:12:28 +03:00
55935b8b92 fix(ir-dma): volatile active + errorCount() observability; live poll() in longData example
- IrDmaTxStm32.h: TxStream::active -> volatile (делится поток<->ISR: busy()/poll()/колбэки/спин applyCarrierMultiply)
- IrDmaTxStm32.h: + volatile errors_ и errorCount(), инкремент в dmaErrorCb (наблюдаемость Transfer-Error)
- test_examples/longData: dmaBackend.poll() в loop, иначе watchdog завершения DMA мёртв в эталонном примере

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:25:36 +03:00
7176fe5321 fix(ir-dma): eliminate isSending latch — free-running TIM, completion watchdog, busy() OR
C: remove racy activeCount_ + shared-timer gating; TIM17 free-running, per-stream DMA arming only (HAL_DMA_Start_IT/Abort_IT). Sets active before arming; never stops TIM on a shared counter.

D: poll() watchdog detects ticksOutput stall (>=stallTimeoutMs_, default 100ms) and forceStop()s the stuck stream under a per-channel NVIC mask; recoveryCount()/setStallTimeoutMs() accessors. dmaErrorCb now checks !active for symmetric single-stop.

E: busy() now returns true if ANY stream active (was AND) — honest contract for irAnyTxBusy()/carrier retune; does not serialize the send path.

Bug: IR TX rarely latched (LED frozen on/off) because stopStream was reachable only from the DMA HT/CPLT IRQ. Hardware bring-up (logic analyzer, recoveryCount telemetry) pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:26:52 +03:00
b1d7016147 analyzer 2026-06-02 11:19:32 +03:00
9935ac192a Merge pull request #11 from Show-maket/fix-DMA-and-Analyzer
Fix dma and analyzer
2026-04-21 07:05:29 +00:00
8361828c44 tx error tracking and inline glitch filter 2026-04-21 10:00:46 +03:00
38d93edd1b upd 2026-04-20 17:47:33 +03:00
2f5b57680b Add documentation for IR TX modes in the library 2026-04-20 14:49:38 +03:00
31ac7a3625 Refactor IR decoder and encoder for improved pulse filtering and ISR handling. Removed unused filtered sub-buffer, updated pulse filter methods, and added support for buffered ISR storage in the encoder. Enhanced documentation for clarity on DMA TX backend and ISR modes. 2026-04-20 14:48:45 +03:00
b4f644c258 Align main with fix-DMA-and-Analyzer (encoder API, sketch, .gitignore)
Restore IR_SendResult + sendDataFULL, fix IR-protocol.ino, match .gitignore to analyzer branch.

Made-with: Cursor
2026-04-17 15:07:54 +03:00
a75fd0c564 Merge branch 'main' of https://github.com/Show-maket/IR-protocol 2026-04-17 14:31:32 +03:00
4061a4ed10 Merge pull request #10 from Show-maket/fix-DMA-and-Analyzer
Fix dma and analyzer
2026-04-17 11:29:56 +00:00
01a34ed3f7 Merge pull request #9 from Show-maket/opti-dma-enc-power
add brightness controll
2026-04-17 06:47:16 +00:00
d788358ac8 add brightness controll 2026-04-17 09:45:32 +03:00
8daff9c46a fix timer overflow? 2026-04-15 11:00:48 +03:00
8631f23b53 fix overflow and mute 2026-04-14 09:36:27 +03:00
ad1e16cfda common fsm 2026-04-13 14:45:38 +03:00
57f79b35c7 fix longPacket sync_bit 2026-04-13 14:44:18 +03:00
f8daa68381 analyzer debug 2026-04-13 10:29:39 +03:00
ddb8a9e143 Filters and stable 2026-04-08 16:11:10 +03:00
c29fe2cf7c Analyzer 2026-04-07 15:08:02 +03:00
7651f07e0a Analyzer plug 2026-04-07 13:25:55 +03:00
e7d7c0e1c1 dma fix priority and debug 2026-04-02 17:25:10 +03:00
af3e012aac fix msgTypeReceive and isReceive 2026-03-30 13:34:04 +03:00
fc93ea41db Merge pull request #8 from Show-maket/STM32
Stm32
2026-03-11 17:06:20 +03:00
0620d98e35 Merge branch 'main' into STM32 2026-03-11 17:06:06 +03:00
4caed06218 Merge pull request #7 from Show-maket/STM32DMA
upd
2026-03-11 16:59:08 +03:00