ESP32-WROOM-32集成了4MB SPI FLASH。对应的,也会对这4MB FLAHS进行分区处理。在编译esp32程序时,通过make menuconfig -> Partition Table可以设置三种分区。
对于不同的模式,分区情况也不同。
通常无OTA分区,分区比较简单,其中并不会划分OTA的区域出来,那么久有更多的空间空余出来。
# Espressif ESP32 Partition Table
Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 24K,
phy_init,data, phy, 0xf000, 4K,
factory, app, factory, 0x10000, 1M,
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
ota_0, app, ota_0, 0x110000, 1M,
ota_1, app, ota_1, 0x210000, 1M,
该分区就根据用户自定义来划分。
1.先拷贝一个.csv文件到工程的根目录下。文件名可自定义,建议与工程名保持一样。
$ tree
.
├── main
│ ├── CMakeLists.txt
│ └── Hello.cpp
├── CMakeLists.txt
└── 工程名.csv #这个文件
2.在终端输入make menuconfig。然后选择partition Table。
3.修改链接CSV链接名。
将名字修改与拷贝到根目录下的.csv文件同名。选择“OK”。
4.选择分区表类型为自定义。
5.退出保存。
6.修改csv文件内容为自定义分区
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
fw1, app, ota_0, 0x10000, 0xF0000
fw2, app, ota_1, 0x100000,0xF0000
7.重新编译
如果在menuconfig中选择了“Custom partition table CSV”,则还需要添加该分区表的CSV文件到项目的路径中。CSV文件可以根据需要,描述任意数量的分区信息。
CSV文件的格式与上面打印的格式相同,但是在CSV文件中并非所有字段都是必需的。例如下面是一个自定义的OTA分区表的CSV文件。
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M
nvs_key, data, nvs_keys, , 0x1000
字段之间的空格会被忽略,任何以#开头的行会被忽略。
CSV文件中的每个非注释行均为一个分区定义
每个分区的Offset字段可以为空,gen_esp32part.py工具会从分区表位置的后面开始自动计算并填充该分区的偏移地址,同时确保每个分区的偏移地址正确对齐。
Name字段可以是任何有意义的名称,但不能超过16个字符(之后的内容将被截断)。该字段对ESP32并不是特别重要。
Type字段可以指定为app(0x00)或者data(0x01),也可以直接使用数字0-254(或0x00-0xFE)。
注:0x00-0x3F不得使用(预留给esp-idf的核心功能)。
如果应用程序需要以ESP-IDF尚未支持的格式存储数据,请在0x40-0xFE内添加一个自定义分区类型。(可以参考esp_partition_type_t,关于app和data分区的枚举定义)。
SubType字段长度为8bit,内容与具体分区Type有关。目前,esp-idf仅仅规定了“app”和“data”两种分区类型的子类型含义。(参考esp_partition_subtype_t,以了解ESP-IDF定义的全部子类型列表)。
当Type定义为app时,SubType字段可以指定为factory(0x00)、ota_0(0x10)....ota_15(0x1f)或者test(0x20)。
factory(0x00)是默认的app分区。启动加载器将默认加载该应用程序。但如果存在类型为data/ota分区,则启动加载器将加载data/ota分区中的数据,进而判断启动哪个OTA镜像文件。
ota_0(0x10)..ota_15(0x1F)为OTA应用程序分区,启动加载器将根据OTA数据分区中数据来决定加载哪个OTA应用程序分区中的程序。在使用OTA功能时,应用程序应至少拥有2个OTA应用程序分区(ota_0和ota_1)。
test(0x20)为预留的子类型,用于工厂测试流程。如果没有其他有效app分区,test将作为备选启动分区使用。也可以配置启动加载器在每次启动时读取GPIO,如果GPIO被拉低则启动该分区。
当Type定义为data时,SubType字段可以指定为ota(0x00)、phy(0x01)、nvs(0x02)、nvs_keys(0x04)或者其他组件特定的子类型
ota(0x00) 即OTA数据分区,用于存储当前所选的OTA应用程序的信息。这个分区的大小需要设定为0x2000。
phy(0x01) 分区用于存放PHY初始化数据,从而保证可以为每个设备单独配置PHY,而非必须采用固件中的统一PHY初始化数据。
nvs(0x02)是专门给非易失性存储(NVS)API使用的分区。
nvs_keys(0x04)是NVS密钥分区。
ESP_IDF还支持其他预定义的子类型用于数据存储,包括 FAT 文件系统 (ESP_PARTITION_SUBTYPE_DATA_FAT), SPIFFS (ESP_PARTITION_SUBTYPE_DATA_SPIFFS) 等。
如果分区类型是由应用程序定义的任意值(0x40-0xFE),那么subtype字段可以由应用程序选择的任何值(0x00-0xFE)。
若分区的偏移地址为空,则会紧跟着前一个分区之后开始;若为首个分区,则将紧跟着分区表开始。
app分区的偏移地址必须要与0x10000(64K)对齐,如果将偏移字段留空,gen_esp32part.py工具会自动计算得到一个满足对齐要求的偏移地址。如果app分区的偏移地址没有与0x10000(64K)对齐,则该工具会报错。
app分区的大小和偏移地址可以采用十进制数,以0x为前缀的十六进制数,且支持K或M的倍数单位(分别代表1024和1024*1024字节)。
如果希望允许分区表中的分区采用任意起始偏移量,请将分区表中所有分区的偏移字段都留空。注意,此时,如果更改了分区表中任意分区的偏移地址,则其他分区的偏移地址也会跟着改变。这种情况下,如果之前还设定了某个分区采用固定偏移地址,则可能造成分区表冲突,从而导致报错。
当前仅支持encrpted标记。如果Flags字段设置为encrypted,且已启用Flash机密功能,则该分区将会被加密。
注:app分区始终会被加密,不管Flags字段是否设置。
针对自定义分区表的内容,可以参考如下代码进行操作:
示例代码中,Type为0x40,Subtype为0x00的自定义分区。
int8_t test_custom_partition()
{
const esp_partition_t *find_partition = NULL;
find_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,ESP_PARTITION_SUBTYPE_DATA_FAT,"fat");
if(find_partition == NULL)
{
printf("[PARTITION]:find partition error!!!\r\n");
}
printf("fat partition address:0x%x\r\n",find_partition->address);
uint8_t temp_data[1024] = {0};
if(esp_partition_read(find_partition,0,temp_data,sizeof(temp_data)) != ESP_OK)
{
printf("fat partition read error\r\n");
return;
}
printf("fat partition read success:%x%x%x%x%x\r\n",temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4]);
if(esp_partition_erase_range(find_partition,0,0x1000) != ESP_OK) //擦除必须4K对齐
{
printf("fat partition erase error\r\n");
return;
}
printf("fat partition erase success\r\n");
memset((void *)temp_data,0,sizeof(temp_data));
uint8_t temp_w_data[6] = {0x31,0x32,0x33,0x34,0x35};
if(esp_partition_write(find_partition,0,temp_w_data,strlen((void *)temp_w_data)) != ESP_OK)
{
printf("fat partition write error\r\n");
return;
}
printf("fat partition write success\r\n");
if(esp_partition_read(find_partition,0,temp_data,sizeof(temp_data)) != ESP_OK)
{
printf("fat partition read error\r\n");
return;
}
printf("fat partition read success:%x%x%x%x%x\r\n",temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4]);
}
结果:
读取到的fat地址与我们自定义的fat的位置是相同的,且读写正常。
分清楚了上面三种分区表类型之后,那么问题来了。如何查看当前工程下的分区表。
在简介中我们说过,可以通过make menuconfig指令来设置分区类型。那设置完之后,如何查看呢?有两种方式可以查看。
正如我们前面文章说过的,编译结束后,会在build/partition_table/文件夹下生成partition-table.bin文件。该文件中存储了分区表信息。而SDK中也提供了工具允许我们将这个bin文件转成cvs文件来阅读。可以通过esp-idf/components/partition_table/gen_esp32part.py工具来转换。
手动将CSV文件转换成二进制文件:
python gen_esp32part.py input_partitions.csv binary_partitions.bin
手动将二进制文件转换成CSV文件:
python gen_esp32part.py binary_partitions.bin input_partitions.csv
转换后,我们就能在工程的partition_table文件夹下看到csv文件了。
点击打开后,就能看到分区表情况了。
官方参考文档:
分区表 - ESP32 - — ESP-IDF 编程指南 release-v5.0 文档