ProtoCAN Boot
protocan-boot — переносимое C99-ядро адресной прошивки приборов по classic
CAN 2.0B и 29-битному ProtoCAN ID. Оно реализует сессию обновления, два
логических слота по 512 КиБ, последовательную запись 8-байтовых блоков,
CRC32, проверку совместимости, продолжение по номеру следующего блока и
безопасный выбор неактивного слота.
Это не готовый загрузчик конкретного STM32: внутри нет HAL, регистров Flash, linker script, обработчиков прерываний, криптографии и перехода в приложение. Эти операции предоставляет проект через таблицу callbacks.
Слои
приложение / CAN RX loop
|
v
pcan_boot_process() — протокол и state machine
|
v
pcan_boot_port_t — Flash, политика подписи, commit, reboot
|
v
STM32 HAL / другой MCU / host-тест
Файлы
| Файл | Назначение | Зависимости |
|---|---|---|
include/pcan_boot.h |
публичный API, команды и структуры | C99 stdint/stdbool |
src/pcan_boot.c |
ProtoCAN ID, state machine и CRC32 | только публичный заголовок |
tests/test_pcan_boot.c |
хостовые тесты с RAM вместо Flash | стандартная библиотека C |
CMakeLists.txt |
сборка библиотеки и теста | CMake 3.13+ |
Модуль не зависит от protocan-transport: разбор пяти полей 29-битного ID
сделан переносимыми сдвигами, без непереносимых C-битовых полей.
Карта MsgType
| Значение | Имя | Назначение |
|---|---|---|
0x9 |
BOOT_CONTROL |
команды и метаданные |
0xA |
BOOT_DATA_A |
блоки слота A |
0xB |
BOOT_DATA_B |
блоки слота B |
0xC |
BOOT_STATUS |
ответы и прогресс |
0xD |
BOOT_DISCOVERY |
идентификация |
Для BOOT_DATA_A/B поле MsgBody — номер блока 0..65535, а payload —
8 байт образа:
offset = MsgBody * 8
65536 * 8 = 512 КиБ на слот
Последний кадр дополняется 0xFF; ядро передаёт во Flash только оставшиеся
байты фактического образа и включает в CRC только их.
Контракт порта
typedef struct {
bool (*send)(void *, uint32_t can_id, const uint8_t *, uint8_t dlc);
bool (*erase_slot)(void *, uint8_t slot);
bool (*write_slot)(void *, uint8_t slot, uint32_t offset,
const uint8_t *, uint8_t length);
bool (*authorize)(void *, const pcan_boot_manifest_t *);
bool (*verify_image)(void *, uint8_t slot,
const pcan_boot_manifest_t *);
bool (*set_pending_slot)(void *, uint8_t slot,
const pcan_boot_manifest_t *);
bool (*confirm_running_slot)(void *);
void (*reboot)(void *);
} pcan_boot_port_t;
Обязательны send, erase_slot, write_slot и set_pending_slot.
authorize проверяет политику до стирания (тип, версия, anti-rollback).
verify_image выполняет платформенную проверку подписанного контейнера после
CRC32. Если callbacks отсутствуют, соответствующие дополнительные проверки
пропускаются; для серийного изделия их следует реализовать.
set_pending_slot должен атомарно сохранить boot metadata. До подтверждения
нового приложения загрузчик сохраняет возможность отката. confirm_running_slot
снимает pending-флаг после самопроверки приложения.
Быстрый старт
pcan_boot_t boot;
pcan_boot_config_t cfg = {
.device_type = 3, .device_id = 5, .product_type = 0x1234,
.hardware_revision = 2, .firmware_version = 0x01020000,
.active_slot = 0, .ack_window = 16
};
pcan_boot_port_t port = {
.send = can_send, .erase_slot = flash_erase_slot,
.write_slot = flash_write_slot, .authorize = image_authorize,
.verify_image = image_verify_signature,
.set_pending_slot = boot_set_pending, .reboot = system_reboot
};
pcan_boot_init(&boot, &cfg, &port, &board);
/* Из CAN RX-задачи, не из длительного Flash-прерывания: */
pcan_boot_process(&boot, rx.ExtId, rx.Data, rx.DLC);
Последовательность обновления
IDENTIFYили discovery получает тип и версии прибора.ENTER_BOOTоткрывает ненулевойSessionID.BEGIN_IMAGEпередаёт размер и CRC32.BEGIN_COMPATпередаёт тип изделия, диапазон HW и версию FW.- Ядро выбирает неактивный слот и отвечает его номером.
ERASEвызывает порт стирания.BOOT_DATA_AлибоBOOT_DATA_Bпередаются строго по порядку.VERIFYсравнивает CRC32 и вызывает проверку образа/подписи порта.COMMITатомарно помечает слот как pending.- После reboot приложение подтверждает запуск через
CONFIRM.
Все изменяющие Flash команды адресуются конкретному DeviceType/DeviceID.
Ядро не принимает broadcast-запись и игнорирует кадры с чужим адресом или
Route=FROM_DEVICE.
State machine
IDLE -> ENTER_BOOT -> METADATA -> BEGIN_IMAGE + BEGIN_COMPAT
-> READY_TO_ERASE -> ERASE -> RECEIVING -> VERIFY -> VERIFIED
-> COMMIT -> REBOOT -> CONFIRM
При ошибке Flash, CRC, совместимости или подписи состояние переходит в
FAILED. Новая сессия начинается командой ENTER_BOOT; ABORT очищает
текущую сессию без изменения Flash.
Сборка тестов
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
Что остаётся проекту устройства
- зарезервировать bootloader, slot A/B и metadata в linker script;
- реализовать выравнивание и размеры страниц Flash;
- не выполнять длительное стирание непосредственно в CAN IRQ;
- атомарно хранить active/pending/confirmed и счётчик попыток запуска;
- проверить вектор, границы и подпись образа;
- включить watchdog и rollback при неподтверждённом запуске;
- согласовать фильтры CAN для
MsgType=0x9..0xD.
Проект пока не подключён ни к одной конкретной плате; это самостоятельный шаблон для интеграции в будущие загрузчики приборов SET.