diff --git a/docs/en/api-reference/storage/nvs_flash.rst b/docs/en/api-reference/storage/nvs_flash.rst index 736b5695262..c84d45961a2 100644 --- a/docs/en/api-reference/storage/nvs_flash.rst +++ b/docs/en/api-reference/storage/nvs_flash.rst @@ -35,7 +35,7 @@ NVS operates on key-value pairs. Keys are ASCII strings; the maximum key length - integer types: ``uint8_t``, ``int8_t``, ``uint16_t``, ``int16_t``, ``uint32_t``, ``int32_t``, ``uint64_t``, ``int64_t`` - floating point types: ``float`` and ``double`` - zero-terminated C-like string -- variable length binary data - blob +- variable length binary data - BLOB .. note:: @@ -64,12 +64,12 @@ The maximum size of a single stored value depends on its data type: - Fixed by the type (1 to 8 bytes); always stored in a single entry. * - String - 4000 bytes, including the null terminator. - * - Blob + * - BLOB - 508,000 bytes, or 97.6% of the partition size minus 4000 bytes, whichever is lower. .. note:: - The string and blob limits above are absolute upper bounds valid on an empty (non-fragmented) data partition. The size that can actually be stored at run time is typically lower and depends on how the partition is fragmented. See :ref:`nvs_space_consumption` for how NVS allocates entries and why fragmentation matters. + The string and BLOB limits above are absolute upper bounds valid on an empty (non-fragmented) data partition. The size that can actually be stored at run time is typically lower and depends on how the partition is fragmented. See :ref:`nvs_space_consumption` for how NVS allocates entries and why fragmentation matters. Namespaces ^^^^^^^^^^ @@ -89,7 +89,7 @@ The open mode parameter controls the access level and security behavior: NVS Iterators ^^^^^^^^^^^^^ -Iterators allow to list key-value pairs stored in NVS, based on specified partition name, namespace, and data type. +Iterators allow listing key-value pairs stored in NVS, based on specified partition name, namespace, and data type. There are the following functions available: @@ -113,7 +113,7 @@ Security, Tampering, and Robustness NVS is not directly compatible with the {IDF_TARGET_NAME} flash encryption system. However, data can still be stored in encrypted form if NVS encryption is used together with {IDF_TARGET_NAME} flash encryption or with the help of the HMAC peripheral. Please refer to :doc:`nvs_encryption` for more details. -If NVS encryption is not used, it is possible for anyone with physical access to the flash chip to read, alter, erase, or add key-value pairs. With NVS encryption enabled, it is not possible to read, alter or add a key-value pair and get recognized as a valid pair without knowing corresponding NVS encryption keys. However, there is no tamper-resistance against the erase operation. +If NVS encryption is not used, it is possible for anyone with physical access to the flash chip to read, alter, erase, or add key-value pairs. With NVS encryption enabled, it is not possible to read, alter, or add a key-value pair and get recognized as a valid pair without knowing corresponding NVS encryption keys. However, there is no tamper-resistance against the erase operation. The library does try to recover from conditions when flash memory is in an inconsistent state. In particular, one should be able to power off the device at any point and time and then power it back on. This should not result in loss of data, except for the new key-value pair if it was being written at the moment of powering off. The library should also be able to initialize properly with any random data present in flash memory. @@ -184,7 +184,7 @@ To address this issue, the Kconfig option :ref:`CONFIG_NVS_FLASH_VERIFY_ERASE` e .. note:: - When NVS is initialized on the writeable partition, the library will attempt to perform recovery operations if the partition is found to be in an inconsistent state. This may involve erasing and rewriting some pages and in continuously unstable power environment subsequently lead to the unintended loss of factory default data. For this reason, it is recommended to keep vital factory default data in separate, read-only partition where the recovery is not performed. + When NVS is initialized on a writable partition, the library will attempt to perform recovery operations if the partition is found to be in an inconsistent state. This may involve erasing and rewriting some pages and, in a continuously unstable power environment, subsequently lead to the unintended loss of factory default data. For this reason, it is recommended to keep vital factory default data in a separate, read-only partition where the recovery is not performed. .. _nvs_bootloader: @@ -231,7 +231,7 @@ Instead of calling the ``nvs_partition_gen.py`` tool manually, the creation of t * - Parameter - Description * - ``FLASH_IN_PROJECT`` - - Name of the NVS partition + - Flash the generated image together with the project * - ``DEPENDS`` - Specify files on which the command depends @@ -255,7 +255,7 @@ You can find code examples in the :example:`storage/nvs` directory of ESP-IDF ex :example:`storage/nvs/nvs_rw_blob` - Demonstrates how to read a single integer value and a blob (binary large object), and write them to NVS to preserve this value between {IDF_TARGET_NAME} module restarts. + Demonstrates how to read a single integer value and a BLOB (Binary Large Object), and write them to NVS to preserve this value between {IDF_TARGET_NAME} module restarts. * value - tracks the number of the {IDF_TARGET_NAME} module soft and hard restarts. * blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. A new run time is added to the table on each manually triggered soft restart, and then the added run time is written to NVS. Triggering is done by pulling down GPIO0. @@ -274,7 +274,7 @@ You can find code examples in the :example:`storage/nvs` directory of ESP-IDF ex Usage statistics are obtained prior to and post writing, with the differences being compared to expected values of newly used entries. - The second part of example shows the effect of NVS partition fragmentation to the blob storage overhead. + The second part of example shows the effect of NVS partition fragmentation to the BLOB storage overhead. :example:`storage/nvs/nvs_iteration` @@ -296,7 +296,7 @@ NVS stores key-value pairs sequentially, with new key-value pairs being added at .. note:: - The NVS component includes flash wear levelling by design. Set operations append new data to the free space after existing entries, and invalidation of old values does not trigger immediate flash erase operations. The organization of NVS space into pages and entries reduces the frequency of flash erase to flash write operations for data types fitting one entry by up to a factor of 126 in the ideal case (one page of single-entry writes per erase). The actual factor is lower in practice and is primarily driven by how full the partition is: as live data grows, NVS space reclaim runs more often, and the erase/write ratio worsens. Large, never-overwritten data chunks may occupy the same NVS page indefinitely — reclaim only selects pages that contain erased entries, so such pages never participate in the erase cycle and therefore reduce the share of flash memory subject to wear levelling. + The NVS component includes flash wear leveling by design. Set operations append new data to the free space after existing entries, and invalidation of old values does not trigger immediate flash erase operations. The organization of NVS space into pages and entries reduces the frequency of flash erase to flash write operations for data types fitting one entry by up to a factor of 126 in the ideal case (one page of single-entry writes per erase). The actual factor is lower in practice and is primarily driven by how full the partition is: as live data grows, NVS space reclaim runs more often, and the erase/write ratio worsens. Large, never-overwritten data chunks may occupy the same NVS page indefinitely — reclaim only selects pages that contain erased entries, so such pages never participate in the erase cycle and therefore reduce the share of flash memory subject to wear leveling. Pages and Entries ^^^^^^^^^^^^^^^^^ @@ -340,7 +340,7 @@ Structure of a Page For now, we assume that flash sector size is 4096 bytes and that {IDF_TARGET_NAME} flash encryption hardware operates on 32-byte blocks. It is possible to introduce some settings configurable at compile-time (e.g., via menuconfig) to accommodate flash chips with different sector sizes (although it is not clear if other components in the system, e.g., SPI flash driver and SPI flash cache can support these other sizes). -Page consists of three parts: header, entry state bitmap, and entries themselves. To be compatible with {IDF_TARGET_NAME} flash encryption, the entry size is 32 bytes. For integer types, an entry holds one key-value pair. For strings and blobs, an entry holds part of key-value pair (more on that in the entry structure description). +Page consists of three parts: header, entry state bitmap, and entries themselves. To be compatible with {IDF_TARGET_NAME} flash encryption, the entry size is 32 bytes. For integer types, an entry holds one key-value pair. For strings and BLOBs, an entry holds part of key-value pair (more on that in the entry structure description). The following diagram illustrates the page structure. Numbers in parentheses indicate the size of each part in bytes. @@ -391,7 +391,7 @@ Erased (2'b00) Structure of Entry ^^^^^^^^^^^^^^^^^^ -For values of primitive types (currently integers from 1 to 8 bytes long), entry holds one key-value pair. For string and blob types, entry holds part of the whole key-value pair. For strings, in case when a key-value pair spans multiple entries, all entries are stored in the same page. Blobs are allowed to span over multiple pages by dividing them into smaller chunks. For tracking these chunks, an additional fixed length metadata entry is stored called "blob index". Earlier formats of blobs are still supported (can be read and modified). However, once the blobs are modified, they are stored using the new format. +For values of primitive types (currently integers from 1 to 8 bytes long), entry holds one key-value pair. For string and BLOB types, entry holds part of the whole key-value pair. For strings, in case when a key-value pair spans multiple entries, all entries are stored in the same page. BLOBs are allowed to span over multiple pages by dividing them into smaller chunks. For tracking these chunks, an additional fixed length metadata entry is stored called "BLOB index". Earlier formats of BLOBs are still supported (can be read and modified). However, once the BLOBs are modified, they are stored using the new format. :: @@ -405,11 +405,11 @@ For values of primitive types (currently integers from 1 to 8 bytes long), entry +-> Fixed length -- | | +---------+--------------+---------------+-------+ | +--------> | Size(4) | ChunkCount(1)| ChunkStart(1) | Rsv(2)| - Data format ---+ Blob Index +---------+--------------+---------------+-------+ + Data format ---+ BLOB Index +---------+--------------+---------------+-------+ | | +----------+---------+-----------+ +-> Variable length --> | Size (2) | Rsv (2) | CRC32 (4) | - (Strings, Blob Data) +----------+---------+-----------+ + (Strings, BLOB Data) +----------+---------+-----------+ Individual fields in entry structure have the following meanings: @@ -421,10 +421,10 @@ Type One byte indicating the value data type. See the :cpp:type:`ItemType` enumeration in :component_file:`nvs_flash/include/nvs_handle.hpp` for possible values. Span - Number of entries used by this key-value pair. For integer types, this is equal to 1. For strings and blobs, this depends on value length. + Number of entries used by this key-value pair. For integer types, this is equal to 1. For strings and BLOBs, this depends on value length. ChunkIndex - Used to store the index of a blob-data chunk for blob types. For other types, this should be ``0xff``. + Used to store the index of a BLOB-data chunk for BLOB types. For other types, this should be ``0xff``. CRC32 Checksum calculated over all the bytes in this entry, except for the CRC32 field itself. @@ -435,26 +435,26 @@ Key Data For integer types, this field contains the value itself. If the value itself is shorter than 8 bytes, it is padded to the right, with unused bytes filled with ``0xff``. - For "blob index" entry, these 8 bytes hold the following information about data-chunks: + For "BLOB index" entry, these 8 bytes hold the following information about data-chunks: - Size - (Only for blob index.) Size, in bytes, of complete blob data. + (Only for BLOB index.) Size, in bytes, of complete BLOB data. - ChunkCount - (Only for blob index.) Total number of blob-data chunks into which the blob was divided during storage. + (Only for BLOB index.) Total number of BLOB-data chunks into which the BLOB was divided during storage. - ChunkStart - (Only for blob index.) ChunkIndex of the first blob-data chunk of this blob. Subsequent chunks have chunkIndex incrementally allocated (step of 1). + (Only for BLOB index.) ChunkIndex of the first BLOB-data chunk of this BLOB. Subsequent chunks have chunkIndex incrementally allocated (step of 1). - For string and blob data chunks, these 8 bytes hold additional data about the value, which are described below: + For string and BLOB data chunks, these 8 bytes hold additional data about the value, which are described below: - Size - (Only for strings and blobs.) Size, in bytes, of actual data. For strings, this includes zero terminators. + (Only for strings and BLOBs.) Size, in bytes, of actual data. For strings, this includes zero terminators. - CRC32 - (Only for strings and blobs.) Checksum calculated over all bytes of data. + (Only for strings and BLOBs.) Checksum calculated over all bytes of data. -Variable length values (strings and blobs) are written into subsequent entries, 32 bytes per entry. The ``Span`` field of the first entry indicates how many entries are used. +Variable length values (strings and BLOBs) are written into subsequent entries, 32 bytes per entry. The ``Span`` field of the first entry indicates how many entries are used. .. _nvs_space_consumption: @@ -466,14 +466,14 @@ NVS stores every record as one or more 32-byte entries within a 4096-byte data p - Integer and floating point values use one self-contained entry; a single free entry available anywhere is enough to store them. - A string uses one metadata entry followed by ``ceil(payload_size / entry_size)`` data entries (the null terminator counts toward the payload). All of them have to be available as a contiguous run within a single page. -- A blob uses one ``BLOB_INDEX`` metadata entry plus one or more data chunks; each chunk is a metadata entry followed by its payload entries and lives on a different page. Storing a blob therefore needs ``1 + k + ceil(blob_size / entry_size)`` entries, where ``k`` is the number of pages the data is split across. +- A BLOB uses one ``BLOB_INDEX`` metadata entry plus one or more data chunks; each chunk is a metadata entry followed by its payload entries and lives on a different page. Storing a BLOB therefore needs ``1 + k + ceil(blob_size / entry_size)`` entries, where ``k`` is the number of pages the data is split across. Before setting a new key-value pair or updating an existing one, NVS looks for a page with enough free (or reclaimable) entries. The space reclaim algorithm is designed for sudden-power-off resiliency and consolidates free space on a single candidate page per call, so the largest contiguous run of available entries is determined on a per-page basis. This has two consequences: - The effective maximum string length is bound by the highest sum of free and deleted entries offered by any single page. -- A blob is split into chunks sized to the entries available on each page after reclaim, and the split continues until the whole value is stored. This lets NVS reuse pages with as few as two free entries, at the cost of one metadata entry per chunk. In the extreme case, the metadata overhead can exceed 100% of the payload size. +- A BLOB is split into chunks sized to the entries available on each page after reclaim, and the split continues until the whole value is stored. This lets NVS reuse pages with as few as two free entries, at the cost of one metadata entry per chunk. In the extreme case, the metadata overhead can exceed 100% of the payload size. -Because of this per-page behavior, the actual limits are lower than the absolute maximums listed in `Record Size Limitations`_ and get tighter as the partition fills up and fragments. The related effect on flash endurance is described in the wear levelling note under `Log of Key-Value Pairs`_. +Because of this per-page behavior, the actual limits are lower than the absolute maximums listed in `Record Size Limitations`_ and get tighter as the partition fills up and fragments. The related effect on flash endurance is described in the wear leveling note under `Log of Key-Value Pairs`_. .. note:: diff --git a/docs/zh_CN/api-reference/storage/nvs_flash.rst b/docs/zh_CN/api-reference/storage/nvs_flash.rst index 8778f64264d..fa3b9ff6eee 100644 --- a/docs/zh_CN/api-reference/storage/nvs_flash.rst +++ b/docs/zh_CN/api-reference/storage/nvs_flash.rst @@ -32,31 +32,44 @@ NVS 使用分区表中类型为 ``data``、子类型为 ``nvs`` 的分区。该 NVS 的操作对象为键值对,其中键是 ASCII 字符串,当前支持的最大键长为 15 个字符。值可以为以下几种类型: -- 整数型:``uint8_t``、``int8_t``、``uint16_t``、``int16_t``、``uint32_t``、``int32_t``、``uint64_t`` 和 ``int64_t``; -- 以 0 结尾的字符串; -- 可变长度的二进制数据 (BLOB) -- 浮点类型:``float`` 和 ``double`` +- 整数类型:``uint8_t``、``int8_t``、``uint16_t``、``int16_t``、``uint32_t``、``int32_t``、``uint64_t``、``int64_t`` +- 浮点数类型:``float`` 和 ``double`` +- 以零终止的类 C 字符串 +- 长度可变的二进制数据 (BLOB) .. note:: - NVS 最适合存储大量的小型数据值,而非存储少量的大型字符串或二进制大对象 (blob) 类型数据。如果需要存储大型 blob 或字符串,考虑使用磨损均衡库上层的 FAT 文件系统功能。 + NVS 最适合存储数量适中、相对稳定的小型数据,例如设备配置、校准数据或状态标志,而不是少量的大型 ``string`` 或 ``blob`` 数据。这里所说的“小型数据”并不意味着 NVS 适合存储持续增长或频繁重写的数据集,例如事件日志或周期性采集的测量数据。随着这类数据不断累积,分区很容易被填满并产生碎片,导致空间回收更加频繁,同时加速 flash 磨损。如果需要存储大型 blob 或字符串数据,或需要持续追加数据,建议改用 ESP-IDF 提供的文件系统。 .. note:: - 字符串值目前限制为 4000 字节(含终止符)。blob 值的限制为 508000 字节,或分区大小的 97.6% 减去 4000 字节,以两者较小值为准。 + 无论特定 SoC 是否配备 FPU,均支持浮点类型 ``float`` 和 ``double``。 + +键在其命名空间内必须唯一。向现有键写入新值会替换之前的键值对。实际数据类型由最近一次写入操作决定。 + +在读取值时,会进行数据类型检查。如果读取操作期望的数据类型与该键对应条目的数据类型不匹配,则返回错误 ``ESP_ERR_NVS_TYPE_MISMATCH``。 + +记录大小限制 +^^^^^^^^^^^^^^^^^^^^^^^ + +单个存储值的最大大小取决于其数据类型: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - 数据类型 + - 值的最大大小 + * - 整数和浮点数 + - 大小由类型决定(1 到 8 字节);始终存储在单个条目中。 + * - 字符串 + - 4000 字节,包括空字符终止符。 + * - 二进制大对象 + - 508,000 字节,或者分区大小的 97.6% 减去 4000 字节,以较小者为准。 .. note:: - 在设置新的键值对或更新现有键值对之前,NVS 页中必须有可用的空闲条目。对于整数类型,至少需要有一个空闲条目。对于字符串值,至少需要一个能够将整个字符串连续存储在空闲条目中的页面。对于 blob 值,空闲条目中需要有足够空间容纳新数据的大小。 - -.. note:: - - 无论特定 SoC 上是否存在 FPU,均支持浮点类型 ``float`` 和 ``double``。 - -键必须唯一。为现有的键写入新值时,会将旧的值及数据类型更新为写入操作指定的值和数据类型。 - -读取值时会执行数据类型检查。如果读取操作预期的数据类型与对应键的数据类型不匹配,则返回错误。 - + 上文提到的字符串和 blob 大小限制是数据分区为空(无碎片)的情况下能达到的理论上限。实际运行时可存储的最大数据大小通常会更小,具体取决于分区的碎片程度。参见 :ref:`nvs_space_consumption`,了解 NVS 如何分配条目以及碎片为何会影响可用存储空间。 命名空间 ^^^^^^^^ @@ -71,7 +84,7 @@ open mode 参数控制访问级别和安全行为: .. note:: - 在不同的 NVS 分区中,同名的的命名空间被视为相互独立的命名空间。 + 在不同的 NVS 分区中,同名的命名空间被视为相互独立的命名空间。 NVS 迭代器 ^^^^^^^^^^^^^ @@ -218,7 +231,7 @@ NVS 分区生成程序帮助生成 NVS 分区二进制文件,可使用烧录 * - 参数 - 描述 * - ``FLASH_IN_PROJECT`` - - NVS 分区名 + - 将生成的镜像与工程一并烧录 * - ``DEPENDS`` - 指定命令依赖的文件 @@ -261,6 +274,8 @@ ESP-IDF :example:`storage/nvs` 目录下提供了数个代码示例: 在写入数据前后分别获取使用情况统计信息,并将两者的差异与新占用条目的预期值进行比较。 + 本示例的第二部分展示了 NVS 分区碎片化对 blob 存储开销的影响。 + :example:`storage/nvs/nvs_iteration` 该示例演示了如何遍历特定(或任意)NVS 数据类型的条目,以及如何获取这些条目的相关信息。 @@ -281,7 +296,7 @@ NVS 按顺序存储键值对,新的键值对添加在最后。因此,如需 .. note:: - NVS 组件在设计上内置了 flash 磨损均衡功能。写入操作会将新数据追加到现有条目之后的空闲空间中,而将旧值标记为无效时,并不需要立即执行 flash 擦除操作。通过将 NVS 空间划分为页面和条目的结构,对于占用单个条目的数据类型,flash 擦除与写入操作的频率比可以有效降低为原来的 1/126。 + NVS 组件在设计上包含 flash 磨损均衡。执行写入操作时,新数据会追加写入现有条目之后的空闲空间,而旧数据失效后不会立即触发 flash 擦除操作。NVS 将存储空间组织为页和条目,从而降低了 flash 擦除操作的频率。对于可存储在单个条目中的数据类型,在理想情况下(每次擦除对应一整页均为单条目写入),flash 擦除与写入操作的频率比可以有效降低为原来的 1/126。实际情况下,这一比例通常会更低,且主要取决于分区的使用率:随着有效数据增长,NVS 会更频繁地执行空间回收,从而导致擦除与写入次数之比增大。此外,对于较大且从未被覆盖的数据块,它们可能会长期占用同一个 NVS 页。由于空间回收仅会选择包含已擦除条目的页,这类页面不会参与擦除循环,因此会减少参与磨损均衡的 flash 空间比例。 页面和条目 ^^^^^^^^^^^^^^^^^ @@ -442,6 +457,29 @@ CRC32 可变长度值(字符串和 BLOB)写入后续条目,每个条目 32 字节。第一个条目的 ``Span`` 字段将指明使用了多少条目。 +.. _nvs_space_consumption: + +空间占用 +^^^^^^^^^^^^^^^^^ + +NVS 将每条记录存储为一个或多个 32 字节的条目,这些条目位于 4096 字节的数据页中。每个数据页包含 126 个可用条目。一个值需要占用多少个条目,以及存储该值所需的空闲条目数,取决于其数据类型: + +- 整数和浮点数只占用一个独立条目;只要任意位置存在一个空闲条目即可存储。 +- 字符串占用一个元数据条目,随后占用 ``ceil(payload_size / entry_size)`` 个数据条目(有效载荷包含字符串结尾的空字符)。这些条目必须在同一个数据页内连续分配。 +- blob 类型占用一个 ``BLOB_INDEX`` 元数据条目以及一个或多个数据块。每个数据块由一个元数据条目和若干数据条目组成,并存储在不同的数据页中。因此,存储一个 blob 共需要 ``1 + k + ceil(blob_size / entry_size)`` 个条目,其中 ``k`` 表示数据被拆分后的页数。 + +在写入新的键值对或更新现有键值对之前,NVS 会查找一个具有足够空闲条目(或可通过空间回收获得足够可用条目)的数据页。空间回收算法为应对突然断电而设计,每次调用仅对一个候选页进行空间整理。因此,可连续分配的最大条目数始终是在单个数据页内决定的。这会带来两个影响: + +- 字符串的实际最大长度受限于单个数据页中空闲条目与已删除条目数量之和的最大值。 +- blob 会根据空间回收后各数据页中可用条目的数量拆分为多个数据块,并持续拆分直至整个值存储完成。这使得 NVS 即使在某个数据页仅剩 2 个空闲条目时也能继续利用该页进行存储,但每个数据块都需要额外占用一个元数据条目。在极端情况下,元数据的开销甚至可能超过有效负载大小的 100%。 + +由于上述按页分配的特性,实际可存储的数据大小通常低于 `记录大小限制`_ 中给出的理论最大值,并且随着分区逐渐填满和碎片不断增加,可存储的数据大小会进一步减小。碎片对 flash 寿命的影响请参阅 `键值对日志`_ 中关于磨损均衡的说明。 + +.. note:: + + 由于在现场已部署的设备上调整 NVS 分区大小较为困难,应将其初始大小设得足够大,以容纳当前需求以及键或其数据的潜在增长。还建议运行足够数量的测试,以真实反映写入和更新 NVS 键的频率。在测试软件更新之前(例如,通过 OTA),先在已被上一版本软件碎片化的数据分区上运行这些测试。 + + 命名空间 ^^^^^^^^^^ @@ -490,7 +528,7 @@ NVS 正常运行所需的默认最小空间为 12 KiB (``0x3000``),即至少 在构建时,可以配置 NVS 访问其底层存储的模式。menuconfig 选项 :ref:`CONFIG_NVS_BDL_STACK` 提供了两种模式。 -**ESP 分区 API(默认)**: NVS 使用 :ref:`esp_partition ` 访问存储。这是默认运行模式,其中 NVS 使用由分区表定义的 SPI flash 分区。在此模式下: +**ESP 分区 API(默认)**:NVS 使用 :ref:`esp_partition ` 访问存储。这是默认运行模式,其中 NVS 使用由分区表定义的 SPI flash 分区。在此模式下: - 初始化函数 (:cpp:func:`nvs_flash_init`, :cpp:func:`nvs_flash_init_partition`) 会查找该分区,并使用 :ref:`esp_partition ` API 访问它。 - 应用程序可以提供自定义的 ``esp_partition_t`` 指针并调用 :cpp:func:`nvs_flash_init_partition_ptr`。这使应用程序能够克服基于分区表的分区所带来的限制,例如使用分区表中未定义的分区。