3、分区表详解:ESP32分区表格式、类型与自定义方法
ESP32 的 Flash 存储空间并非一个单一的整体,而是被划分为多个功能不同的区域,这些区域的布局由一张被称为“分区表”(Partition Table)的配置表决定。分区表定义了每个区域的起始地址、大小、类型和名称。无论是使用 SPIFFS 还是 LittleFS 管理文件,还是通过 OTA 升级固件,都依赖于一个正确配置的分区表。理解分区表的结构与自定义方法,是进行嵌入式文件系统开发和固件管理的基础。
上图展示了 ESP32 Flash 中典型的分区布局。Bootloader 和分区表本身占据固定位置,其后是应用程序分区(app)和数据分区(data)。文件系统(SPIFFS/LittleFS)属于数据分区的一种,其大小和位置完全由分区表决定。下面我们将深入解析分区表的格式、类型以及如何根据项目需求进行自定义。
3.1 分区表格式与核心字段解析
ESP32 的分区表是一个二进制文件,通常由 CSV(逗号分隔值)格式的文本文件通过构建工具 gen_esp32part.py 生成。在工程中,我们直接编辑 CSV 文件,然后由 ESP-IDF 构建系统自动完成转换。CSV 文件的每一行代表一个分区,格式如下:
# name, type, subtype, offset, size, flags
nvs, data, nvs, 0x9000, 0x5000,
phy_init, data, phy, 0xe000, 0x1000,
factory, app, factory, 0x10000, 0x200000,
spiffs, data, spiffs, 0x210000, 0x1F0000,
各字段含义如下:
- name:分区名称,用于在代码中通过
esp_partition_find或esp_vfs_spiffs_register等 API 查找分区。名称不能包含空格。 - type:分区类型,主要分为
app(应用程序)和data(数据)两大类。自定义类型可指定为 0x40-0xFE 之间的数值。 - subtype:子类型,进一步细化分区用途。对于
app类型,常见的有factory、ota_0、ota_1等;对于data类型,有nvs、phy、spiffs、littlefs等。 - offset:分区在 Flash 中的起始地址(十六进制)。如果留空,系统会自动根据前一个分区的结束地址计算。
- size:分区大小(十六进制)。必须保证分区不重叠,且不超过 Flash 总容量。
- flags:可选标志位,目前通常留空。加密标志
encrypted可用于 Flash 加密场景。
3.2 常用分区类型与子类型详解
ESP-IDF 预定义了多种分区类型,理解它们的用途是合理规划 Flash 空间的前提。下表列出了最常见的分区类型及其适用场景:
| 类型 | 子类型 | 典型用途 | 注意事项 |
|---|---|---|---|
app |
factory |
出厂固件,默认启动应用 | 如果存在 OTA 分区,factory 可作为回滚点 |
ota_0 / ota_1 |
OTA 升级的交替槽位 | 至少需要两个 OTA 分区才能实现安全升级 | |
test |
工厂测试固件 | 不常用,一般开发阶段使用 | |
data |
nvs |
非易失性存储,保存密钥、校准数据、系统参数 | NVS 有磨损均衡,但单次写入不宜过大 |
phy_init |
WiFi/BT 物理层初始化数据 | 出厂时烧录,一般不需要修改 | |
spiffs |
SPIFFS 文件系统 | 适合小文件、日志存储,但掉电恢复能力较弱 | |
littlefs |
LittleFS 文件系统 | 具备掉电保护、磨损均衡,推荐用于数据存储 |
在 ESP-IDF 中,spiffs 和 littlefs 子类型在分区表中是明确区分的。虽然两者都是文件系统,但底层实现不同,挂载时使用的驱动也不同。例如,LittleFS 在 ESP-IDF 中通过 esp_littlefs 组件支持,而 SPIFFS 则使用 esp_spiffs 组件。
3.3 自定义分区表的操作流程
自定义分区表通常有两种方式:修改默认的 partitions.csv 文件,或在工程配置中选择预设模板。以下是标准操作流程:
- 确定 Flash 容量:通过
idf.py menuconfig的 “Serial flasher config” 菜单,确认目标 Flash 大小(如 4MB、8MB)。 - 创建或编辑 CSV 文件:在工程根目录创建
partitions.csv,或复制默认模板进行修改。默认模板位于$IDF_PATH/components/partition_table/partitions_singleapp.csv。 - 计算分区大小与偏移:确保所有分区大小之和不超过 Flash 容量,且地址对齐(通常 4KB 对齐)。Bootloader 占用 0x1000 起始的 0x1000 字节,分区表本身占用 0x8000 起始的 0x1000 字节。
- 配置工程使用自定义分区表:在
menuconfig中,进入 “Partition Table” 选项,选择 “Custom partition table CSV”,并指定 CSV 文件路径。 - 编译烧录:执行
idf.py build flash monitor,系统会解析 CSV 并生成二进制分区表烧录到 Flash。
idf.py erase-flash 擦除整个 Flash,再烧录新固件。
3.4 实战案例:为 LittleFS 自定义分区
假设我们使用一块 4MB Flash 的 ESP32 开发板,需要实现 OTA 升级功能,同时希望使用 LittleFS 存储用户配置和日志文件。默认的 partitions_singleapp.csv 只有一个 app 分区和一个 1.5MB 的 SPIFFS 分区,无法满足 OTA 需求。我们需要设计一个包含双 OTA 槽位和 LittleFS 分区的自定义分区表。
背景与约束:
- Flash 总容量:4MB(0x400000)。
- Bootloader 区域:0x1000 - 0x8000(固定)。
- 分区表区域:0x8000 - 0x9000(固定)。
- NVS 分区:0x9000 - 0xE000(20KB),用于存储系统参数。
- PHY init 分区:0xE000 - 0xF000(4KB),用于 WiFi 校准数据。
- OTA 应用槽位:每个槽位至少 1.3MB,以保证固件有足够空间。
- LittleFS 分区:剩余空间,用于文件存储。
初始现象与目标: 默认分区表无法进行 OTA 升级,且 SPIFFS 在异常掉电时容易损坏文件。目标是通过自定义分区表,实现安全的 OTA 升级和可靠的文件存储。
关键输入数据与计算:
Flash 总大小: 0x400000 (4MB)
Bootloader: 0x1000 (4KB)
Partition表: 0x8000 (4KB)
NVS: 0x9000 (20KB) -> 0x9000 + 0x5000 = 0xE000
PHY init: 0xE000 (4KB) -> 0xE000 + 0x1000 = 0xF000
OTA 槽位 0: 0x10000 (1.3MB) -> 0x10000 + 0x150000 = 0x160000
OTA 槽位 1: 0x160000 (1.3MB) -> 0x160000 + 0x150000 = 0x2B0000
LittleFS: 0x2B0000 (剩余 0x150000 = 1.3MB) -> 0x2B0000 + 0x150000 = 0x400000
实际操作步骤:
- 在工程目录下创建
partitions_ota_littlefs.csv文件,内容如下:
# name, type, subtype, offset, size, flags
nvs, data, nvs, 0x9000, 0x5000,
phy_init, data, phy, 0xe000, 0x1000,
factory, app, factory, 0x10000, 0x150000,
ota_1, app, ota_1, 0x160000, 0x150000,
littlefs, data, littlefs, 0x2B0000, 0x150000,
- 在
menuconfig中设置 “Partition Table” 为 “Custom partition table CSV”,并指定该文件路径。 - 在代码中挂载 LittleFS 时,指定分区标签为
littlefs:
esp_vfs_littlefs_conf_t conf = {
.base_path = "/store",
.partition_label = "littlefs",
.format_if_mount_failed = true,
.dont_mount = false,
};
esp_vfs_littlefs_register(&conf);
结果对比与复盘:
- 升级前:只能运行单固件,无法 OTA;SPIFFS 在掉电测试中出现过文件损坏。
- 升级后:支持双槽位 OTA,升级失败可自动回滚;LittleFS 在连续 50 次随机掉电测试中,文件系统完整性保持良好。
- 代价:LittleFS 分区大小从默认的 1.5MB 减少到 1.3MB,但换来了 OTA 能力和更高的数据可靠性。
3.5 常见误区与总结复盘
在实际项目中,分区表配置错误是导致启动失败、文件系统无法挂载的常见原因。以下是几个高频误区:
- 误区一:分区大小超过 Flash 容量。编译时可能不报错,但烧录时会出现校验失败或运行时崩溃。务必使用
idf.py partition-table命令检查生成的分区表信息。 - 误区二:忽略 4KB 对齐。某些 Flash 操作要求地址对齐,未对齐可能导致读写异常。所有 offset 和 size 建议为 0x1000(4KB)的整数倍。
- 误区三:混淆 SPIFFS 和 LittleFS 的子类型。在分区表中将
littlefs写成spiffs,会导致挂载时驱动不匹配,返回错误。必须严格对应。 - 误区四:OTA 分区数量不足。如果只有
factory分区而没有ota_0和ota_1,OTA 功能将无法启用。至少需要两个 app 分区(一个作为当前运行,一个作为升级目标)。
总结复盘: 分区表是 ESP32 存储管理的“宪法”,它决定了系统如何分配和使用 Flash 资源。通过本章的学习,我们掌握了分区表的 CSV 格式、关键字段、常用类型以及自定义方法。在实际工程中,建议遵循以下原则:
- 先规划后编码:在项目启动阶段就确定 Flash 布局,避免后期频繁调整。
- 显式指定 offset:避免隐式计算带来的不确定性。
- 使用版本化管理:将分区表 CSV 文件纳入版本控制,记录每次修改的原因。
- 测试掉电场景:对于文件系统分区,务必进行掉电可靠性测试,验证分区配置是否满足需求。
掌握分区表的自定义能力,意味着你可以根据具体应用场景(如 OTA、数据存储、日志记录)灵活裁剪 Flash 空间,为后续的固件升级和文件管理打下坚实基础。