mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
fix msgTypeReceive and isReceive
This commit is contained in:
125
IR_DMA_ISR_signal_analysis.md
Normal file
125
IR_DMA_ISR_signal_analysis.md
Normal file
@ -0,0 +1,125 @@
|
||||
# IR DMA vs ISR: анализ согласованности сигнала и ответа версии
|
||||
|
||||
Связка с остальным пультом (модули, настройки): **[`ARCHITECTURE.md`](ARCHITECTURE.md)**.
|
||||
|
||||
Документ фиксирует наблюдения по переходу машинки (проект Car) на **DMA-передачу** ИК через `IR_Encoder::setExternalTxBackend` и `IrDmaBackend`, сравнение со **старым путём** (таймер + **`_isr()`**), ручную проверку CRC по логу пульта и роль **`buildGateRuns`** в библиотеке **IR-protocol**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Контекст
|
||||
|
||||
- До введения DMA передача шла через **`IR_Encoder::begin(..., IR_Encoder::isr)`**: на каждый тик таймера (`carrierFrec * 2`) вызывается **`_isr()`**, формируются преамбула, данные, синхробиты.
|
||||
- После коммита с **IR_DMA** (`Car`, `IR.cpp`): **`beginClockOnly`**, **`setExternalTxBackend`**, фактическая модуляция — **`IrDmaBackend::start`** → **`IR_Encoder::buildGateRuns`** + DMA в **BSRR**.
|
||||
- Ответ версии — один из самых **длинных** кадров (до **31 байта** полного кадра по заголовку). Короткие пакеты (эхо `Version_Query`, 8 байт) в логе остаются **FrameOK**; длинный ответ версии даёт **CRC fail** / `Frame reject`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Два пути: ISR и DMA
|
||||
|
||||
| Этап | Старый ISR | DMA |
|
||||
|------|------------|-----|
|
||||
| Байты пакета + CRC | `sendDataFULL` → `sendBuffer` | То же; в `buildGateRuns` — `memcpy` в локальный буфер размером `dataByteSizeMax` |
|
||||
| Развёртка в импульсы | **`_isr()`**: счётчик `toggleCounter`, ветки preamb / data / sync | **`buildGateRuns`**: RLE-сегменты `(gate, lenTicks)` → **`nextWord()`** по тикам таймера |
|
||||
| Останов передачи | `signal == noSignal`, `isSending = false` | `ticksOutput >= totalTicks`, `sum(runs[i].lenTicks)` |
|
||||
|
||||
Идея `buildGateRuns`: **эмулировать** шаги FSM, которые в ISR выполняются при **`toggleCounter == 0`** (см. комментарий в `IR_Encoder.cpp` рядом с внутренним `while`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Ключевое наблюдение: `runLenTicks = toggleCounter + 1`
|
||||
|
||||
В **`IR_Encoder::buildGateRuns`** на каждой итерации внешнего цикла:
|
||||
|
||||
```cpp
|
||||
const uint16_t runLenTicks = (uint16_t)toggleCounterLocal + 1U;
|
||||
```
|
||||
|
||||
В **`_isr()`** при стартовом **`toggleCounter == N`** выполняется **ровно N** раз ветка `if (toggleCounter) { toggleCounter--; }` подряд, пока счётчик не станет **0**; **следующий** тик попадает в `else` и делает один шаг `switch (signal)`.
|
||||
|
||||
Между двумя такими визитами в `else` проходит **N тиков таймера**, не **N+1**.
|
||||
|
||||
В `buildGateRuns` для того же начального `toggleCounterLocal` в run записывается **`N + 1` тик**. Это даёт **систематическое удлинение каждого сегмента на 1 тик** относительно модели «счётчик убывает N раз до нуля».
|
||||
|
||||
**Следствие:**
|
||||
|
||||
- `totalTicks = Σ lenTicks` в **`IrDmaBackend::startStream`** **больше**, чем число тиков, которое дал бы чистый ISR при том же пакете.
|
||||
- Число внешних итераций `buildGateRuns` (шагов FSM) совпадает с числом таких сегментов; приближённо:
|
||||
`totalTicks ≈ totalTicks_ISR + (число_внешних_шагов)`.
|
||||
|
||||
Короткий кадр: ошибка может «теряться» в допусках приёмника. Длинный (версия) — **накопление** ошибки по времени → сдвиг границ битов → **неверные байты**, в том числе **CRC**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Ручная проверка CRC по логу (пульт)
|
||||
|
||||
Алгоритм: **`IR_FOX::crc8`** (`IR_config.cpp`), два байта как в **`sendDataFULL`**:
|
||||
|
||||
- первый байт CRC = `crc8(data, 0, packSize - 2, poly1)`;
|
||||
- второй = `crc8(data, 0, packSize - 1, poly2)` (в расчёт второго входит уже первый байт CRC).
|
||||
|
||||
Пример **31-байтного** кадра из лога `Frame reject`:
|
||||
|
||||
- Тело **0…28** (29 байт).
|
||||
- Байты **29…30** — CRC на проводе.
|
||||
|
||||
Для фиксированного дампа байтов **0…28** корректная пара CRC по формуле библиотеки — **`6E 54`**, в логе на проводе — **`96 62`** → **не совпадает**; приёмник обоснованно отклоняет кадр.
|
||||
|
||||
Это **не** объясняется разницей AVR vs STM32: счёт идёт по массиву `uint8_t` побайтно.
|
||||
|
||||
Эхо **8 байт** `C8 FA 2A FD E8 5D AA B4`: пересчёт даёт **`AA B4`** — совпадает с последними байтами кадра → для этого пакета цепочка **байт → CRC** согласована.
|
||||
|
||||
---
|
||||
|
||||
## 5. Скрипт симуляции
|
||||
|
||||
В репозитории: **`docs/scripts/ir_protocol_gate_runs_sim.py`**.
|
||||
|
||||
Запуск:
|
||||
|
||||
```bash
|
||||
python docs/scripts/ir_protocol_gate_runs_sim.py
|
||||
```
|
||||
|
||||
Скрипт:
|
||||
|
||||
1. Считает **CRC** для примеров пакетов (8 байт эха и 31 байт из reject).
|
||||
2. Воспроизводит логику **`buildGateRuns`** (с дополнением буфера до `dataByteSizeMax`, как в C++).
|
||||
3. Печатает **`totalTicks`**, число **внешних шагов** FSM и связь **`totalTicks - outer_steps`** как оценку «тиков в модели ISR без +1 на каждый шаг».
|
||||
|
||||
Пример вывода (значения могут слегка отличаться при смене констант в `IR_config.h`):
|
||||
|
||||
- `preambToggle = 97`
|
||||
- для 8-байт пакета: сотни шагов FSM, `totalTicks` порядка тысяч тиков
|
||||
- для 31-байт: больше шагов и `totalTicks` (~25k+ тиков для текущих констант)
|
||||
|
||||
---
|
||||
|
||||
## 6. Связь с проектами
|
||||
|
||||
- **Car** (`Executer.cpp`): ответ версии через **`IR_Module::getENC().sendData(...)`** — тот же **`sendDataFULL`**, затем **`rawSend`** → DMA.
|
||||
- **ControlPointUnion** (`CustomCmd.h`, слоты): запрос версии через **`sendResp`** с **`version_query`** — задержка **`IR_ResponseDelay`**, затем **`sendData`** на адрес машинки.
|
||||
- **ControlPointUnion** (`Plan_B.ino`): разбор **`version_response`** из **`gotData` / `gotBackData`** только после **успешного CRC** в декодере.
|
||||
|
||||
---
|
||||
|
||||
## 7. Выводы
|
||||
|
||||
1. **Байты в RAM** на передаче формируются корректно библиотекой; проблема «после DMA» укладывается в **расхождение тайминговой развёртки** (`buildGateRuns` + DMA) со **старой** развёрткой ISR, а не в «другой CRC на машинке» при неизменённой библиотеке.
|
||||
2. **Подозрение №1:** `runLenTicks = toggleCounter + 1` в **`buildGateRuns`** не совпадает с числом тиков ISR между шагами FSM (**`N`** vs **`N+1`**). Требуется сверка с эталонной трассой ISR или логическим анализатором.
|
||||
3. **Проверка на будущее:** сравнить побитово выходы ISR и DMA на **одном** буфере (8 и 31 байт); при необходимости поправить формулу длины run в **`IR-protocol`** и пересобрать Car и пульт.
|
||||
|
||||
---
|
||||
|
||||
## 8. Ссылки на файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| `Documents/Arduino/libraries/IR-protocol/IR_Encoder.cpp` | `buildGateRuns`, `_isr`, `rawSend` |
|
||||
| `Documents/Arduino/libraries/IR-protocol/IR_config.cpp` | `crc8` |
|
||||
| `Car/src/IR/IR.cpp` | `setExternalTxBackend`, `txStart` |
|
||||
| `Car/src/IR/IrDmaBackend.cpp` | `startStream`, `totalTicks`, `nextWord` |
|
||||
| `ControlPointUnion/Plan_B/TestPoints/CustomCmd.h` | `sendResp` / `version_query` для тестовых слотов |
|
||||
|
||||
---
|
||||
|
||||
*Документ составлен по обсуждению в чате; при смене версии IR-protocol числа констант и `totalTicks` пересчитывайте скриптом.*
|
||||
Reference in New Issue
Block a user