首先明确,普通 esp32 和 s3 是不同的,本文并不适用于普通 esp32。
术语
单位用的都是字节,0x1000 转成 10 进制为 4096,即 4kb。
启动过程
S3 芯片上电后,首先运行芯片内部固化的微型 BootLoader,称为一级引导程序,它会读取外部 Flash 的 0 地址的程序,加载并运行。
位于外部 Flash 的 0 地址的程序,称为二级引导程序,它是由 idf 在编译过程中生成的,他可以由我们定制,他负责读取分区表,找到 app 分区后启动我们的主程序。当 ota 时,由它确定接下来跑产品出厂的代码还是 ota 后的代码。
接下来,我们的实际代码由二级引导启动,freertos 调度器开始运行。我们再代码中看到的 app_main 实际是一个 task,他紧接着启动。
二级引导所在位置
对于 S3,二级引导必须放在 flash@0x0 的位置。顺带一提,C3 和 S3 一样,但普通的 esp32,在 0x1000,而 P4 却在 0x2000。
它的大小在默认情况下,使用官方提供的引导代码,编译出来约为 20 多 kb。
分区表
从上边的启动过程可知,一级引导只知道从 0 读取二级引导,二级引导并不知道 app 分区在哪,改去哪读取程序。esp32 引入了分区表的概念,记录 Flash 的分区。
CONFIG_PARTITION_TABLE_OFFSET 可配置分区表所在位置,默认为 0x8000,这也意味着,上边而二级引导默认不能超过 0x8000,否则会越界。但是可通过调整这个配置项,让分区表后移,给二级引导更大的空间。分区表本身能够最多存储 95 个分区,占用 3kb 的空间,但实际占用了 4kb,为了对齐。也就是说下一段实际可用的地址是 0x8000+4kb=0x900。
分区表由 csv 定义,默认使用自带的 partitions_singleapp.csv 作为分区,其内容如下。我们注意到,Offset 列为空,这是让分区表生成器自动计算。
# 默认的分区表
# 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, # 简单的 kv 存储,用于量产刷 sn& 秘钥等
phy_init, data, phy, , 0x1000, # 射频校准参数,不用管
factory, app, factory, , 1M, # 代码存放处
可以用以下命令,完成 csv 到分区表后,再把分区表转成 csv。
python gen_esp32part.py partitions.csv partitions.bin
python gen_esp32part.py partitions.bin partitions-full.csv
转换后的分区表会显示出自动补全的 Offset,这也印证了,第一个分区确实在 0x9000 的位置。
# 转换后
nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K,
factory,app,factory,0x10000,1M,