2、多分区架构设计:Bootloader分区、App分区、Recovery分区、Data分区的功能与布局
多分区OTA升级,说白了就是把固件拆成几个独立区域,各管各的事。我见过不少团队一上来就搞单分区,结果升级失败变砖,只能拆机烧录。嗯,分区设计就是给系统留条后路。
这一章我们拆开来看四个核心分区:Bootloader、App、Recovery、Data。每个分区干什么、放哪里、多大合适,我都会结合项目经验讲清楚。
2.1 分区总览:一张图看懂布局
先看整体框架。下面这张图是我画的一个典型双备份OTA分区布局,适用于MCU或Linux小系统。你想想看,如果App区坏了,Recovery区还能救回来。
2.2 Bootloader分区:系统的守门员
Bootloader是上电后第一个跑的程序。它不干别的,就两件事:校验App区是否完整,然后跳过去执行。如果App区坏了,它就进Recovery模式。
我个人习惯把Bootloader做得尽量小,只包含必要的硬件初始化和升级逻辑。为什么?因为Bootloader一旦写死,后面改起来很麻烦——没有自升级能力,只能通过烧录器更新。
Bootloader的核心功能清单:
- 硬件初始化:时钟、内存、关键外设(如Flash控制器)
- 分区校验:检查App分区的CRC或签名,决定启动路径
- 升级入口:检测升级标志(GPIO电平、特定Flash标记、网络指令)
- 跳转执行:干净地跳转到App或Recovery入口地址
2.3 App分区:主战场
App分区就是你的业务固件所在地。设备正常工作时,代码和数据都在这里跑。App分区的大小直接决定了你能塞多少功能。
我建议App分区至少留出 30% 的余量。为什么?因为固件会不断迭代,功能越加越多。你想想看,如果分区刚好塞满,下次加个新协议栈就尴尬了。
| 设备类型 | 典型App分区大小 | 备注 |
|---|---|---|
| MCU(Cortex-M0/M3) | 128KB~512KB | 不含文件系统,裸机或RTOS |
| MCU(Cortex-M4/M7) | 512KB~2MB | 可含轻量文件系统 |
| Linux小系统 | 4MB~32MB | 含内核+根文件系统 |
| 应用处理器 | 64MB以上 | 含完整用户空间 |
App分区还有一个关键设计:版本标记。我习惯在App分区头部预留一个结构体,存放固件版本号、CRC、编译时间。Bootloader读这个结构体就能判断要不要升级。
// App分区头部结构体示例
typedef struct {
uint32_t magic; // 魔数,用于校验分区有效性
uint32_t version; // 固件版本号
uint32_t crc32; // 整个App分区的CRC校验值
uint32_t size; // 固件实际大小
uint8_t reserved[16]; // 保留字段,用于扩展
} app_header_t;
2.4 Recovery分区:最后的救命稻草
Recovery分区,说白了就是备用系统。当App分区升级失败或损坏时,设备能切到Recovery模式运行。Recovery里通常包含一个最小可用的固件,能支持基本的网络通信和升级功能。
我记得有个项目,设备部署在偏远山区,App升级到一半断电了。重启后Bootloader发现App区CRC校验失败,自动切到Recovery。Recovery里有一个精简的HTTP客户端,从云端拉取完整固件重新刷写。如果没有Recovery分区,那台设备就得派人去现场拆机烧录——来回成本够买几十台设备了。
Recovery分区的大小通常和App分区相当,或者略小。为什么?因为Recovery固件可以裁剪掉非核心功能,比如去掉UI、去掉高级算法,只保留升级链路。
Recovery分区的布局要点:
- 独立完整性:Recovery分区自身必须有CRC校验,确保它不会坏
- 最小依赖:尽量不依赖Data分区,防止Data损坏导致Recovery也启动不了
- 升级能力:Recovery必须能刷写App分区,也能刷写自身(通过双备份机制)
2.5 Data分区:持久化存储
Data分区存的是运行时数据,比如设备配置、网络参数、日志、升级缓存。它和固件分开,好处是升级App时不会丢配置。
我见过一些设计把配置参数放在App分区里,每次升级都要重新配置。用户抱怨说“升级完又要重新设Wi-Fi密码”,体验很差。Data分区就是解决这个问题的。
Data分区的典型内容:
- 系统配置:网络参数、设备ID、校准数据
- 升级状态:升级进度、下载缓存、回退标记
- 运行日志:错误记录、调试信息(循环写入)
- 用户数据:传感器阈值、场景配置等
Data分区的大小取决于你的数据量。一般来说:
- 轻量设备:16KB~128KB(只存配置和少量日志)
- 中等设备:256KB~1MB(含升级缓存和较多日志)
- 复杂设备:2MB以上(含文件系统、数据库等)
2.6 分区布局的常见模式
实际项目中,分区布局不是一成不变的。我总结了几种常见模式,供你参考:
| 模式 | 布局 | 适用场景 | 优缺点 |
|---|---|---|---|
| 单备份模式 | Boot + App + Data | 资源极受限,升级失败可接受 | 简单省空间,但升级失败可能变砖 |
| 双备份模式 | Boot + App + Recovery + Data | 大多数IoT设备 | 安全可靠,占用空间较大 |
| A/B分区模式 | Boot + App_A + App_B + Data | 高可用设备,要求零停机升级 | 升级失败可回退,但Flash需求翻倍 |
| 三区隔离模式 | Boot + App + Recovery + Factory + Data | 工业/医疗设备,要求出厂恢复 | 最安全,但分区管理复杂 |
我个人最常用的是双备份模式。它在安全性和空间占用之间取得了平衡。如果Flash容量允许,我会升级到A/B分区模式,实现无缝升级。
2.7 分区布局的实操建议
最后,分享几个我在项目中踩过的坑和总结的经验:
- 分区地址要对齐:Flash的擦除块大小通常是4KB或64KB,分区起始地址必须对齐到擦除块边界。否则擦除操作会影响到相邻分区。
- 预留升级标志区:在Bootloader和App之间留一个专门的标志区域,用于存放升级指令。这个区域要独立于Data分区,防止Data损坏导致无法升级。
- 考虑磨损均衡:Data分区频繁写入,要使用支持磨损均衡的文件系统,或者预留足够的冗余块。
- 分区表固化:分区表一旦确定,尽量不要改动。如果必须改,要通过Bootloader升级来更新分区表,并且要兼容旧分区布局。