跳过正文

分析 ESP32 固件和分区表

Rust Esp32
目录
rust-esp32 - 这篇文章属于一个选集。
§ 11: 本文

使用 esptool.py image_info 查看 app 固件内容
#

参考 app_image_format.html

esptool.py --chip esp32s3 image_info build/app.bin
esptool.py v2.3.1
Image version: 1
Entry point: 40080ea4
13 segments

Segment 1: len 0x13ce0 load 0x3f400020 file_offs 0x00000018 SOC_DROM
Segment 2: len 0x00000 load 0x3ff80000 file_offs 0x00013d00 SOC_RTC_DRAM
Segment 3: len 0x00000 load 0x3ff80000 file_offs 0x00013d08 SOC_RTC_DRAM
Segment 4: len 0x028e0 load 0x3ffb0000 file_offs 0x00013d10 DRAM
Segment 5: len 0x00000 load 0x3ffb28e0 file_offs 0x000165f8 DRAM
Segment 6: len 0x00400 load 0x40080000 file_offs 0x00016600 SOC_IRAM
Segment 7: len 0x09600 load 0x40080400 file_offs 0x00016a08 SOC_IRAM
Segment 8: len 0x62e4c load 0x400d0018 file_offs 0x00020010 SOC_IROM
Segment 9: len 0x06cec load 0x40089a00 file_offs 0x00082e64 SOC_IROM
Segment 10: len 0x00000 load 0x400c0000 file_offs 0x00089b58 SOC_RTC_IRAM
Segment 11: len 0x00004 load 0x50000000 file_offs 0x00089b60 SOC_RTC_DATA
Segment 12: len 0x00000 load 0x50000004 file_offs 0x00089b6c SOC_RTC_DATA
Segment 13: len 0x00000 load 0x50000004 file_offs 0x00089b74 SOC_RTC_DATA
Checksum: e8 (valid)
Validation Hash: 407089ca0eae2bbf83b4120979d3354b1c938a49cb7a0c997f240474ef2ec76b (valid)

查看 Bootloader Image Format:

esptool.py --chip esp32s3 image_info build/bootloader/bootloader.bin --version 2

File size: 26576 (bytes)

ESP32 image header
==================
Image version: 1
Entry point: 0x40080658
Segments: 4
Flash size: 2MB
Flash freq: 40m
Flash mode: DIO

ESP32 extended image header
===========================
WP pin: 0xee
Flash pins drive settings: clk_drv: 0x0, q_drv: 0x0, d_drv: 0x0, cs0_drv: 0x0, hd_drv: 0x0, wp_drv: 0x0
Chip ID: 0
Minimal chip revision: v0.0, (legacy min_rev = 0)
Maximal chip revision: v3.99

Segments information
====================
Segment   Length   Load addr   File offs  Memory types
-------  -------  ----------  ----------  ------------
    1  0x01bb0  0x3fff0030  0x00000018  BYTE_ACCESSIBLE, DRAM, DIRAM_DRAM
    2  0x03c90  0x40078000  0x00001bd0  CACHE_APP
    3  0x00004  0x40080400  0x00005868  IRAM
    4  0x00f2c  0x40080404  0x00005874  IRAM

ESP32 image footer
==================
Checksum: 0x65 (valid)
Validation hash: 6f31a7f8512f26f6bce7c3b270f93bf6cf1ee4602c322998ca8ce27433527e92 (valid)

Bootloader information
======================
Bootloader version: 1
ESP-IDF: v5.1-dev-4304-gcb51a3b-dirty
Compile time: Mar 30 2023 19:14:17

分区表
#

使用 idf.py partition-table 来显示分区表内容。

使用 gen_esp32part.py 工具来将 CSV 分区表转换为烧写到 FLASH 的 bin 格式。

# Erase partition with name 'storage'
parttool.py --port "/dev/ttyUSB1" erase_partition --partition-name=storage

# Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin'
parttool.py --port "/dev/ttyUSB1" read_partition --partition-type=data --partition-subtype=spiffs --output "spiffs.bin"

# Write to partition 'factory' the contents of a file named 'factory.bin'
parttool.py --port "/dev/ttyUSB1" write_partition --partition-name=factory --input "factory.bin"

# Print the size of default boot partition
parttool.py --port "/dev/ttyUSB1" get_partition_info --partition-boot-default --info size

Partition Tables
#

https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/partition-tables.html

A single ESP32-S3’s flash can contain =multiple apps, as well as many different kinds of data= (calibration data, filesystems, parameter storage, etc). For this reason a partition table is =flashed to (default offset) 0x8000 in the flash.=

  • 前 0x8000 空间分给 second stage bootloader 代码使用了。

默认分区表给应用的 flash 空间是 1M,如果超过了编译会警告,需要在烧写是指定 flash size。

=The partition table length is 0xC00 bytes=, as we allow a maximum of 95 entries. An MD5 checksum, used for checking the integrity of the partition table at runtime, is appended after the table data. Thus, the partition table =occupies an entire flash sector, which size is 0x1000 (4 KB)=. As a result, any partition following it must be at least located at (default offset) + 0x1000.

Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.

The simplest way to use the partition table is to open the project configuration menu ( =idf.py menuconfig= ) and choose one of the simple predefined partition tables under CONFIG_PARTITION_TABLE_TYPE:

  1. “Single factory app, no OTA” 2 “Factory app, two OTA definitions”

In both cases =the factory app is flashed at offset 0x10000=. If you execute idf.py partition-table then it will print a summary of the partition table.

使用 idf.py menuconfig 来配置 CONFIG_PARTITION_TABLE_TYPE 变量, 指定内置的分区表类型:

  1. 内置两种类型分区表:“Single factory app, no OTA”, “Factory app, two OTA definitions”;
  2. 完全自定义 CSV 分区表;

使用 idf.py partition-table 来显示分区表内容. 使用 gen_esp32part.py 工具来将 CSV 分区表转换为烧写到 FLASH 的 bin 格式。

Built-in Partition Tables
#

Here is the summary printed for the “Single factory app, no OTA” configuration:

# ESP-IDF Partition Table
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x6000,
phy_init, data, phy,     0xf000,  0x1000,
factory,  app,  factory, 0x10000, 1M,
At a =0x10000 (64 KB) offset in the flash= is the app labelled "factory". The bootloader will run
=this app= by default.

There are also two data regions defined in the partition table for storing NVS library partition
and PHY init data.

Here is the summary printed for the “Factory app, two OTA definitions” configuration:

# 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,
There are now =three app partition= definitions. The type of the factory app (at 0x10000) and the
next two "OTA" apps are all set to "app", but their subtypes are different.

There is also a new "otadata" slot, which holds the data for OTA updates. The bootloader
consults this data in order to know =which app to execute=. If "ota data" is empty, it will
execute =the factory app=.

Creating Custom Tables
#

If you choose =“Custom partition table CSV”= in menuconfig then you can also enter the name of a CSV file (in =the project directory= ) to use for your partition table. The CSV file can describe any number of definitions for the table you need.

The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the “input” CSV for the OTA 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,    ,         1M
ota_1,    app,  ota_1,    ,         1M
nvs_key,  data, nvs_keys, ,        0x1000
Whitespace between fields is ignored, and so is any line starting with # (comments).

Each non-comment line in the CSV file is a partition definition.

The "Offset" field for each partition is empty. The =gen_esp32part.py= tool =fills in= each blank
offset, starting after the partition table and making sure each partition is aligned correctly.

Generating Binary Partition Table
#

The partition table which is flashed to the ESP32 is in =a binary format=, not CSV. The tool =partition_table/gen_esp32part.py= is used to =convert between CSV and binary formats= .

If you configure the partition table CSV name in the project configuration (=idf.py menuconfig=) and then build the project or run =idf.py partition-table=, this conversion is done as part of the build process.

To convert CSV to Binary manually:
python gen_esp32part.py input_partitions.csv binary_partitions.bin
To convert binary format back to CSV manually:
python gen_esp32part.py binary_partitions.bin input_partitions.csv
To display the contents of a binary partition table on stdout (this is how the summaries displayed
when running idf.py partition-table are generated:
python gen_esp32part.py binary_partitions.bin

** Partition Size Checks

The ESP-IDF build system will automatically check if =generated binaries fit in the available partition space= , and will fail with an error if a binary is too large.

Currently these checks are performed for the following binaries:

  1. =Bootloader binary= must fit in space before partition table (see Bootloader Size).
  2. =App binary= should fit in at least one partition of type “app”. If the app binary does not fit in any app partition, =the build will fail=. If it only fits in some of the app partitions, a warning is printed about this.

Although the build process will fail if the size check returns an error, =the binary files are still generated and can be flashed= (although they may not work if they are too large for the available space.)

** Flashing the Partition Table

  1. =idf.py partition-table-flash=: will flash the partition table with esptool.py.
  2. =idf.py flash=: Will =flash everything= including the partition table.

A manual flashing command is also printed as part of =idf.py partition-table= output.

Note that updating the partition table does not erase data that may have been stored according to the old partition table. You can use =idf.py erase-flash= (or =esptool.py erase_flash=) to erase the entire flash contents.

Partition Tool (parttool.py)
#

The component partition_table provides a tool =parttool.py= for performing partition-related operations on a target device. The following operations can be performed using the tool:

    reading a partition and saving the contents to a file (read_partition)

    writing the contents of a file to a partition (write_partition)

    erasing a partition (erase_partition)

    retrieving info such as name, offset, size and flag ("encrypted") of a given partition (get_partition_info)

The tool can either be imported and used from another Python script or invoked from shell script for users wanting to perform operation programmatically. This is facilitated by the tool’s Python API and command-line interface, respectively.

# Erase partition with name 'storage'
parttool.py --port "/dev/ttyUSB1" erase_partition --partition-name=storage

# Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin'
parttool.py --port "/dev/ttyUSB1" read_partition --partition-type=data --partition-subtype=spiffs --output "spiffs.bin"

# Write to partition 'factory' the contents of a file named 'factory.bin'
parttool.py --port "/dev/ttyUSB1" write_partition --partition-name=factory --input "factory.bin"

# Print the size of default boot partition
parttool.py --port "/dev/ttyUSB1" get_partition_info --partition-boot-default --info size

nvs
#

位于 FLASH 中的一个特殊分区,使用 data type 和 nvs subtype,可以用来保存少量的 KV 数据,数据来源于 项目的 CSV 文件。可以使用工具 nvs_partition_gen.py 来从 CSV 文件创出可以烧写到 FLASH 的 bin 文件。

常用于保存项目的一些动态配置信息,如 wifi 账号密码。

Non-Volatile Storage Library: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/storage/nvs_flash.html

Non-volatile storage (NVS) library is designed to =store key-value pairs in flash=. This section introduces some concepts used by NVS.

Currently, NVS uses a portion of =main flash memory= through the esp_partition API. The library uses =all the partitions with data type and nvs subtype=. The application can choose to use the partition with =the label nvs= through the nvs_open() API function or any other partition by specifying its name using the nvs_open_from_partition() API function.

Future versions of this library may have other storage backends to keep data in another flash chip (SPI or I2C), RTC, FRAM, etc.

NVS Partition Generator Utility

This utility helps generate NVS partition binary files which can =be flashed separately on a dedicated partition= via a flashing utility. Key-value pairs to be flashed onto the partition can be provided via =a CSV file=. For more details, please refer to NVS Partition Generator Utility.

key,type,encoding,value     <-- column header
namespace_name,namespace,,  <-- First entry should be of type "namespace"
key1,data,u8,1
key2,file,string,/path/to/file

运行分区工具:

# https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/storage/nvs_partition_gen.html
# python nvs_partition_gen.py [-h] {generate,generate-key,encrypt,decrypt} ...
python nvs_partition_gen.py generate sample_singlepage_blob.csv sample.bin 0x3000
Instead of calling the =nvs_flash/nvs_partition_generator/nvs_partition_gen.py= tool manually, the
creation of the partition binary files can also be done directly from CMake using the function
=nvs_create_partition_image= :
nvs_create_partition_image( [FLASH_IN_PROJECT] [DEPENDS dep dep dep …])

If FLASH_IN_PROJECT is not specified, the image will still be generated, but you will have to flash it manually using =idf.py -flash= (e.g., if your partition name is nvs, then use idf.py nvs-flash).

nvs_create_partition_image must be called from one of the component CMakeLists.txt files. Currently, only non-encrypted partitions are supported.

nvs 分区 bin 文件创建工具:nvs_partition_gen.py
#

# https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/storage/nvs_partition_gen.html
# python nvs_partition_gen.py [-h] {generate,generate-key,encrypt,decrypt} ...
python nvs_partition_gen.py generate sample_singlepage_blob.csv sample.bin 0x3000
rust-esp32 - 这篇文章属于一个选集。
§ 11: 本文

相关文章

Rust 驱动 Audio - 播放和录音
Rust Esp32
Rust 驱动 Camera - 采集和播放
Rust Esp32
Rust 驱动 LCD - 显示中英文
Rust Esp32
Rust 驱动 LCD - 显示图片
Rust Esp32