Add a production-ready, educational bootloader extension for STM32 MCUs. Core features: - Custom binary protocol with CRC16/CRC32 verification - AES-256 encryption for firmware security - Dual-bank firmware management with rollback support - Version management and firmware validation - Modular architecture with BSP abstraction layer Project structure: - include/: Header files (bootloader.h, bsp_flash.h, bsp_uart.h) - src/: Core implementation (bootloader, protocol, crypto, firmware manager) - port/: MCU-specific adaptation layer (STM32F4xx) - docs/: Documentation (integration guide, porting guide) Supported platforms: - STM32F4xx (primary) - STM32F1xx (via porting) Quick start: 1. Copy extension module to project 2. Configure bootloader_config.h 3. Modify linker script for APP_BASE_ADDR 4. Build and flash bootloader to 0x08000000 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 KiB
12 KiB
Bootloader集成指南
本文档详细介绍如何将Bootloader扩展模块集成到您的STM32项目中。
目录
前置要求
硬件要求
- STM32系列MCU (本模块主要针对STM32F4系列)
- 至少一个UART接口
- 足够的Flash空间:
- Bootloader: 16KB ~ 32KB
- 应用程序: 取决于您的需求
软件要求
- ARM GCC工具链
- CMake 3.10+ (可选)
- STM32CubeMX (用于生成初始化代码)
- 串口调试工具
开发环境
- Windows: STM32CubeIDE / Keil MDK / IAR
- Linux: VSCode + CMake + ARM GCC
- macOS: VSCode + CMake + ARM GCC
集成方式
方式一:嵌入式集成(推荐新手)
Bootloader与应用程序共用一个工程,通过条件编译切换。
优点:
- 配置简单
- 易于调试
- 适合学习
缺点:
- 需要重新编译整个工程
- 不够灵活
方式二:独立工程集成(推荐生产)
Bootloader和应用程序分别作为独立工程。
优点:
- 独立开发
- 灵活部署
- 适合生产
缺点:
- 需要管理两个工程
- 配置相对复杂
详细步骤
方式一:嵌入式集成
步骤1: 复制文件
# 复制整个extensions目录到您的项目根目录
cp -r extensions/ /path/to/your/project/
步骤2: 修改CMakeLists.txt
在您的项目CMakeLists.txt中添加:
# 添加Bootloader模块
add_subdirectory(extensions/bootloader)
# 链接Bootloader库
target_link_libraries(${PROJECT_NAME}.elf bootloader)
# 包含头文件
target_include_directories(${PROJECT_NAME}.elf PRIVATE
extensions/bootloader/include
)
步骤3: 配置bootloader_config.h
编辑 extensions/bootloader/include/bootloader_config.h:
// MCU配置
#define MCU_FAMILY_STM32F4XX 1
// Flash布局
#define BOOTLOADER_SIZE_KB 32
#define APP_BASE_ADDR 0x08008000
// 功能开关
#define BOOT_ENABLE_AES 1
#define BOOT_ENABLE_CRC 1
#define BOOT_ENABLE_DUAL_BANK 1
步骤4: 修改main.c
#include "main.h"
#include "bootloader.h"
int main(void) {
// HAL初始化
HAL_Init();
SystemClock_Config();
// === Bootloader入口 ===
bootloader_init();
// 检查是否进入Bootloader模式
if (bootloader_check_enter()) {
// Bootloader主循环
while (1) {
bootloader_process();
// 检查是否需要跳转
if (bootloader_get_state() == BOOT_STATE_JUMP_APP) {
bootloader_jump_to_app(APP_BASE_ADDR);
}
}
}
// =====================
// 应用程序初始化
MX_GPIO_Init();
MX_USART1_UART_Init();
// ... 其他初始化
// 应用程序主循环
while (1) {
// 您的应用代码
}
}
步骤5: 修改链接脚本
找到您的链接脚本 (例如 STM32F407XX_FLASH.ld),修改Flash起始地址:
/* 原始配置 */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
}
/* 修改为 (为Bootloader预留32KB) */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K
}
步骤6: 修改中断向量表
在应用程序的 system_stm32f4xx.c 中,添加中断向量表偏移:
void SystemInit(void) {
// ... 原有代码
// 设置中断向量表偏移
#ifdef VECT_TAB_SRAM
SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FLASH_BASE | 0x8000; // 0x8000 = 32KB偏移
#endif
}
或者在 main.c 的开头添加:
#define VECT_TAB_OFFSET 0x8000 // 32KB偏移
int main(void) {
// 设置中断向量表
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
HAL_Init();
// ...
}
方式二:独立工程集成
步骤1: 创建Bootloader工程
mkdir bootloader_project
cd bootloader_project
# 复制Bootloader模块
cp -r /path/to/bootloader/extensions/bootloader ./
步骤2: 创建Bootloader的main.c
#include "bootloader.h"
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化Bootloader
bootloader_init();
// Bootloader主循环
while (1) {
bootloader_process();
if (bootloader_get_state() == BOOT_STATE_JUMP_APP) {
// 检查应用程序是否存在
if (bootloader_check_app_valid(APP_BASE_ADDR)) {
bootloader_jump_to_app(APP_BASE_ADDR);
}
}
}
}
步骤3: 配置Bootloader链接脚本
Bootloader从 0x08000000 开始:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
}
步骤4: 创建应用程序工程
应用程序从 0x08008000 开始:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K
}
步骤5: 编译烧录顺序
# 1. 编译Bootloader
cd bootloader_project
make
# 烧录到 0x08000000
st-flash write bootloader.bin 0x08000000
# 2. 编译应用程序
cd application_project
make
# 通过Bootloader升级,或直接烧录到 0x08008000
配置参数
必须配置的参数
| 参数 | 说明 | 示例值 |
|---|---|---|
| MCU_FAMILY_* | MCU系列 | STM32F4XX |
| BOOTLOADER_SIZE_KB | Bootloader大小 | 32 |
| APP_BASE_ADDR | 应用起始地址 | 0x08008000 |
| BOOT_UART_BAUDRATE | 串口波特率 | 115200 |
可选配置的参数
| 参数 | 说明 | 默认值 |
|---|---|---|
| BOOT_ENABLE_AES | 启用AES加密 | 1 |
| BOOT_ENABLE_CRC | 启用CRC校验 | 1 |
| BOOT_ENABLE_DUAL_BANK | 启用双Bank | 1 |
| BOOT_ENABLE_VERSION_MGR | 启用版本管理 | 1 |
| BOOT_TRIGGER_TIMEOUT_MS | 启动等待超时 | 3000 |
链接脚本修改
完整示例 (STM32F407ZGTx)
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM);
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
/* 根据您的需求选择其一 */
/* 方式1: 应用程序链接脚本 (从32KB开始) */
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K
/* 方式2: Bootloader链接脚本 (从0开始) */
/* FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K */
}
/* 中断向量表偏移 (仅应用程序需要) */
_isr_vector = ORIGIN(FLASH);
注意事项
- Bootloader工程: Flash起始地址 = 0x08000000
- 应用程序工程: Flash起始地址 = 0x08000000 + Bootloader大小
- 不要重叠: 确保Bootloader和应用程序的Flash区域不重叠
中断向量表
为什么需要修改?
STM32上电后,从 0x08000000 读取中断向量表。如果应用程序不在 0x08000000,必须重定向中断向量表。
修改方法
方法1: 修改system_stm32f4xx.c
void SystemInit(void) {
// ... 原有代码
/* Configure the Vector Table location -------------------------------------*/
#define VECT_TAB_OFFSET 0x8000 /* 32KB offset for bootloader */
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
}
方法2: 在main.c中设置
int main(void) {
// 第一件事:设置中断向量表
SCB->VTOR = 0x08008000; // 应用程序起始地址
// 然后初始化HAL
HAL_Init();
// ...
}
方法3: 使用宏定义
在 main.h 中定义:
#define APPLICATION_ADDRESS 0x08008000
// 在main函数开头
SCB->VTOR = APPLICATION_ADDRESS;
编译与烧录
编译Bootloader
cd bootloader_project
mkdir build && cd build
cmake .. -DMCU_FAMILY=stm32f4xx
make -j4
编译应用程序
cd application_project
mkdir build && cd build
cmake ..
make -j4
烧录顺序
方法1: 使用ST-Link
# 烧录Bootloader
st-flash write bootloader.bin 0x08000000
# 烧录应用程序 (首次烧录)
st-flash write application.bin 0x08008000
方法2: 使用STM32CubeProgrammer
# 烧录Bootloader
STM32_Programmer_CLI -c port=SWD -w bootloader.bin 0x08000000 -v
# 烧录应用程序
STM32_Programmer_CLI -c port=SWD -w application.bin 0x08008000 -v
方法3: 通过Bootloader升级
# 使用上位机工具升级
./flash_tool --port COM3 --write application.bin
测试验证
基础功能测试
测试1: Bootloader启动测试
// 在bootloader.c中添加调试输出
void bootloader_init(void) {
// 初始化串口
bsp_uart_init(BOOT_UART_BAUDRATE);
printf("Bootloader v1.0.0\r\n");
printf("Waiting for command...\r\n");
// ...
}
预期结果: 串口输出Bootloader版本信息
测试2: 跳转测试
// 在main.c中添加
bootloader_jump_to_app(APP_BASE_ADDR);
// 在应用程序main.c开头添加
printf("Application started!\r\n");
预期结果: 成功跳转到应用程序并输出信息
测试3: 通信测试
使用串口调试工具发送握手命令:
发送: AA 01 00 00 55 (握手命令)
预期: 接收到应答
完整功能测试
测试清单
- Bootloader正常启动
- 串口通信正常
- 握手命令成功
- Flash擦除成功
- 固件写入成功
- CRC校验通过
- AES解密正确
- 跳转应用程序成功
- 应用程序正常运行
- 双Bank切换成功
- 版本管理正常
故障排查
问题1: 编译错误 - 找不到头文件
症状:
fatal error: bootloader.h: No such file or directory
解决:
# 在CMakeLists.txt中添加包含路径
target_include_directories(${PROJECT_NAME} PRIVATE
extensions/bootloader/include
)
问题2: 链接错误 - 重复定义
症状:
multiple definition of `main'
解决: 检查是否有多个main函数,确保只有一个main入口。
问题3: 运行时错误 - 无法启动
症状: 程序不运行或立即崩溃
排查步骤:
- 检查Flash布局是否正确
- 检查中断向量表是否正确设置
- 使用调试器检查PC指针位置
- 检查堆栈大小是否足够
问题4: 跳转失败
症状: 跳转到应用程序后无响应
排查步骤:
- 确认应用程序起始地址正确
- 检查中断向量表是否偏移
- 检查应用程序是否正确烧录
- 使用调试器查看应用程序是否正常运行
调试代码:
void bootloader_jump_to_app(uint32_t app_addr) {
// 检查应用程序是否存在
uint32_t app_stack = *((uint32_t*)app_addr);
uint32_t app_entry = *((uint32_t*)(app_addr + 4));
printf("App stack: 0x%08X\r\n", app_stack);
printf("App entry: 0x%08X\r\n", app_entry);
// 检查栈指针是否在RAM范围内
if (app_stack < 0x20000000 || app_stack > 0x20000000 + 128*1024) {
printf("Invalid stack pointer!\r\n");
return;
}
// 禁用所有中断
__disable_irq();
// 跳转
typedef void (*app_entry_t)(void);
app_entry_t entry = (app_entry_t)app_entry;
entry();
}
问题5: Flash写入失败
症状: Flash写入返回错误
排查步骤:
- 确认Flash已解锁
- 确认写入地址已擦除
- 确认地址对齐
- 检查Flash页大小配置
调试代码:
int flash_port_write(uint32_t addr, const uint8_t *data, uint32_t len) {
printf("Writing to 0x%08X, len=%d\r\n", addr, len);
// 检查Flash状态
FLASH_TypeDef *flash = FLASH;
printf("Flash SR: 0x%08X\r\n", flash->SR);
// 写入操作...
}
下一步
完成集成后,请阅读:
技术支持
如遇到问题,请:
- 查阅本文档的故障排查章节
- 查看 GitHub Issues
- 提交新的Issue,附带:
- MCU型号
- 完整的错误信息
- 相关配置文件
- 调试日志
祝您集成顺利!