博客 / 詳情

返回

《ESP32-S3使用指南—IDF版 V1.6》第七章 分區表管理與使用

第七章 分區表管理與使用

在ESP32中,分區表是定義Flash存儲區域的關鍵組成部分。它決定了應用程序、數據存儲和OTA更新等如何在芯片內部分配和管理,從而影響到整個應用的性能和功能。
本章將分為如下幾個小節:
7.1 分區表概述
7.2 分區表API函數

7.1 分區表概述
在ESP32開發中,分區表(Partition Table)是一個關鍵的系統組件,用於定義芯片上Flash存儲器的分配方式。通過分區表,可以指定Flash存儲的不同區域分別用來存放應用程序、文件系統、OTA更新數據等。簡單來講,分區表用於告訴ESP32設備如何劃分其內部的Flash存儲區域。每個分區都有特定的用途,例如:
1)factory分區:存儲應用程序固件。
2)ota分區:用於存儲空中下載(OTA)更新的固件。
3)nvs分區:用於存儲非易失性存的數據,常用於保存配置信息。
4)spiffs/fat分區:文件系統分區,用於存儲用户數據。
5)phy_init分區:專門用於存儲Wi-Fi和藍牙物理層初始化數據的區域
6)自定義分區:用於存儲用户的數據。
接下來,筆者將重點講解分區表的格式及分區表條目結構。通過化整為零的方式,我們將逐步瞭解分區表的各個組成部分,深入剖析每個元素的作用和意義。

7.1.1 分區表的格式
ESP32的分區表通常使用兩種格式來定義存儲空間的佈局:
1)csv格式:開發人員使用的格式,方便更改和配置各個分區的偏移地址和大小。它是易於閲讀和修改的文本文件。
2)bin格式:用於燒錄到設備的二進制文件。在編譯過程中,系統會將CSV格式的分區錶轉換為bin格式,供燒錄工具使用。
在編寫程序時,開發人員可以通過修改 .csv 文件來定義各個子分區,如應用程序代碼、OTA更新、SPIFFS文件系統等的存儲區域。當項目編譯時,CSV格式會自動轉化為BIN文件,並在燒錄設備時使用。下面我們來看一下本書籍提供的例程示例分區表,如下內容所示。

image001

圖7.1.1.1 書籍示例的分區表
上圖中,分區的大小和偏移決定了每個分區在閃存中的位置和空間分配,以下是各個子分區的描述內容:
1,nvs分區:用於存儲非易失性存儲數據,通常用於保存設備的配置參數。它從地址0x9000開始,佔用24KB的空間。這部分存儲器為設備提供了一個可以在斷電後仍然保存數據的區域。
2,phy_init分區:用於存儲物理層初始化數據,主要用於Wi-Fi或藍牙的硬件配置。該分區從地址0xF000開始,佔用4KB的存儲空間。
3,factory分區:是設備默認的應用程序存儲區,它從地址0x10000開始,佔用1MB的空間。設備上電後,會從這個分區加載並運行主要的程序代碼。
4,vfs分區:用於FAT文件系統存儲,適合用於文件操作或存儲大容量數據。它從地址0x200000 開始,佔用10MB的空間,通常用於掛載外部存儲設備,如SD卡。
5,storage分區:用於SPIFFS文件系統,適合存儲小型文件或嵌入式應用中的配置數據。該分區從地址0xC00000開始,佔用4MB的空間。
為了幫助讀者更好地理解分區表,以下將以圖形化的形式進行描述,如下圖所示。

image003

圖7.1.1.2 16MB Flash劃分區域示意圖
從上圖可知。用户可根據需求調整分區的大小和偏移位置,但筆者建議儘量不要修改啓動區域和分區表燒入區域,保持默認設置。這有助於避免潛在的啓動問題和系統不兼容情況,從而確保系統的穩定性和正常運行。值得注意的是:Factory /OTA等程序存儲分區視為加密分區,用户不可隨意修改。
綜上所述,分區表是用户為系統準備的Flash區域劃分目錄,系統從地址0x8000讀取分區表信息,以瞭解區域的範圍、大小及各自的功能和作用。

7.1.2 分區表條目結構
分區表中的每個條目都由以下幾個部分組成:
1,Name:分區的名稱,用於標識該分區的具體用途。
2,Type:分區的類型,主要分為 app(應用程序) 和 data(數據)。
3,SubType:分區的子類型,用於進一步説明數據的用途,例如 nvs(非易失性存儲)、phy(物理初始化數據)、fat(FAT文件系統)和 spiffs(SPIFFS文件系統)。
4,Offset:分區在閃存中的起始地址,通常以十六進制表示。
根據這些關鍵字段,系統能夠精確定位每個分區並執行相應操作。例如,系統通過Name字段識別分區的用途;通過Type和SubType進一步明確數據的類型和處理方式;Offset則幫助系統找到分區在閃存中的起始位置和大小。這些信息使系統能夠準確地進行讀寫、擦除等操作,從而確保分區操作的可靠性和有效性。
根據樂鑫ESP-IDF編程指南所示,分區表的長度為0xC00字節,最多可容納95條分區表條目,也就是説每個條目佔用32字節(3072 (0xC00十進制數值)÷ 95 = 32)。接下來,我們來看一下在ESP-IDF(SDK)程序中如何定義分區表結構體,以下是相關代碼:

/**
 * @brief       ESP32 分區結構體
 */
typedef struct 
{ 
    esp_flash_t* flash_chip;            /* 指向所使用的 Flash 芯片的指針 */
    esp_partition_type_t type;          /* 分區的類型 */
    esp_partition_subtype_t subtype;    /* 分區的子類型 */
    uint32_t address;                   /* 分區在閃存中的起始地址 */
    uint32_t size;                      /* 分區的大小 */
    uint32_t erase_size;                /* 分區的擦除大小 */
    char label[17];               /* 分區的標籤,最多可包含16個字符(末尾包含終止符)*/
    bool encrypted;                     /* 是否啓用加密 */
    bool readonly;                      /* 是否為只讀分區 */
} esp_partition_t;

很多人認為,通過調用sizeof(esp_partition_t)可以獲取分區表條目的大小,其實並不完全正確。因為每個分區表條目由Name、Type、SubType和Offset組成,上述結構體成員變量僅部分描述了這一結構。下面筆者將基於分區表條目的組成,對esp_partition_t結構體進行一次縮減,代碼如下:

/**
 * @brief       ESP32 分區結構體
 */
typedef struct 
{ 
    esp_partition_type_t type;          /* 分區的類型 */
    esp_partition_subtype_t subtype;    /* 分區的子類型 */
    uint32_t address;                   /* 分區在閃存中的起始地址 */
    char label[17]; /* 分區的標籤,最多可包含16個字符(末尾包含終止符) */
} esp_partition_t;

這樣我們就可以得到描述每個條目的結構體,然後使用sizeof(reduced_esp_partition_t)來獲取分區表條目的大小。經過系統計算,最終得出的大小接近32字節。這確認了每個條目的存儲結構符合預期。
注意:為確保分區表的完整性,系統在分區表末尾附加了MD5校驗和(根據分區表內容計算,可在設備啓動階段用於驗證分區表的完整性),以便在運行時進行驗證。整個分區表佔據一個完整的Flash扇區,大小為0x1000(4KB)。因此,緊隨分區表之後的任何分區,其起始地址必須位於默認偏移地址 + 0x1000處,以避免與分區表區域重疊。這種設計確保了分區表與其他數據區域的安全隔離,並有助於系統在啓動時正確加載各個分區。
若讀者想關閉MD5校驗和操作,則可以在Menuconfig菜單配置下失能MDK5校驗和,如下圖所示。

image005

圖7.1.2.1 Menuconfig菜單下失能MDK5校驗和
在上圖中,取消勾選紅色框中的選項即可關閉MD5校驗和。

7.2 分區表API函數
esp_partition組件是ESP-IDF中用於管理ESP32及其系列芯片上Flash分區的關鍵組件。它提供了一組高層次的API函數,允許開發者方便地訪問和操作定義在分區表中的各個分區。這些高層次的API函數為開發者提供了簡潔易用的接口,以進行讀取、寫入、擦除分區內容等操作。這些分區表API函數可以在components/esp_partition/include/esp_partition.h路徑下找到。
1,根據一個或多個參數查找第一個分區esp_partition_find_first
該函數是用於根據一個或多個參數查找第一個分區,該函數原型如下所示:

const esp_partition_t *esp_partition_find_first(esp_partition_type_t type,
                                                esp_partition_subtype_t subtype,
                                                const char *label)

函數形參:

ScreenShot_2026-03-02_093856_919

表7.2.1 esp_partition_find函數函數描述
返回值:
句柄:指向 esp_partition_t 結構體的指針。
NULL:未找到分區。
2,從分區中讀取數據esp_partition_read
該函數是用於從分區中讀取數據,該函數原型如下所示:

esp_err_t esp_partition_read( const esp_partition_t* partition,
                              size_t src_offset, void* dst, size_t size)

函數形參:

ScreenShot_2026-03-02_093914_914

表7.2.2 esp_partition_read函數函數描述
返回值:
ESP_OK:讀取成功。
ESP_ERR_INVALID_ARG:超過分區大小。
ESP_ERR_INVALID_SIZE:讀取會超出分區邊界。
3,將數據寫入分區esp_partition_write
該函數是用於將數據寫入分區,該函數原型如下所示:

esp_err_t esp_partition_write( const esp_partition_t* partition,
                               size_t dst_offset, void* src, size_t size)

函數形參:

ScreenShot_2026-03-02_093930_075

表7.2.3 esp_partition_write函數函數描述
返回值:
ESP_OK:如果數據寫入成功。
ESP_ERR_INVALID_ARG:如果 dst_offset 超出了分區大小。
ESP_ERR_INVALID_SIZE:如果寫入範圍超出了分區邊界。
ESP_ERR_NOT_ALLOWED:如果分區為只讀。
4,擦除分區的一部分區域esp_partition_erase_range
該函數是用於擦除分區的一部分,該函數原型如下所示:

esp_err_t esp_partition_erase_range(const esp_partition_t *partition,
                                    size_t offset, size_t size)

函數形參:

ScreenShot_2026-03-02_093941_445

表7.2.4 esp_partition_erase_range函數函數描述
返回值:
ESP_OK:如果範圍擦除成功。
ESP_ERR_INVALID_ARG:如果傳入的參數無效(如 iterator 或 dst 為 NULL)。
ESP_ERR_INVALID_SIZE:如果擦除範圍超出了分區邊界。
ESP_ERR_NOT_ALLOWED:如果分區為只讀。
上述列舉的函數是訪問和操作分區表時常用的API函數。若需進一步瞭解或學習其他剩餘的分區表API函數,可查閲esp_partition.h頭文件。如果讀者想了解這些函數的使用方法,可以查看本書提供的示例28_chinese_display下的fonts.c文件,筆者使用這些函數將GBK字庫更新至storage分區,並完成了漢字顯示實驗。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.