Files
IR-protocol/ref/IR_EDGE_TRACE_FORMAT.md
2026-04-02 17:25:10 +03:00

6.0 KiB
Raw Permalink Blame History

Формат журнала фронтов ИК (IR_EDGE_TRACE)

Включается в IR_config.h: раскомментируйте #define IR_EDGE_TRACE.
Размер кольца задаётся IR_EDGE_TRACE_CAPACITY (по умолчанию 512 записей).
Память: примерно IR_EDGE_TRACE_CAPACITY × 6 байт на экземпляр IR_DecoderRaw.

Назначение

В ISR на каждый аппаратный фронт на линии приёмника пишется запись: абсолютное время micros(), уровень линии после фронта, флаги. Это отдельное кольцо от subBuffer: при переполнении subBuffer фронты в журнале всё равно сохраняются, пока не заполнится это кольцо.

При передаче ИК по DMA на STM32 важно, чтобы прерывание приёма (EXTI) имело более высокий приоритет NVIC, чем DMA канала передачи — иначе метки времени и сам поток фронтов в логе искажаются. См. IR_DMA_ISR_signal_analysis.md (раздел 2.1).

Вывод в Serial

При включённом IR_EDGE_TRACE протокол сам сбрасывает накопленные фронты в конце каждого IR_DecoderRaw::tick() (и на ветке «subBuffer пуст»): в цикле вызывается edgeTraceFlushChunk(Serial, 48), пока в кольце есть записи.

Вручную тот же смысл: edgeTraceFlushChunk(Print &out, maxRec) печатает ровно одну строку (если есть что выгрузить; при пустом кольце выход без печати):

  1. Символ перевода строки \n (отделяет блок от предыдущего вывода).
  2. Литеральный префикс @IRF1v1: (удобно grep-ать; не используйте этот текст в других Serial.print, чтобы строки не сливались).
  3. Нижний регистр hex без пробелов: полезная нагрузка бинарного блока ниже.
  4. \n в конце.

Другой текст (например IR raw: из IRDEBUG_SERIAL_PACK) не содержит @IRF1v1:, поэтому визуально и по парсеру блоки разделимы.

Если авто-сброс в tick() отключён или нужен другой Print, вызывайте edgeTraceFlushChunk в цикле, пока возвращаемое значение > 0.

Бинарная полезная нагрузка (до кодирования в hex)

Все многобайтовые целые — little-endian, порядок байт от младшего к старшему.

Смещение Размер Поле
0 1 meta
1 2 count — число записей в этой строке (uint16_t)
3 count × 6 Массив записей

meta (байт)

Бит Значение
0 overflow: кольцо хотя бы раз переполнилось с момента последнего edgeTraceClear(); новые фронты терялись, пока не освободилось место. Сбрасывается только edgeTraceClear().
1 truncated: после этой выгрузки в буфере ещё есть записи (chunk урезан лимитом maxRec или внутренним максимумом 64).

Биты 27 зарезервированы (0).

Одна запись (6 байт)

Смещение в записи Размер Поле
0 4 t_us — значение micros() на момент фронта (uint32_t). При переполнении micros() (~70 мин) разницы между соседними записями всё ещё корректны, если обрабатывать как unsigned.
4 1 level — уровень входа приёмника после фронта: 0 = LOW, 1 = HIGH (как port->IDR & mask в ISR).
5 1 flags
бит 0 SKIP_DECODE (IR_EDGE_TRACE_F_SKIP_DECODE): фронт записан, но в subBuffer не попал, потому что был активен isPairSending (пара передаёт). Алгоритм декодирования этот фронт не видит.

Минимальный разбор (Python 3)

import binascii, re

def parse_irf1_line(line: str):
    m = re.search(r"@IRF1v1:([0-9a-f]+)\s*$", line.strip())
    if not m:
        return None
    raw = binascii.unhexlify(m.group(1))
    meta, cnt_lo, cnt_hi = raw[0], raw[1], raw[2]
    count = cnt_lo | (cnt_hi << 8)
    recs = []
    p = 3
    for _ in range(count):
        t = raw[p] | (raw[p+1]<<8) | (raw[p+2]<<16) | (raw[p+3]<<24)
        level, flags = raw[p+4], raw[p+5]
        recs.append((t, level, flags))
        p += 6
    return {
        "overflow": bool(meta & 1),
        "truncated": bool(meta & 2),
        "count": count,
        "records": recs,
    }

Рекомендации по съёму

  • При очень плотном потоке фронтов кольцо всё же может переполниться до следующего tick() — увеличьте IR_EDGE_TRACE_CAPACITY или уменьшите нагрузку на ISR.
  • Для «чистого» лога отключите или сильно урежьте IRDEBUG_SERIAL_PACK, иначе объём Serial будет очень большим.
  • Для полного сброса состояния перед тестом: edgeTraceClear().