feat(protocan-transport): транспортный уровень ProtoCAN и каталог GUI

Перенесён из репозитория protocan-transport, который подключался
сабмодулем в CAN_to_RS485.

Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232,
USB CDC), разбор 29-битного идентификатора, общее адресное пространство
регистров и каталог с подпиской на поток значений для SETGUI. Состояние
живёт в структурах вызывающего, поэтому в одной прошивке поднимается
сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте.

Хостовые тесты test_transport и test_gui проходят.
This commit is contained in:
2026-08-23 01:15:35 +03:00
parent 873ac438f3
commit 3dc636e012
27 changed files with 3646 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
# Транспортный кадр
## Формат
```
+------+------+-----+-----+-------+----------------+-------------+-------+-------+
| 0xAA | 0x55 | LEN | SEQ | FLAGS | ID0 ID1 ID2 ID3| DATA[0..8] | CRC_L | CRC_H |
+------+------+-----+-----+-------+----------------+-------------+-------+-------+
```
| Поле | Байт | Описание |
|---|---:|---|
| SOF | 2 | сигнатура `0xAA 0x55` |
| `LEN` | 1 | длина участка `SEQ..DATA` = `6 + DLC`, диапазон 6..14 |
| `SEQ` | 1 | счётчик кадров 0..255, инкремент на каждый успешно отданный кадр |
| `FLAGS` | 1 | см. ниже |
| `ID` | 4 | 29-битный CAN-идентификатор, little-endian (старшие 3 бита = 0) |
| `DATA` | 0..8 | `DLC = LEN - 6` байт данных CAN |
| `CRC` | 2 | CRC-16/CCITT-FALSE, little-endian |
CRC считается по байтам от `LEN` до последнего байта `DATA` включительно;
сигнатура SOF в расчёт не входит. Полином `0x1021`, начальное значение
`0xFFFF`, без рефлексии и без финального XOR — контрольное значение для
строки `123456789` равно `0x29B1`.
Максимальный размер кадра — 19 байт (`PCAN_FRAME_MAX`).
## FLAGS
| Бит | Имя | Значение |
|---:|---|---|
| 0 | `IDE` | 1 = расширенный ID (29 бит), 0 = стандартный (11 бит) |
| 1 | `RTR` | 1 = remote frame |
| 2 | `DIR` | 0 = кадр пришёл из CAN, 1 = кадр надо передать в CAN |
| 3 | `ERR` | 1 = служебный кадр моста (диагностика), не трафик шины |
| 7..4 | — | резерв, передавать нулями |
## Синхронизация
Приёмник ищет `0xAA 0x55`, читает `LEN`, проверяет диапазон `6..14`,
набирает `LEN + 2` байт и сверяет CRC. При неверном `LEN` или несовпадении
CRC разборщик возвращается к поиску сигнатуры, причём байт, оборвавший
разбор, сам проверяется на `0xAA` — последовательность `AA AA 55` тоже
распознаётся. Потеря синхронизации стоит не больше одного кадра.
Счётчики разбора (`pcan_parse_stats_t`) отдельно считают кадры, ошибки CRC,
неверные `LEN` и байты вне кадров — по ним видно, шумит линия или сбоит
источник.
## SEQ
`SEQ` инкрементируется только на успешно отданном кадре: если кадр не влез
в очередь передачи, счётчик не двигается, и приёмник не засчитает потерю
там, где кадра просто не было. Разрыв в `SEQ` на приёме означает реальную
потерю в линии.
## Почему так
- **Длина плюс CRC, без байт-стаффинга.** Стаффинг раздувает кадр
непредсказуемо и усложняет расчёт таймингов на полудуплексной линии.
Фиксированный заголовок даёт заранее известный максимум 19 байт.
- **Сигнатура из двух байт.** Один байт слишком часто встречается в
случайных данных; два дают приемлемую вероятность ложного старта,
который всё равно отсеет CRC.
- **`LEN` в начале.** Приёмник сразу знает, сколько байт набирать, и не
зависит от содержимого данных.
- **Little-endian везде.** Совпадает с порядком регистров в
`PROTOCAN_SEND_GENERAL_ADDRESS_SPACE()` и с обоими целевыми МК.
## Пример
CAN-кадр: ID `0x1234567`, DLC 2, данные `AA BB`, `SEQ = 1`, флаг `IDE`.
```
AA 55 08 01 01 67 45 23 01 AA BB FE 14
```
`LEN = 6 + 2 = 8`, `FLAGS = 0x01`, CRC = `0x14FE`.