【ESP32笔记】ESP中的分区表
本节目标看懂分区表 CSV 的每一列会给工程添加自定义分区并用 esp_partition API 完成擦除、写入、读取一、本节要解决的问题ESP32 的程序不是整个 bin 一股脑烧到某个固定地址而是把整块 flash 划分成多个分区partition每个分区承担一种职责放 bootloader、放分区表自己、放应用程序、存 NVS 配置、存 PHY 校准数据、放 OTA 固件、当文件系统、存用户数据……这样划分的好处好管理大工程按功能分区升级程序只烧 app 分区不会动到配置数据功能清晰bootloader 靠分区表就知道哪块 flash 是要启动的程序、哪块是数据避免冲突擦除 NVS 不会误擦程序OTA 升级也不会破坏用户数据。如果不用分区表所有地址都得写死在代码里应用一多就极易互相踩踏。本节先把 ESP-IDF 默认分区表逐列拆开分析再自定义一个分区并用官方 API 对它擦写、读取。二、相关知识ESP32 flash 的固定布局ESP32 的 flash 从 0x0 开始编址有几处地址是约定俗成、不能乱动的区域起始地址大小说明Bootloader0x1000-上电后最先运行的程序分区表0x80000x1000描述其余 flash 怎么分的那张表第一个可用分区0x9000-除开上面两块剩下的从 0x9000 开始分配所以分区表里所有分区的 Offset 都必须大于等于 0x9000默认分区表的第一个分区nvs正是从 0x9000 开始的。三、知识点3.1 分区表 CSV 的六列分区表是一个逗号分隔的文本文件.csv#开头的是注释。列头固定为六列教程重点要求看懂前五列列名含义取值说明Name分区名称标签如 nvs、factory、user同一张表内不能重名Type分区类型app(0x00) 放程序data(0x01) 放数据用户自定义可写 0x400xFESubType子类型含义随 Type 变化Typeappfactory(0x00)、ota_0(0x10)ota_15(0x1F)Typedataota(0x00)、phy(0x01)、nvs(0x02)、nvs_keys(0x04) 及组件特定子类型Offset分区在 flash 中的起始地址可以留空由构建工具自动计算不能与其他分区重叠app 分区必须与 0x10000 对齐Size分区总大小支持 1K、1M 这种写法1M 0x100000Flags标志位本节两个例子都留空入门阶段不用管逐列再补充几句Name教程说它并不重要其实它有两个用处——保证表内唯一便于管理以及可以被 API 当作 label 按名字查找分区配套例程后面正是这样用。Type只分 app 和 data 两大类其他值0x400xFE全部留给用户自定义0x000x3F 是系统保留范围。SubType它必须结合 Type 理解——同是 0x00在 app 下是 factory在 data 下就是 ota自定义类型的子类型怎么写、怎么解释完全由用户自己约定。Offset最灵活也最容易出错的一列留空时由工具计算手填时必须自己保证不重叠。3.2 Offset 留空时的自动计算规则Offset 不写时构建工具从 0x9000 开始把上一个分区的 Offset Size作为下一个分区的起点并按类型做对齐app 补齐到 0x10000data 只要求 4 字节对齐。以最简单的默认分区表为例# Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, , 0x6000, phy_init, data, phy, , 0x1000, factory, app, factory, , 1M,分区计算过程实际 Offsetnvs紧接着分区表从 0x9000 开始0x9000大小 0x600024KBphy_init0x9000 0x60000xF000大小 0x1000factory0xF000 0x1000app 对齐到 0x100000x10000大小 1M3.3 各个分区的作用分区Type / SubType作用nvsdata / nvs非易失存储区掉电数据不丢存放 PHY 校准数据、Wi-Fi 配置、应用自己的键值数据下一节详讲phy_initdata / phy存放每台设备单独的 PHY 初始化数据默认配置下并不启用PHY 数据直接编进应用程序所以这个分区一般用不到、可以删掉factoryapp / factory默认的应用程序分区上电后 bootloader 从这里启动程序otadatadata / ota仅 OTA 分区表有记录当前该从哪个 OTA 分区启动大小固定 0x2000ota_0 / ota_1app / ota_0、ota_1仅 OTA 分区表有两个可升级的应用程序分区bootloader 依据 otadata 的内容决定加载哪一个自定义分区0x400xFE / 自定义用户自己存放数据怎么解读完全由应用程序决定3.4 为什么 app 分区不能重叠编译期ESP-IDF 会用脚本把 CSV 转成二进制分区表脚本会检查每个分区的地址范围只要某个分区的起点落进了前一个分区的区间就直接报Partitions overlap并中止编译。这是第一道保险。运行期更要命bootloader 按分区表把 app 分区里的程序加载到内存执行OTA 升级时还会往 ota_x 分区写新固件。两个 app 分区一旦重叠新固件就会覆盖另一个分区里的程序下次 bootloader 切回旧分区读到的就是被破坏的内容设备可能无法启动。数据分区重叠也是同理——擦其中一块另一块的数据跟着一起没了。所以手工填 Offset 时一定要自己把地址算清楚。顺带解释另一个问题app 分区为什么必须 0x10000 对齐因为 bootloader 按 64KB 为单位做 flash 到内存的地址映射不满足对齐要求的分区无法被正常映射执行工具链会直接拒绝。3.5 ESP-IDF 内置的默认分区表ESP-IDF 自带几张常用分区表放在 IDF 安装目录的components/partition_table/下在idf.py menuconfig的 Partition Table 菜单里选择选项对应文件内容Single factory apppartitions_singleapp.csvnvs 0x6000 phy_init 0x1000 factory 1M最简单的三行Single factory app, largepartitions_singleapp_large.csv与上面相同只是 factory 放大到 1.5MFactory app, two OTApartitions_two_ota.csv在单 app 基础上增加 otadata、ota_0、ota_1支持 OTA 升级Custom partition table CSV工程自己的 CSV勾选后由 CONFIG_PARTITION_TABLE_CUSTOM_FILENAME 指定工程根目录下的 CSV 文件名两者区别一句话内置表是选现成的自定义表是自己写一张 CSV 交给编译系统。不需要额外分区时用内置的最省事一旦要加 spiffs、自定义数据区比如本节例程就必须切到 Custom。OTA 表里为什么至少要有两个 OTA app 分区因为 OTA 的基本玩法是把新固件写进当前没在运行的那个分区写完整、校验通过后再让 otadata 指向它重启。只有一个分区的话没有备胎升级失败就没有退路了。3.6 本仓库的分区表使用现状仓库 27 个工程里有 23 个工程的sdkconfig都含有CONFIG_PARTITION_TABLE_CUSTOM_FILENAMEpartitions.csv这一行但工程目录里并不存在partitions.csv而且这些工程真正的开关是CONFIG_PARTITION_TABLE_SINGLE_APPyCONFIG_PARTITION_TABLE_CUSTOM is not set。也就是说那行文件名只是一个没被启用的默认值编译时实际用的仍是 ESP-IDF 内置的partitions_singleapp.csv。真正启用了自定义分区表的只有 4 个工程partitionpartitions_user.csv、spiffspartitions_example.csv、web_monitorpartitions_webserver.csv、onenetpartitions_two_ota_nofactory.csv。想让自己的工程用上自定义分区表光改文件名不够必须在 menuconfig 里把 Partition Table 选项切到 Custom partition table CSV。四、代码分析4.1 工程结构partition/ ├── CMakeLists.txt ├── partitions_user.csv # 自定义分区表本节重点 ├── sdkconfig # 已启用自定义分区表 └── main/ ├── CMakeLists.txt └── main.c # 找分区 → 擦除 → 写入 → 读取sdkconfig里与本节有关的关键几行说明自定义分区表已经启用并指向 partitions_user.csvCONFIG_PARTITION_TABLE_CUSTOMy CONFIG_PARTITION_TABLE_CUSTOM_FILENAMEpartitions_user.csv CONFIG_PARTITION_TABLE_FILENAMEpartitions_user.csv CONFIG_PARTITION_TABLE_OFFSET0x80004.2 自定义分区表 partitions_user.csv在默认三行之后追加了一行user分区Type0x40自定义类型、SubType0x01自己约定的标记、Offset 留空自动计算、Size0x10004KB。# Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, , 0x6000, phy_init, data, phy, , 0x1000, factory, app, factory, , 1M, user, 0x40, 0x01, , 0x1000,各行的落点nvs 在 0x9000、phy_init 在 0xF000、factory 在 0x10000 且到 0x110000 结束所以 user 自动排到0x110000大小 0x1000。因为 Type 是自定义的 0x40SubType 写 0x01 只是自己作标记编译系统不会按它做特殊处理但代码里必须用相同的 0x40/0x01 才能找到这个分区。4.3 main.c找分区、擦除、写入、读取第一步是按类型、子类型和名字找到分区拿到一个指向分区描述符的指针后续所有操作都基于它#defineUSER_PARTITION_TYPE0x40//自定义的分区类型#defineUSER_PARTITION_SUBTYPE0x01//自定义的分区子类型//分区指针staticconstesp_partition_t*partition_resNULL;//读取缓存staticcharg_esp_buf[1024];voidapp_main(void){//找到自定义分区返回分区指针后续用到这个指针进行各种操作partition_resesp_partition_find_first(USER_PARTITION_TYPE,USER_PARTITION_SUBTYPE,user);if(partition_resNULL){ESP_LOGI(TAG,Cant find partition,return);return;}esp_partition_find_first的三个参数与 CSV 一一对应Type0x40、SubType0x01、Name“user”找不到返回 NULL所以必须判空。接着就是 flash 的经典三步——先擦、再写、后读//擦除ESP_ERROR_CHECK(esp_partition_erase_range(partition_res,0x0,0x1000));//测试字符串constchar*test_strthis is for test string;//从分区偏移位置0x0写入字符串ESP_ERROR_CHECK(esp_partition_write(partition_res,0x00,test_str,strlen(test_str)));//从分区偏移位置0x0读取字符串ESP_ERROR_CHECK(esp_partition_read(partition_res,0x00,g_esp_buf,strlen(test_str)));ESP_LOGI(TAG,Read partition str:%s,g_esp_buf);esp_partition_erase_range(分区, 偏移, 大小)这里从 0x0 开始擦满整个 4KB 分区偏移和大小都必须 0x1000 对齐esp_partition_write(分区, 偏移, 数据, 长度)写之前必须先擦除否则写不进去flash 只能把 1 变 0擦除才能把 0 恢复成 1esp_partition_read(分区, 偏移, 缓存, 长度)按字节读回。最后while(1)里延时 1 秒让程序一直挂着不退出。与教程不一致的地方PDF 里esp_partition_find_first的第三个参数写的是NULL只按类型/子类型查找仓库代码改成了user即同时用 CSV 中的 Name 作为标签。这样更严格——如果 CSV 里分区名被改动会直接找不到分区而报错问题能更早暴露。另外 PDF 文字说esp_partition_write 第四个参数是写入的数据内容这是笔误第四个参数是长度数据内容在第三个参数。五、关键 API 速查API作用esp_partition_find_first(type, subtype, label)按类型/子类型/标签查找第一个分区返回esp_partition_t*找不到返回 NULLesp_partition_erase_range(part, offset, size)擦除分区内一段区域offset 与 size 必须 0x1000 对齐esp_partition_write(part, offset, src, size)向分区写数据写前必须先擦除esp_partition_read(part, offset, dst, size)从分区读数据ESP_ERROR_CHECK()检查返回值出错时打印文件名和行号并重启以上 esp_partition API 都声明在esp_partition.h中。六、编译与现象验证cdesp32-board/partition idf.py build idf.py-pCOM端口flash monitor工程 sdkconfig 的目标芯片是 esp32如果重新指定过目标或清空过配置先执行idf.py set-target esp32。正常运行时串口打印一行关键日志I (xxx) main: Read partition str:this is for test string说明写进去的字符串被原样读回来了。验证数据真的在 flash 里的小实验把esp_partition_erase_range和esp_partition_write两行注释掉重新编译烧录重启后仍然能读出这句话——数据不是存在 RAM 里而是上次烧录时写进 flash 的。

相关新闻

亲和路由(inspect)

亲和路由(inspect)

Inspect 要解决的是“多轮对话在普通选路下被分到不同渠道”导致缓存失效和兼容性出错的问题;它通过回答“值不值得亲和、按什么粒度分组、用哪些稳定字段”三个问题,输出分类、触发原因和内容哈希;它被设计成无外部状态的纯分析器&#xff0…

2026/10/15 19:04:55 阅读更多 →
AI本体建模-------GB/T 48000.3—2026 标准详解

AI本体建模-------GB/T 48000.3—2026 标准详解

AI本体建模之 GB/T 48000.3—2026 标准详解 当大模型与 Agent 逐渐成为企业基础设施,真正的护城河正从「数据」转向「可被机器理解、推理、校验、共享的知识」。而 本体(Ontology) 正是这套知识体系的「模式层」与「契约层」。 2026 年 1 月&…

2026/10/15 19:04:55 阅读更多 →
线上 Redis 慢查询,不一定就是命令本身慢

线上 Redis 慢查询,不一定就是命令本身慢

上周值班的时候,监控突然弹出 Redis 慢查询告警,慢查询日志里全是 HGETALL,单条耗时经常冲到 80ms 以上。第一反应,又是经典问题,大 Hash 别用 HGETALL,这个知识点群里都说烂了。 一开始我也是这么处理的&a…

2026/10/15 19:04:55 阅读更多 →

最新新闻

Claude Code 使用指南:从 Node.js 环境到终端与 IDE 的完整配置

Claude Code 使用指南:从 Node.js 环境到终端与 IDE 的完整配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 19:46:41 阅读更多 →
Redis 技术分享主线:数据结构、缓存三大问题与高可用落地

Redis 技术分享主线:数据结构、缓存三大问题与高可用落地

简介:围绕 Redis 核心技术点的分享课件,适合后端开发、运维人员及对内存数据库感兴趣的学习者快速建立知识框架。PPT 从简介、配置文件、持久化机制到常用命令逐步展开,涵盖每秒 11 万次读、8.1 万次写的高性能表现,string、hash、…

2026/10/15 19:46:41 阅读更多 →
go-gitignore:基于简单树索引的高性能 Go 免提交匹配库

go-gitignore:基于简单树索引的高性能 Go 免提交匹配库

云原生开发工具DevOps容器编排 【免费下载链接】devspace DevSpace - The Fastest Developer Tool for Kubernetes ⚡ Automate your deployment workflow with DevSpace and develop software directly inside Kubernetes. 项目地址: https://gitcode.com/gh_mirro…

2026/10/15 19:46:41 阅读更多 →
常微分方程数值解法详解:从欧拉法到有限差分法的MATLAB实战

常微分方程数值解法详解:从欧拉法到有限差分法的MATLAB实战

常微分方程的数值解法,在工程计算里几乎是绕不开的。不管是电路瞬态分析、化学反应速率模拟,还是机械系统动力学建模,最后都会落到一个“给定导数关系、给初始条件、积分出整条曲线”的问题上。MATLAB里虽然直接有 ode45、ode23 这些现成求解…

2026/10/15 19:46:41 阅读更多 →
基于 Next.js + Notion 搭建个人网站:nextjs-notion-starter-kit 配置、部署与源码原理全解析

基于 Next.js + Notion 搭建个人网站:nextjs-notion-starter-kit 配置、部署与源码原理全解析

前端 【免费下载链接】nextjs-notion-starter-kit Deploy your own Notion-powered website in minutes with Next.js and Vercel. 项目地址: https://gitcode.com/gh_mirrors/ne/nextjs-notion-starter-kit 点击查看 免费下载 本文以 readme.md 为骨架&#xff0c…

2026/10/15 19:46:41 阅读更多 →
多个传感器如何协同作战?World2Agent多传感器组合与fanout传输完整指南

多个传感器如何协同作战?World2Agent多传感器组合与fanout传输完整指南

【免费下载链接】world2agent World2Agent(W2A) is an open protocol that standardizes how Al agents perceive the real world. 项目地址: https://gitcode.com/gh_mirrors/wo/world2agent 点击查看 免费下载 World2Agent(W2A)是一个开放…

2026/10/15 19:45:41 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →