15. ESP32的分区表

一、分区表概述

  在 ESP32 开发中,分区表(Partition Table)是一个关键的系统组件,用于定义芯片上 Flash存储器的分配方式。通过分区表,可以指定 Flash 存储的不同区域分别用来存放应用程序、文件系统、OTA 更新数据等。简单来讲,分区表用于告诉 ESP32 设备如何划分其内部的 Flash 存储区域。每个分区都有特定的用途。

  每片 ESP32 的 flash 可以包含多个应用程序,以及多种不同类型的数据(例如校准数据、文件系统数据、参数存储数据等)。因此,我们在 flash 的默认偏移地址(0x8000)处烧写一张分区表。

  分区表的长度为 0xC00 字节,最多可以保存 95 条分区表条目,也就是说每个条目占用 32 字节(0xC00 十进制数值 ÷ 95 = 32)。MD5 校验和附加在分区表之后,用于在运行时验证分区表的完整性。分区表占据了整个 flash 扇区,大小为 0x1000 (4 KB)。因此,它后面的任何分区至少需要位于 默认偏移地址 + 0x1000 处。

  若想关闭 MD5 校验和操作,则可以在 Menuconfig 菜单配置下失能 MDK5 校验和。

是否使能MD5校验和

二、分区表的格式

  ESP32 的分区表通常使用两种格式来定义存储空间的布局:

  • csv 格式:开发人员使用的格式,方便更改和配置各个分区的偏移地址和大小。它是易于阅读和修改的文本文件。
  • bin 格式:用于烧录到设备的二进制文件。在编译过程中,系统会将 CSV 格式的分区表转换为 bin 格式,供烧录工具使用。

  在编写程序时,开发人员可以通过修改 .csv 文件来定义各个子分区,如应用程序代码、OTA 更新、SPIFFS 文件系统等的存储区域。当项目编译时,CSV 格式会自动转化为 BIN 文件,并在烧录设备时使用。

  分区表中的每个条目都包括以下几个部分:Name(标签)、Type(app、data 等)、SubType 以及在 flash 中的偏移量(分区的加载地址)。

  • Name分区的名称,用于标识该分区的具体用途。
  • Type分区的类型,主要分为 app应用程序) 和 data数据)。
  • SubType分区的子类型,用于进一步说明数据的用途,例如 nvs非易失性存储)、phy物理初始化数据)、fatFAT 文件系统)和 spiffsSPIFFS 文件系统)。
  • Offset分区在闪存中的起始地址,通常以十六进制表示。
# ESP-IDF Partition Table
# Name,     Type, SubType, Offset,  Size, Flags
  nvs,      data, nvs,     0x9000,  24K,
  phy_init, data, phy,     0xf000,  4K,
  factory,  app,  factory, 0x10000, 1M,
  • nvs 分区:用于 存储非易失性存储数据,通常用于保存设备的配置参数。它从地址 0x9000 开始,占用 24KB 的空间。这部分存储器为设备提供了一个可以在断电后仍然保存数据的区域。
  • phy_init 分区:用于 存储物理层初始化数据,主要用于 WiFi 或蓝牙的硬件配置。该分区从地址 0xF000 开始,占用 4KB 的存储空间。
  • factory 分区:是 设备默认的应用程序存储区,它从地址 0x10000 开始,占用 1MB 的空间。设备上电后,会从这个分区加载并运行主要的程序代码。

三、分区表API函数

  esp_partition 组件是 ESP-IDF 中用于管理 ESP32 及其系列芯片上 Flash 分区的关键组件。它提供了一组高层次的 API 函数,允许开发者方便地访问和操作定义在分区表中的各个分区。这些高层次的 API 函数为开发者提供了简洁易用的接口,以进行读取、写入、擦除分区内容等操作。这些分区表 API 函数可以在 components/esp_partition/include/esp_partition.h 路径下找到。

3.1、根据一个或多个参数查找第一个分区

  我们可以使用 esp_partition_find_first() 函数 根据一个或多个参数查找第一个分区,该函数原型如下所示:

/**
 * @brief 根据一个或多个参数查找第一个分区
 * 
 * @param type 子分区类型
 * @param subtype 子类型
 * @param label 分区标签
 * @return const esp_partition_t* 指向 esp_partition_t 结构体的指针
 */
const esp_partition_t *esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char *label);

  形参 type子分区类型,它的可选值如下:

typedef enum 
{
    ESP_PARTITION_TYPE_APP = 0x00,                  // 应用程序分区类型
    ESP_PARTITION_TYPE_DATA = 0x01,                 // 数据分区类型
    ESP_PARTITION_TYPE_BOOTLOADER = 0x02,           // Bootloader分区类型
    ESP_PARTITION_TYPE_PARTITION_TABLE = 0x03,      // 分区表分区类型

    ESP_PARTITION_TYPE_ANY = 0xff,                  // 任意分区类型
} esp_partition_type_t;

  形参 subtype子类型,它的可选值如下:

typedef enum 
{
    ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY = 0x00,                            // Primary Bootloader
    ESP_PARTITION_SUBTYPE_BOOTLOADER_OTA = 0x01,                                // Temporary OTA storage for Bootloader, where the OTA uploads a new Bootloader image

    ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY = 0x00,                       // Primary Partition table
    ESP_PARTITION_SUBTYPE_PARTITION_TABLE_OTA = 0x01,                           // Temporary OTA storage for Partition table, where the OTA uploads a new Partition table image

    ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00,                                   // Factory application partition
    ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10,                                   // Base for OTA partition subtypes
    ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0,    // OTA partition 0
    ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1,    // OTA partition 1
    ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2,    // OTA partition 2
    ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3,    // OTA partition 3
    ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4,    // OTA partition 4
    ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5,    // OTA partition 5
    ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6,    // OTA partition 6
    ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7,    // OTA partition 7
    ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8,    // OTA partition 8
    ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9,    // OTA partition 9
    ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10,  // OTA partition 10
    ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11,  // OTA partition 11
    ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12,  // OTA partition 12
    ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,  // OTA partition 13
    ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,  // OTA partition 14
    ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,  // OTA partition 15
    ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16, // Max subtype of OTA partition
    ESP_PARTITION_SUBTYPE_APP_TEST = 0x20,                                      // Test application partition

    ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00,                                      // OTA selection partition
    ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01,                                      // PHY init data partition
    ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02,                                      // NVS partition
    ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03,                                 // COREDUMP partition
    ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS = 0x04,                                 // Partition for NVS keys
    ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM = 0x05,                                 // Partition for emulate eFuse bits
    ESP_PARTITION_SUBTYPE_DATA_UNDEFINED = 0x06,                                // Undefined (or unspecified) data partition

    ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80,                                 // ESPHTTPD partition
    ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81,                                      // FAT partition
    ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82,                                   // SPIFFS partition
    ESP_PARTITION_SUBTYPE_DATA_LITTLEFS = 0x83,                                 // LITTLEFS partition

#if __has_include("extra_partition_subtypes.inc")
    #include "extra_partition_subtypes.inc"
#endif

    ESP_PARTITION_SUBTYPE_ANY = 0xff,                                           // Used to search for partitions with any subtype
} esp_partition_subtype_t;

  该函数的返回值是 指向 esp_partition_t 结构体的指针,它的定义如下:

typedef struct 
{
    esp_flash_t* flash_chip;            // 分区所在的SPI闪存芯片
    esp_partition_type_t type;          // 分区类型(app/data)
    esp_partition_subtype_t subtype;    // 分区子类型
    uint32_t address;                   // flash中分区的起始地址
    uint32_t size;                      // 分区大小,以字节为单位
    uint32_t erase_size;                // 擦除操作应对齐的大小
    char label[17];                     // 分区标签,以零结尾的ASCII字符串
    bool encrypted;                     // 如果分区被加密,标志设置为true
    bool readonly;                      // 如果分区为只读,则该标志设置为true
} esp_partition_t;

3.2、从分区中读取数据

  我们可以使用 esp_partition_read() 函数 从分区中读取数据,该函数原型如下所示:

/**
 * @brief 从分区中读取数据
 * 
 * @param partition 分区结构指针
 * @param src_offset 读取数据的地址
 * @param dst 存储数据的指针
 * @param size 读取数据大小
 * @return esp_err_t ESP_OK: 读取成功; 
 *                   ESP_ERR_INVALID_ARG: 超过分区大小; 
 *                   ESP_ERR_INVALID_SIZE: 读取会超出分区边界;
 */
esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size);

3.3、将数据写入分区

  我们可以使用 函数 将数据写入分区,该函数原型如下所示:

/**
 * @brief 将数据写入分区
 * 
 * @param partition 分区结构指针
 * @param dst_offset 写入数据的地址
 * @param src 写入数据
 * @param size 写入数据大小
 * @return esp_err_t ESP_OK: 数据写入成功; 
 *                   ESP_ERR_INVALID_ARG: dst_offset 超出了分区大小;
 *                   ESP_ERR_INVALID_SIZE: 写入范围超出了分区边界;
 *                   ESP_ERR_NOT_ALLOWED: 如果分区为只读;
 */
esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size);

3.4、擦除分区的一部分区域

  我们可以使用 函数 擦除分区的一部分,该函数原型如下所示:

/**
 * @brief 擦除分区的一部分区域
 * 
 * @param partition 分区结构指针
 * @param offset 擦除数据的地址
 * @param size 擦除数据大小
 * @return esp_err_t ESP_OK: 范围擦除成功;
 *                   ESP_ERR_INVALID_ARG: 传入的参数无效;
 *                   ESP_ERR_INVALID_SIZE: 如果擦除范围超出了分区边界;
 *                   ESP_ERR_NOT_ALLOWED: 如果分区为只读;
 */
esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t offset, size_t size);
posted @ 2025-03-25 22:10  星光映梦  阅读(708)  评论(0)    收藏  举报