Перенесён из репозитория protocan-transport, который подключался сабмодулем в CAN_to_RS485. Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232, USB CDC), разбор 29-битного идентификатора, общее адресное пространство регистров и каталог с подпиской на поток значений для SETGUI. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте. Хостовые тесты test_transport и test_gui проходят.
8.1 KiB
Каталог общего адресного пространства и поток значений
Расширение GUI-протокола SETGUI (A5 5A, см. gui_desktop/core/protocol.py)
для работы с общим адресным пространством: прибор сам объявляет, какие
регистры у него есть и как они называются, а оператор выбирает, что
показывать. Схема повторяет то, как устроен реестр регистров в
ST Motor Control Workbench / Motor Pilot.
Транспорт не меняется: A5 5A | ver | type | seq(BE) | size(BE) | payload | CRC32(LE),
payload до 512 байт. Добавлены только три типа сообщений.
прибор GUI
│ ── GAS_CATALOG (seq = 0) ──────────► │ каталог приходит сам
│ ── GAS_CATALOG (seq = 0) ──────────► │ при инициализации
│ ── GAS_CATALOG (seq = 0) ──────────► │
│ │ оператор отмечает нужное
│ ◄──────────────── GAS_WATCH_SET ──── │
│ ── GAS_WATCH_SET (эхо) ────────────► │
│ ── GAS_WATCH_DATA (seq = 0) ───────► │ поток значений
│ ── GAS_WATCH_DATA (seq = 0) ───────► │
Типы сообщений
Заняты из свободного диапазона 0x11..0x1F (между READ_LOGS = 0x10
и SENSOR_SCAN = 0x20). Существующие значения не перенумерованы.
| Код | Имя | Направление |
|---|---|---|
0x11 |
GAS_CATALOG |
запрос GUI → прибор; записи прибор → GUI |
0x12 |
GAS_WATCH_SET |
GUI → прибор, прибор отвечает эхом |
0x13 |
GAS_WATCH_DATA |
прибор → GUI, без запроса, sequence = 0 |
Незапрошенные кадры прибор публикует с sequence = 0 — так же, как уже
устроены SENSOR_DATA и UI_STATE.
GAS_CATALOG (0x11)
Запрос (GUI → прибор), 4 байта
| Смещение | Тип | Поле |
|---|---|---|
| 0 | u16 LE | start_index — порядковый номер записи, не адрес |
| 2 | u16 LE | max_count — сколько записей вернуть; 0 = сколько влезет |
Ответ и автопубликация (прибор → GUI)
| Смещение | Тип | Поле |
|---|---|---|
| 0 | u16 LE | total — всего записей в каталоге |
| 2 | u16 LE | start_index — индекс первой записи в пакете |
| 4 | u16 LE | count — записей в пакете |
| 6 | count записей по 32 байта |
Одна запись — 32 байта:
| Смещение | Тип | Поле |
|---|---|---|
| 0 | u16 LE | address — адрес в общем адресном пространстве |
| 2 | u8 | type — формат значения |
| 3 | u8 | flags — доступ |
| 4 | i8 | scale_pow10 — значение = raw * 10^scale |
| 5 | u8 | unit — код единицы измерения |
| 6 | u16 LE | резерв, нули |
| 8 | 24 байта | name — UTF-8, дополнено нулями |
При payload 512 байт в один пакет входит (512 - 6) / 32 = 15 записей.
Под имя отведено 24 байта — это 12 кириллических символов в UTF-8. На 16 байтах не помещалось даже «Температура», поэтому поле шире, чем кажется нужным для латиницы.
type
| Код | Значение | Регистров |
|---|---|---|
| 0 | U16 — беззнаковое |
1 |
| 1 | I16 — знаковое |
1 |
| 2 | U32 — беззнаковое, младшее слово первым |
2 |
| 3 | I32 — знаковое, младшее слово первым |
2 |
| 4 | BITS — битовое поле |
1 |
Многословные значения занимают подряд идущие адреса; в потоке они приходят отдельными словами, GUI собирает их сам.
flags
| Бит | Смысл |
|---|---|
| 0 | доступно на чтение |
| 1 | доступно на запись |
| 2 | включить в подписку по умолчанию |
unit
| Код | Единица | Код | Единица |
|---|---|---|---|
| 0 | — | 6 | мс |
| 1 | В | 7 | с |
| 2 | А | 8 | кбит/с |
| 3 | °C | 9 | шт. |
| 4 | % | 10 | об/мин |
| 5 | Гц | 11 | Вт |
GAS_WATCH_SET (0x12)
Запрос (GUI → прибор)
| Смещение | Тип | Поле |
|---|---|---|
| 0 | u16 LE | period_ms — период потока; 0 останавливает поток |
| 2 | u16 LE | count — число адресов, не больше GAS_WATCH_MAX (64) |
| 4 | count × u16 LE — адреса в нужном порядке |
Порядок адресов сохраняется: значения в GAS_WATCH_DATA приходят
ровно в том же порядке, без повторной передачи адресов.
Ответ (прибор → GUI), 4 байта
| Смещение | Тип | Поле |
|---|---|---|
| 0 | u16 LE | period_ms — период, который прибор реально установил |
| 2 | u16 LE | count — сколько адресов принято |
Прибор может принять меньше, чем попросили: адрес вне карты в подписку
не берётся. Расхождение count с запросом — сигнал GUI, что часть
адресов отвергнута.
GAS_WATCH_DATA (0x13)
Прибор → GUI, sequence = 0, без запроса.
| Смещение | Тип | Поле |
|---|---|---|
| 0 | u32 LE | timestamp_ms — время прибора от старта |
| 4 | u16 LE | count |
| 6 | count × u16 LE — значения в порядке подписки |
timestamp_ms берётся у прибора, а не у хоста: по нему видно реальный
период и провалы, которые иначе замаскировала бы буферизация UART.
Замечания по реализации
- Каталог статичен. Он описывает прошивку, а не состояние, поэтому публикуется один раз при инициализации периферии. GUI может перечитать его запросом в любой момент.
- Подписка живёт до переподключения. Прибор не сохраняет её в
энергонезависимой памяти: после сброса поток молчит, пока GUI не
пришлёт
GAS_WATCH_SETснова. - Поток не должен забивать линию. При периоде 10 мс и 64 адресах
выходит 134 байта на пакет и 13.4 кбайт/с — половина пропускной
способности 256000 бод. Прибор пропускает такт, если в очереди
передачи нет места для целого пакета, и это видно по разрыву
timestamp_ms. - Значения передаются сырыми. Пересчёт в физические величины делает
GUI по
scale_pow10иunit— прибор не тратит на это такты и не теряет точность на промежуточном округлении.