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>
643 lines
12 KiB
Markdown
643 lines
12 KiB
Markdown
# 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: 复制文件
|
||
|
||
```bash
|
||
# 复制整个extensions目录到您的项目根目录
|
||
cp -r extensions/ /path/to/your/project/
|
||
```
|
||
|
||
#### 步骤2: 修改CMakeLists.txt
|
||
|
||
在您的项目CMakeLists.txt中添加:
|
||
|
||
```cmake
|
||
# 添加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`:
|
||
|
||
```c
|
||
// 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
|
||
|
||
```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起始地址:
|
||
|
||
```ld
|
||
/* 原始配置 */
|
||
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` 中,添加中断向量表偏移:
|
||
|
||
```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` 的开头添加:
|
||
|
||
```c
|
||
#define VECT_TAB_OFFSET 0x8000 // 32KB偏移
|
||
|
||
int main(void) {
|
||
// 设置中断向量表
|
||
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
|
||
|
||
HAL_Init();
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 方式二:独立工程集成
|
||
|
||
#### 步骤1: 创建Bootloader工程
|
||
|
||
```bash
|
||
mkdir bootloader_project
|
||
cd bootloader_project
|
||
|
||
# 复制Bootloader模块
|
||
cp -r /path/to/bootloader/extensions/bootloader ./
|
||
```
|
||
|
||
#### 步骤2: 创建Bootloader的main.c
|
||
|
||
```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 开始:
|
||
|
||
```ld
|
||
MEMORY
|
||
{
|
||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||
}
|
||
```
|
||
|
||
#### 步骤4: 创建应用程序工程
|
||
|
||
应用程序从 0x08008000 开始:
|
||
|
||
```ld
|
||
MEMORY
|
||
{
|
||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 992K
|
||
}
|
||
```
|
||
|
||
#### 步骤5: 编译烧录顺序
|
||
|
||
```bash
|
||
# 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)
|
||
|
||
```ld
|
||
/* 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);
|
||
```
|
||
|
||
### 注意事项
|
||
|
||
1. **Bootloader工程**: Flash起始地址 = 0x08000000
|
||
2. **应用程序工程**: Flash起始地址 = 0x08000000 + Bootloader大小
|
||
3. **不要重叠**: 确保Bootloader和应用程序的Flash区域不重叠
|
||
|
||
---
|
||
|
||
## 中断向量表
|
||
|
||
### 为什么需要修改?
|
||
|
||
STM32上电后,从 0x08000000 读取中断向量表。如果应用程序不在 0x08000000,必须重定向中断向量表。
|
||
|
||
### 修改方法
|
||
|
||
#### 方法1: 修改system_stm32f4xx.c
|
||
|
||
```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中设置
|
||
|
||
```c
|
||
int main(void) {
|
||
// 第一件事:设置中断向量表
|
||
SCB->VTOR = 0x08008000; // 应用程序起始地址
|
||
|
||
// 然后初始化HAL
|
||
HAL_Init();
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### 方法3: 使用宏定义
|
||
|
||
在 `main.h` 中定义:
|
||
|
||
```c
|
||
#define APPLICATION_ADDRESS 0x08008000
|
||
|
||
// 在main函数开头
|
||
SCB->VTOR = APPLICATION_ADDRESS;
|
||
```
|
||
|
||
---
|
||
|
||
## 编译与烧录
|
||
|
||
### 编译Bootloader
|
||
|
||
```bash
|
||
cd bootloader_project
|
||
mkdir build && cd build
|
||
cmake .. -DMCU_FAMILY=stm32f4xx
|
||
make -j4
|
||
```
|
||
|
||
### 编译应用程序
|
||
|
||
```bash
|
||
cd application_project
|
||
mkdir build && cd build
|
||
cmake ..
|
||
make -j4
|
||
```
|
||
|
||
### 烧录顺序
|
||
|
||
#### 方法1: 使用ST-Link
|
||
|
||
```bash
|
||
# 烧录Bootloader
|
||
st-flash write bootloader.bin 0x08000000
|
||
|
||
# 烧录应用程序 (首次烧录)
|
||
st-flash write application.bin 0x08008000
|
||
```
|
||
|
||
#### 方法2: 使用STM32CubeProgrammer
|
||
|
||
```bash
|
||
# 烧录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升级
|
||
|
||
```bash
|
||
# 使用上位机工具升级
|
||
./flash_tool --port COM3 --write application.bin
|
||
```
|
||
|
||
---
|
||
|
||
## 测试验证
|
||
|
||
### 基础功能测试
|
||
|
||
#### 测试1: Bootloader启动测试
|
||
|
||
```c
|
||
// 在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: 跳转测试
|
||
|
||
```c
|
||
// 在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
|
||
```
|
||
|
||
**解决:**
|
||
```cmake
|
||
# 在CMakeLists.txt中添加包含路径
|
||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||
extensions/bootloader/include
|
||
)
|
||
```
|
||
|
||
### 问题2: 链接错误 - 重复定义
|
||
|
||
**症状:**
|
||
```
|
||
multiple definition of `main'
|
||
```
|
||
|
||
**解决:**
|
||
检查是否有多个main函数,确保只有一个main入口。
|
||
|
||
### 问题3: 运行时错误 - 无法启动
|
||
|
||
**症状:**
|
||
程序不运行或立即崩溃
|
||
|
||
**排查步骤:**
|
||
1. 检查Flash布局是否正确
|
||
2. 检查中断向量表是否正确设置
|
||
3. 使用调试器检查PC指针位置
|
||
4. 检查堆栈大小是否足够
|
||
|
||
### 问题4: 跳转失败
|
||
|
||
**症状:**
|
||
跳转到应用程序后无响应
|
||
|
||
**排查步骤:**
|
||
1. 确认应用程序起始地址正确
|
||
2. 检查中断向量表是否偏移
|
||
3. 检查应用程序是否正确烧录
|
||
4. 使用调试器查看应用程序是否正常运行
|
||
|
||
**调试代码:**
|
||
```c
|
||
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写入返回错误
|
||
|
||
**排查步骤:**
|
||
1. 确认Flash已解锁
|
||
2. 确认写入地址已擦除
|
||
3. 确认地址对齐
|
||
4. 检查Flash页大小配置
|
||
|
||
**调试代码:**
|
||
```c
|
||
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);
|
||
|
||
// 写入操作...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 下一步
|
||
|
||
完成集成后,请阅读:
|
||
|
||
- [协议文档](PROTOCOL.md) - 了解通信协议细节
|
||
- [移植指南](PORTING.md) - 移植到其他MCU平台
|
||
- [上位机工具](../tools/flash_tool/README.md) - 使用烧写工具
|
||
|
||
---
|
||
|
||
## 技术支持
|
||
|
||
如遇到问题,请:
|
||
|
||
1. 查阅本文档的故障排查章节
|
||
2. 查看 [GitHub Issues](https://github.com/your-repo/issues)
|
||
3. 提交新的Issue,附带:
|
||
- MCU型号
|
||
- 完整的错误信息
|
||
- 相关配置文件
|
||
- 调试日志
|
||
|
||
---
|
||
|
||
**祝您集成顺利!** |