1. 背景
在软件开发的过程中,由于设计缺陷和业务需求的更新,完美的软件是不存在的。因此,软件一直在不断地进行升级和完善。本文关注的重点是如何将新软件取代正在运行中的旧软件,特别是在电子产品领域,设备的空中升级(OTA)需要考虑硬件资源的限制,因此需要选择不同的升级方案。
2. 空中升级流程
简单来说,空中升级流程包括设备在运行旧软件的同时获取新软件包,执行特定操作来覆盖旧软件,并最终运行新软件。通过在线升级流程,我们能够简化升级的过程,确保设备能够平稳地切换到新软件的运行。
根据硬件资源和系统整体框架,选择不同的升级方案,方案需要结合实际选择最佳的,技术层面是次要的。
3. 空中升级的方案
3.1. 整包升级
以STM8单片机升级为例,单片机最小系统运行流程如下:
要加入在线升级功能,就需要将主应用程序拆分,类似有2套程序在设备内运行,标准称呼是bootload+app,其中bootload始终不变,它接收新软件包并覆盖app区域。
硬件限制 | 解决方案 |
---|---|
单片机自身没有网络连接功能,只能基于外挂的网络模块从远端服务器下载新软件包,再通过串口传输给单片机 | 由外挂主机下载新软件包,并通知单片机进入升级模式 |
单片机内部RAM小,不能进行复制运算或者缓存大量数据;单片EEPROM小,不能保存完整的新软件包 | 只能在Bootload期间分段接收新软件并立刻写入flash |
整体方案如下图:
难点与风险
-
单片机在app收到升级请求,需要重启进入bootload,对于以单片机为主控的设备,需要保证单片机重启期间网络模块不断电,所以因升级进入bootload要立刻恢复网络模块主机供电,必要时需要硬件支持。 -
单片机ram和rom有限,采用分段接收新软件包,直接写入flash,因此在应用层协议上需要确保数据包不会错误或遗漏或重发,目前采用Ymodem协议居多。升级包需要转成bin文件,单片机接收后写到app的起始地址。 -
单片机分段接收后写入flash,整个过程需要10秒以上,期间如果断电,单片机软件的app处于损坏状态,需要单片机有超时机制重启,再次开启升级并主动要求网络模块重传新软件包。 -
一般PC软件无需考虑内存和存储空间,也是采用整包升级,两个文件同时保存。例如app.exe运行时下载新的app_new.exe,下载校验后,app.exe自毁删除自身,然后将app_new.exe重命名为app.exe并启动。
3.2. 差分升级
差分升级软件框架同整包升级一样,区别在于新软件包的提供方式不同。
单片机软件一般不超过50KB,复杂微处理器的软件一般都比较大,但其RAM也大,下载完整的新软件包耗时长且浪费流量,因此可以基于新旧两版软件的差异,将差异文件传给设备,由设备运算还原出新软件包升级。
难点与风险
-
差分包的制作与还原算法验证,在bootload还原出新软件包时考虑到RAM,差分包是按块生成,还原也是按块执行,每块新软件写入前,需要先备份旧块,防止异常断电无法还原。 -
万一出现异常,重启还是进入bootlaod,查询上次已经还原到第几块,继续后面操作。有些为了再次减小差分包大小,还会对文件进行压缩,还原前先解压。 -
升级时,必须保证生成的差分包是基于当前设备内运行的版本,如设备运行V01,但是提供的差分包却是基于V02到V03的,则会导致异常。或者在文件中预设特殊版字符,版本匹配才进行差分还原升级。而整包没有该缺点,只要bootlaod正常,任意app软件版本可以互相升级。
3.3. 动态加载
动态加载在PC软件中很常见,多个exe可执行文件共用dll库文件,这样exe文件很小,缺点是要保证exe正常运行,需要在指定的路径下存放dll文件。
对嵌入式平台,动态加载的可以理解为始终保持底层基础框架不变,修改或替换不同的上层业务逻辑,实现不同的效果。为保证底层和上层之间调用关系,必须固定部分接口地址,也就是两者中间的接口映射,主要通过链接实现。
嵌入式软件从源码到可下载到设备的映像文件,需要经过的步骤:

动态加载就是在链接阶段,将上层代码obj编译成axf可动态加载的文件,而不是直接与其他obj合并成可执行文件。主要是使用armlink配置-entry指定映像文件的初始入口点或者在代码中使用#pragma arm section code关键字,保证动态的上层有固定的入口地址。凡是上层调用的底层接口,在编译阶段函数体指针都赋为空指令保证编译,后续再指向底层的真实地址。