# Общее адресное пространство (GAS) Плоское пространство 16-битных регистров с адресом `0x0000..0xFFFF`. Пространство собирается из **регионов**; регионы не перекрываются и хранятся отсортированными по адресу, поиск — двоичный. ## Регион ```c 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`, перекрытие, нарушение порядка и регион без источника данных. Вызывайте её один раз при инициализации — ошибка в таблице ловится сразу, а не через месяц в поле. ## Доступ ```c 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`. Кодировка выбрана так, чтобы не занимать новых типов сообщений и не конфликтовать с существующим форматом ответа. ```c 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()` возвращает число фактически записанных регистров. - Атомарности между регистрами нет. Если два регистра обязаны меняться вместе, заведите колбэк, который применяет их по записи второго.