ProtoCAN Boot
Каноническое описание обмена загрузчика перенесено из SETCAN в
docs/BOOTLOADER.md. Реализация и документ меняются
вместе в этом переносимом модуле.
protocan-boot — переносимое C99-ядро адресной прошивки приборов по classic
CAN 2.0B и 29-битному ProtoCAN ID. Оно реализует сессию обновления, два
логических слота по 512 КиБ либо single-slot обновление, последовательную запись 8-байтовых блоков,
CRC32, проверку совместимости, продолжение по номеру следующего блока и
безопасный выбор неактивного слота.
Ядро не содержит HAL или регистров Flash. Готовые аппаратные callbacks для
STM32F103C8, STM32F407VE и STM32G474VE находятся в ports; подключение описано ниже.
Linker script, startup, тактирование, GPIO и переход в приложение предоставляет
проект устройства. Криптография в этих single-slot портах не реализована.
Для STM32G474VET6 из CANdle_stm32 задаются
slot_count = 1 и max_image_size = 480 * 1024. В этом режиме запись идёт в единственную
область приложения, callback set_pending_slot не требуется, а аппаратный
rollback недоступен. Значение slot_count = 0 сохраняет прежний A/B-режим.
Слои
приложение / 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+ |
Модуль не зависит от legacy-модулей set-protocol: разбор пяти полей 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
512 КиБ = 512 * 1024 = 524 288 байт
524 288 / 8 = 65 536 блоков на слот
MsgBody |
Номер блока | Смещение | Байты слота |
|---|---|---|---|
0x0000 |
0 | 0x00000 |
0x00000..0x00007 |
0x0001 |
1 | 0x00008 |
0x00008..0x0000F |
0xFFFF |
65 535 | 0x7FFF8 |
0x7FFF8..0x7FFFF |
Последний блок заканчивается на смещении 0x7FFFF. Значение 0x80000
равно размеру слота и является первой позицией за его границей. В коде эти
границы заданы константами PCAN_BOOT_BLOCK_COUNT=65536 и
PCAN_BOOT_SLOT_SIZE=524288.
Последний кадр дополняется 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; в A/B также set_pending_slot.
В single-slot этот callback необязателен и может завершать активацию образа.
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.
Ядро поддерживает платформенные порты; особенности интеграции перечислены ниже.
F28335 / большая EEPROM
Порт M95M02 подключён в 28335_periph, ветка 167.
Использует отдельный Flash-загрузчик и два слота EEPROM по 130560 байт.
Реализованы readback/CRC, проверка SCI8, журнал метаданных, пробный запуск,
CONFIRM/откат, PING/discovery и сервисный паспорт. Первичная установка Flash-
загрузчика и выбор Flash boot описаны в README порта.
STM32 / прошивка по ProtoCAN
| Порт | Источник профиля | Область приложения | Ёмкость |
|---|---|---|---|
| STM32F103C8 | KONOR_ds18b20/src/bootloader.c |
0x08003000..0x08010000 |
52 КиБ |
| STM32F407VE | climate_control_f407vet6_f4/Bootloader/bootloader_f407.c |
0x08008000..0x08040000 |
224 КиБ |
| STM32G474VE | CANdle_stm32/src/boot/g474_can_bootloader.c |
0x08008000..0x08080000 |
480 КиБ |
Верхние границы исключены. F103/F407 предоставляют готовые CMSIS boot_main.c
для исходных проектов и отдельные reusable HAL-порты. G474 предоставляет
reusable HAL FDCAN/Flash-порт, CAN/UART boot main, startup и вход из приложения.
Общие компоненты и правила подключения описаны в
ports/stm32-common. Это ProtoCAN Boot v1 по
CAN, с отдельной картой памяти относительно USB SET v2-загрузчиков из set-protocol.
Конфигурация задаётся по именам полей и принудительно использует slot_count=1.
Образы пишутся в логический слот A; A/B-rollback и постоянные commit-метаданные
отсутствуют. Ограничения запуска после потери питания описаны в общей инструкции.
Проекты KONOR, climate и CANdle G474 подключены к этим исходникам через свои
submodule templates; в старых путях оставлены тонкие include-обёртки.
Состав сборок, разметка SRAM-запросов и результаты проверки перечислены в
projects/STM32.md.
Самостоятельные проекты CCS
Готовые проекты F28335_CAN_AB (M95M02) и F2812_CAN_NOR (AM29LV800BT),
включая необходимые файлы поддержки TI, находятся в projects/ccs.
Импорт, сборка, карты памяти и установка.