手把手教你用STM32F103C8T6实现串口IAP升级(附W25Q64存储方案与源码)

张开发
2026/4/19 12:27:38 15 分钟阅读

分享文章

手把手教你用STM32F103C8T6实现串口IAP升级(附W25Q64存储方案与源码)
STM32F103C8T6串口IAP升级实战从BootLoader设计到W25Q64存储方案在嵌入式开发中固件升级是一个永恒的话题。想象一下当你的智能家居设备部署在用户家中后发现了一个需要修复的bug或者需要增加新功能时难道要一个个拆下来重新烧录吗显然不现实。这就是IAP(In-Application Programming)技术存在的意义——它让设备能够在不依赖专用烧录器的情况下通过通信接口完成固件更新。本文将带你从零开始基于STM32F103C8T6和W25Q64 Flash芯片构建一套完整的串口IAP解决方案。1. IAP基础与系统设计1.1 IAP工作原理剖析IAP技术的核心在于将程序存储区分成两个部分BootLoader区和应用程序(APP)区。BootLoader是一段特殊的代码它负责检查是否需要更新应用程序并完成新固件的接收、校验和写入工作。整个过程可以概括为上电后首先运行BootLoaderBootLoader检查更新标志位如果需要更新则进入固件接收模式如果不需要更新则直接跳转到APP执行在APP运行期间可以通过特定条件(如接收到升级命令)触发重启并设置更新标志位使系统在下一次启动时进入升级流程关键设计考量Flash分区大小BootLoader需要足够空间但也不能浪费宝贵的Flash资源跳转机制如何安全地从BootLoader跳转到APP固件存储接收到的固件临时存放在哪里通信协议如何可靠地传输固件数据1.2 硬件选型与资源规划我们选择的STM32F103C8T6是一款性价比极高的Cortex-M3内核MCU具有64KB Flash和20KB RAM。配合W25Q64(8MB SPI Flash)作为外部存储可以很好地满足IAP需求。Flash分区方案区域起始地址大小用途BootLoader0x0800000020KB存放BootLoader程序APP0x0800500044KB存放主应用程序备份区W25Q64 Block14KB存储更新标志和固件信息提示实际项目中BootLoader大小应根据功能复杂度调整。20KB对于基础IAP功能已经足够如果加入更多高级功能(如网络升级、加密校验等)可能需要更大的空间。2. BootLoader实现详解2.1 启动流程与跳转机制BootLoader的核心任务之一是判断是否需要更新APP并根据判断结果决定是进入升级模式还是直接跳转到APP。以下是关键代码实现// 检查更新标志 OTA_InfoCB ota_info; W25Q64_Read((uint8_t*)ota_info, OTA_INFO_ADDR, sizeof(OTA_InfoCB)); if(ota_info.OTA_flag NEED_UPDATE) { // 进入升级模式 uart_iap_process(); } else { // 跳转到APP jump_to_app(); }跳转到APP需要正确设置堆栈指针和程序计数器void jump_to_app(void) { typedef void (*pFunction)(void); pFunction Jump_To_Application; uint32_t JumpAddress; // 检查APP起始地址是否有有效程序 if(((*(__IO uint32_t*)APP_ADDRESS) 0x2FFE0000) 0x20000000) { // 设置堆栈指针 __set_MSP(*(__IO uint32_t*)APP_ADDRESS); // 设置程序计数器 JumpAddress *(__IO uint32_t*)(APP_ADDRESS 4); Jump_To_Application (pFunction)JumpAddress; // 跳转 Jump_To_Application(); } }2.2 W25Q64存储方案实现由于STM32F103C8T6内部Flash容量有限我们使用W25Q64来存储更新标志位和固件信息。W25Q64是Winbond公司生产的8MB SPI Flash组织为128个块(Block)每个块64KB可以擦除、编程和读取。存储结构设计typedef struct { uint32_t OTA_flag; // 更新标志位 uint32_t fw_size; // 固件大小 uint32_t fw_crc; // 固件CRC校验值 uint8_t fw_version[32]; // 固件版本号 } OTA_InfoCB;注意W25Q64的擦写寿命约为10万次虽然比不上EEPROM的百万次级别但对于不频繁的固件更新已经足够。如果预计会有非常频繁的更新操作建议还是使用EEPROM。3. 串口IAP与Xmodem协议实现3.1 Xmodem协议解析Xmodem是一种广泛使用的文件传输协议特别适合在资源受限的嵌入式系统中实现可靠的数据传输。其数据包格式如下字段大小(字节)说明SOH1起始头固定为0x01包序号1当前包序号(1-255)包序号反码1包序号的按位取反数据128有效载荷CRC2循环冗余校验传输流程接收方发送NAK(0x15)请求开始传输发送方发送第一个数据包接收方校验数据包如果正确回复ACK(0x06)如果错误回复NAK(0x15)请求重传重复2-3步骤直到传输完成发送方发送EOT(0x04)表示传输结束接收方回复ACK确认3.2 串口IAP实现代码以下是基于Xmodem协议的固件接收核心代码框架void uart_iap_process(void) { uint8_t response NAK; uint32_t write_addr APP_ADDRESS; // 擦除APP区域 FLASH_Erase(APP_ADDRESS, APP_SIZE); while(1) { // 发送NAK请求数据包 USART_SendByte(response); // 接收数据包头 if(USART_ReceiveByte(header, 1000) ! SUCCESS) { // 超时处理 continue; } if(header EOT) { // 传输完成 USART_SendByte(ACK); break; } else if(header SOH) { // 处理数据包 if(process_xmodem_packet(packet) SUCCESS) { // 写入Flash FLASH_Program(write_addr, packet.data, 128); write_addr 128; response ACK; } else { response NAK; } } } // 更新标志位 ota_info.OTA_flag NO_UPDATE; W25Q64_Write((uint8_t*)ota_info, OTA_INFO_ADDR, sizeof(OTA_InfoCB)); }4. 实战技巧与常见问题4.1 开发中的避坑指南在实际开发过程中以下几个问题特别需要注意中断向量表重映射APP中必须重设中断向量表偏移量// 在APP的main函数开始处添加 SCB-VTOR FLASH_BASE | 0x5000; // 0x5000是APP区的偏移量Flash编程对齐STM32F103的Flash编程必须按半字(2字节)对齐写入前必须确保目标区域已被擦除W25Q64使用注意事项写操作前必须先擦除(擦除最小单位是4KB扇区)写操作有最大时间限制需要检查忙状态void W25Q64_WaitForWriteEnd(void) { uint8_t status 0; do { W25Q64_ReadStatusReg(status); } while(status 0x01); // 检查BUSY位 }4.2 性能优化技巧双缓冲接收使用双缓冲技术可以提高串口数据接收效率当一个缓冲区正在处理时另一个缓冲区可以继续接收数据CRC校验优化使用硬件CRC单元(如果可用)可以大幅提高校验速度uint32_t calculate_crc32(uint32_t* data, uint32_t length) { CRC-CR CRC_CR_RESET; for(uint32_t i0; ilength; i) { CRC-DR data[i]; } return CRC-DR; }固件压缩在传输前对固件进行压缩可以显著减少传输时间在BootLoader中加入解压算法5. 进阶扩展思路当掌握了基础的串口IAP实现后可以考虑以下几个方向的扩展无线升级(OTA)通过Wi-Fi/蓝牙等无线模块实现远程升级需要增加网络协议栈支持安全加固增加固件签名验证防止未授权更新使用AES等算法加密固件差分升级只传输新旧版本之间的差异部分大幅减少传输数据量特别适合无线升级场景多APP支持在Flash中划分多个APP区域实现APP之间的安全切换和回滚在实际项目中我遇到过因为忘记重设中断向量表而导致APP中中断无法正常工作的问题调试了整整一天才发现这个细节。这也提醒我们在嵌入式开发中对硬件细节的理解往往能决定项目的成败。

更多文章