STM32只有64KB也能做可靠OTA:我把断电也能“续传”的升级做成了常态

半夜接到客户电话,说远程设备升级掉线后就不再上线,那种心慌我还记得。说实话,大多数人面对STM32这种资源受限的MCU时,第一反应是放弃片内Flash OTA,转而去加外置闪存或把升级时间预约到白天。可我觉得,真正机智的做法不是换硬件,而是把升级流程设计成“可中断、可恢复”的东西,这样即便半途掉电,设备也能自我修复,用户也不会在凌晨打电话来骂你。
核心思路很简单但容易被忽视:把新固件先安全地放到片上备用区,别急着替换运行区;用片内最后一页当做“升级标记”,一旦开始搬运就更新这个标记;搬运采用分块写入并校验,完成后再把标记复位并重启。换句话说,先把风险隔离在一个可回滚的区域,再把“上位”操作做成幂等的、可重启的流程。具体到STM32f103C8T6上,我把64KB片内Flash粗暴分成两个32KB分区,一块跑APP,一块存OTA文件,最后一页则留作标志位和校验信息。这种分区方法不是唯一解,但足以支持18KB左右的应用并保留余量给完整性校验和恢复逻辑。

实现细节里有几个容易出错的点,事先想到能省一堆麻烦。第一,写Flash前必定要做擦除并分块写入,写完后马上做CRC校验;千万不要把“标记先写入再搬运”的顺序当成捷径,我的一个同事小李就由于在搬运前把标记置为“有更新”而被刷成了砖头,重启后设备以为已经写好了新固件,结果待机中断导致启动异常。其次,搬运过程必须设计成可续传:用固定大小的chunk逐块复制,每块复制后更新一个进度记录并校验,这样掉电重启后可以从上次成功的位置继续搬运而不是从头开始。再者,涉及到Flash写操作的函数最好在RAM中执行或确保不在被写页面执行指令,避免由于写操作影响执行流而出现不可预期的问题。
我在代码层面做了两个保证来提高可靠性。一个是“双标记+CRC”策略,即在OTA区末尾先写入固件CRC和一个临时状态标记,搬运完成并验证无误后才把运行区标记切换到新固件状态,这样可以避免由于掉电导致的半成品被误执行。另一个是“搬运即清理”的策略,搬运成功后立即擦除OTA区并把状态标记复位,避免意外重启再次触发误判。要注意,最后一页用作标记不能频繁写入同一扇区,否则会加速闪存磨损,所以把标记设计成写入频率低且采用耐写策略也很重大。

关于接口和测试,片上Flash的通用接口我封装在flash_on_chip.c和flash_on_chip.h里,接口包括分区读写、块校验、进度记录和恢复入口。串口读写用作最简单的OTA传输手段,可以先通过串口把固件流式写入OTA分区并在每个包后返回ACK,确保传输层没有丢包。测试时我提议先在仿真环境里模拟掉电中断,确认设备能从最近的已写块继续搬运并最终启动新固件,然后再在真实设备上做带电源抖动的长时间稳定性测试。我的同事张姐用了这种方法后,连续几百次随机重启测试都没再遇到砖机的问题,这比一次性把所有风险压在重启那一刻要安全得多。
设计完成后还要思考用户体验和运维成本。第一,升级过程要能在后台平滑进行,尽量不影响设备正常功能的最低运行集。其次,固件包里应包含版本号、发布时间和完整性信息,后台管理平台在下发时做二次校验,避免人为下发错误包。这些看似运维细节的东西,实则是避免客户在凌晨给你打电话的关键。最后,从趋势角度看,随着物联网设备增多,能在片上做出可靠的断电续传OTA,反而成为产品的一项差异化卖点,节省硬件成本的同时提升用户信任度。
说到具体操作步骤,我的实践是先计算分区边界并保护bootloader区,接着实现串口接收写入到OTA分区并在每个chunk后做存盘式记录,随后在检测到完整固件并通过CRC后进入搬运流程,搬运采用分块校验并在每块成功后更新进度寄存器,搬运完毕后才切换运行标记并擦掉OTA区,最后软重启进入新固件。整个过程中尽量让标记写操作发生在完成性最强的时刻,避免半途改变标记造成逻辑混乱。实际工程里我把这些逻辑都封装在flash_on_chip.c/.h里,外层只需调用“开始接收”“开始搬运”“检查恢复”这类接口,并通过串口做读写测试验证。
说到底,做片上Flash OTA不是技术秀场,而是把不可靠变成可控的工程学。把升级设计成可中断、可重启、可验证的流程,比追求所谓“秒刷成功率”更能降低运维成本,也更能让用户信任你的产品。反正我是这么做的,既省了外置闪存成本,也把夜里被叫醒的次数降到了零。
你有没有遇到过由于OTA失败而需要物理去刷机的经历?你的项目里是怎么设计升级和恢复流程的,或者你最担心OTA会出什么幺蛾子?欢迎把你的故事、做法或者疑问写出来,我们一起把这个“断电也能续传”的细节做好。





![[C++探索之旅] 第一部分第十一课:小练习,猜单词 - 鹿快](https://img.lukuai.com/blogimg/20251015/da217e2245754101b3d2ef80869e9de2.jpg)










暂无评论内容