Files
templates/c/protocan-transport/docs/GAS.md
Andrey Kruchinkin 3dc636e012 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 проходят.
2026-08-23 01:15:35 +03:00

4.8 KiB
Raw Blame History

Общее адресное пространство (GAS)

Плоское пространство 16-битных регистров с адресом 0x0000..0xFFFF. Пространство собирается из регионов; регионы не перекрываются и хранятся отсортированными по адресу, поиск — двоичный.

Регион

typedef struct pcan_gas_region {
    uint16_t          base;      /* адрес первого регистра */
    uint16_t          count;     /* число регистров        */
    uint16_t         *storage;   /* массив либо NULL       */
    pcan_gas_read_fn  read;
    pcan_gas_write_fn write;
    uint8_t           flags;     /* PCAN_GAS_RDONLY / PCAN_GAS_WRONLY */
    void             *user;
    const char       *name;
} pcan_gas_region_t;

Если storage != NULL, чтение и запись идут прямо в массив — это самый дешёвый вариант для обычных уставок. Если нужен вычисляемый регистр (счётчик, состояние периферии, время работы), задайте read/write: колбэк получает смещение внутри региона и указатель user.

pcan_gas_map_validate() проверяет карту на этапе старта: нулевые регионы, выход за 0xFFFF, перекрытие, нарушение порядка и регион без источника данных. Вызывайте её один раз при инициализации — ошибка в таблице ловится сразу, а не через месяц в поле.

Доступ

uint16_t v;
pcan_gas_read(&map, 0x0002, &v);
pcan_gas_write(&map, 0x0002, 0x1234);

uint16_t block[4];
uint16_t n = pcan_gas_read_block(&map, 0x0000, block, 4);

Блочное чтение обрывается на первом адресе, которого нет в карте, поэтому вызывающий всегда получает непрерывный кусок и знает его длину.

Отображение на кадры ProtoCAN

Тип сообщения PCAN_MSG_GAS (0b0011). Адрес первого регистра лежит в MsgBody, данные — до 4 регистров подряд, младшим байтом вперёд. Это ровно то, что делает PROTOCAN_SEND_GENERAL_ADDRESS_SPACE() в SETCAN/Src/protocan.c, поэтому обмен совместим с существующими устройствами.

Кадр DLC Смысл
GAS, MsgBody = addr 0 запрос на чтение
GAS, MsgBody = addr 2..8 значения регистров начиная с addr

Запрос на чтение с DLC = 0расширение: в исходном коде SETCAN такой кодировки нет, там ответы на GAS формировала заглушка ProtoCanMsgToGeneralAddressSpace(), возвращавшая строку GAS-XXXX. Кодировка выбрана так, чтобы не занимать новых типов сообщений и не конфликтовать с существующим форматом ответа.

pcan_frame_t rsp;
if (pcan_gas_handle(&map, &incoming, &rsp)) {
    pcan_link_send(&link, &rsp);   /* был запрос на чтение */
}

pcan_gas_handle():

  • на запрос чтения кладёт в rsp до 4 регистров и переключает Route на FROM_DEVICE, возвращает true;
  • на запись пишет регистры в карту и возвращает false — ответа нет;
  • если адреса нет в карте, возвращает false: отвечать нечем, а молчание честнее, чем ответ с нулями.

Ограничения

  • В один кадр помещается не больше 4 регистров (PCAN_GAS_REGS_PER_FRAME). Длинные блоки разбивайте на несколько кадров.
  • Частичная запись: если в середине блока попался адрес вне карты или регион только для чтения, запись обрывается на нём. pcan_gas_write_block() возвращает число фактически записанных регистров.
  • Атомарности между регистрами нет. Если два регистра обязаны меняться вместе, заведите колбэк, который применяет их по записи второго.