Перенесён из репозитория protocan-transport, который подключался сабмодулем в CAN_to_RS485. Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232, USB CDC), разбор 29-битного идентификатора, общее адресное пространство регистров и каталог с подпиской на поток значений для SETGUI. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте. Хостовые тесты test_transport и test_gui проходят.
4.8 KiB
Общее адресное пространство (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()возвращает число фактически записанных регистров. - Атомарности между регистрами нет. Если два регистра обязаны меняться вместе, заведите колбэк, который применяет их по записи второго.