1. 分区表基础概念

各位同学好,我是你们这堂课的老王。做嵌入式固件开发这么多年,我见过太多因为分区表设计不合理导致的升级失败案例。今天咱们就来聊聊这个看似基础、实则至关重要的东西——分区表。

1.1 什么是分区表

说白了,分区表就是一张「地图」。它告诉芯片:你的代码放在哪、数据存在哪、升级时该往哪写。

我习惯这么理解:Flash 就像一栋大楼,分区表就是楼层索引。没有索引,你连自己的办公室都找不到,更别提让系统正常启动了。

分区表的核心定义: 存储在 Flash 起始位置(通常是偏移 0x0 或固定地址)的一张结构化表格,记录了每个分区的起始地址、大小、属性和用途。

每个分区表条目通常包含以下字段:

字段 说明 典型值
分区名称 标识分区用途 boot, app, data
起始地址 在 Flash 中的偏移 0x08000000
分区大小 分配给该分区的空间 0x10000 (64KB)
分区类型 App / Data / Recovery 0x00 (App)
属性标志 可读、可写、可执行 0x07 (RWX)

我的经验: 分区表最好放在 Flash 的独立扇区,并且保留两份备份。我曾经遇到过 Flash 坏块刚好命中分区表区域,结果整机变砖。从那以后,我所有项目都强制做分区表冗余。

1.2 分区表在固件升级中的作用

升级不是简单的「擦除-写入」两步走。你想想看,如果升级到一半断电了,系统怎么恢复?分区表就是用来解决这个问题的。

具体来说,分区表在升级中扮演三个角色:

  1. 空间规划——告诉升级程序哪些区域可以写,哪些不能碰
  2. 版本管理——通过分区状态标记当前运行的是哪个版本
  3. 故障恢复——升级失败时,根据分区表回滚到旧版本

我记得有一次做智能电表项目,客户要求升级失败后必须能自动回滚。当时我就在分区表里加了一个「升级状态位」,每次升级前先标记「升级中」,成功后改为「完成」。如果重启检测到状态是「升级中」,就自动切回备份分区。这个方案后来成了我们团队的标配。

避坑指南: 分区表本身在升级过程中绝对不能修改!我曾经见过一个团队把分区表和固件放在同一个扇区,结果升级时擦除操作把分区表也干掉了,整批设备全部变砖。分区表必须放在独立、受保护的存储区域。

1.3 常见分区类型

做固件升级这么多年,我总结下来,嵌入式系统里最常见的就是这四种分区类型。咱们一个一个说。

Boot 分区

Boot 分区存放的是引导加载程序(Bootloader)。它的职责很简单:上电后最先执行,检查应用分区是否完整,然后跳转到 App 分区执行。

Boot 分区有几个特点:

  • 大小固定,通常 16KB ~ 64KB
  • 只读属性,升级时一般不动它
  • 必须放在 Flash 起始地址(0x0 或 0x08000000)

关键点: Boot 分区是系统的「守门员」。如果 Boot 分区坏了,整个系统就彻底废了。所以我建议 Boot 分区使用 CRC 校验,并且在启动时做自检。

App 分区

App 分区存放的是主应用程序固件。这是升级操作的主要目标区域。

在实际项目中,App 分区通常有两种设计方式:

  • 单分区方案: 只有一个 App 分区,升级时直接覆盖。简单,但升级失败无法回滚。
  • 双分区方案(A/B 分区): 两个 App 分区交替使用。一个运行,一个升级。升级失败可以切回另一个。

我个人更推荐双分区方案。虽然多占一倍空间,但换来的是「永不砖机」的可靠性。尤其是做物联网设备,你不可能派人去现场刷机。

Data 分区

Data 分区存放的是运行时数据、配置参数、日志等。它和固件升级的关系在于:升级时 Data 分区的内容通常要保留。

举个例子,你家的智能路由器升级后,WiFi 密码不能丢吧?这些配置就存在 Data 分区里。

我的习惯: Data 分区使用文件系统(如 LittleFS、SPIFFS),而不是裸数据存储。这样升级时可以精确控制哪些文件保留、哪些重置。我在一个工业网关项目里就用这个方案,客户配置了 200 多个参数,升级后一个都没丢。

Recovery 分区

Recovery 分区是最后的「救命稻草」。当 App 分区损坏、升级失败、Boot 分区检测到异常时,系统会进入 Recovery 模式。

Recovery 分区通常包含:

  • 一个最小化的固件(能跑网络或 USB 通信就行)
  • 恢复用的工具函数(擦除、写入、校验)
  • 一个简单的命令行或 Web 界面

我记得有个车载项目,OTA 升级时网络断了,App 分区只写了一半。重启后 Boot 检测到 CRC 错误,自动切到 Recovery 分区。用户通过手机 App 重新下载固件,Recovery 分区负责接收并写入,最后成功恢复。如果没有 Recovery 分区,那台车就得开回 4S 店了。

1.4 分区布局示例

说了这么多理论,咱们看一个实际的 Flash 分区布局。这是一个典型的 2MB Flash 芯片的分配方案:

Flash 地址范围         分区名称      大小        用途
0x000000 - 0x00FFFF   Boot        64KB       引导加载程序
0x010000 - 0x07FFFF   App_A       448KB      主应用(版本 A)
0x080000 - 0x0EFFFF   App_B       448KB      主应用(版本 B)
0x0F0000 - 0x0FFFFF   Recovery    64KB       恢复固件
0x100000 - 0x1FFFFF   Data        1MB        配置、日志、用户数据

这个布局里,App_A 和 App_B 就是双分区方案。升级时往空闲的那个分区写,写完后切换启动目标。Data 分区占了将近一半空间,因为设备需要记录大量历史数据。

注意: 分区大小必须是 Flash 擦除块大小的整数倍。比如某款 Flash 的擦除块是 4KB,那每个分区的大小就得是 4KB 的倍数。否则擦除操作会越界,把隔壁分区的数据也干掉。这个坑我踩过,说出来都是泪。

1.5 分区表结构图

下面这张图展示了分区表在 Flash 中的位置,以及各分区之间的关系。我特意把 Boot 分区放在最前面,因为它最先被执行。

典型嵌入式 Flash 分区布局 Flash 芯片 (2MB) Boot 分区 (64KB) App_A 分区 (448KB) 当前运行版本 v2.1 App_B 分区 (448KB) 待升级版本 v2.2 升级完成后切换启动目标 Recovery 分区 (64KB) — 故障恢复 Data 分区 (1MB) — 配置、日志、用户数据 分区表通常存储在 Boot 分区头部或 Flash 的独立区域,不占用上述分区空间

从这张图可以清楚看到:Boot 分区在最前面,负责引导;App_A 和 App_B 是双分区方案,一个运行一个待升级;Recovery 分区在中间位置,作为最后一道防线;Data 分区放在最后,因为它容量最大,而且升级时通常不动它。

1.6 小结

分区表不是什么高深的技术,但它决定了你的固件升级系统是否可靠。我见过太多项目,前期不重视分区设计,后期升级出问题才来补窟窿,代价往往是几倍的开发时间。

记住三点:

  • 分区表是 Flash 的「地图」,必须独立保护
  • 升级的核心是「安全切换」,分区表负责记录状态
  • Boot、App、Data、Recovery 各司其职,缺一不可