简介:面向STM32嵌入式开发者的OTA升级参考资源,特别适配工业现场通过RS485总线远程维护设备的需求。资源包含自制bootloader与App两套完整Keil工程,演示了从固件分包传输、存储到跳转运行的全链路实现。包内共277个文件,以C/H源码为核心,另含axf、bin、hex等编译输出文件以及工程配置文档,压缩包仅2.86MB,目录结构清晰,便于对比Bootloader与App的协作关系。已有2534人学习,适合正在规划IAP功能或设备联网升级方案的中高级工程师。读者可获得串口/485帧协议解析、Flash擦写与跳转、固件地址规划等关键代码,并可直接在此工程基础上进行二次开发。
1. 为什么要用串口/485做OTA升级:先把场景想清楚
做嵌入式这些年,我接过不少"设备已经量产铺出去了,固件出了Bug要现场改"的活儿。早期最痛苦的方式是派个人拎着仿真器跑现场,拆壳、接线、烧录、合壳,运气好半小时,运气不好当天都搞不定。后来慢慢把OTA升级通道做进产品里,才发现这个决策能省下大量人力成本,也让我意识到一个问题:很多团队在规划OTA方案时,一上来就奔着Wi-Fi、4G、以太网这些"高级"通道去,反而忽略了串口/485这种最基础、最可靠、成本最低的升级方式。
1.1 串口/485 OTA的典型应用场景
先说清楚什么叫"串口/485 OTA"。在STM32F4这类MCU上,OTA一般指IAP(In-Application Programming),也就是程序运行过程中,通过某种通信接口把新的固件数据接收下来,写入Flash,然后跳转到新程序执行。串口UART是最常用的通道,485本质上是串口加了一颗收发器芯片(如SP3485、MAX485),物理层变成差分信号,传输距离更远、抗干扰更强,但协议栈层面的处理逻辑和串口基本一致。
哪些场景适合用串口/485 OTA?我归纳下来大概是这四类:
- 工业现场设备:比如伺服驱动器、PLC扩展模块、仪器仪表,很多设备本身就只有RS485接口,没有网络条件;
- 有上位机/触摸屏的产线设备:产线设备一般都有上位机通过串口或485在通信,升级固件时直接复用这条链路就行;
- 批量生产环节:生产时通过串口烧录固件,比用J-Link一个一个插上去快得多,配合自动化治具效率翻倍;
- 售后维护场景:设备出问题后,客服通过远程指导现场人员接一根USB转485线,几分钟就能完成升级,不用拆壳。
1.2 为什么"方式1"不选网络通道
很多初学者问我,为什么STM32F4这么强的芯片,不直接用以太网或者CAN做OTA?其实不是不行,而是"复杂度"和"可靠性"的取舍问题。串口/485 OTA有几个天然优势:
- 协议简单:不需要TCP/IP协议栈,不需要MAC地址、IP配置,MCU端代码量小,调试容易;
- 链路可控:串口是点对点通信,速率低、逻辑简单,出错容易排查,不像网络环境有各种不确定性;
- 成本极低:很多产品MCU上本来就预留了串口引脚,硬件上零成本改动;
- 通用性强:一台电脑加一根USB转串口线或USB转485线就能当升级工具,不需要专门的烧录器。
所以"方式1"的核心思路是:用最简单可靠的物理链路,配合一套严谨的通信协议和引导程序,实现固件的分包传输、校验、写入和跳转。这篇文章我尽量把整个方案的设计思路、代码架构、踩坑经验都写清楚,让你看完之后能直接在自己项目里落地。
2. STM32F4的Flash分区与引导程序设计:IAP的地基
OTA能不能稳定跑起来,一半的功夫在引导程序(Bootloader)的规划设计上。很多人第一次做IAP,上来就写跳转代码,结果不是跳不过去,就是跳过去了跑飞,归根结底是对STM32F4的Flash布局和中断向量表机制理解不到位。
2.1 Flash空间规划:给Bootloader和应用各分一块地
STM32F4系列Flash容量从256KB到1MB不等,以最常见的STM32F407ZGT6为例,Flash一共1MB,分为12个扇区(Sector 0~11),每个扇区大小不同——前4个扇区是16KB,第5个扇区是64KB,后面全是128KB。这个扇区结构在规划分区时必须心里有数,因为擦除操作是按扇区来的。
典型的双区规划是:
| 分区 | 起始地址 | 大小 | 存放内容 |
|---|---|---|---|
| Bootloader区 | 0x08000000 | 32KB | 引导程序、升级逻辑 |
| App区 | 0x08008000 | 剩余空间 | 应用程序 |
| 标志位区 | Flash末尾 | 几个字节 | 升级标志、固件信息 |
Bootloader放32KB是够用的,如果你用STM32CubeMX生成工程,HAL库加串口驱动再加Flash驱动,编译出来一般也就十几KB,余量充足。App起始地址选0x08008000,对应Sector 2——因为Sector 0和1各16KB,加起来正好32KB,这样App区从Sector 2开始,后面都是连续的128KB大扇区,读写在逻辑上也简单。
2.2 引导程序的完整执行流程
引导程序的工作分两种情况:冷启动引导和升级模式。冷启动时,Bootloader检查有没有升级请求,没有就跳转App;有升级请求就进入升级流程,接收固件数据并写入Flash。
整个流程图不需要画出多么复杂的时序,核心逻辑其实就这几步:
- 系统上电,Bootloader初始化时钟、串口、GPIO;
- 检查升级标志(正常启动时清除,有升级需求时置位);
- 若无升级标志,校验App区首地址是否为有效堆栈地址,有效则跳转;
- 若有升级标志或收到上位机的升级指令,进入升级状态机,开始接收固件;
- 升级完成后更新标志,软件复位重启。
这里有一个关键细节:跳转前要正确设置主栈指针和中断向量表偏移。App程序在编译时必须把IROM1的起始地址改成0x08008000,同时在SystemInit之后调用SCB->VTOR = APP_ADDR;来重定向中断向量表。如果忽略这个,App里的串口中断、定时器中断一触发就直接跑飞,这是最常见的IAP失败原因。
2.3 写Flash的几个注意事项
STM32F4的Flash编程有几个硬性要求,写不对不仅会触发HardFault,严重时甚至会把Bootloader区都擦掉:
- 必须按字(32位)写入:HAL库的
HAL_FLASH_Program函数要求传入的是uint64_t类型数据(内部实际按双字写),你不能直接传一个字节; - 擦除是整扇区的:哪怕你只想改一个字节,也得先把整个扇区的内容读出来、擦掉、再写回去;
- 写Flash时不能执行Flash里的代码:如果你的代码在Flash里跑着,然后又去写Flash,会触发总线错误。解决办法是把写Flash的函数放到RAM里执行,或者用HAL库(HAL库内部已经处理了这个问题,底层会暂停CPU中断并处理指令预取);
- 注意看门狗:Flash擦写耗时较长,特别是128KB的大扇区擦除可能要几百毫秒到1秒,如果你的产品开了独立看门狗IWDG,在升级过程中必须及时喂狗,否则升级到一半系统复位,App区处于半写状态,设备就变砖了。
关于变砖的问题,后面第4节我会专门讲保护机制。
3. 传输协议怎么设计:稳定升级的核心不在Flash而在通信
说句实在话,Flash写入代码写对了其实没什么技术含量,真正的难点在通信。串口/485这种物理链路,没有TCP的拥塞控制、没有确认重传机制,你要自己设计一套"够用但不复杂"的协议,才能保证大固件(比如100KB的App)传输过程中不出错。我现在用的这套协议,是从Modbus RTU和YMODEM协议的思路里提炼出来的,兼顾了简单性和可靠性。
3.1 帧格式与通信流程设计
我用的是帧头+命令+长度+序号+数据+CRC校验的固定帧格式,每个字段的含义如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| 帧头 | 2字节 | 0xAA 0x55,用于帧同步 |
| 命令 | 1字节 | 0x01握手 0x02传数据 0x03结束 0x04取消 |
| 数据长度 | 2字节 | 大端模式,指示数据字段的长度 |
| 帧序号 | 2字节 | 从0开始递增,用于丢帧检测 |
| 数据 | N字节 | 固件数据或其他有效载荷 |
| CRC16 | 2字节 | 从命令字段到数据字段末尾的CRC校验 |
每次升级的通信流程是三次握手+批量传输+结束确认。上位机先发握手请求,Bootloader收到后回一个带固件长度和CRC的信息;上位机确认无误后,开始按每包256字节或512字节分包发送;每发一包,Bootloader写入Flash后回一个ACK;上位机收到ACK再发下一包。如果收到NACK,则重发当前包,连续重发N次失败就中止升级。
3.2 握手阶段:先谈恋爱再结婚
很多人做IAP会把流程做得很糙:上位机直接哗啦哗啦发数据,MCU一边收一边写。这样在理想环境下也许能跑通,但实际用起来非常脆弱——你根本不知道当前设备里是什么版本的固件、扇区状态怎么样、能不能支持升级。
我的握手逻辑是这样的:
- 上位机发
0xAA 0x55 0x01 0x00 0x00 0x00 0x00,即握手请求; - Bootloader回复设备信息:Bootloader版本号、App区起始地址、App区总容量、每包最大长度;
- 上位机根据设备信息构造固件头,发回固件长度、固件CRC32校验值;
- Bootloader收到后计算升级时间,回复OK或拒绝;
- 双方进入数据阶段。
这个握手过程看起来增加了不少代码量,但带来的收益非常大:升级前你就知道版本是否匹配、空间是否足够、链路是否正常,不用等传了一半才发现问题。
3.3 CRC校验和ACK/NACK机制
我之前偷懒用过求和校验,后来发现实际传输中,串口偶尔会出现连续多位翻转的情况,求和校验根本查不出来,固件写进去之后设备运行到某个角落就莫名死机。后来换成CRC16-CCITT之后,再也没出过校验漏检的问题。
数据传输阶段,MCU每收到一包数据,先检查帧格式、帧序号、长度,然后做CRC16校验。校验通过就把数据写入Flash,置位写入完成标志,回ACK;校验失败或者序号不对就回NACK,上位机收到NACK后重发当前包。上位机端还需要一个超时机制——发出数据包后500ms内没收到任何回复,自动重发,连续3次超时则报错提示用户检查物理连接。
这里有个容易被忽略的点:MCU写Flash是耗时的,特别是碰到扇区边界需要先擦除。如果你每收一包都立刻回ACK,可能会因为Flash编程时间过长导致上位机超时误判。解决办法是MCU先回ACK再写Flash,或者上位机把超时时间放宽到1秒以上。我用的是先回ACK再写Flash,因为串口波特率一般115200,发512字节大概也就45ms,这个时间内回ACK完全来得及。
3.4 波特率的选择:不是越快越好
很多人一上来就想用921600甚至2Mbps的波特率,觉得这样传得快。但我要泼一盆冷水:OTA升级场景中,可靠性永远优于速度。
原因有几个:
- CH340这种常见的USB转串口芯片,高波特率下丢包率会明显上升,特别是USB的总线调度有延迟;
- 485链路在长距离下,波特率越高,信号衰减和反射越严重,误码率急剧上升;
- MCU端如果用中断接收,高波特率下中断频率过高,会挤压主循环时间,Flash擦写时如果被中断打断,时序就可能出问题。
我实测下来,115200到256000是比较舒服的范围。一个256KB的App,115200波特率下大概传250秒,其实完全可以接受——毕竟升级不是频繁操作,稳定把固件写进去才是硬道理。
4. 485通信模式下要注意的硬件细节:自动收发电路与方向切换
如果你的设备走的是RS485总线,那么软件层面的协议设计基本不变,但硬件和驱动层面有几个"隐藏关卡",处理不好就是升级到一半总线冲突、数据乱码。
4.1 收发切换的三种方案
RS485是半双工通信,发送和接收共用一对差分线,所以必须通过DE/RE引脚控制收发器的方向。实际项目中常见三种做法:
- MCU引脚控制方向:发送前拉高DE,延时等数据发完再拉低,回接收态。控制简单但时序要算准,串口发送完成中断或者TC标志位要用对;
- 自动收发电路:利用三极管或比较器根据TXD信号自动切换方向,硬件自动处理,软件不用操心;
- 专用自动收发芯片:比如MAX13487这类芯片内置了方向控制逻辑,价格略高,但最省心。
我做量产产品时,首选自动收发电路,原因很简单:软件里少一个"切换方向-延时-切换回来"的状态机,升级逻辑更干净,也不容易因为发送完成标志判断失误导致尾巴没发完就切回接收态。自动收发电路的典型做法是在RO和DI信号线上加三极管检测TXD的起始位,硬件层面实现对DE的控制,具体的搭建电路网上有大量参考可以查。
4.2 自动收发电路的一个致命坑:发送尾巴被截断
这里必须分享一个我踩过的坑。自动收发电路有个通病:当TXD变成空闲高电平后,DE延时一小段时间才会拉低,如果这个延时不够长,数据帧的最后几个bit还没完全发出去,方向就切回接收了,导致对端收到的是残缺帧。
表现症状是:偶尔第一包握手成功,后面数据包全部CRC错误;或者发一条指令,对端收到的是莫名其妙的乱码。
排查思路是拿示波器看A/B差分波形和DE引脚波形,对比数据帧结束时的时序。解决办法是硬件调整RC时间常数,或者接收端在协议层加容错——帧尾加一个字节的延时确认,又或者软件里在发送最后一字节后主动延时1-2个字节的发送时间再切换方向。
如果你用的是MCU引脚控制的方案,记住一个原则:不要用"发送寄存器为空"作为发完的标志,要用USART的TC(发送完成)标志,因为TC标志才表示数据已经全部移出移位寄存器,真正送到了线上。
4.3 485总线的终端匹配与接地
做485通信,如果速率和距离上去了,终端匹配电阻不是可选项。我们实际测试过,100米以上距离、115200波特率,不加120欧终端电阻时,如果总线上出现阻抗不匹配,信号会在末端反射,产生振铃,直接导致某一包数据的CRC连续出错。
另一个容易被忽略的问题是地线:RS485是差分信号,理论上不需要共地也能通信,但如果两端设备的地电位差太大(比如超过7V),接收端芯片可能直接烧毁。所以长距离485通信建议用带隔离的收发器方案,最常用的是在MCU侧加一颗隔离电源和数字隔离器,比如ADI的ADM2483、TI的ISO3082。如果只是短距离实验,USB转485线头和设备之间保持共地也能凑合。
5. 上位机与MCU联调:Keil配置、串口助手和常见坑
到这里,Bootloader代码写好了、协议设计完了、硬件也检查过了,接下来就是最考验耐心的联调环节。这一节我把实际操作中最关键的几个配置和最容易遇到的问题挨个说一遍。
5.1 Keil工程里必须改的三个地方
编译App程序时,如果你用的Keil MDK,以下三个配置不修改,跳转后必出问题:
IROM1起始地址:在Target选项卡里,把IROM1的起始地址从0x08000000改成0x08008000,大小改成剩余Flash容量。如果不改,生成的hex文件下载时必须烧写到Bootloader之前,但运行在App里时中断向量表偏移与编译地址不一致,函数跳转和中断全乱。
中断向量表偏移:App工程里在
SystemInit之后,或者main函数最开头,添加:SCB->VTOR = 0x08008000;有的HAL库版本会自动从VECT_TAB_OFFSET宏读取偏移量,把这个宏改成0x8000也行,看具体工程模板。
生成可烧写的bin文件:Keil里配置User选项卡,添加一条After Build命令,调用
fromelf.exe把axf转成bin:fromelf --bin --output=.\Build\app.bin .\Build\app.axf上位机传输固件时一般用bin文件而不是hex,因为bin是纯二进制数据,没有地址信息,更便于按包发送。
还有个细节:Debug调试时注意烧录范围。如果你用J-Link调试App,下载算法默认会从0x08000000开始擦写,如果不修改Flash Download的起始地址,一调试就把Bootloader冲掉了。建议联调阶段先把Bootloader烧好,然后App用串口升级方式烧写,顺便验证IAP链路。
5.2 CH340、FTDI驱动的坑:串口打不开和打开就卡死
调试串口/485升级,上位机这边用的USB转串口线质量参差不齐,驱动也各有脾气。CH340是最常见的国产方案,驱动装好之后在设备管理器里识别为COM口,兼容性整体不错,但有几个容易踩的坑:
- 买到劣质CH340模块:有些小厂模块用了假的CH340芯片,驱动装上后识别成未知设备,或者收发不稳定。建议买正规品牌或者直接选FTDI方案的线,FTDI的驱动稳定性和兼容性确实好一点,但价格贵了不少;
- 串口被占用:串口助手打开串口后,如果你再用别的软件尝试打开同一个COM口,要么打不开,要么刚打开就卡死。调试时只开一个串口助手,别程序里和工具同时去抢占;
- 收发显示乱码:确认波特率、数据位、停止位、校验位两边完全一致。串口调试助手里常见的是8数据位、1停止位、无校验,如果你代码里配置成了2停止位,上位机忘记改,大概率就是乱码;
- 串口自动关闭问题:Windows下拔插USB转串口线后,COM口号可能变化,串口助手软件如果缓存了旧的句柄,重新打开就会报"串口打开失败"。把线拔了重插,刷新串口号再连。
5.3 串口助手的"换行"陷阱
调试协议帧时我发现新手特别喜欢在串口助手里勾选"发送新行"选项,默认发完数据会自动追加\r\n(0x0D 0x0A)。这在调试普通AT指令时没什么问题,但在OTA协议里就是灾难——MCU端把换行符当成帧数据,长度字段对不上,CRC算不对,整个链路就没法正常通信。
我的做法是代码里实现一个状态机解析器:按字节接收,每收到一字节就判断当前状态,是找帧头、收长度、还是收数据。这样即使上位机不小心多发了几字节,MCU也能通过帧头重新同步。你自己做上位机时,也尽量把帧的收发逻辑做成"纯数据模式",不叠加任何文本转义。
5.4 升级中途失败且无法恢复
这是IAP场景中最焦虑的一个问题:升级到一半,串口意外断开,或者电脑断电,Flash里写了一半的App区变成了半残状态,设备重启后Bootloader发现App区的升级标志还在,于是又进入升级模式继续等数据,但上位机已经退出了——这时如果没有超时退出机制,设备就卡在升级模式里,看起来像"变砖"。
解决办法是双保险:
- 升级标志置位后加上超时:Bootloader进入升级模式后,如果X秒内没收到任何有效数据包,自动清零升级标志并跳转当前已有的App;
- 签名验证机制(进阶版):在App区末尾写入固定魔数+固件哈希值,Bootloader每次启动时先校验App区是否完整。不完整就强制进入升级模式等待;完整才跳转。这样即使升级中断,设备也只是停在升级模式等待新固件,不会彻底锁死。
我目前用的是"魔数校验+超时退出",量产几年了一直稳定。如果追求更严苛的可靠性,可以在App的编译脚本里自动生成CRC32表,追加到固件末尾,Bootloader启动时全片校验,大概能覆盖100%的损坏场景。
6. 485双向通信下,主从机同时收发导致的总线冲突排查
前面聊的都是串口点对点场景。如果你的设备走的是RS485主从总线,比如Modbus RTU网络里,设备既要用485做主从通信,又希望通过同一根总线OTA升级,问题就复杂得多了——最典型的现象是热词里提到的那个:"485 Modbus主机从机分别测试都正常,主机连接从机就不正常"。
6.1 问题本质:收发时序的闭环复制
我分析这类问题的经验是:先抛开"OTA升级"这个高级需求,把485通信当成一个纯粹的总线时序问题来排查。分开测、主机单独发、从机单独回,各自都正常;一接在一起就不行,大概率是这两个问题之一:
- 收发器方向切换时序不对:主机发完请求后,如果方向控制还没完全切回接收态就开始等从机应答,会错过从机回复的第一个字节,从机发的数据只有半个字节能被收到,看起来就是"从机没反应";
- 总线上有第三个收发器在捣乱:比如调试时你电脑上挂了一个USB转485,它虽然没发包,但它的接收器一直挂在总线上,如果它的输入阻抗不够高,会拉低总线电平,导致正常通信的边沿幅度不足。
排查链路我建议按这个顺序走:先用示波器抓A/B两端波形,看数据发送时差分电平是否清晰到达;再抓DE/RE引脚波形,确认方向切换的临界点;最后断开所有无关设备,只留主机和从机,逐步加回设备,找出正确时序下不该出现的干扰源。
6.2 升级过程中和Modbus主从通信怎么共存
如果你的设备平时运行在Modbus从机模式下,要在不干扰正常主从轮询的前提下插入OTA升级,必须设计一个升级模式入口。我常用的方案是定义一个特殊功能码,或者复用一个保持寄存器作为"升级请求标志"。主机想升级某台从机时,先写这个寄存器,从机置位升级标志并回包,然后从机不再响应普通Modbus请求,进入升级模式。升级完成后,从机软复位,重新进入正常主从模式。
这样做的关键是:升级期间总线上只有升级交互的双方,其他从机必须进入静默状态。如果你的上位机既要轮询其他从机,又要给目标从机传数据,就把升级数据的帧格式和Modbus的从机地址区分开,比如升级帧里带目标从机地址,只有目标从机才响应该地址的升级命令。总的来说,要把OTA升级做成485总线上的一个"临时会话",用地址和命令字隔离,避免和正常业务互相踩踏。
6.3 从串口升级到485升级:代码层面到底改了啥
从实现角度说,串口OTA和485 OTA在MCU端的代码差异其实很小,主要就两点:
- GPIO配置:串口用TXD/RXD两个引脚直接接USB转串口;485要在移植时初始化DE/RE引脚并实现方向切换逻辑;
- 发送函数要加方向控制:普通串口发送就是往数据寄存器写数据;485需要先拉高DE,发送完成后再拉低DE。封装一个
RS485_SendBuffer函数,里面先RS485_SetDir(1),发完再RS485_SetDir(0),其他地方不用动。
所以我做方案时,一般把通信底层抽象成UART_SendBytes/UART_ReceiveByte,485只是在这个接口上多包了一层方向控制。将来产品要改用CAN OTA或者以太网OTA,上面的协议逻辑和Bootloader结构都可以复用,只需要换掉最底层的收发函数。这个层次划分值得一开始就做好,不然后面扩展会很痛苦。
7. 从一次实际升级事故讲起:我是怎么定位Flash写入失败的
最后分享一个实操中比较典型的问题案例,是我在给一台量产的STM32F407设备做485 OTA时遇到的。现象是:传输过程中前几十包都很正常,到某一包开始,MCU回NACK,上位机重发N次仍然失败,升级终止。把设备断电重启之后,能正常进入App,但App版本次数不对,说明前面写进去的数据有一部分没生效。
7.1 排查过程:从怀疑链路到锁定Flash
一开始我怀疑是485链路的问题,毕竟物理层受干扰的可能性最大。我先用示波器抓了A/B差分信号,波形干净,边沿清晰,排除了信号完整性问题。又换了新的USB转485线,问题依旧。后来我打印了MCU收到每一包数据的序号和CRC结果,发现一个规律:失败的位置总是出现在一个固定区域——64KB扇区的边界附近。
这就很能说明问题了。STM32F407的Flash扇区前四个16KB,接着是64KB,再往后是128KB。如果我的App从0x08008000开始,Bootloader占32KB,那么App区前两个扇区是16KB+16KB,接着就是64KB扇区。数据写到0x08010000(64KB扇区)的边界时,Flash控制器要做扇区切换,如果代码在扇区切换时没有正确管理擦除时机,很可能出现写入地址错位的情况。
7.2 根因:跨扇区边界时的擦除时序
我的代码逻辑是"先查当前地址所在的扇区,如果跟上一个写入的扇区不同,就擦除新扇区再写"。问题在于擦除和写入没有做原子操作——某次升级中,擦除完成了但是写入还没开始,此时上位机的超时重发机制认为上一包失败,重发了相同序号,但MCU侧的写入地址已经推进到新扇区,导致接收方状态机和发送方错位,每包序号对不上,CRC也全乱。
弄清根因后我改了两处:一是让Bootloader在擦除扇区时不响应任何新数据包,擦完再继续接收;二是上位机在MCU回复"擦除中"这个状态时,不重发当前包,而是等待固定延时再发下一包。修改后连续测试了十几次,跨扇区边界再也没有失败过。
7.3 经验沉淀:给正在做OTA的你几条建议
做完这个项目,我把自己做OTA的流程沉淀成了一份清单,每次新项目都按这个走,很少再出大问题:
- 先在串口模式下把整个升级链路跑通,再切换485。485比串口多一个方向控制的变量,把变量留到最后加,排查起来简单很多;
- 在Bootloader里加详细日志输出:收到的包序号、CRC结果、Flash擦写状态,通过串口实时打印。调试完再关掉日志代码,但要保留一个宏开关,方便现场远程定位问题;
- 升级前把App的版本号放到一个固定地址:比如Flash最后一个扇区的前四个字节,每次升级时校验新旧版本,避免误刷同版本或者降级刷入导致配置不兼容;
- 给上位机做"升级进度+剩余时间"显示:用户等太久会焦虑,进度条能极大提升体验,而且能及时发现链路卡死;
- 升级过程中严禁断电:这个必须醒目标注在说明书里,最好在产品上设计一个升级指示灯,让操作人员一眼看到状态。
8. 从串口助手到批量烧录治具:这套方案的下一步扩展
做完一次OTA方案后,你会发现这套东西就像打通了任督二脉——它不只是"升级固件"这么简单,还能往上叠很多生产、测试、维护的工具。我的经验是,先把基础链路通,然后根据实际需要逐步开发周边工具。
最常用的扩展方向有三个。第一是批量烧录治具:产线里用一块F407的板子当"烧录器",通过数组存储bin文件,再用485总线并发地往多个设备烧录,比人工一个个插J-Link效率高一个量级。第二是远程协助升级:设备的485总线如果接了DTU或串口服务器,上位机通过MQTT或TCP下发升级指令和固件包,DTU把它转成串口数据,就能实现"云端触发、链路复用"的远程升级。这个方向很多物联网网关方案已经在用,协议设计思路和我前面讲的完全一致。第三是日志回传:设备运行状态、错误日志通过485总线定时上报,方便远程诊断,排查问题不用再跑现场。
如果真的想把OTA这套东西做得更扎实,我强烈建议你研究一下YMODEM协议和STM32官方应用笔记AN4657里的IAP实现思路。前者对大数据传输的通信细节处理得非常完善,后者对Flash编程、跳转稳定性方面有不少成熟的参考代码。看完之后你会发现自己设计的协议在很多细节上都能再打磨一轮——比如对"最后一包不满长度时的补零规则""断线重连后的续传机制"这些边角情况的处理,工程上差之毫厘,成品稳定性就谬以千里。
本文还有配套的精品资源,点击获取