SETCAN
руководство разработчика
Карта библиотеки ProtoCAN, формат кадров и практический маршрут переноса в существующую прошивку STM32.
Что находится в проекте
SETCAN — не готовый CubeIDE-проект, а подключаемый C-модуль поверх STM32 HAL. Его задача — фильтровать, принимать, разбирать и отправлять прикладные сообщения ProtoCAN.
Что ожидает модуль
main.hиcan.hиз CubeMX- STM32 HAL CAN, RTC и TIM
- CAN FIFO0 и Extended ID
- STM32 GCC ABI для C bit-fields
Путь кадра
IRQ вычитывает FIFO0. Pulse обрабатывается сразу, остальные Extended-кадры попадают в кольцевой буфер и разбираются в основном цикле.
Единая точка отправки
PROTOCAN_SEND() выбирает упаковщик по MsgType. GAS и Modbus автоматически разбиваются на кадры до 8 байт.
Что приложение реализует само
Прикладные действия, хранение SETTINGS, EEPROM, DS18B20, таймаут online/offline и полноценный bootloader не входят в ядро.
Состав библиотеки
Два файла образуют один модуль. Заголовок — контракт интеграции; C-файл — транспортная реализация и демонстрационные слабые обработчики.
protocan.h
Конфигурация текущего устройства, размеры буфера, enum типов сообщений, битовые представления ID/Body, структуры TX/RX и публичные функции.
Меняют при переносе: CURRENT_TYPE_DEVICE, CURRENT_ID_DEVICE, иногда PROTOCAN_RX_BUFFER_SIZE.
protocan.c
Инициализация, фильтры, кольцевой буфер, диспетчеризация, RTC sync, pulse и упаковка кадров.
Обычно не правят: прикладную логику лучше добавлять переопределением __weak-функций.
Внешний слой
CAN_HandleTypeDef, RTC_HandleTypeDef и TIM_HandleTypeDef передаются в PROTOCAN_INIT(). Запуск периферии выполняет приложение.
Основной API
| Функция | Назначение | Где вызывать |
|---|---|---|
| PROTOCAN_INIT | Сохраняет HAL handles, настраивает фильтры и callback’и при доступной регистрации. | После MX_*_Init(), до HAL_CAN_Start(). |
| PROTOCAN_ProcessAllRxMsgs | Обрабатывает всю накопленную RX-очередь и возвращает первую ошибку. | В основном цикле. |
| PROTOCAN_ProcessSingleRxMsg | Обрабатывает максимум один кадр. | В планировщике или ограниченном временном слоте. |
| PROTOCAN_SEND | Упаковывает и отправляет сообщение выбранного типа. | Из логики приложения, не удерживая изменяемые данные. |
| ProtoCanRxFifo0MsgPendingCallback | Забирает кадры из CAN FIFO0. | Из HAL callback, если авто-регистрация выключена. |
| ProtoCanPulseCallback | Отправляет периодический pulse со счётчиком. | Из callback нужного базового таймера. |
| PROTOCAN_SEND_SETTINGS_RESPONSE / ERROR | Формирует успешный либо ошибочный ответ привязки ROM. | Из пользовательского обработчика SETTINGS. |
Точки расширения
Переопределите нужную __weak-функцию в собственном C-файле: семейства ProtoCanMsgToBroadcast…, …Discrete…, …Analog…, ProtoCanMsgToSettings, ProtoCanMsgToGeneralAddressSpace и …Modbus…. Не редактируйте демонстрационную реализацию в ядре — так обновлять библиотеку проще.
Формат ProtoCAN
Протокол использует только CAN 2.0B Extended ID. Адрес устройства — пара DeviceType/DeviceID; назначение младших 16 бит зависит от типа сообщения.
29-битный идентификатор
28 27 26··24 23··20 19··16 15········0
Priority Route DeviceType DeviceID MsgType MsgBody
1 бит 1 бит 3 бита 4 бита 4 бита 16 битРеестр MsgType
| Код | Тип | Назначение | Статус |
|---|---|---|---|
| 0x0 | BROADCAST | Общие команды всем устройствам | stable |
| 0x1 | DISCRETE | Состояния, команды, флаги | stable |
| 0x2 | ANALOG | Датчики U / I / T и универсальные данные | stable |
| 0x3 | GAS | Общее 16-битное адресное пространство | stable |
| 0x4 | GAS SLOT | Word0 — маска битов 1…3, Word1…Word3 — три значения | stable |
| 0x5…0x7 | MODBUS | Discrete, Holding, Input | stable |
| 0x8 | ERROR | Код ошибки и дополнительная информация | stable |
| 0x9…0xD | BOOT | Управление, блоки A/B, статус, discovery | draft |
| 0xE | SETTINGS | Привязка 1-Wire ROM к локации Z/Y | stable |
| 0xF | PULSE | Присутствие устройства | stable |
Как портировать в свой STM32-проект
Рабочая последовательность для CubeMX/CubeIDE. Имена hcan, hrtc и htim2 замените на handles своего проекта.
Добавьте исходники
Inc/protocan.hв include pathSrc/protocan.cв сборку- Проверьте доступность
main.hиcan.h
Настройте адрес
В protocan.h задайте тип 0…7 и ID 0…15. Пара должна быть уникальной на шине.
#define CURRENT_TYPE_DEVICE 0b011
#define CURRENT_ID_DEVICE 0b0101Настройте CubeMX
- CAN: Extended ID, FIFO0
- RTC: если нужна синхронизация времени
- TIM: период pulse
- Проверьте filter banks 0, 1, 2 и границу 14
Инициализация и основной цикл
#include "protocan.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN_Init();
MX_RTC_Init();
MX_TIM2_Init();
if (PROTOCAN_INIT(&hcan, &hrtc, &htim2) != PROTOCAN_INIT_OK) {
Error_Handler();
}
if (HAL_CAN_Start(&hcan) != HAL_OK) {
Error_Handler();
}
if (HAL_CAN_ActivateNotification(
&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) {
Error_Handler();
}
while (1) {
(void)PROTOCAN_ProcessAllRxMsgs();
}
}Свяжите callback’и при отключённой регистрации HAL
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan_ptr)
{
if (hcan_ptr == &hcan) {
ProtoCanRxFifo0MsgPendingCallback(hcan_ptr);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim_ptr)
{
if (htim_ptr == &htim2) {
ProtoCanPulseCallback(htim_ptr);
}
}Не дублируйте события
Если USE_HAL_*_REGISTER_CALLBACKS == 1, библиотека регистрирует callback’и сама. Не вызывайте те же функции ещё раз из стандартных callback’ов.
Перенесите прикладную логику
Создайте, например, protocan_app.c и определите там только нужные weak handlers. Так ядро остаётся обновляемым.
#include "protocan.h"
PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg msg)
{
msgBodyAnalogType body = {0};
body.Body = msg.eID.Fields.MsgBody;
uint16_t sensor_id = body.Fields.SensorID;
/* Получить температуру sensor_id и сформировать ответ. */
return PROTOCAN_OK;
}Проверка после переноса
- Проект собирается без повторных определений HAL callback’ов.
- CAN стартует и принимает только Extended ID.
- Pulse появляется с периодом выбранного таймера.
- Эталонный ID
0x13590702декодируется как DeviceType=3, DeviceID=5, MsgType=9, Body=0x0702. - При нагрузке учтено, что буфер размера 128 хранит максимум 127 кадров.
Готовые шаблоны
Минимальные примеры формирования исходящего кадра и обработчика SETTINGS. Их можно вынести в прикладной слой проекта.
Отправка трёх регистров GAS
uint16_t registers[] = { 0x1234, 0x5678, 0x9ABC };
ProtoCanId_t id = {0};
id.Fields.Priority = PROTOCAN_PRIORITY_STANDARD;
id.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE;
id.Fields.DeviceType = CURRENT_TYPE_DEVICE;
id.Fields.DeviceID = CURRENT_ID_DEVICE;
id.Fields.MsgType = PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE;
ProtoCanData_t tx = {0};
tx.GeneralAddressSpaceData.RegStartAdr = 100;
tx.GeneralAddressSpaceData.Data = registers;
tx.GeneralAddressSpaceData.RegCount = 3;
if (PROTOCAN_SEND(id, tx) != PROTOCAN_OK) {
/* CAN занят или HAL вернул ошибку. */
}SETTINGS: прикладное хранение ROM
PROTOCAN_StatusTypeDef ProtoCanMsgToSettings(
const ProtoCanSettingsMsg_t *message)
{
uint8_t current_rom[PROTOCAN_SETTINGS_ROM_SIZE] = {0};
switch (message->Operation) {
case PROTOCAN_SETTINGS_GET:
/* Загрузить ROM из EEPROM в current_rom. */
return PROTOCAN_SEND_SETTINGS_RESPONSE(
message->AssemblySerial, message->Position, current_rom);
case PROTOCAN_SETTINGS_WRITE:
/* Проверить CRC/наличие и атомарно записать message->Rom. */
return PROTOCAN_SEND_SETTINGS_RESPONSE(
message->AssemblySerial, message->Position, message->Rom);
case PROTOCAN_SETTINGS_CLEAR:
/* Очистить запись; current_rom должен содержать нули. */
return PROTOCAN_SEND_SETTINGS_RESPONSE(
message->AssemblySerial, message->Position, current_rom);
default:
return PROTOCAN_SEND_SETTINGS_ERROR(
message->AssemblySerial, message->Position,
PROTOCAN_SETTINGS_RESULT_INVALID_DLC);
}
}Полная документация ProtoCAN
Нормативные документы и тестовые векторы собраны в эту страницу из исходников doc/setcan/protocan.
Документация ProtoCAN
Статус комплекта: Draft
Версия комплекта: 2.1
Дата редакции: 2026-10-23
Транспорт: Classic CAN 2.0B, Extended ID, DLC 0…8
Этот каталог разделяет нормативное описание протокола, загрузчик и реестр
общего адресного пространства. Большой исходный документ
Протокол CAN и ОАП.md сохранён как
совместимое представление таблиц из Excel.
Документы
| Документ | Назначение | Статус источника |
|---|---|---|
| PROTOCOL.md | 29-битный CAN ID, адресация, реестр MsgType, порядок байтов |
нормативный |
| BOOTLOADER.md | обновление прошивки, кадры, состояния, ошибки и A/B-слоты | нормативный draft |
| OAP.md | правила ведения общего адресного пространства | нормативный индекс |
| ../Протокол CAN и ОАП.xlsx | редактируемый реестр ОАП | источник таблиц |
| examples/test-vectors.json | машинные эталоны CAN ID и payload | нормативные примеры |
| CHANGELOG.md | история версий документа | нормативный |
Приоритет источников
При расхождении данных действует следующий порядок:
PROTOCOL.md— структура ProtoCAN и реестр типов сообщений.BOOTLOADER.md— загрузочный сервис0x9…0xD.- XLSX — адреса и свойства регистров ОАП.
- Сгенерированный
../index.html— только представление, не самостоятельный источник.
Сборка HTML
Из корня проекта:
./doc/setcan/build-html.bat
Все документы и тестовые векторы включаются в единый файл
doc/setcan/index.html.
Скрипт не изменяет исходные Markdown/XLSX и пригоден для запуска в CI.
GAS: регистры 0x3 и слоты 0x4
Адресация GAS: 0x3 — отдельные 16-битные регистры 0…65535, запись 1…4 слов. 0x4 — слот с Word0-маской и тремя значениями; PERIPH сохраняет 200 слов в 67 слотах 0xMM00…0xMM42. Формат определяется MsgType, без настройки профиля.
ProtoCAN — базовый протокол
Статус: Stable с зарезервированным загрузочным расширением
Версия: 2.1: GAS 0x3 и масочный GAS SLOT 0x4
Порядок байтов payload: little-endian, если явно не указано иное
Назначение
ProtoCAN — прикладной протокол поверх classic CAN 2.0B. Используются только
расширенные 29-битные идентификаторы (IDE=1) и payload длиной 0…8 байт.
Термины
| Термин | Значение |
|---|---|
| ПМ | управляющий модуль |
| прибор | адресуемый узел на шине |
DeviceType |
тип прибора, 0…7 |
DeviceID |
экземпляр прибора данного типа, 0…15 |
MsgType |
класс сообщения или сервис |
MsgBody |
16-битное поле, формат которого зависит от MsgType |
Пара DeviceType/DeviceID задаёт до 8 × 16 = 128 уникальных адресов.
Расширенный CAN ID
28 27 26...24 23...20 19...16 15........0
Priority Route DeviceType DeviceID MsgType MsgBody
1 бит 1 бит 3 бита 4 бита 4 бита 16 бит
can_id =
((uint32_t)priority << 28) |
((uint32_t)route << 27) |
((uint32_t)device_type << 24) |
((uint32_t)device_id << 20) |
((uint32_t)msg_type << 16) |
msg_body;
| Поле | Значения | Назначение |
|---|---|---|
Priority |
0 critical, 1 standard |
CAN-арбитраж |
Route |
0 от ПМ, 1 от прибора |
логическое направление |
DeviceType |
0…7 |
тип прибора |
DeviceID |
0…15 |
номер экземпляра |
MsgType |
0…15 |
тип сообщения |
MsgBody |
0…65535 |
команда, адрес или номер блока |
Route не является направлением физического трансивера. Ответ прибора
сохраняет адрес DeviceType/DeviceID и устанавливает Route=1.
Реестр MsgType
| Код | Имя | Основное направление | DLC | Статус |
|---|---|---|---|---|
0x0 |
BROADCAST |
ПМ → все | зависит от команды | stable |
0x1 |
DISCRETE |
оба | 0…8 | stable |
0x2 |
ANALOG |
оба | 0…8 | stable |
0x3 |
GAS |
оба | 0/2/4/6/8 | регистры 0…65535 |
0x4 |
GAS_SLOT |
оба | 0/8 | Word0 — маска, Word1…Word3 — значения |
0x5 |
MODBUS_DISCRETE |
оба | 0…8 | stable |
0x6 |
MODBUS_HOLDING |
оба | 0…8 | stable |
0x7 |
MODBUS_INPUT |
оба | 0…8 | stable |
0x8 |
ERROR |
прибор → ПМ | 0 | stable |
0x9 |
BOOT_CONTROL |
ПМ → прибор | 0/8 | draft |
0xA |
BOOT_DATA_A |
ПМ → прибор | 8 | draft |
0xB |
BOOT_DATA_B |
ПМ → прибор | 8 | draft |
0xC |
BOOT_STATUS |
прибор → ПМ | 8 | draft |
0xD |
BOOT_DISCOVERY |
прибор → ПМ | 8 | draft |
0xE |
SETTINGS |
оба | 0/1/8 | stable |
0xF |
PULSE |
прибор → сеть | 1 | stable |
Подробный формат 0x9…0xD находится в BOOTLOADER.md.
Разметки MsgBody
MsgType |
Биты MsgBody |
|---|---|
| broadcast | команда [15:4], параметр [3:0] |
| discrete/analog | подтип [15:12], значение/адрес [11:0] |
| Modbus | начальный адрес [15:4], количество [3:0] |
| GAS 0x3 | адрес первого 16-битного регистра [15:0] |
| GAS SLOT 0x4 | адрес слота [15:0]; маска — в Word0 payload, значения — Word1…Word3 |
| error | дополнительная информация [15:8], код [7:0] |
| settings | номер сборки [15:8], позиция [7:0] |
| boot control/status | SessionID[15:8], команда [7:0] |
| boot data | BlockIndex[15:0] |
Контракт обоих GAS: OAP.md. Тип 0x3 адресует отдельные слова; тип 0x4 имеет маску битов 1…3 в Word0 и три значения. Для PERIPH 0x4 используются 67 слотов 0xMM00…0xMM42, последний содержит два значения. Старый Coil 0x4 заменён; формат определяется MsgType.
Общие правила обмена
Профиль синхронизации часов и сообщения платы о завершении инициализации: GLOBAL_TIME.md. Он сохраняет штатный BROADCAST RTCSETUP и определяет общий пакет готовности поверх DISCRETE/FLAGS.
- Многобайтовые значения в
DATAпередаются little-endian. - Узел игнорирует адресованные кадры с чужим
DeviceType/DeviceID. - Прибор принимает команды ПМ с
Route=0; ПМ принимает ответы сRoute=1. - Стандартные 11-битные CAN ID не являются кадрами ProtoCAN.
- RTR для загрузочного сервиса запрещён.
- Неописанные комбинации
MsgType/MsgBody/DLCдолжны отвергаться.
Эталон упаковки ID
Priority = 1
Route = 0
DeviceType = 3
DeviceID = 5
MsgType = 0x9
MsgBody = 0x0702
CAN ID = 0x13590702
Этот пример соответствует ENTER_BOOT, SessionID=7. Машинные варианты
находятся в examples/test-vectors.json.
ProtoCAN Boot Protocol
Статус: Draft
Версия протокола: 1.0
Совместимость: classic CAN 2.0B, Extended ID, DLC 0…8
Реализация: templates/c/protocan-boot
Назначение
Сервис обновляет адресованный прибор по CAN и поддерживает два логических слота A/B. Активный слот не стирается: новый образ записывается в неактивный, проверяется и атомарно назначается кандидатом на запуск.
Карта сообщений
MsgType |
Имя | MsgBody |
Payload |
|---|---|---|---|
0x9 |
BOOT_CONTROL |
SessionID[15:8] \| Command[7:0] |
параметры команды |
0xA |
BOOT_DATA_A |
BlockIndex[15:0] |
8 байт слота A |
0xB |
BOOT_DATA_B |
BlockIndex[15:0] |
8 байт слота B |
0xC |
BOOT_STATUS |
SessionID[15:8] \| Command[7:0] |
статус и прогресс |
0xD |
BOOT_DISCOVERY |
подтип ответа | идентификация |
Все команды записи адресуются конкретному DeviceType/DeviceID и имеют
Route=0. Ответы сохраняют адрес прибора и имеют Route=1.
Адресация образа
MsgBody кадра данных — номер 8-байтового блока:
offset = (uint32_t)BlockIndex * 8U;
address = SLOT_X_BASE + offset;
512 КиБ = 524 288 байт
524 288 / 8 = 65 536 блоков
BlockIndex = 0x0000…0xFFFF
BlockIndex |
Смещение | Диапазон байтов |
|---|---|---|
0x0000 |
0x00000 |
0x00000…0x00007 |
0x0001 |
0x00008 |
0x00008…0x0000F |
0xFFFF |
0x7FFF8 |
0x7FFF8…0x7FFFF |
0x80000 является первой позицией за границей слота. Последний кадр
дополняется 0xFF, но CRC32 вычисляется только по ImageSize байтам.
Команды BOOT_CONTROL
| Код | Команда | DLC | Payload | Допустимое состояние |
|---|---|---|---|---|
0x01 |
IDENTIFY |
0 | отсутствует | любое |
0x02 |
ENTER_BOOT |
0 | отсутствует | любое; SessionID != 0 |
0x03 |
BEGIN_IMAGE |
8 | размер и CRC32 | metadata |
0x04 |
BEGIN_COMPAT |
8 | совместимость и версия | metadata |
0x05 |
ERASE |
0 | отсутствует | ready-to-erase |
0x06 |
VERIFY |
0 | отсутствует | образ получен |
0x07 |
COMMIT |
0 | отсутствует | verified |
0x08 |
CONFIRM |
0 | отсутствует | запущенное приложение |
0x09 |
REBOOT |
0 | отсутствует | активная сессия |
0x0A |
ABORT |
0 | отсутствует | активная сессия |
0x0B |
QUERY_PROGRESS |
0 | отсутствует | активная сессия |
BEGIN_IMAGE
DATA[0..3] ImageSize, uint32 little-endian
DATA[4..7] ImageCRC32, uint32 little-endian
BEGIN_COMPAT
DATA[0..1] ProductType, uint16 little-endian
DATA[2] HardwareRevisionMin
DATA[3] HardwareRevisionMax
DATA[4..7] FirmwareVersion, uint32 little-endian
До ERASE прибор обязан получить обе части метаданных и проверить размер,
тип изделия, аппаратную ревизию, версию и политику anti-rollback.
BOOT_STATUS
MsgBody[15..8] SessionID
MsgBody[7..0] команда, на которую дан ответ
DATA[0] Status
DATA[1] TargetSlot: 0=A, 1=B, 0xFF=не выбран
DATA[2..3] NextBlock, uint16 little-endian
DATA[4..7] RunningCRC32, uint32 little-endian
| Код | Статус | Повтор допустим |
|---|---|---|
0x00 |
OK |
— |
0x01 |
BUSY |
да, после задержки |
0x02 |
INVALID_COMMAND |
после исправления |
0x03 |
WRONG_DEVICE |
нет для этого образа |
0x04 |
WRONG_HARDWARE |
нет для этого образа |
0x05 |
INVALID_SIZE |
нет для этого образа |
0x06 |
CRC_ERROR |
новая передача |
0x07 |
FLASH_ERROR |
зависит от платформы |
0x08 |
SEQUENCE_ERROR |
да, с NextBlock |
0x09 |
SIGNATURE_ERROR |
нет |
0x0A |
SESSION_ERROR |
открыть новую сессию |
0x0B |
VOLTAGE_ERROR |
да после нормализации питания |
0x0C |
INVALID_STATE |
выполнить правильный переход |
State machine
IDLE
└─ ENTER_BOOT ─> METADATA
├─ BEGIN_IMAGE
└─ BEGIN_COMPAT
│
v
READY_TO_ERASE
│ ERASE
v
RECEIVING
│ VERIFY
v
VERIFIED
│ COMMIT
v
PENDING + REBOOT
│ CONFIRM
v
CONFIRMED
Ошибка Flash, CRC, совместимости или подписи переводит сессию в FAILED.
Новая ENTER_BOOT создаёт чистую сессию. ABORT прекращает текущую передачу,
не активируя частично записанный слот.
Надёжность и повторы
- Блоки передаются строго по возрастанию
BlockIndex. - Дубликат или пропуск возвращает
SEQUENCE_ERRORи ожидаемыйNextBlock. - Базовый режим подтверждает каждый блок.
- Рабочий режим может подтверждать окно из 16 блоков.
- После потери связи
QUERY_PROGRESSвозвращает следующий ожидаемый блок, пока состояние загрузчика сохранено. - Для продолжения после перезагрузки порт должен сохранять session metadata и восстановить её при инициализации; ядро версии 1.0 само это не делает.
Безопасность и A/B-обновление
active=A -> target=B -> verify -> pending=B
active=B -> target=A -> verify -> pending=A
CRC32 защищает только от случайного повреждения. Серийный загрузчик должен
дополнительно проверить подпись контейнера, границы вектора, совместимость и
anti-rollback. Bootloader не обновляется командами BOOT_DATA_A/B.
Boot metadata должна атомарно хранить:
- активный слот;
- pending-слот;
- подтверждение запуска;
- число неудачных попыток;
- версию и CRC32 образа.
Если приложение не выполняет CONFIRM за установленное число запусков,
загрузчик возвращается к предыдущему подтверждённому слоту.
Эталонный сценарий
- ПМ адресно отправляет
IDENTIFY. - ПМ открывает ненулевой
SessionIDкомандойENTER_BOOT. - ПМ отправляет
BEGIN_IMAGEиBEGIN_COMPAT. - Прибор сообщает выбранный неактивный слот.
- ПМ выполняет
ERASEи передаётBOOT_DATA_AлибоBOOT_DATA_B. - ПМ выполняет
VERIFY, затемCOMMITиREBOOT. - Новое приложение после самопроверки выполняет
CONFIRM.
Общее адресное пространство: GAS и GAS SLOT
Дата редакции: 2026-10-23. Номер типа однозначно определяет единицу адресации:
| MsgType | Имя | MsgBody | Payload |
|---|---|---|---|
0x3 |
GAS | адрес первого 16-битного регистра | 1…4 последовательных регистра, DLC 2/4/6/8 |
0x4 |
GAS SLOT | адрес слота | Word0 — маска; Word1…Word3 — три значения; DLC 8 |
В обоих случаях запрос чтения — extended data frame с DLC=0, Route=0.
Ответ имеет Route=1, тот же узел и адрес. Прикладного ACK записи нет;
успех применения уставки проверяется прикладным ответом или последующим чтением.
RTR, error frame, стандартный 11-битный ID и нечётные длины не исполняются.
GAS 0x3: отдельные регистры
MsgBody=RegisterAddress[15:0]. Адреса 0…65535 (0x0000…0xFFFF)
обозначают 65 536 отдельных 16-битных регистров, логически 128 КиБ на узел.
Это адреса протокола, а не указатели RAM MCU; provider подключает только существующие регионы.
Payload содержит значения с адресов addr, addr+1, addr+2, addr+3
в порядке little-endian. Количество равно DLC/2, допускаются DLC 2, 4, 6, 8.
Для изменения одной уставки по адресу 0x0001 на 0x1234 передаются
MsgBody=0x0001, DLC=2, Data=34 12; соседние слова не изменяются.
Чтение возвращает до четырёх существующих последовательных регистров,
останавливаясь на первом недоступном адресе или границе 0xFFFF.
Запись, пересекающая 0xFFFF, отвергается до изменения данных.
Регионы карты, права и значения проверяет pcan_gas; callback остаётся
ответственностью приложения. Блочная запись может остановиться на первом отказе provider:
атомарность нескольких регистров не обещается. Связанные u32/float32 и команды
требуют согласованной прикладной операции. Основной контракт: c/set-protocol/docs/legacy/GAS.md.
GAS 167/v1, GAS Logger, GAS_WATCH2, NAND и Altera Logic сохраняют регистровые адреса.
Номер mode задаёт адрес узла; его нельзя автоматически прибавлять к адресу регистра.
GAS SLOT 0x4: маска в Word0
MsgBody=SlotAddress[15:0]. Восемь октетов имеют фиксированное расположение:
| Октеты | Wire-слово | Назначение |
|---|---|---|
| 0…1 | Word0 | 16-битная маска, little-endian |
| 2…3 | Word1 | первое значение |
| 4…5 | Word2 | второе значение |
| 6…7 | Word3 | третье значение |
Биты маски 1, 2, 3 соответствуют Word1, Word2, Word3. Бит 0 и биты 4…15
зарезервированы и должны быть нулевыми. Маска непустая; все допустимые маски:
0x0002, 0x0004, 0x0006, 0x0008, 0x000A, 0x000C, 0x000E.
При записи отмеченные значения применяются, неотмеченные позиции игнорируются
и сохраняют текущие значения устройства. Нули не являются специальными маркерами.
При чтении маска ответа отмечает достоверные/доступные значения.
Например, изменить только Word2 на 0x1234: Data=04 00 00 00 34 12 00 00.
Word1 и Word3 сохраняются. Полная запись трёх значений использует маску 0x000E.
Передаются всегда восемь октетов, кроме пустого запроса чтения. Сжатия или сегментации нет.
Provider проверяет все выбранные права и значения до первого изменения, затем применяет
выбранные слова под общей блокировкой. Невыбранные readonly-поля не записываются.
Ядро не выполняет опасное чтение-изменение-запись слота на стороне клиента.
Полное пространство 0x4 содержит 65 536 слотов × 3 значения = 196 608 слов = 384 КиБ.
Word0 — метаданные кадра, не четвёртый хранимый регистр. Координата значения —
(SlotAddress, WordIndex), где WordIndex=1…3. Любой узел поддерживается общими
pcan_gas_slot_id() и pcan_gas_slot_handle_address(); адрес слота не ограничен страницами PERIPH.
Тип 0x4 MODBUS_COIL заменён этим сервисом. Modbus 0x5/0x6/0x7 сохраняет номера.
Старое оборудование с Coil 0x4 требует согласованного обновления; автоопределения формата нет.
PERIPH: проекция 200 слов на слоты 0x4
mode=0…31; DeviceType=1+mode/16, DeviceID=mode mod 16.
SlotAddress=(mode<<8)|LocalSlot. Маски не занимают места в карте значений.
На mode сохранены 200 слов, поэтому используются 67 слотов LocalSlot=0x00…0x42:
66 полных слотов и два значения последнего. В последнем разрешена только маска
из битов 1/2 (0x0002, 0x0004, 0x0006); Word3 не существует.
LocalSlot=0x43…0xFF — резерв. Узел и старший октет слота должны совпадать.
Смещение технологического слова: n=3*LocalSlot+(WordIndex-1).
Обратное преобразование: LocalSlot=n/3, WordIndex=1+n mod 3.
Соседние native-сервисы могут оказаться в одном слоте, поэтому provider проверяет
доступ и наличие каждой отмеченной позиции отдельно.
| mode | Назначение | DeviceType / DeviceID | Слоты 0x4 | Запрос первого слота |
|---|---|---|---|---|
| 0 | Резерв | 0x01 / 0x00 |
0x0000…0x0042 |
0x11040000 |
| 1 | Трансформатор 1 | 0x01 / 0x01 |
0x0100…0x0142 |
0x11140100 |
| 2 | Трансформатор 2 | 0x01 / 0x02 |
0x0200…0x0242 |
0x11240200 |
| 3 | Силовой узел 1 | 0x01 / 0x03 |
0x0300…0x0342 |
0x11340300 |
| 4 | Силовой узел 2 | 0x01 / 0x04 |
0x0400…0x0442 |
0x11440400 |
| 5 | УМП1 | 0x01 / 0x05 |
0x0500…0x0542 |
0x11540500 |
| 6 | УМП2 | 0x01 / 0x06 |
0x0600…0x0642 |
0x11640600 |
| 7 | ENG1 | 0x01 / 0x07 |
0x0700…0x0742 |
0x11740700 |
| 8 | Задатчик / пульт | 0x01 / 0x08 |
0x0800…0x0842 |
0x11840800 |
| 9 | Шкаф | 0x01 / 0x09 |
0x0900…0x0942 |
0x11940900 |
| 10 | Общий адрес: служебная роль | 0x01 / 0x0A |
0x0A00…0x0A42 |
0x11A40A00 |
| 11 | Терминал: служебная роль | 0x01 / 0x0B |
0x0B00…0x0B42 |
0x11B40B00 |
| 12 | Резерв назначения | 0x01 / 0x0C |
0x0C00…0x0C42 |
0x11C40C00 |
| 13 | Резерв назначения | 0x01 / 0x0D |
0x0D00…0x0D42 |
0x11D40D00 |
| 14 | Резерв назначения | 0x01 / 0x0E |
0x0E00…0x0E42 |
0x11E40E00 |
| 15 | Резерв назначения | 0x01 / 0x0F |
0x0F00…0x0F42 |
0x11F40F00 |
| 16 | Резерв назначения | 0x02 / 0x00 |
0x1000…0x1042 |
0x12041000 |
| 17 | Резерв назначения | 0x02 / 0x01 |
0x1100…0x1142 |
0x12141100 |
| 18 | Резерв назначения | 0x02 / 0x02 |
0x1200…0x1242 |
0x12241200 |
| 19 | Резерв назначения | 0x02 / 0x03 |
0x1300…0x1342 |
0x12341300 |
| 20 | Резерв назначения | 0x02 / 0x04 |
0x1400…0x1442 |
0x12441400 |
| 21 | Резерв назначения | 0x02 / 0x05 |
0x1500…0x1542 |
0x12541500 |
| 22 | Резерв назначения | 0x02 / 0x06 |
0x1600…0x1642 |
0x12641600 |
| 23 | Резерв назначения | 0x02 / 0x07 |
0x1700…0x1742 |
0x12741700 |
| 24 | Резерв назначения | 0x02 / 0x08 |
0x1800…0x1842 |
0x12841800 |
| 25 | Резерв назначения | 0x02 / 0x09 |
0x1900…0x1942 |
0x12941900 |
| 26 | Резерв назначения | 0x02 / 0x0A |
0x1A00…0x1A42 |
0x12A41A00 |
| 27 | Резерв назначения | 0x02 / 0x0B |
0x1B00…0x1B42 |
0x12B41B00 |
| 28 | Резерв назначения | 0x02 / 0x0C |
0x1C00…0x1C42 |
0x12C41C00 |
| 29 | Резерв назначения | 0x02 / 0x0D |
0x1D00…0x1D42 |
0x12D41D00 |
| 30 | Резерв назначения | 0x02 / 0x0E |
0x1E00…0x1E42 |
0x12E41E00 |
| 31 | Резерв назначения | 0x02 / 0x0F |
0x1F00…0x1F42 |
0x12F41F00 |
Назначения mode из periph_28335
Источники: 28335_periph/Source/Internal/Include/package.h, peripher.c:get_Mode(), cntrl_adr.c. Код платы Desk не участвует в вычислении адреса.
| mode | Назначение | Исходный символ |
|---|---|---|
| 0 | Резерв | Перемычками текущей платы не задаётся |
| 1 / 2 | Трансформаторные узлы 1 / 2 | adr_TRN1 / adr_TRN2 |
| 3 / 4 | Силовые узлы 1 / 2 | adr_POW1 / adr_POW2 |
| 5 / 6 | УМП1 / УМП2 | adr_LOA1 / adr_LOA2, Desk=dsk_LOAD=6 |
| 7 | ENG1 | adr_ENG1 |
| 8 | Задатчик / пульт | adr_PULT; имя «Задатчик» принято в реестре PERIPH |
| 9 | Шкаф | adr_SHKF |
| 10 | Общий адрес, служебная роль | CNTRL_ADDR_UNIVERSAL, ADDR_FOR_ALL; не отдельная плата |
| 11 | Терминал, служебная роль | ADDR_TERMINAL; не доказательство отдельной платы |
| 12…31 | Резерв назначения | Перемычки текущей платы дают только 1…16 |
Соответствия native-сервисам PERIPH
| Смещение n | Native-адрес | Назначение | Доступ |
|---|---|---|---|
| 0…127 | 0x0000…0x007F |
технологические данные, уставки, команды | по provider |
| 128…143 | 0x1000…0x100F |
статус регистратора УМП | R |
| 144…155 | 0x1200…0x120B |
паспорт прошивки | R |
| 156…163 | 0x1300…0x1307 |
защита УМП | W только 0x1302/0x1303 |
| 164…167 | 0x1000…0x1003 |
команды регистратора | по операции |
| 168…199 | нет | резерв | отказ |
pcan_gas_slot_native() принимает WordIndex=1…3; WordIndex=0 является маской
и не переводится в регистр. Наличие сервиса, права и блокировку проверяет приложение.
GAS 0x3 сохраняет собственные адреса подключённых регионов; эта проекция относится только к 0x4.
Архив УМП, окна 120/124 слова и физическая EEPROM сохраняют native-контракты.
Реализация и реестр
- GAS 0x3:
pcan_gas.h/.c, существующие ABI и клиенты. - GAS SLOT 0x4:
pcan_gas_slot.h/.c; тонкие PythonGasSlotsи Android/JNIGasSlots. - Формат определяется MsgType, параметр
gas_profileне используется. - C28x хранит wire-октеты в
uint_least8_t; DLC измеряется октетами линии. - Перенос в другую единицу адресации или fallback после отказа запрещены.
XLSX «Интерфейс протокола» содержит оба типа; «Общее адресное пространство» —
проекцию 0x4 и native-адреса её значений; «ОАП legacy» сохраняет исходный словный реестр 0x3.
Уникальный ключ реестра включает узел, MsgType, адрес и для 0x4 WordIndex.
Права, тип, единица, масштаб и порядок слов u32/float32 задаются явно.
Эталоны: examples/gas-slot-vectors.json; регистровые проверки — test_gas_register.c.
История изменений ProtoCAN
Формат основан на Keep a Changelog. Версия относится к спецификации, а не к версии прошивки отдельного прибора.
[Unreleased]
Changed — 2026-10-23
- GAS 0x3 возвращён к адресации отдельных 16-битных регистров 0…65535, DLC 0/2/4/6/8.
- Слотный GAS перенесён на 0x4 вместо MODBUS_COIL; Word0 содержит маску битов 1…3, Word1…Word3 — значения.
- PERIPH сохраняет 200 значений на mode в 67 слотах; последний содержит два значения.
- Формат выбирается номером MsgType; Python/JNI, STM32, Modbus routing, тесты, XLSX и HTML согласованы.
Added
- Структурированный комплект документации
doc/setcan/protocan. - Загрузочный сервис
MsgType=0x9…0xD. - Два логических слота по 512 КиБ, 65 536 блоков по 8 байт каждый.
- Машинные эталоны CAN ID в
examples/test-vectors.json.
[1.0] — 2026-08-29
Added
- Зафиксирована 29-битная структура ProtoCAN ID.
- Зафиксирована адресация 8 типов по 16 экземпляров.
- Существующие сообщения
0x0…0x8,0xE,0xFсохранены.
Тестовые векторы
Машинные эталоны из examples/test-vectors.json.
{
"schema_version": 1,
"byte_order": "little-endian",
"frames": [
{
"name": "enter_boot_session_7",
"direction": "pm_to_device",
"priority": 1,
"route": 0,
"device_type": 3,
"device_id": 5,
"msg_type": 9,
"msg_body": 1794,
"can_id_hex": "0x13590702",
"dlc": 0,
"data_hex": ""
},
{
"name": "slot_b_block_1",
"direction": "pm_to_device",
"priority": 1,
"route": 0,
"device_type": 3,
"device_id": 5,
"msg_type": 11,
"msg_body": 1,
"can_id_hex": "0x135B0001",
"dlc": 8,
"data_hex": "1011121314151617"
},
{
"name": "enter_boot_ok",
"direction": "device_to_pm",
"priority": 1,
"route": 1,
"device_type": 3,
"device_id": 5,
"msg_type": 12,
"msg_body": 1794,
"can_id_hex": "0x1B5C0702",
"dlc": 8,
"data_hex": "00FF000000000000"
}
]
}