(十九)ESP32-S3之分区表
分区表概述
分区表简介
ESP32 分区表在 ESP32 的 flash 存储管理中扮演着至关重要的角色。其主要意义体现在以下
几个方面:
1,存储空间优化:通过将 flash 划分为不同的区域,每个区域根据其功能进行定义,可以更有效地利用存储空间。开发者可以根据实际需求为不同的应用或数据分配适当的存储空间,确保资源得到最大化利用。
2,数据管理和维护:每个分区都有其特定的作用,比如存储应用程序、文件系统数据、校准数据等。这种分区的设置使得数据的管理和维护变得更加方便。当需要对特定数据进行更新或修复时,只需关注相关的分区,而无需对整个 flash 进行操作。
3,应用程序更新和回滚:ESP32 支持在线升级(OTA)功能,这意味着应用程序可以在运行时进行更新。通过分区表,可以设置一个或多个用于 OTA 的分区,使得新版本的应用程序可以安全地下载并存储在 flash 中,然后在需要时切换到新版本。同时,如果新版本存在问题,还可以轻松回滚到旧版本,确保系统的稳定性。
4,灵活性:分区表可以根据用户的需求进行自定义。这意味着开发者可以根据其项目的特定需求来定义分区的数量、大小和功能。这种灵活性使得 ESP32 能够适应各种不同的应用场景。
在 ESP32 中,分区表固定长度为 0xc000 字节,也就是说一个分区表只能创建 95 条子分区(条目),并且 MD5 校验位和附加在分区表之后,用于在运行时验证分区表的完整性。
分区表格式
ESP32的分区表主要有两种格式,一种是.csv格式,它方便开发人员进行更改和设置各个子分区的偏移与空间大小;另一种则是用于烧录设备的.bin 文件格式。在系统编译时,系统会将.csv 文件转化为.bin 文件格式的分区表。下面展示了基础例程的分区表文件的内容。

上图中展示了 ESP32的多个子分区及其功能。其中,nvs子分区是专为开发者设计的非易失性存储(NVS)设备区域;phy_init 子分区用于存放 PHY 初始化数据,确保每个设备都能单独配置其 PHY;factory子分区则专门用于存储应用程序区域;vfs子分区作为虚拟文件系统的存储区域;而 storage 子分区则是自定义的 SPIFFS 文件系统区域。这些子分区共同构成了 ESP32 的分区结构,满足了不同功能的需求。
分区表条目结构
从上图中可以看到,每一个子分区都由以下几个部分组成:
1, name:子分区名称。该字段对 ESP32-S3 并不是特别重要。
2, Type:子分区的存储类型。设置子分区的存储格式,app (0x00) 和 data (0x01)。
3, SubType:进一步描述或分类分区表的条目。如果这个子分区 Type 为 app,则 SubType
只能设置 factory、ota_0、ota_15 和 test;如果这个子分区 Type 为 data,则 SubType 只
能设置 ota、phy、nvs 和 nvs_keys。
4, Offset:偏移地址。编译地址必须是 4KB 的倍数
5, Size:大小。子分区的大小
6, Flags:标志位。一般不设置该字段。
分区表的类型
分区表的类型具有四种,如下表所示:

根据上述内容,如果不打算使用 OTA 升级功能,可以根据项目的大小选择第一种或第二种分区类型。而想要使用 OTA 升级功能时,应选择第三种分区类型。作者推荐采用最后一种分区类型,因为它允许自定义分区表,能更精确地满足项目工程的需求,从而实现完美的契合。从上述内容可以了解到,分区表是对 flash 闪存进行区域划分,以便根据不同的功能需求将flash 划分为不同的区域。这些区域可以包括应用、数据等不同类型,例如应用可以进一步细分为 Factory 程序、OTA 程序等,数据则可以包括校准数据、文件系统数据、参数存储数据等。每个分区都有其特定的作用,开发者可以根据自己的需求进行配置以及修改。这样的设计使得开发者能够更方便地管理和使用 flash 空间,同时也有助于提高系统的稳定性和安全性。
分区表 API 函数
esp_partition 组件是 ESP-IDF 中用于管理 ESP32 及其系列芯片上 flash 分区的一个关键组件。它提供了一组高层次的 API 函数,允许开发者方便地访问和操作定义在分区表中的各个分区。这些高层次的 API 函数为开发者提供了简洁和易用的接口,以进行诸如读取、写入、擦除分区内容等操作。这些函数可在 components/esp_partition/include/esp_partition.h 路径下找到这些分区表 API 函数。
esp_partition_find 函数
该函数查找子分区,该函数原型如下所示:
const esp_partition_t *esp_partition_find_first(esp_partition_type_t type,esp_partition_subtype_t subtype,const char *label)

esp_partition_read 函数
该函数用于读取子分区的某个地址的数据,该函数原型如下所示:

esp_err_t esp_partition_read( const esp_partition_t* partition,size_t src_offset,void* dst, size_t size)
esp_partition_write 函数
该函数用于写入子分区的某个地址的数据,该函数原型如下所示:
esp_err_t esp_partition_write( const esp_partition_t* partition,size_t dst_offset,void* src, size_t size)

esp_partition_range 函数
该函数用于擦除子分区的某个地址的数据,该函数原型如下所示:
esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t offset, size_t size)

分区表如下
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1M,
storage, data, , , 0x40000,
void app_main(void)
{
const esp_partition_t *partition_ptr = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
assert(partition_ptr!= NULL);
static char store_data[] = "ESP-IDF Paritition Operations Example (Read, Erase, Write)";
static char read_data[sizeof(store_data)];
memset(read_data, 0xFF, sizeof(read_data));
ESP_ERROR_CHECK(esp_partition_erase_range(partition_ptr, 0, partition_ptr->size));
ESP_ERROR_CHECK(esp_partition_write(partition_ptr, 0, store_data, sizeof(store_data)));
ESP_LOGI(TAG, "Written data: %s", store_data);
ESP_ERROR_CHECK(esp_partition_read(partition_ptr, 0, read_data, sizeof(read_data)));
ESP_LOGI(TAG, "Read data: %s", read_data);
ESP_ERROR_CHECK(esp_partition_erase_range(partition_ptr, 0, SPI_FLASH_SEC_SIZE));
memset(store_data, 0xFF, sizeof(read_data));
ESP_ERROR_CHECK(esp_partition_read(partition_ptr, 0, read_data, sizeof(read_data)));
assert(memcmp(store_data, read_data, sizeof(read_data)) == 0);
ESP_LOGI(TAG, "Erased data");
ESP_LOGI(TAG, "Example end");
}
更多推荐



所有评论(0)