STM32程序如何通过USB实现运行时全Flash自我更新?
STM32F103C8 全Flash自我更新方案实现
你的思路完全可行,这是无需独立Bootloader实现全Flash自更新的标准方案,核心是仅在更新阶段将关键逻辑移至SRAM执行,正常运行时仍从Flash启动。以下是具体实现步骤和注意事项:
核心实现步骤
1. 隔离RAM执行的更新核心代码
将USB DFU通信、Flash擦写、系统复位这些更新必需的逻辑,单独编译为可在RAM中运行的代码段:
- 用编译器属性标记相关函数,例如GCC中:
__attribute__((section(".ram_code"))) void dfu_update_in_ram(void) { // 此处实现Flash擦写、USB接收固件、写入Flash、复位逻辑 } __attribute__((section(".ram_code"))) void flash_erase_full(void) { // 全Flash擦除逻辑(严格按照STM32F1手册流程) } - 修改链接脚本(.ld),将
.ram_code段分配到SRAM地址范围(STM32F103C8的SRAM起始为0x20000000,大小20KB),同时保留该段在Flash中的存储地址(用于运行时拷贝):RAM_CODE_START = 0x20000000; RAM_CODE_SIZE = 0x2000; /* 预留8KB足够使用 */ .ram_code : { __ram_code_flash_start = .; *(.ram_code) __ram_code_flash_end = .; } >FLASH AT>FLASH .ram_code_run : { __ram_code_ram_start = .; *(.ram_code) __ram_code_ram_end = .; } >RAM AT>FLASH
2. 更新触发后的执行流程
当程序收到更新指令后,按以下步骤执行:
- 关闭全局中断:避免中断服务函数(位于Flash)在跳转后被触发导致错误。
- 拷贝RAM代码到SRAM:从Flash中的
__ram_code_flash_start地址,将代码拷贝到SRAM的__ram_code_ram_start地址,拷贝长度为__ram_code_flash_end - __ram_code_flash_start。 - 跳转到RAM代码执行:修改栈指针SP为RAM中的栈地址(可在链接脚本中定义
__ram_stack_top),然后通过函数指针跳转到RAM中的更新入口:typedef void (*RamFunc)(void); RamFunc update_func = (RamFunc)__ram_code_ram_start; update_func(); - 在RAM中完成全Flash更新:
- 解锁Flash控制器;
- 擦除整个Flash(包括原程序所在的
0x08000000起始区域); - 通过USB接收新固件数据,逐页写入Flash;
- 锁定Flash控制器;
- 系统复位:调用
NVIC_SystemReset(),MCU重启后自动从0x08000000加载新固件。
关键注意事项
- RAM代码不能依赖Flash中的任何资源:所有用到的寄存器操作、变量都要在RAM代码内实现,避免引用Flash中的全局变量或函数(比如不能调用原程序中的USB驱动函数,必须在RAM代码里重新实现最简USB DFU通信逻辑)。
- 准确计算RAM代码的大小:确保预留的SRAM空间足够容纳更新核心逻辑,避免覆盖其他RAM数据。
- Flash操作严格遵循手册流程:STM32F1的Flash解锁需要写入特定密钥,擦除和编程有时间限制,这些细节必须准确实现,否则会导致Flash损坏。
- 测试阶段需验证复位后固件的完整性:可以在新固件中加入CRC校验,启动时自动验证固件合法性,避免更新失败后无法启动。
方案优势
相比常规Bootloader方案,该方案无需预留Bootloader区域,充分利用STM32F103C8的64KB Flash空间;更新全程无需用户手动切换Bootloader模式,交互更流畅。
内容的提问来源于stack exchange,提问作者étale-cohomology
相关产品推荐
相关产品推荐

