# 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型号 - 完整的错误信息 - 相关配置文件 - 调试日志 --- **祝您集成顺利!**