docs: Provide translation for fatfs docs

This commit is contained in:
Zhang Shuxian
2026-03-26 12:01:47 +08:00
committed by Tomáš Rohlínek
parent f44bd4546f
commit bd97dbb85c
3 changed files with 516 additions and 229 deletions

View File

@@ -9,7 +9,7 @@ ESP-IDF uses the `FatFs <http://elm-chan.org/fsw/ff/00index_e.html>`_ library to
FatFs is an ambiguous term, as it refers to the filesystem itself, the upstream library, as well as the component in ESP-IDF. For clarity, this documentation uses the following terms:
- **FAT filesystem**: the on-disk FAT format (FAT12/FAT16/FAT32); exFAT (see :ref:`Optional exFAT Support <fatfs-optional-exfat-support>`).
- **FAT filesystem**: the on-disk FAT format (FAT12/FAT16/FAT32), exFAT (see :ref:`Optional exFAT Support <fatfs-optional-exfat-support>`).
- **FatFs library**: the upstream `FatFs library <http://elm-chan.org/fsw/ff/00index_e.html>`_ used by ESP-IDF.
- **FatFs component**: the ESP-IDF integration layer around the FatFs library (mount helpers, wrappers, and related APIs) in :component:`fatfs`.
@@ -27,9 +27,9 @@ The general mounting workflow is:
#. Select a mount path (for example, ``"/spiflash"`` or ``"/sdcard"``) and set ``esp_vfs_fat_mount_config_t``.
#. Mount the volume with a convenience helper from :component_file:`fatfs/vfs/esp_vfs_fat.h`:
- :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` for SPI flash partitions with wear leveling
- :cpp:func:`esp_vfs_fat_spiflash_mount_ro` for read-only SPI flash partitions **without** wear leveling
- :cpp:func:`esp_vfs_fat_sdmmc_mount` or :cpp:func:`esp_vfs_fat_sdspi_mount` for SD cards
- :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` for SPI flash partitions with wear leveling.
- :cpp:func:`esp_vfs_fat_spiflash_mount_ro` for read-only SPI flash partitions **without** wear leveling.
- :cpp:func:`esp_vfs_fat_sdmmc_mount` or :cpp:func:`esp_vfs_fat_sdspi_mount` for SD cards.
#. Use standard file APIs (``stdio.h`` or POSIX) on paths under the mount path.
#. Close open files and call the matching unmount helper.
@@ -66,13 +66,13 @@ These options control how the FatFs library calculates and reports free space:
Differences from the POSIX Standard
-----------------------------------
#. :cpp:func:`link`: FAT filesystems do not support hard links, therefore :cpp:func:`link` copies file contents instead. (This applies only to files on FAT filesystem volumes.)
#. :cpp:func:`link`: FAT filesystems do not support hard links, therefore :cpp:func:`link` copies file contents instead, which applies only to files on FAT filesystem volumes.
#. :cpp:func:`unlink`: If ``CONFIG_FATFS_FS_LOCK`` is enabled, removing an open file fails with ``EBUSY``. Otherwise, behavior is undefined and can corrupt the filesystem.
#. ``.`` and ``..`` are not supported as references to the current and parent directory.
.. _fatfs-formatting:
Formatting a FAT Filesystem
Format a FAT Filesystem
---------------------------
Formatting creates a new FAT filesystem on the target volume and erases the existing directory structure and file data on that volume. Use it for first-time provisioning, or to recover from ``FR_NO_FILESYSTEM``.
@@ -84,8 +84,8 @@ For mount-time auto-formatting, set ``esp_vfs_fat_mount_config_t.format_if_mount
For explicit formatting after initialization, use the helper that matches the storage type:
* :cpp:func:`esp_vfs_fat_spiflash_format_rw_wl` or :cpp:func:`esp_vfs_fat_spiflash_format_cfg_rw_wl` for SPI flash partitions with wear leveling
* :cpp:func:`esp_vfs_fat_sdcard_format` or :cpp:func:`esp_vfs_fat_sdcard_format_cfg` for SD cards
* :cpp:func:`esp_vfs_fat_spiflash_format_rw_wl` or :cpp:func:`esp_vfs_fat_spiflash_format_cfg_rw_wl` for SPI flash partitions with wear leveling.
* :cpp:func:`esp_vfs_fat_sdcard_format` or :cpp:func:`esp_vfs_fat_sdcard_format_cfg` for SD cards.
The SPI flash formatting helpers can be called whether the filesystem is currently mounted or not. The SD card formatting helpers require the card (but not the filesystem) to be mounted first.
@@ -109,11 +109,15 @@ An in-depth description of the FatFs partition generator and analyzer can be fou
Build System Integration with FatFs Partition Generator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Invoke the FatFs generator directly from the CMake build system by calling ``fatfs_create_spiflash_image``::
Invoke the FatFs generator directly from the CMake build system by calling ``fatfs_create_spiflash_image``:
.. code-block:: none
fatfs_create_spiflash_image(<partition> <base_dir> [optional arguments])
To generate a **read-only** partition without wear leveling support, use ``fatfs_create_rawflash_image``::
To generate a **read-only** partition without wear leveling support, use ``fatfs_create_rawflash_image``:
.. code-block:: none
fatfs_create_rawflash_image(<partition> <base_dir> [optional arguments])
@@ -130,7 +134,9 @@ The function arguments are:
#. flag ``ONE_FAT`` - Optionally generate a FAT filesystem volume with one FAT (File Allocation Table) instead of two. This increases free space in the FAT filesystem volume by ``number of logical sectors used by one FAT * logical sector size``, but it also increases corruption risk.
For example::
For example:
.. code-block:: none
fatfs_create_spiflash_image(my_fatfs_partition my_folder FLASH_IN_PROJECT)
@@ -145,7 +151,9 @@ FatFs Partition Parser
The :component_file:`fatfsparse.py <fatfs/fatfsparse.py>` tool performs the reverse operation of :component_file:`fatfsgen.py <fatfs/fatfsgen.py>`. It reconstructs a host folder structure from a FAT filesystem image.
Usage::
Usage
.. code-block:: none
./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] [--verbose] fatfs_image.img
@@ -157,6 +165,7 @@ Size Requirements and Limits
----------------------------
The FatFs component supports FAT12, FAT16, and FAT32 filesystem types, and can optionally support exFAT (see :ref:`Optional exFAT Support <fatfs-optional-exfat-support>`).
The filesystem type depends on the number of clusters on the volume:
.. math::
@@ -174,7 +183,7 @@ The required size of a raw FAT filesystem consists of the FAT filesystem metadat
.. math::
fatfs_size = (reserved + fats + root\_dir + data) * logical\_sector\_size
fatfs\_size = (reserved + fats + root\_dir + data) × logical\_sector\_size
Where:
@@ -194,22 +203,22 @@ The FAT area depends on the selected FAT type and the number of clusters:
- FAT Sizing
* - FAT12
- ``<= 4085``
- Each FAT entry uses 12 bits, so one FAT copy requires ``ceil(((cluster_count + 2) * 3 / 2) / logical_sector_size)`` logical sectors.
- Each FAT entry uses 12 bits, so one FAT copy requires ``ceil(((cluster_count + 2) × 3 / 2) / logical_sector_size)`` logical sectors.
* - FAT16
- ``4085 < cluster_count < 65526``
- Each FAT entry uses 16 bits, so one FAT copy requires ``ceil(((cluster_count + 2) * 2) / logical_sector_size)`` logical sectors.
- Each FAT entry uses 16 bits, so one FAT copy requires ``ceil(((cluster_count + 2) × 2) / logical_sector_size)`` logical sectors.
* - FAT32
- ``>= 65526``
- Each FAT entry uses 32 bits, so one FAT copy requires ``ceil(((cluster_count + 2) * 4) / logical_sector_size)`` logical sectors.
- Each FAT entry uses 32 bits, so one FAT copy requires ``ceil(((cluster_count + 2) × 4) / logical_sector_size)`` logical sectors.
* - exFAT
- ``16 .. 0x7FFFFFFD``
- Each FAT entry uses 32 bits, so one FAT copy requires ``ceil(((cluster_count + 2) * 4) / logical_sector_size)`` logical sectors.
- Each FAT entry uses 32 bits, so one FAT copy requires ``ceil(((cluster_count + 2) × 4) / logical_sector_size)`` logical sectors.
For FAT12 and FAT16, the root directory is outside the data area, so:
.. math::
root\_dir = ceil((root\_dir\_entries * 32) / logical\_sector\_size)
root\_dir = ceil((root\_dir\_entries × 32) / logical\_sector\_size)
For FAT32, the root directory is stored in the data area, so ``root_dir = 0`` in the formula above.
@@ -235,7 +244,7 @@ For a flash partition with wear leveling, the total size is the raw FAT filesyst
.. math::
partition_size = wl_metadata_flash_sectors * flash_sector_size + fatfs_size
partition\_size = wl\_metadata\_flash\_sectors × flash\_sector\_size + fatfs\_size
Where:
@@ -265,21 +274,22 @@ The minimum supported partition size depends on the WL configuration:
For more details, see :doc:`File System Considerations <../../api-guides/file-system-considerations>`.
.. note::
The wear leveling layer protects the underlying flash by distributing write and erase cycles across the managed storage region. Its effectiveness therefore scales with the size of that region. Small partitions restrict wear distribution and can significantly reduce protection, so their use is strongly discouraged. For more information, refer to :doc:`wear leveling <wear-levelling>`.
Optimizing
----------
Optimization
------------
FatFs behavior can be tuned for different priorities by choosing the right configuration options and filesystem layout.
The main variables that affect behavior are:
* Buffer placement and sizing (:ref:`CONFIG_FATFS_ALLOC_PREFER_ALIGNED_WORK_BUFFERS`, :ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM`, :ref:`CONFIG_FATFS_USE_DYN_BUFFERS`, :ref:`CONFIG_FATFS_PER_FILE_CACHE`, and application I/O buffer sizes)
* Wear leveling logical sector size and mode (``CONFIG_WL_SECTOR_SIZE_*`` and ``CONFIG_WL_SECTOR_MODE_*``)
* Sync strategy (:ref:`CONFIG_FATFS_IMMEDIATE_FSYNC`)
* Workload pattern (transaction sizes, sequential vs random access, read vs write ratio)
* Buffer placement and sizing (:ref:`CONFIG_FATFS_ALLOC_PREFER_ALIGNED_WORK_BUFFERS`, :ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM`, :ref:`CONFIG_FATFS_USE_DYN_BUFFERS`, :ref:`CONFIG_FATFS_PER_FILE_CACHE`, and application I/O buffer sizes).
* Wear leveling logical sector size and mode (``CONFIG_WL_SECTOR_SIZE_*`` and ``CONFIG_WL_SECTOR_MODE_*``).
* Sync strategy (:ref:`CONFIG_FATFS_IMMEDIATE_FSYNC`).
* Workload pattern (transaction sizes, sequential vs random access, read vs write ratio).
Optimizing for I/O Performance
Optimize I/O Performance
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For throughput-oriented workloads:
@@ -295,9 +305,10 @@ For throughput-oriented workloads:
* Enable :ref:`CONFIG_FATFS_USE_FASTSEEK` for read-heavy workloads with long backward seeks.
.. note::
For a baseline of what to expect in a performance-optimized scenario, see :example:`storage/perf_benchmark`.
Optimizing for memory usage
Optimize Memory Usage
^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Disable :ref:`CONFIG_FATFS_PER_FILE_CACHE` to use a single shared cache when reducing RAM usage is the priority.
@@ -309,8 +320,8 @@ Optimizing for memory usage
* If long filenames are required, reduce ``CONFIG_FATFS_MAX_LFN`` to the smallest value that meets your needs.
* Enable :ref:`CONFIG_FATFS_USE_DYN_BUFFERS` so each mounted volume uses buffers sized to its actual sector size.
Optimizing for storage efficiency
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Optimize Storage Efficiency
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For maximizing usable filesystem capacity:
@@ -320,7 +331,7 @@ For maximizing usable filesystem capacity:
* Keep long filenames short where possible; long names consume additional directory entries.
* For read-only data, consider raw FAT partitions without wear leveling where appropriate.
Advanced operations
Advanced Operations
-------------------
Manual mounting may be required when you use storage media that ESP-IDF does not support directly or when you need access to low-level FatFs library functions.
@@ -341,7 +352,7 @@ Architecture Overview
The following diagram illustrates the layered architecture of FatFs and its relationship with other ESP-IDF components:
::
.. code-block:: none
+---------------------------------------------------------------+
| Newlib/Picolib (C Standard Library) |
@@ -367,17 +378,16 @@ The following diagram illustrates the layered architecture of FatFs and its rela
.. _fatfs-manual-mount:
Manually Mounting a FAT Filesystem Partition
Manually Mount a FAT Filesystem Partition
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When mounting a FAT filesystem partition manually, follow the same general flow used by the convenience wrappers described in :ref:`Mount and Use FatFs <fatfs-mount-and-use>`: register the filesystem with the FatFs library, then mount it through the VFS.
#.
Register the filesystem with the VFS by calling :cpp:func:`esp_vfs_fat_register`. This allocates the ``FATFS`` structure and sets up the interface between the VFS and the FatFs library. Specify:
#. Register the filesystem with the VFS by calling :cpp:func:`esp_vfs_fat_register`. This allocates the ``FATFS`` structure and sets up the interface between the VFS and the FatFs library. Specify:
- The path prefix where the filesystem will be mounted (for example, ``"/sdcard"`` or ``"/spiflash"``)
- The FatFs drive number
- A variable that receives a pointer to the ``FATFS`` structure
- The path prefix where the filesystem will be mounted (for example, ``"/sdcard"`` or ``"/spiflash"``).
- The FatFs drive number.
- A variable that receives a pointer to the ``FATFS`` structure.
#. Register the disk I/O driver for the drive number used in Step 1 by calling :cpp:func:`ff_diskio_register`.
@@ -408,9 +418,9 @@ The disk I/O layer uses :cpp:struct:`ff_diskio_impl_t` to provide medium access
ESP-IDF provides convenience wrappers for the supported storage media. These include:
- :cpp:func:`ff_diskio_register_wl_partition` for SPI flash partitions with wear leveling
- :cpp:func:`ff_diskio_register_raw_partition` for read-only SPI flash partitions **without** wear leveling
- :cpp:func:`ff_diskio_register_sdmmc` for both SDMMC and SPI connected SD cards
- :cpp:func:`ff_diskio_register_wl_partition` for SPI flash partitions with wear leveling.
- :cpp:func:`ff_diskio_register_raw_partition` for read-only SPI flash partitions **without** wear leveling.
- :cpp:func:`ff_diskio_register_sdmmc` for both SDMMC and SPI connected SD cards.
For API reference, see :ref:`Disk I/O API Reference <fatfs-diskio-api-reference>`.

View File

@@ -101,8 +101,7 @@ FatFS 是最适用于常见应用的文件系统,如文件/目录访问、数
- `FatFS 源站点 <http://elm-chan.org/fsw/ff/>`_
- 更多关于 `FAT 表大小限制 <https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Size_limits>`_
- :ref:`using-fatfs-with-vfs`
- :ref:`using-fatfs-with-vfs-and-sdcards`
- :ref:`fatfs-mount-and-use`
- ESP-IDF FatFS 工具: :ref:`FatFS 分区生成器 <fatfs-partition-generator>`:ref:`FatFS 分区分析器 <fatfs-partition-analyzer>`
**示例:**

View File

@@ -3,96 +3,481 @@ FAT 文件系统
:link_to_translation:`en:[English]`
ESP-IDF 使用 `FatFs <http://elm-chan.org/fsw/ff/00index_e.html>`_ 库来实现 FAT 文件系统。FatFs 库位于 ``fatfs`` 组件中,支持直接使用,也可以借助 C 标准库和 POSIX API 通过 VFS虚拟文件系统使用 FatFs 库的大多数功能
此外,我们对 FatFs 库进行了扩展,新增了支持可插拔磁盘 I/O 调度层,从而允许在运行时将 FatFs 驱动映射到物理磁盘。
.. _using-fatfs-with-vfs:
FatFs 与 VFS 配合使用
----------------------------
头文件 :component_file:`fatfs/vfs/esp_vfs_fat.h` 定义了连接 FatFs 和 VFS 的函数。
函数 :cpp:func:`esp_vfs_fat_register` 分配一个 ``FATFS`` 结构,并在 VFS 中注册特定路径前缀。如果文件路径以此前缀开头,则对此文件的后续操作将转至 FatFs API。
函数 :cpp:func:`esp_vfs_fat_unregister_path` 删除在 VFS 中的注册,并释放 ``FATFS`` 结构。
多数应用程序在使用 ``esp_vfs_fat_`` 函数时,采用如下步骤:
#.
调用 :cpp:func:`esp_vfs_fat_register`,指定:
- 挂载文件系统的路径前缀(例如,``"/sdcard"````"/spiflash"``
- FatFs 驱动编号
- 一个用于接收指向 ``FATFS`` 结构指针的变量
#. 调用 :cpp:func:`ff_diskio_register`,为步骤 1 中的驱动编号注册磁盘 I/O 驱动;
#. 如需使用与传递到 :cpp:func:`esp_vfs_fat_register` 相同的驱动编号挂载文件系统,可调用 FatFs 函数 :cpp:func:`f_mount`。如果目标逻辑驱动上不存在该文件系统,:cpp:func:`f_mount` 将调用失败并报告 ``FR_NO_FILESYSTEM`` 错误。此时,应首先调用 :cpp:func:`f_mkfs`,在驱动上创建新的 FatFS 结构体,然后重新调用 :cpp:func:`f_mount`。注意,应在上述步骤之前调用 :cpp:func:`f_fdisk` 对 SD 卡进行分区。请参考 `FatFs 文档 <http://elm-chan.org/fsw/ff/doc/mount.html>`_,查看更多信息;
#. 调用 C 标准库和 POSIX API 对路径中带有步骤 1 中所述前缀的文件(例如,``"/sdcard/hello.txt"``)执行打开、读取、写入、擦除、复制等操作。文件系统默认使用 `8.3 文件名 <https://en.wikipedia.org/wiki/8.3_filename>`_ 格式 (SFN)。如需使用长文件名 (LFN),启用 :ref:`CONFIG_FATFS_LONG_FILENAMES` 选项。请参考 `FatFs 文件系统 <http://elm-chan.org/fsw/ff/doc/filename.html>`_查看更多信息
#. 可以直接调用 FatFs 库函数,但需要使用没有 VFS 前缀的路径,如 ``"/hello.txt"``
#. 关闭所有打开的文件;
#. 调用 FatFs 函数 :cpp:func:`f_mount` 并使用 NULL ``FATFS*`` 参数,为与上述编号相同的驱动卸载文件系统;
#. 调用 FatFs 函数 :cpp:func:`ff_diskio_register` 并使用 NULL ``ff_diskio_impl_t*`` 参数和相同的驱动编号,来释放注册的磁盘 I/O 驱动;
#. 调用 :cpp:func:`esp_vfs_fat_unregister_path` 并使用文件系统挂载的路径将 FatFs 从 VFS 中移除,并释放步骤 1 中分配的 ``FATFS`` 结构。
便捷函数 :cpp:func:`esp_vfs_fat_sdmmc_mount`:cpp:func:`esp_vfs_fat_sdspi_mount`:cpp:func:`esp_vfs_fat_sdcard_unmount` 对上述步骤进行了封装,并加入了对 SD 卡初始化的处理。我们将在下一章节详细介绍以上函数。
与 POSIX 标准的差异
---------------------
#. :cpp:func:`link`:由于 FAT 文件系统不支持硬链接,调用 :cpp:func:`link` 后会复制文件内容(仅适用于 FatFs 卷上的文件)。
#. :cpp:func:`unlink`:当尝试删除已打开的文件时,如果启用了 ``CONFIG_FATFS_FS_LOCK``,操作将失败并返回 ``EBUSY``。如果未启用,则行为未定义(可能导致文件系统损坏)。
.. _using-fatfs-with-vfs-and-sdcards:
FatFs 与 VFS 和 SD 卡配合使用
---------------------------------
头文件 :component_file:`fatfs/vfs/esp_vfs_fat.h` 定义了便捷函数 :cpp:func:`esp_vfs_fat_sdmmc_mount`:cpp:func:`esp_vfs_fat_sdspi_mount`:cpp:func:`esp_vfs_fat_sdcard_unmount`。这些函数分别执行上一章节的步骤 1-3 和步骤 7-9并初始化 SD 卡,但仅提供有限的错误处理功能。我们鼓励开发人员查看源代码,将更多高级功能集成到产品应用中。
便捷函数 :cpp:func:`esp_vfs_fat_sdmmc_unmount` 用于卸载文件系统并释放从 :cpp:func:`esp_vfs_fat_sdmmc_mount` 函数获取的资源。
FatFs 与 VFS 配合使用(只读模式下)
--------------------------------------
头文件 :component_file:`fatfs/vfs/esp_vfs_fat.h` 也定义了两个便捷函数 :cpp:func:`esp_vfs_fat_spiflash_mount_ro`:cpp:func:`esp_vfs_fat_spiflash_unmount_ro`。上述两个函数分别对 FAT 只读分区执行步骤 1-3 和步骤 7-9。有些数据分区仅在工厂配置时写入一次之后在整个硬件生命周期内都不会再有任何改动。利用上述两个函数处理这种数据分区非常方便。
配置选项
--------
FatFs 组件有以下配置选项:
* :ref:`CONFIG_FATFS_USE_FASTSEEK` - 如果启用该选项POSIX :cpp:func:`lseek` 函数将以更快的速度执行。快速查找不适用于编辑模式下的文件,所以,使用快速查找时,应在只读模式下打开(或者关闭然后重新打开)文件。
* :ref:`CONFIG_FATFS_IMMEDIATE_FSYNC` - 如果启用该选项FatFs 将在每次调用 :cpp:func:`write`:cpp:func:`pwrite`:cpp:func:`link`:cpp:func:`truncate`:cpp:func:`ftruncate` 函数后,自动调用 :cpp:func:`f_sync` 以同步最近的文件改动。该功能提升了 FatFs 的文件一致性和文件大小报告的准确性,但频繁的磁盘操作会降低性能。
* :ref:`CONFIG_FATFS_LINK_LOCK` - 如果启用该选项,可保证 API 的线程安全,但如果应用程序需要快速频繁地进行小文件操作(例如将日志记录到文件),则可能有必要禁用该选项。请注意,如果禁用该选项,调用 :cpp:func:`link` 后的复制操作将是非原子的,此时如果在不同任务中对同一卷上的大文件调用 :cpp:func:`link`,则无法确保线程安全。
以下选项用于设置 FatFs 文件系统计算和报告空闲空间的策略:
* :ref:`CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT` 如果设置为 1将忽略空闲簇计数。默认值为 0。
* :ref:`CONFIG_FATFS_DONT_TRUST_LAST_ALLOC` 如果设置为 1将忽略上次分配的簇号。默认值为 0。
ESP-IDF 通过 FatFs 组件使用 `FatFs <http://elm-chan.org/fsw/ff/00index_e.html>`_ 库来实现 FAT 文件系统。挂载 FAT 文件系统卷后VFS 层会暴露标准 C 库和 POSIX API
.. note::
将这两个选项设为 1 可能会提高 :cpp:func:`f_getfree` 输出的准确性,但性能会下降,例如,可能需要执行完整的 FAT 扫描。
FatFs 可用于指代多个概念,如文件系统本身、上游库,或 ESP-IDF 组件。为清晰起见,本文档使用以下术语:
- **FAT 文件系统**:磁盘上的 FAT 格式 (FAT12/FAT16/FAT32)exFAT参见 :ref:`可选的 exFAT 支持 <fatfs-optional-exfat-support>`)。
- **FatFs 库**ESP-IDF 使用的上游 `FatFs 库 <http://elm-chan.org/fsw/ff/00index_e.html>`_
- **FatFs 组件**ESP-IDF 中围绕 FatFs 库的集成层(挂载辅助函数、封装器和相关 API位于 :component:`fatfs`
本文档相关的其他术语在 :ref:`术语表 <fatfs-glossary>` 中进行了解释。
.. _fatfs-mount-and-use:
挂载和使用 FatFs
----------------
FatFs 组件提供了便利的封装函数,用于通过 VFS 层挂载文件系统。在大多数用例中,应使用这些封装函数,而不是手动挂载文件系统。有关手动设置的说明,请参考 :ref:`手动挂载 FAT 文件系统分区 <fatfs-manual-mount>`
一般的挂载流程是:
#. 选择挂载路径(例如 ``"/spiflash"````"/sdcard"``),并设置 ``esp_vfs_fat_mount_config_t``
#. 使用 :component_file:`fatfs/vfs/esp_vfs_fat.h` 中的便利辅助函数挂载卷:
- :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` 用于带磨损均衡的 SPI flash 分区。
- :cpp:func:`esp_vfs_fat_spiflash_mount_ro` 用于 **不带** 磨损均衡的只读 SPI flash 分区。
- :cpp:func:`esp_vfs_fat_sdmmc_mount`:cpp:func:`esp_vfs_fat_sdspi_mount` 用于 SD 卡。
#. 在挂载路径下的路径上使用标准文件 API``stdio.h`` 或 POSIX
#. 关闭打开的文件,并调用相应的卸载辅助函数。
.. _fatfs-configuration-options:
配置选项
--------------------
FatFs 组件提供以下配置选项:
* ``CONFIG_FATFS_LONG_FILENAMES`` - 选择 FatFs 库如何处理长文件名 (LFN) 支持。可用选项包括 :ref:`CONFIG_FATFS_LFN_NONE <CONFIG_FATFS_LFN_NONE>` 以禁用 LFN 支持并将名称限制为 `8.3 格式 <https://en.wikipedia.org/wiki/8.3_filename>`_ (仅 SFN :ref:`CONFIG_FATFS_LFN_HEAP <CONFIG_FATFS_LFN_HEAP>` 以启用 LFN 支持并将 LFN 工作缓冲区存储在堆上(默认),以及 :ref:`CONFIG_FATFS_LFN_STACK <CONFIG_FATFS_LFN_STACK>` 以启用 LFN 支持并将 LFN 工作缓冲区存储在栈上。详细信息请参阅 `FatFs 文件名 <http://elm-chan.org/fsw/ff/doc/filename.html>`_
* :ref:`CONFIG_FATFS_VOLUME_COUNT` - 设置逻辑 FatFs 卷的数量。增加此值可能会增加基础内存使用量。
* :ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM` - 如果启用FatFs 库在分配内部缓冲区时优先使用外部 RAM。如果外部 RAM 分配失败,则回退到内部 RAM。这可能会对热 I/O 路径产生明显的性能开销。禁用此选项可优先考虑性能;启用可减少内部 RAM 使用量。
* :ref:`CONFIG_FATFS_ALLOC_PREFER_ALIGNED_WORK_BUFFERS` - 如果启用FatFs 库首先尝试在支持 DMA、缓存对齐的内存中分配堆工作缓冲区以便 SDMMC 传输避免额外的拷贝。此选项在使用 PSRAM 和 SDMMC DMA 的目标芯片上(例如 ESP32-P4非常有用。如果同时启用此选项和 :ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM`FatFs 库会先尝试支持 DMA 的 RAM然后是外部 RAM最后是内部 RAM。
* :ref:`CONFIG_FATFS_USE_DYN_BUFFERS` - 如果启用FatFs 库会单独分配实例缓冲区,并根据每个已挂载卷的逻辑扇区大小调整其大小。当多个 FatFs 实例使用不同的逻辑扇区大小时,此选项非常有用,因为它可以减少内存使用量。如果禁用,所有实例都使用为最大配置逻辑扇区大小调整大小的缓冲区。
* :ref:`CONFIG_FATFS_PER_FILE_CACHE` - 如果启用,每个打开的文件使用单独的缓存缓冲区。这提高了 I/O 性能,但当多个文件打开时会增加 RAM 使用量。如果禁用,则使用单个共享缓存,这减少了 RAM 使用量,但可能会增加存储读写操作。
* :ref:`CONFIG_FATFS_USE_FASTSEEK` - 如果启用POSIX :cpp:func:`lseek` 运行更快。快速定位不适用于以写入模式打开的文件。要使用快速查找,请以只读模式打开文件,或关闭后以只读模式重新打开。
* :ref:`CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE` - 设置当 :ref:`CONFIG_FATFS_USE_FASTSEEK` 启用时快速查找使用的 CLMT簇链接映射表缓冲区大小。较大的缓冲区可以改善较大文件上的查找行为但会使用更多 RAM。
* :ref:`CONFIG_FATFS_VFS_FSTAT_BLKSIZE` - 设置通过 VFS 使用的默认 stdio 文件缓冲区块大小。此选项主要与基于 stdio 的 I/O例如 ``fread``/``fgets``)相关,不是直接 POSIX ``read``/``write`` 路径的主要调整参数。较大的值可以提高缓冲读取吞吐量,但会增加堆使用量。
* :ref:`CONFIG_FATFS_IMMEDIATE_FSYNC` - 如果启用FatFs 库会在每次调用 :cpp:func:`write`:cpp:func:`pwrite`:cpp:func:`link`:cpp:func:`truncate`:cpp:func:`ftruncate` 后自动调用 :cpp:func:`f_sync`。此选项提高了文件一致性和大小报告的准确性,但由于会触发频繁的磁盘操作而降低了性能。
* :ref:`CONFIG_FATFS_LINK_LOCK` - 如果启用,此选项保证 :cpp:func:`link` 函数的 API 线程安全。禁用此选项可以帮助执行频繁小文件操作(例如文件日志记录)的应用程序。禁用时,:cpp:func:`link` 执行的拷贝是非原子的。在这种情况下,从另一个任务在同一卷上对大文件使用 :cpp:func:`link` 不保证线程安全。
* 其他相关选项包括 :ref:`CONFIG_FATFS_FS_LOCK`:ref:`CONFIG_FATFS_TIMEOUT_MS```CONFIG_FATFS_CHOOSE_CODEPAGE`` (特别是 ``CONFIG_FATFS_CODEPAGE_DYNAMIC`` 对代码大小的影响)。其他选项包括 ``CONFIG_FATFS_SECTOR_SIZE````CONFIG_FATFS_MAX_LFN````CONFIG_FATFS_API_ENCODING````CONFIG_FATFS_USE_STRFUNC_CHOICE``
这些选项控制 FatFs 库如何计算和报告可用空间:
* :ref:`CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT` - 如果设置为 1FatFs 库忽略空闲簇计数。默认值为 0。
* :ref:`CONFIG_FATFS_DONT_TRUST_LAST_ALLOC` - 如果设置为 1FatFs 库忽略上次分配编号。默认值为 0。
.. note::
将这些选项设置为 1 可以提高 :cpp:func:`f_getfree` 的准确性,但会降低性能,例如强制进行完整的 FAT 扫描。
与 POSIX 标准的差异
-----------------------------------
#. :cpp:func:`link`FAT 文件系统不支持硬链接,因此 :cpp:func:`link` 会复制文件内容(这仅适用于 FAT 文件系统卷上的文件)。
#. :cpp:func:`unlink`:如果启用 ``CONFIG_FATFS_FS_LOCK``,删除打开的文件会失败并返回 ``EBUSY``。否则,行为未定义,可能会损坏文件系统。
#. 不支持将 ``.````..`` 作为当前目录和上级目录的引用。
.. _fatfs-formatting:
格式化 FAT 文件系统
---------------------------
格式化会在目标卷上创建一个新的 FAT 文件系统,并擦除该卷上现有的目录结构和文件数据。用于首次配置,或从 ``FR_NO_FILESYSTEM`` 中恢复。
对于挂载时自动格式化,请在调用挂载辅助函数之前将 ``esp_vfs_fat_mount_config_t.format_if_mount_failed`` 设置为 ``true``。当卷在首次启动时预期为空白时,这非常有用。当以这种方式触发格式化时,挂载配置还控制新文件系统的布局:
* ``allocation_unit_size`` 设置 :cpp:func:`f_mkfs` 使用的簇大小。较大的值可以提高吞吐量,但对于许多小文件会浪费更多空间。
* ``use_one_fat`` 创建一个 FAT 而不是两个。这节省了一些空间,但降低了冗余性。
对于初始化后的显式格式化,请使用与存储类型匹配的辅助函数:
* :cpp:func:`esp_vfs_fat_spiflash_format_rw_wl`:cpp:func:`esp_vfs_fat_spiflash_format_cfg_rw_wl` 用于带磨损均衡的 SPI flash 分区。
* :cpp:func:`esp_vfs_fat_sdcard_format`:cpp:func:`esp_vfs_fat_sdcard_format_cfg` 用于 SD 卡。
无论当前是否已挂载文件系统,都可以调用 SPI flash 格式化辅助函数。SD 卡格式化辅助函数要求先挂载卡(但不是文件系统)。
使用 :cpp:func:`esp_vfs_fat_spiflash_mount_ro` 挂载的只读 SPI flash 分区不能在目标上重新格式化。要填充只读分区,请在主机上使用 :ref:`FatFs 分区生成器 <fatfs-partition-generator>` 生成镜像。
.. _fatfs-partition-generator:
FatFs 分区生成器
-------------------------
ESP-IDF 提供了一个 FatFs 分区生成器 :component_file:`wl_fatfsgen.py <fatfs/wl_fatfsgen.py>`,该工具已集成到构建系统中。
使用此工具在主机上创建文件系统镜像,并从指定的主机文件夹填充它们。
该脚本基于分区生成器 (:component_file:`fatfsgen.py <fatfs/fatfsgen.py>`)。除了生成 FatFs 分区外,``wl_fatfsgen.py`` 还初始化磨损均衡,因此通常用于可写的 SPI flash 文件系统。对于 SD 卡镜像和只读 SPI flash 镜像,请改用 ``fatfsgen.py``
此工具支持短文件名和长文件名、FAT12 和 FAT16。长文件名最多 255 个字符,文件名中可以包含多个点(``.``),但路径中不能包含。它们还可以包含以下附加字符:``+````,````;````=````[````]``
有关 FatFs 分区生成器和分析器的详细说明,请参阅 :doc:`在主机上生成和解析 FAT 分区 <fatfsgen>`
与 FatFs 分区生成器的构建系统集成
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
通过调用 ``fatfs_create_spiflash_image`` 直接从 CMake 构建系统调用 FatFs 生成器:
.. code-block:: none
fatfs_create_spiflash_image(<partition> <base_dir> [optional arguments])
要生成不带磨损均衡支持的 **只读** 分区,请使用 ``fatfs_create_rawflash_image``
.. code-block:: none
fatfs_create_rawflash_image(<partition> <base_dir> [optional arguments])
在项目的 ``CMakeLists.txt`` 中调用 ``fatfs_create_spiflash_image````fatfs_create_rawflash_image``
函数参数为:
#. partition - 分区表中定义的分区名称(例如,:example_file:`storage/fatfs/fatfsgen/partitions_example.csv`)。
#. base_dir - 编码到 FAT 文件系统分区中并可选择烧录到目标的目录。确保分区表定义了适当的分区大小。
#. 标志 ``FLASH_IN_PROJECT`` - 可选择在运行 ``idf.py flash -p <PORT>`` 时自动将镜像与应用程序二进制文件、分区表和其他输出一起烧录。如果未指定 ``FLASH_IN_PROJECT``,镜像仍然会生成,但你必须使用 ``esptool`` 或自定义构建系统目标手动烧录。
#. 标志 ``PRESERVE_TIME`` - 可选择保留目标镜像中源文件夹的时间戳。如果没有此标志,该工具将所有时间戳设置为 FatFs 库默认初始时间1980 年 1 月 1 日)。
#. 标志 ``ONE_FAT`` - 可选择生成具有一个 FAT文件分配表而不是两个的 FAT 文件系统卷。这通过 ``一个 FAT 使用的逻辑扇区数 * 逻辑扇区大小`` 增加了 FAT 文件系统卷中的可用空间,但也增加了损坏风险。
例如:
.. code-block:: none
fatfs_create_spiflash_image(my_fatfs_partition my_folder FLASH_IN_PROJECT)
如果你为 SD 卡生成镜像ESP-IDF 目前不提供主机端烧录步骤来将镜像直接写入卡。请使用其他主机工具(例如 ``dd``)来部署镜像。
有关示例项目,请参阅 :example:`storage/fatfs/fatfsgen`
.. _fatfs-partition-analyzer:
FatFs 分区解析器
----------------------
:component_file:`fatfsparse.py <fatfs/fatfsparse.py>` 工具执行 :component_file:`fatfsgen.py <fatfs/fatfsgen.py>` 的逆向操作。它从 FAT 文件系统镜像重建主机文件夹结构。
用法:
.. code-block:: none
./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] [--verbose] fatfs_image.img
``--verbose`` 选项在工具生成文件夹结构之前打印 FAT 文件系统镜像引导扇区的详细信息。
.. _fatfs-minimum-partition-size:
大小要求和限制
----------------------------
FatFs 组件支持 FAT12、FAT16 和 FAT32 文件系统类型,并且可以选择支持 exFAT参见 :ref:`可选的 exFAT 支持 <fatfs-optional-exfat-support>`)。
文件系统类型取决于卷上的簇数:
.. math::
cluster\_count = data\_logical\_sectors / logical\_sectors\_per\_cluster
因此,最小分区大小取决于 FAT 元数据和至少一个数据簇所需的逻辑扇区数。对于下面使用的术语,请参见 :ref:`术语表 <fatfs-glossary>`
.. _fatfs-minimum-partition-size-raw:
原始 FAT 文件系统大小
^^^^^^^^^^^^^^^^^^^^^^^
原始 FAT 文件系统的所需大小包括 FAT 文件系统元数据加上数据区:
.. math::
fatfs\_size = (reserved + fats + root\_dir + data) × logical\_sector\_size
其中:
* ``reserved`` 是保留的逻辑扇区数
* ``fats`` 是所有 FAT 副本占用的逻辑扇区总数
* ``root_dir`` 是 FAT12/FAT16 上根目录占用的逻辑扇区数
* ``data`` 是数据区中的逻辑扇区数
FAT 区域取决于所选的 FAT 类型和簇数:
.. list-table::
:widths: 18 25 57
:header-rows: 1
* - FAT 类型
- 簇数
- FAT 大小计算
* - FAT12
- ``<= 4085``
- 每个 FAT 条目使用 12 位,因此一个 FAT 副本需要 ``ceil(((cluster_count + 2) × 3 / 2) / logical_sector_size)`` 个逻辑扇区。
* - FAT16
- ``4085 < cluster_count < 65526``
- 每个 FAT 条目使用 16 位,因此一个 FAT 副本需要 ``ceil(((cluster_count + 2) × 2) / logical_sector_size)`` 个逻辑扇区。
* - FAT32
- ``>= 65526``
- 每个 FAT 条目使用 32 位,因此一个 FAT 副本需要 ``ceil(((cluster_count + 2) × 4) / logical_sector_size)`` 个逻辑扇区。
* - exFAT
- ``16 .. 0x7FFFFFFD``
- 每个 FAT 条目使用 32 位,因此一个 FAT 副本需要 ``ceil(((cluster_count + 2) × 4) / logical_sector_size)`` 个逻辑扇区。
对于 FAT12 和 FAT16根目录在数据区之外因此
.. math::
root\_dir = ceil((root\_dir\_entries × 32) / logical\_sector\_size)
对于 FAT32根目录存储在数据区中因此上述公式中 ``root_dir = 0``
exFAT 使用不同的元数据布局。除了 FAT 区域外,它还需要一个分配位图和一个大小写转换表,其根目录存储在数据区中。因此,上述 FAT12/FAT16 根目录公式不适用于 exFAT。
当启用 exFAT 支持时,最小 exFAT 卷大小为 4096 个逻辑扇区,最大簇数为 ``0x7FFFFFFD``
在实践中:
* 增加分区大小会添加数据逻辑扇区,从而增加可用存储空间。
* 在小的 FAT12/FAT16 卷上,根目录可以占用 1 个逻辑扇区。在较大的卷上,它可以占用 4 个逻辑扇区。
* 对于 FAT12、FAT16 和 FAT32默认布局使用两个 FAT。与 ``use_one_fat = true`` 相比,这增加了一个额外 FAT 副本所需的逻辑扇区。exFAT 使用单个 FAT。
* 对于不带磨损均衡且逻辑扇区大小为 512 字节的只读分区,实际最小分区大小为 2 KB。此最小布局使用 4 个逻辑扇区1 个保留扇区、1 个 FAT 扇区、1 个根目录扇区和 1 个数据扇区。
此最小原始布局适用于 SD 卡和只读原始 flash 分区等介质。
.. _fatfs-minimum-partition-size-wl:
带磨损均衡的 FAT 文件系统
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
对于带磨损均衡的 flash 分区,总大小为 :ref:`原始 FAT 文件系统大小 <fatfs-minimum-partition-size-raw>` 加上磨损均衡元数据区:
.. math::
partition\_size = wl\_metadata\_flash\_sectors × flash\_sector\_size + fatfs\_size
其中:
* ``wl_metadata_flash_sectors`` 是为磨损均衡元数据保留的 flash 扇区数
* ``flash_sector_size`` 是 SPI flash 擦除扇区大小。在几乎所有 ESP 开发板上,它是 4096 字节
* ``fatfs_size`` 是原始 FAT 文件系统大小,如 :ref:`原始 FAT 文件系统大小 <fatfs-minimum-partition-size-raw>` 中所定义
最小支持的分区大小取决于 WL 配置:
.. list-table::
:widths: 28 18 54
:header-rows: 1
* - 条件
- 最小大小
- 说明
* - ``CONFIG_WL_SECTOR_SIZE = 4096``
- 32 KB
- 最小布局由磨损均衡层保留的 4 个 flash 扇区加上最小原始 FAT 文件系统布局的 4 个逻辑扇区组成。在此配置中,逻辑扇区大小和 flash 扇区大小均为 4096 字节。
* - ``CONFIG_WL_SECTOR_SIZE = 512`` 且 WL 性能模式
- 20 KB
- 原始 FAT 文件系统仍然需要相同的最小布局的 4 个逻辑扇区,但磨损均衡层将其元数据保留在 4096 字节的 flash 扇区中。
* - ``CONFIG_WL_SECTOR_SIZE = 512`` 且 WL 安全模式
- 28 KB
- 与上述原始 FAT 最小值相同,加上磨损均衡层保留的 2 个额外 flash 扇区。
有关更多详细信息,请参阅 :doc:`文件系统注意事项 <../../api-guides/file-system-considerations>`
.. note::
磨损均衡层通过在受管存储区域中均匀分布写入和擦除周期来保护底层 flash。因此其有效性随该区域的大小而缩放。小分区会限制磨损分布并可能显著降低保护效果因此强烈不建议使用。有关更多信息请参阅 :doc:`磨损均衡 <wear-levelling>`
优化
----------
通过选择正确的配置选项和文件系统布局,可以针对不同的优先级调整 FatFs 行为。
影响行为的主要变量是:
* 缓冲区位置和大小(:ref:`CONFIG_FATFS_ALLOC_PREFER_ALIGNED_WORK_BUFFERS`:ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM`:ref:`CONFIG_FATFS_USE_DYN_BUFFERS`:ref:`CONFIG_FATFS_PER_FILE_CACHE` 和应用程序 I/O 缓冲区大小)。
* 磨损均衡逻辑扇区大小和模式(``CONFIG_WL_SECTOR_SIZE_*````CONFIG_WL_SECTOR_MODE_*``)。
* 同步策略(:ref:`CONFIG_FATFS_IMMEDIATE_FSYNC`)。
* 工作负载模式(事务大小、顺序访问与随机访问、读取与写入比率)。
优化 I/O 性能
^^^^^^^^^^^^^^^^^^^^^^
对于面向吞吐量的工作负载:
* 除非一致性要求需要,否则保持禁用 :ref:`CONFIG_FATFS_IMMEDIATE_FSYNC`
* 如果峰值速度是最高优先级,请禁用 :ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM`,以便缓冲区保留在内部 RAM 中。
* 优先选择较大的读写事务大小,而不是许多小操作。
* 尽可能将事务大小与活动扇区大小(例如 512 B 或 4096 B对齐并在需要时填充写入以减少部分扇区开销。
* 对于带磨损均衡的 SPI flash当 RAM 预算允许时,优先选择 ``CONFIG_WL_SECTOR_SIZE_4096``,因为它通常更高效。
* 如果使用 512 字节的 WL 扇区,当应用程序可以接受 flash 扇区擦除期间更高的断电风险时,请使用 ``CONFIG_WL_SECTOR_MODE_PERF``
* 在热路径上尽可能优先选择 POSIX ``read``/``write`` 而不是 ``fread``/``fwrite``。有关更广泛的速度指导,请参阅 :doc:`最大化执行速度 <../../api-guides/performance/speed>`
* 在 SDMMC DMA 目标(例如带有 PSRAM 的 ESP32-P4启用 :ref:`CONFIG_FATFS_ALLOC_PREFER_ALIGNED_WORK_BUFFERS` 以减少额外的缓冲区拷贝。
* 对于具有长反向查找的读取密集型工作负载,启用 :ref:`CONFIG_FATFS_USE_FASTSEEK`
.. note::
有关性能优化场景下预期基准的参考,请参阅 :example:`storage/perf_benchmark`
优化内存使用
^^^^^^^^^^^^^^^^^^^
* 当减少 RAM 使用是优先事项时,禁用 :ref:`CONFIG_FATFS_PER_FILE_CACHE` 以使用单个共享缓存。
* 尽可能低地调整 ``esp_vfs_fat_mount_config_t.max_files`` (参见 :ref:`挂载和使用 FatFs <fatfs-mount-and-use>`);每个同时打开的文件都会增加 RAM 使用量。
* 如果启用了 :ref:`CONFIG_FATFS_PER_FILE_CACHE`,优先选择 ``CONFIG_WL_SECTOR_SIZE_512`` 以减少每文件缓存大小。
* 如果禁用了 :ref:`CONFIG_FATFS_PER_FILE_CACHE```CONFIG_WL_SECTOR_SIZE_4096`` 可能是更好的权衡。
* 在支持外部 RAM 的目标上启用 :ref:`CONFIG_FATFS_ALLOC_PREFER_EXTRAM` 以减少内部 RAM 压力,但预计 I/O 性能会降低。
* 当 SFN (8.3) 文件名可接受时,考虑使用 ``CONFIG_FATFS_LONG_FILENAMES = CONFIG_FATFS_LFN_NONE``
* 如果需要长文件名,将 ``CONFIG_FATFS_MAX_LFN`` 减小到满足需求的最小值。
* 启用 :ref:`CONFIG_FATFS_USE_DYN_BUFFERS`,以便每个挂载的卷使用根据其实际扇区大小调整大小的缓冲区。
优化存储效率
^^^^^^^^^^^^^^^^^^^^^^^^^
为了最大化可用文件系统容量:
* 对于生成的只读镜像,考虑在 ``fatfs_create_spiflash_image`` 中使用 ``ONE_FAT`` 选项(参见 :ref:`FatFs 分区生成器 <fatfs-partition-generator>`)以使用一个 FAT 而不是两个。
* 当最小化开销是优先事项时,考虑使用 ``CONFIG_WL_SECTOR_SIZE_512``,因为在某些布局中,较小的扇区可以提高可用存储效率。
* 为分区留有余量。非常小的分区具有不成比例的高元数据和磨损均衡开销。
* 尽可能保持长文件名简短;长名称会消耗额外的目录条目。
* 对于只读数据,在适当的情况下考虑不带磨损均衡的原始 FAT 分区。
高级操作
-------------------
当你使用 ESP-IDF 不直接支持的存储介质,或者当你需要访问底层 FatFs 库函数时,可能需要手动挂载。
.. _fatfs-optional-exfat-support:
可选的 exFAT 支持
^^^^^^^^^^^^^^^^^^^^^^
上游 FatFs 库包括可选的 exFAT 支持,但 ESP-IDF 默认不启用它。
如果你的项目需要 exFAT你必须在 :component:`fatfs` 组件中手动修改 FatFs 配置并将生成的配置集成到你的构建和验证流程中。ESP-IDF 不提供专门的 menuconfig 选项或标准的 exFAT 高级启用路径。
在启用 exFAT 之前,请审查可能适用于你的产品、分销模式和目标市场的任何许可或其他知识产权考虑因素。
架构概述
^^^^^^^^^^^^^^^^^^^^^
下图说明了 FatFs 的分层架构及其与 ESP-IDF 其他组件的关系:
.. code-block:: none
+---------------------------------------------------------------+
| Newlib/Picolib (C 标准库) |
| fopen, fread, fwrite, fclose, fseek 等 |
+---------------------------------------------------------------+
| VFS (虚拟文件系统) |
| POSIX API: open, read, write, close, lseek, stat 等 |
+---------------------------------------------------------------+
| FatFs 库 |
| f_mount, f_open, f_read, f_write, f_close, f_mkfs |
+---------------------------------------------------------------+
| 磁盘 I/O 层 |
| ff_diskio_register, disk_read, disk_write, disk_ioctl 等 |
+-------------------------------+-------------------------------+
| 磨损均衡(可选) | |
| wl_mount, wl_read, wl_write | SD/MMC 驱动 |
+-------------------------------+ |
| esp_partition | sdmmc_read/write_sectors |
| esp_partition_read/write | |
+-------------------------------+-------------------------------+
| SPI flash | SD 卡 |
+-------------------------------+-------------------------------+
.. _fatfs-manual-mount:
手动挂载 FAT 文件系统分区
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
手动挂载 FAT 文件系统分区时,请遵循 :ref:`挂载和使用 FatFs <fatfs-mount-and-use>` 中描述的便利封装函数使用的相同一般流程:向 FatFs 库注册文件系统,然后通过 VFS 挂载它。
#. 通过调用 :cpp:func:`esp_vfs_fat_register` 向 VFS 注册文件系统。这会分配 ``FATFS`` 结构,并设置 VFS 和 FatFs 库之间的接口。指定:
- 文件系统将挂载的路径前缀(例如 ``"/sdcard"````"/spiflash"``)。
- FatFs 驱动器号。
- 一个接收指向 ``FATFS`` 结构的指针的变量。
#. 通过调用 :cpp:func:`ff_diskio_register` 为步骤 1 中使用的驱动器号注册磁盘 I/O 驱动程序。
#. 通过调用 :cpp:func:`f_mount` 为传递给 :cpp:func:`esp_vfs_fat_register` 的驱动器号挂载文件系统。
如果目标逻辑驱动器上不存在文件系统,:cpp:func:`f_mount` 将失败并返回 ``FR_NO_FILESYSTEM``。在这种情况下,使用 :cpp:func:`f_mkfs` 在驱动器上创建一个新的 FAT 文件系统,然后再次调用 :cpp:func:`f_mount`
对于 SD 卡,首先使用 :cpp:func:`f_fdisk` 创建分区表。有关详细信息,请参阅 `FatFs 文档 <http://elm-chan.org/fsw/ff/doc/mount.html>`_。
要卸载,请遵循以下一般步骤:
#. 通过使用相同的驱动器号和 NULL ``FATFS*`` 参数调用 :cpp:func:`f_mount` 来卸载文件系统。
#. 通过使用相同的驱动器号和 NULL ``ff_diskio_impl_t*`` 参数调用 :cpp:func:`ff_diskio_register` 来注销磁盘 I/O 驱动程序。
#. 通过使用挂载路径调用 :cpp:func:`esp_vfs_fat_unregister_path` 来删除 VFS 注册并释放在挂载过程的步骤 1 中分配的 ``FATFS`` 结构。
调用 FatFs 库提供的底层函数时,使用不带挂载点前缀的路径。例如,使用 ``"/hello.txt"``
.. _fatfs-diskio-layer:
FatFs 磁盘 I/O 层
-------------------
^^^^^^^^^^^^^^^^^^^^
我们对 FatFs API 函数进行了扩展,实现了运行期间注册磁盘 I/O 驱动。
FatFs 提供 API 以在运行时注册磁盘 I/O 驱动程序。这些 API 构成了介质特定访问函数和 FatFs 库之间的接口。
上述 API 为 SD/MMC 卡提供了磁盘 I/O 函数实现方式,可使用 :cpp:func:`ff_diskio_register_sdmmc` 函数注册指定的 FatFs 驱动编号。
磁盘 I/O 层使用 :cpp:struct:`ff_diskio_impl_t` 来提供介质访问函数。将此结构传递给 :cpp:func:`ff_diskio_register`
ESP-IDF 为支持的存储介质提供便利封装函数。这些包括:
- :cpp:func:`ff_diskio_register_wl_partition` 用于带磨损均衡的 SPI flash 分区。
- :cpp:func:`ff_diskio_register_raw_partition` 用于 **不带** 磨损均衡的只读 SPI flash 分区。
- :cpp:func:`ff_diskio_register_sdmmc` 用于 SDMMC 和 SPI 连接的 SD 卡。
有关 API 参考,请参见 :ref:`磁盘 I/O API 参考 <fatfs-diskio-api-reference>`
.. _fatfs-glossary:
术语表
--------
.. list-table::
:widths: 20 80
:header-rows: 1
* - 术语
- 描述
* - VFS
- 虚拟文件系统ESP-IDF 提供的抽象层,允许应用程序通过统一的 POSIX 和 ``stdio.h`` 接口访问不同的底层文件系统。
* - 逻辑扇区
- 呈现给 FatFs 和磁盘 I/O 层的逻辑块大小。对于带磨损均衡的 SPI flash这是 WL 扇区大小。对于只读原始 SPI flash 分区,这是 flash 扇区大小。对于 SD 卡,通常为 512 字节。
* - WL 扇区
- 磨损均衡层暴露给 FatFs 的逻辑扇区大小。根据 ``CONFIG_WL_SECTOR_SIZE``,可以是 512 字节或 4096 字节。
* - flash 扇区
- flash 芯片使用的底层 SPI flash 擦除单元。在几乎所有 ESP 开发板上,这是 4096 字节。当磨损均衡配置为 512 字节扇区时,它可以不同于呈现给 FatFs 的 WL 扇区大小。
* - 簇
- 最小的逻辑 FAT 分配单元,由一个或多个连续的逻辑扇区组成。
* - FAT
- 文件分配表,记录每个簇的使用状态以及文件数据块之间的链接关系。
* - 磨损均衡
- 一种通过均匀分布写入操作来延长 flash 存储器寿命的技术,防止特定块因频繁擦除而过早磨损。
* - 驱动器号
- FatFs 用于标识不同逻辑驱动器的数字 (0-9),每个驱动器对应一个独立的文件系统实例。
* - 挂载
- 将文件系统与指定路径关联的过程,使应用程序能够通过该路径访问文件系统内容。
* - SFN/LFN
- 短文件名8.3 格式)和长文件名(最多 255 个字符)。
.. _fatfs-application-examples:
应用程序示例
--------------------
- :example:`storage/fatfs/getting_started` 演示了使用 FatFs 在 SPI flash 上存储持久数据所需的最小设置,包括挂载文件系统、打开文件、执行基本的读写操作和卸载文件系统。
- :example:`storage/fatfs/fs_operations` 演示了高级 FatFs 操作,包括读写文件、创建、移动和删除文件和目录,以及检查文件详细信息。
- :example:`storage/fatfs/ext_flash` 演示了如何操作格式化为 FatFs 的外部 SPI flash包括初始化 SPI 总线、配置 flash 芯片、将其注册为分区以及执行读写操作。
.. _fatfs-high-level-api-reference:
高级 API 参考
------------------------
.. include-build-file:: inc/esp_vfs_fat.inc
.. _fatfs-diskio-api-reference:
磁盘 I/O API 参考
^^^^^^^^^^^^^^^^^^^^^^
.. doxygenfunction:: ff_diskio_register
.. doxygenstruct:: ff_diskio_impl_t
@@ -100,110 +485,3 @@ FatFs 磁盘 I/O 层
.. doxygenfunction:: ff_diskio_register_sdmmc
.. doxygenfunction:: ff_diskio_register_wl_partition
.. doxygenfunction:: ff_diskio_register_raw_partition
.. _fatfs-partition-generator:
FatFs 分区生成器
-------------------------
我们为 FatFs (:component_file:`wl_fatfsgen.py<fatfs/wl_fatfsgen.py>`) 提供了分区生成器,该生成器集成在构建系统中,方便用户在自己的项目中使用。
该生成器可以在主机上创建文件系统镜像,并用指定的主机文件夹内容对其进行填充。
该脚本是建立在分区生成器的基础上 (:component_file:`fatfsgen.py<fatfs/fatfsgen.py>`),目前除了可以生成分区外,也可以初始化损耗均衡。
目前的最新版本支持短文件名、长文件名、FAT12 和 FAT16。长文件名的上限是 255 个字符,文件名中可以包含多个 ``.`` 字符以及其他字符,如 ``+````,````;````=````[`` and ``]`` 等。
如需进一步了解 FatFs 分区生成器或分区分析器,请查看 :doc:`Generating and parsing FAT partition on host <./fatfsgen>`
构建系统中使用 FatFs 分区生成器
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
通过调用 ``fatfs_create_partition_image`` 可以直接从 CMake 构建系统中调用 FatFs 分区生成器::
fatfs_create_spiflash_image(<partition> <base_dir> [FLASH_IN_PROJECT])
如果不希望在生成分区时使用损耗均衡,可以使用 ``fatfs_create_rawflash_image``::
fatfs_create_rawflash_image(<partition> <base_dir> [FLASH_IN_PROJECT])
``fatfs_create_spiflash_image`` 以及 ``fatfs_create_rawflash_image`` 必须从项目的 CMakeLists.txt 中调用。
如果决定使用 ``fatfs_create_rawflash_image`` (不支持损耗均衡),请注意它仅支持在设备中以只读模式安装。
该函数的参数如下:
#. partition - 分区的名称,需要在分区表中定义(如 :example_file:`storage/fatfs/fatfsgen/partitions_example.csv`)。
#. base_dir - 目录名称,该目录会被编码为 FatFs 分区,也可以选择将其被烧录进设备。但注意必须在分区表中指定合适的分区大小。
#. ``FLASH_IN_PROJECT`` 标志 - 可选参数,用户可以通过指定 ``FLASH_IN_PROJECT``,选择在执行 ``idf.py flash -p <PORT>`` 时让分区镜像自动与应用程序二进制文件、分区表等一同烧录进设备。
#. ``PRESERVE_TIME`` 标志 - 可选参数,用户可强制让目标镜像保留源文件夹的时间戳。如果不保留,每个目标镜像的时间戳都将设置为 FATFS 默认初始时间1980 年 1 月 1 日)。
#. ``ONE_FAT`` 标志 - 可选参数,支持生成仅包含单个 FAT文件分配表的 FATFS 卷。与包含两个 FAT 的 FATFS 卷相比,这样做可以拥有相对较大的可用空间(通过 ``FAT 使用的扇区数 * 扇区大小`` 计算),但会增加 FATFS 卷损坏的风险。
例如::
fatfs_create_partition_image(my_fatfs_partition my_folder FLASH_IN_PROJECT)
没有指定 FLASH_IN_PROJECT 时也可以生成分区镜像,但是用户需要使用 ``esptool`` 或自定义的构建系统目标对其手动烧录。
相关示例请查看 :example:`storage/fatfs/fatfsgen`
.. _fatfs-partition-analyzer:
FatFs 分区分析器
------------------
我们为 FatFs 提供分区分析器 (:component_file:`fatfsparse.py<fatfs/fatfsparse.py>`)。
该分析器为 FatFs 分区生成器 (:component_file:`fatfsgen.py<fatfs/fatfsgen.py>`) 的逆向工具,可以根据 FatFs 镜像在主机上生成文件夹结构。
可以使用::
./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] [--verbose] fatfs_image.img
生成文件夹结构之前,参数 --verbose 将根据 FatFs 镜像的引导扇区在终端打印详细信息。
FATFS 最小分区大小及限制
------------------------
FATFS 组件支持 FAT12、FAT16 和 FAT32 文件系统类型。文件系统类型取决于卷上簇的数量(簇数通过数据扇区数量除以每簇包含的扇区数计算得出)。最小分区大小由分配给 FAT 表、根目录和数据簇的扇区数量决定。
* 对于 4096 字节的扇区,启用损耗均衡的 FAT 分区大小最小支持 32 KB。对于 512 字节的扇区,最小分区大小取决于损耗均衡的配置:性能模式下,最小支持 20 KB安全模式下最小支持 28 KB需要额外的 2 个扇区)。
* 启用了损耗均衡的分区会预留 4 个扇区用于损耗均衡操作。此外FATFS 本身也会使用 4 个扇区,分别为 1 个保留扇区、1 个 FAT 扇区、1 个根目录扇区和 1 个数据扇区。
* 增加分区大小将分配更多的数据扇区,提供更大的存储空间。
* 对小于 528 KB 的分区,将分配 1 个根目录扇区;对于更大的分区,将分配 4 个根目录扇区。
* 默认会创建两个 FAT 扇区,因此分区大小会增加一个扇区来容纳这个额外的 FAT 扇区。如要启用单个 FAT 扇区,可以在 `struct esp_vfs_fat_mount_config_t` 中(参见 :component_file:`fatfs/vfs/esp_vfs_fat.h`)设置 `use_one_fat` 选项。启用此选项后,最小分区大小可减少至 32 KB。
* 计算损耗均衡分区大小的一般公式为::
partition_size = 损耗均衡扇区数 * FLASH_SEC_SIZE + FATFS 分区扇区数量 * FAT_SEC_SIZE
其中:
- 损耗均衡扇区数固定为 4 个
- FLASH_SEC_SIZE 为 4096 字节
- FATFS 分区扇区包括1 个保留扇区 + FAT 扇区 + 根目录扇区 + 数据扇区
- FAT_SEC_SIZE 根据不同的配置,可以是 512 字节或 4096 字节
* 对于未启用损耗均衡、扇区大小为 512 字节的只读分区,最小分区大小可减少至 2 KB。
更多详情请参考 :doc:`文件系统注意事项 <../../api-guides/file-system-considerations>`
应用示例
-----------------
- :example:`storage/fatfs/getting_started` 演示了如何使用 FatFS 在 SPI flash 上存储永久数据的基本设置,包括挂载文件系统、打开文件、执行基本的读写操作以及卸载文件系统。
- :example:`storage/fatfs/fs_operations` 演示了更全面的 FatFS 操作,包括读取和写入文件、创建、移动和删除文件及目录,以及检查文件详细信息。
- :example:`storage/fatfs/ext_flash` 演示了如何操作使用 FatFS 格式化的外部 SPI flash包括初始化 SPI 总线、配置 flash、将其注册为分区以及执行读写操作。
高级 API 参考
------------------------
.. include-build-file:: inc/esp_vfs_fat.inc