VBBoot is a minimal CAN bootloader for STM32G431.
The bootloader:
- checks application validity at startup and jumps to app if valid;
- otherwise stays in boot mode and accepts firmware frames over Classic CAN 2.0 (
FDCAN_FRAME_CLASSIC, no FD/BRS); - writes firmware to flash at
0x08003000..0x0801FFFF.
- CAN ID is
node_id(standard 11-bit identifier). - RX/TX use the same CAN ID.
- ACK payload is 1 byte:
0xD0(done) or0xE0(error).
BOOT_CMD_START(0x01)- Part 0 frame:
[0x01, 0x00, size_u32_le(4), crc32_low16_le(2)] - Part 1 frame:
[0x01, 0x01, crc32_high16_le(2)]
- Part 0 frame:
BOOT_CMD_DATA(0x02)- Frame:
[0x02, data...] - Data is buffered and written to flash in 8-byte aligned chunks.
- Frame:
BOOT_CMD_DONE(0x03)- Finalize, verify size + CRC32, ACK and jump to app on success.
BOOT_CMD_GET_ID enum value exists in code (0x05) but is not handled in transport state machine.
node_id is read from external I2C EEPROM (same idea as VBDrive config backend):
- I2C bus:
I2C2(hi2c2) - EEPROM device address:
0x50 - EEPROM offset:
0x0000 - Record format:
typedef struct __attribute__((packed)) {
uint32_t magic; // must be NODE_ID_MAGIC (0x424C4E49)
uint8_t node_id; // valid range: 1..127
uint8_t reserved[3];
} NodeIdRecord;If EEPROM is not ready, read fails, magic is invalid, or node_id is out of range, bootloader uses DEFAULT_NODE_ID (0x69).
Use CMake presets (toolchain is configured in cmake/gcc-arm-none-eabi.cmake):
cmake --preset RelWithDebInfo
cmake --build build/RelWithDebInfoOr explicitly:
cmake -S . -B build/RelWithDebInfo -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_TOOLCHAIN_FILE=cmake/gcc-arm-none-eabi.cmake
cmake --build build/RelWithDebInfoPost-build artifacts:
VBBoot.elfVBBoot.hexVBBoot.bin
- Bootloader flash region:
0x08000000, length12K(see linker script). - Application start:
0x08003000(APP_START_ADDR).
Test script is in tests/test_bootloader_fdcan.py.
Example:
pytest tests/test_bootloader_fdcan.py \
--hex=/path/to/app.hex \
--can-iface=socketcan \
--can-channel=can0 \
--ack-timeout=0.8 \
--node-id=0x69