从零深入理解嵌入式OTA升级:Bootloader、IAP与升级流程全解析
·
引言(Opening)
想象一下,你开发的一款智能水杯、一个环境监测设备或者一台共享充电宝,已经部署到了成千上万的用户手中。突然,你发现了一个软件bug,或者需要增加一个酷炫的新功能。你不可能派人跑到每个设备面前用USB线给它烧录程序。这时,OTA(空中升级)技术给你一把 钥匙。
第一部分:核心概念解析——Bootloader, IAP, OTA
在深入流程之前,我们必须先理清这三个容易混淆的概念。
- Bootloader(引导加载程序):
- 是什么:这是一段存储在微控制器(MCU)启动地址(通常是Flash起始地址)的小程序。它是设备上电后运行的第一段代码,就像是设备的“BIOS”。
- 职责:它的核心工作是决定程序的流向。通常是:初始化基本硬件(时钟、内存) -> 检查是否有升级请求 -> 如果有,则留在Bootloader等待升级;如果没有,则跳转到用户应用程序(Application, APP)执行。
- IAP(在应用编程):
- 是什么:一种允许应用程序在运行过程中对自身Flash存储器进行重新编程的技术。
- 如何工作:MCU的内部Flash通常被划分为多个区(Bootloader区、APP区、备份区、数据区等)。IAP利用MCU自带Flash控制器(Flash Controller)的擦除/编程函数,将接收到的新固件数据写入到指定的Flash区域。
- 关键点:IAP是实现OTA的技术手段。
- OTA(空中升级):
- 是什么:一个更大的概念,指通过无线网络(如Wi-Fi, 4G/5G, Bluetooth)进行远程升级的整个系统和服务端架构。
- 组成部分:
- 设备端:包含支持IAP的Bootloader和应用程序。
- 服务器端:存储新固件包(firmware.bin)、提供下载接口、管理设备版本。
- 通信协议:设备与服务器之间的交互规则(如HTTP, MQTT, CoAP)。
- 关系:OTA = 通信能力 + IAP能力。我们本文重点讨论的是OTA中的设备端IAP部分。
第二部分:OTA升级流程详细分解
下图清晰地展示了一次完整的OTA升级 流程:

现在,我们来逐一拆解图中的每一个关键步骤。
步骤一:接收升级指令与跳转Bootloader
- 触发时机:设备正常运行在APP中,通过无线模块(如ESP8266、NB-IoT模组)与服务器保持通信。
- 指令下达:服务器通过某种通信协议(如MQTT主题 device/001/update)向设备发送升级指令。指令中通常包含新固件的版本号、大小、哈希值以及下载地址。
- 预处理:APP收到指令后,需要做两件至关重要的事:
- 校验自身状态:是否在充电?电量是否充足?是否处于空闲状态?防止升级中途断电变“砖”。
- 设置“升级标志”:这是一个非常关键的动作。在MCU的Flash(通常是一个专有的“数据区”)或备份寄存器(Backup Register)中,写入一个特定的值(如0xA5A5A5A5)。这个区域需要保证在芯片复位后数据依然保持。
- 执行跳转:不是直接擦写Flash,而是软件复位(Soft Reset) 单片机。复位后,PC指针回到起始地址,Bootloader开始运行。
步骤二:Bootloader的判断与升级启动
- Bootloader初始化:上电/复位后,Bootloader最先运行,进行最基本的硬件初始化(时钟、中断向量表)。
- 检查“升级标志”:Bootloader会去预定义的地址读取“升级标志”的值。
- 如果标志有效:说明本次复位是由升级请求触发的,Bootloader将停留在原地,不会跳转到APP。并开始执行接下来的升级任务。
- 如果标志无效或不存在:Bootloader会延时一小段时间(如100ms),等待是否有来自串口等调试接口的升级命令(用于本地调试)。如果没有,则直接跳转到APP的起始地址,正常启动应用程序。
步骤三:Bootloader开始升级与接收数据
- 建立连接:Bootloader通过设备的网络模块,按照APP之前收到的下载地址(地址信息也需要存储在Flash的某个地方)主动连接服务器(例如发起HTTP GET请求)。
- 接收数据:服务器将固件文件(bin文件)发送给设备。由于嵌入式设备内存(RAM)有限,无法一次性接收整个固件,必须采用分段接收、分段写入的方式。
- 数据包格式:为了可靠传输,通常需要自定义简单的协议帧。例如:
[包头][包序号][数据长度][数据内容][校验和]。
- 数据包格式:为了可靠传输,通常需要自定义简单的协议帧。例如:
-
流程控制:每收到一包数据,Bootloader需要回复一个ACK确认包,服务器再发送下一包。如果超时未收到,则请求重发该包。
步骤四:分段写入Flash与校验
这是IAP的核心操作,非常考验代码的健壮性。
- Flash布局:必须事先规划好。例如:
- 0x0800 0000 - 0x0800 7FFF: Bootloader区(32KB)
- 0x0800 8000 - 0x0807 FFFF: APP区(480KB)
- 0x0808 0000 - 0x0808 1FFF: 数据区(8KB,存放升级标志、版本号等)
- 擦除APP区:在开始写入新固件之前,必须先完整擦除旧的APP区。Flash的特性是只能由1写0,但需要擦除(全部变为1)才能再次写入。擦除操作通常以扇区(Sector)或页(Page)为单位进行。
- 分段写入:
- 开辟一个RAM缓冲区(如1-4KB)。
- 当接收到一包或多包数据填满缓冲区后,调用MCU的Flash编程函数,将缓冲区数据写入APP区的指定地址。
- 更新写入地址指针。
- 校验:
- 局部校验:对每一包数据进行校验和(Checksum)或CRC校验,确保数据在传输过程中没有出错。
- 全局校验:当接收完所有数据包后,对整个APP区的固件进行一次完整的校验,通常计算其SHA-256哈希值,并与服务器提供的哈希值进行比对。这是防止固件错误、确保升级完整性的最后一道保险。
步骤五:收尾工作与版本确认
- 清除升级标志:全局校验通过后,立即将之前设置的“升级标志”清除(如写入0xFFFFFFFF),表示升级成功完成。这一步很重要,防止下次复位后又陷入升级循环。
- 存储新版本号:将服务器下发的版本号信息存储到Flash的特定位置。
- 复位设备:执行软件复位,让整个系统重新启动。
步骤六:启动新APP与OTA结束
- Bootloader再次启动:本次复位后,Bootloader检查“升级标志”,发现已被清除。
- 跳转新APP:Bootloader顺利跳转到新的APP起始地址,新程序开始运行。
- 上报新版本:APP启动后,可以读取Flash中存储的版本号,并主动上报给服务器,告知升级成功。至此,一次完整的OTA升级结束。
第三部分:关键技术与注意事项
- 中断向量表重映射:Bootloader和APP有各自的中断向量表。跳转后,必须将中断向量表地址切换到APP的向量表起始地址,否则中断无法正确响应。
- 通信协议稳定性:尤其是在Bootloader中实现网络通信,需要考虑断线重连、包序管理、超时重传等机制,设计一个简单的应用层协议非常必要。
- 变砖与恢复:
- 原因:升级过程中断电、固件校验失败、跳转地址错误等。
- 对策:
- 双备份(A/B系统):保留两个APP区,一个运行,一个备用升级。新固件写入备用区,校验成功后再切换引导地址。
- Bootloader自救:在Bootloader中预留一个通过串口升级的“后门”,即使APP区损坏,也能通过有线方式重新烧录。
- 安全性:
- 固件加密:防止传输过程中被窃取。
- 数字签名:验证固件来源的合法性,防止恶意固件被写入设备。
更多推荐

所有评论(0)