# FAT 文件系统

[English]

ESP-IDF 通过 FatFs 组件使用 FatFs 库来实现 FAT 文件系统。挂载 FAT 文件系统卷后，VFS 层会暴露标准 C 库和 POSIX API。

备注

FatFs 可用于指代多个概念，如文件系统本身、上游库，或 ESP-IDF 组件。为清晰起见，本文档使用以下术语：

- FAT 文件系统：磁盘上的 FAT 格式 (FAT12/FAT16/FAT32)，exFAT（参见 可选的 exFAT 支持）。

- FatFs 库：ESP-IDF 使用的上游 FatFs 库。

- FatFs 组件：ESP-IDF 中围绕 FatFs 库的集成层（挂载辅助函数、封装器和相关 API），位于 fatfs。

本文档相关的其他术语在 术语表 中进行了解释。

## 挂载和使用 FatFs

FatFs 组件提供了便利的封装函数，用于通过 VFS 层挂载文件系统。在大多数用例中，应使用这些封装函数，而不是手动挂载文件系统。有关手动设置的说明，请参考 手动挂载 FAT 文件系统分区。

一般的挂载流程是：

1. 选择挂载路径（例如 `"/spiflash"` 或 `"/sdcard"`），并设置 `esp_vfs_fat_mount_config_t`。

2. 使用 fatfs/vfs/esp_vfs_fat.h 中的便利辅助函数挂载卷：

   > - `esp_vfs_fat_spiflash_mount_rw_wl()` 用于带磨损均衡的 SPI flash 分区。

   > - `esp_vfs_fat_spiflash_mount_ro()` 用于 不带 磨损均衡的只读 SPI flash 分区。

   > - `esp_vfs_fat_sdmmc_mount()` 或 `esp_vfs_fat_sdspi_mount()` 用于 SD 卡。

3. 在挂载路径下的路径上使用标准文件 API（`stdio.h` 或 POSIX）。

4. 关闭打开的文件，并调用相应的卸载辅助函数。

## 配置选项

FatFs 组件提供以下配置选项：

- `CONFIG_FATFS_LONG_FILENAMES` - 选择 FatFs 库如何处理长文件名 (LFN) 支持。可用选项包括 CONFIG_FATFS_LFN_NONE 以禁用 LFN 支持并将名称限制为 8.3 格式 （仅 SFN）， CONFIG_FATFS_LFN_HEAP 以启用 LFN 支持并将 LFN 工作缓冲区存储在堆上（默认），以及 CONFIG_FATFS_LFN_STACK 以启用 LFN 支持并将 LFN 工作缓冲区存储在栈上。详细信息请参阅 FatFs 文件名。

- CONFIG_FATFS_VOLUME_COUNT - 设置逻辑 FatFs 卷的数量。增加此值可能会增加基础内存使用量。

- CONFIG_FATFS_ALLOC_PREFER_EXTRAM - 如果启用，FatFs 库在分配内部缓冲区时优先使用外部 RAM。如果外部 RAM 分配失败，则回退到内部 RAM。这可能会对热 I/O 路径产生明显的性能开销。禁用此选项可优先考虑性能；启用可减少内部 RAM 使用量。

- CONFIG_FATFS_USE_DYN_BUFFERS - 如果启用，FatFs 库会单独分配实例缓冲区，并根据每个已挂载卷的逻辑扇区大小调整其大小。当多个 FatFs 实例使用不同的逻辑扇区大小时，此选项非常有用，因为它可以减少内存使用量。如果禁用，所有实例都使用为最大配置逻辑扇区大小调整大小的缓冲区。

- CONFIG_FATFS_PER_FILE_CACHE - 如果启用，每个打开的文件使用单独的缓存缓冲区。这提高了 I/O 性能，但当多个文件打开时会增加 RAM 使用量。如果禁用，则使用单个共享缓存，这减少了 RAM 使用量，但可能会增加存储读写操作。

- CONFIG_FATFS_USE_FASTSEEK - 如果启用，POSIX `lseek()` 运行更快。快速定位不适用于以写入模式打开的文件。要使用快速查找，请以只读模式打开文件，或关闭后以只读模式重新打开。

- CONFIG_FATFS_FAST_SEEK_BUFFER_SIZE - 设置当 CONFIG_FATFS_USE_FASTSEEK 启用时快速查找使用的 CLMT（簇链接映射表）缓冲区大小。较大的缓冲区可以改善较大文件上的查找行为，但会使用更多 RAM。

- CONFIG_FATFS_VFS_FSTAT_BLKSIZE - 设置通过 VFS 使用的默认 stdio 文件缓冲区块大小。此选项主要与基于 stdio 的 I/O（例如 `fread`/`fgets`）相关，不是直接 POSIX `read`/`write` 路径的主要调整参数。较大的值可以提高缓冲读取吞吐量，但会增加堆使用量。

- CONFIG_FATFS_IMMEDIATE_FSYNC - 如果启用，FatFs 库会在每次调用 `write()`、`pwrite()`、`link()`、`truncate()` 和 `ftruncate()` 后自动调用 `f_sync()`。此选项提高了文件一致性和大小报告的准确性，但由于会触发频繁的磁盘操作而降低了性能。

- CONFIG_FATFS_LINK_LOCK - 如果启用，此选项保证 `link()` 函数的 API 线程安全。禁用此选项可以帮助执行频繁小文件操作（例如文件日志记录）的应用程序。禁用时，`link()` 执行的拷贝是非原子的。在这种情况下，从另一个任务在同一卷上对大文件使用 `link()` 不保证线程安全。

- 其他相关选项包括 CONFIG_FATFS_FS_LOCK、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 库如何计算和报告可用空间：

- CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT - 如果设置为 1，FatFs 库忽略空闲簇计数。默认值为 0。

- CONFIG_FATFS_DONT_TRUST_LAST_ALLOC - 如果设置为 1，FatFs 库忽略上次分配编号。默认值为 0。

备注

将这些选项设置为 1 可以提高 `f_getfree()` 的准确性，但会降低性能，例如强制进行完整的 FAT 扫描。

## 与 POSIX 标准的差异

1. `link()`：FAT 文件系统不支持硬链接，因此 `link()` 会复制文件内容（这仅适用于 FAT 文件系统卷上的文件）。

2. `unlink()`：如果启用 `CONFIG_FATFS_FS_LOCK`，删除打开的文件会失败并返回 `EBUSY`。否则，行为未定义，可能会损坏文件系统。

3. 不支持将 `.` 和 `..` 作为当前目录和上级目录的引用。

## 格式化 FAT 文件系统

格式化会在目标卷上创建一个新的 FAT 文件系统，并擦除该卷上现有的目录结构和文件数据。用于首次配置，或从 `FR_NO_FILESYSTEM` 中恢复。

对于挂载时自动格式化，请在调用挂载辅助函数之前将 `esp_vfs_fat_mount_config_t.format_if_mount_failed` 设置为 `true`。当卷在首次启动时预期为空白时，这非常有用。当以这种方式触发格式化时，挂载配置还控制新文件系统的布局：

- `allocation_unit_size` 设置 `f_mkfs()` 使用的簇大小。较大的值可以提高吞吐量，但对于许多小文件会浪费更多空间。

- `use_one_fat` 创建一个 FAT 而不是两个。这节省了一些空间，但降低了冗余性。

对于初始化后的显式格式化，请使用与存储类型匹配的辅助函数：

- `esp_vfs_fat_spiflash_format_rw_wl()` 或 `esp_vfs_fat_spiflash_format_cfg_rw_wl()` 用于带磨损均衡的 SPI flash 分区。

- `esp_vfs_fat_sdcard_format()` 或 `esp_vfs_fat_sdcard_format_cfg()` 用于 SD 卡。

无论当前是否已挂载文件系统，都可以调用 SPI flash 格式化辅助函数。SD 卡格式化辅助函数要求先挂载卡（但不是文件系统）。

使用 `esp_vfs_fat_spiflash_mount_ro()` 挂载的只读 SPI flash 分区不能在目标上重新格式化。要填充只读分区，请在主机上使用 FatFs 分区生成器 生成镜像。

## FatFs 分区生成器

ESP-IDF 提供了一个 FatFs 分区生成器 wl_fatfsgen.py ，该工具已集成到构建系统中。

使用此工具在主机上创建文件系统镜像，并从指定的主机文件夹填充它们。

该脚本基于分区生成器 (fatfsgen.py )。除了生成 FatFs 分区外，`wl_fatfsgen.py` 还初始化磨损均衡，因此通常用于可写的 SPI flash 文件系统。对于 SD 卡镜像和只读 SPI flash 镜像，请改用 `fatfsgen.py`。

此工具支持短文件名和长文件名、FAT12 和 FAT16。长文件名最多 255 个字符，文件名中可以包含多个点（`.`），但路径中不能包含。它们还可以包含以下附加字符：`+`、`,`、`;`、`=`、`[` 和 `]`。

有关 FatFs 分区生成器和分析器的详细说明，请参阅 在主机上生成和解析 FAT 分区。

### 与 FatFs 分区生成器的构建系统集成

通过调用 `fatfs_create_spiflash_image` 直接从 CMake 构建系统调用 FatFs 生成器：

    fatfs_create_spiflash_image(<partition> <base_dir> [optional arguments])

要生成不带磨损均衡支持的 只读 分区，请使用 `fatfs_create_rawflash_image`：

    fatfs_create_rawflash_image(<partition> <base_dir> [optional arguments])

在项目的 `CMakeLists.txt` 中调用 `fatfs_create_spiflash_image` 或 `fatfs_create_rawflash_image`。

函数参数为：

1. partition - 分区表中定义的分区名称（例如，storage/fatfs/fatfsgen/partitions_example.csv）。

2. base_dir - 编码到 FAT 文件系统分区中并可选择烧录到目标的目录。确保分区表定义了适当的分区大小。

3. 标志 `FLASH_IN_PROJECT` - 可选择在运行 `idf.py flash -p <PORT>` 时自动将镜像与应用程序二进制文件、分区表和其他输出一起烧录。如果未指定 `FLASH_IN_PROJECT`，镜像仍然会生成，但你必须使用 `esptool` 或自定义构建系统目标手动烧录。

4. 标志 `PRESERVE_TIME` - 可选择保留目标镜像中源文件夹的时间戳。如果没有此标志，该工具将所有时间戳设置为 FatFs 库默认初始时间（1980 年 1 月 1 日）。

5. 标志 `ONE_FAT` - 可选择生成具有一个 FAT（文件分配表）而不是两个的 FAT 文件系统卷。这通过 `一个 FAT 使用的逻辑扇区数 * 逻辑扇区大小` 增加了 FAT 文件系统卷中的可用空间，但也增加了损坏风险。

例如：

    fatfs_create_spiflash_image(my_fatfs_partition my_folder FLASH_IN_PROJECT)

如果你为 SD 卡生成镜像，ESP-IDF 目前不提供主机端烧录步骤来将镜像直接写入卡。请使用其他主机工具（例如 `dd`）来部署镜像。

有关示例项目，请参阅 storage/fatfs/fatfsgen。

## FatFs 分区解析器

fatfsparse.py 工具执行 fatfsgen.py 的逆向操作。它从 FAT 文件系统镜像重建主机文件夹结构。

用法：

    ./fatfsparse.py [-h] [--wl-layer {detect,enabled,disabled}] [--verbose] fatfs_image.img

`--verbose` 选项在工具生成文件夹结构之前打印 FAT 文件系统镜像引导扇区的详细信息。

## 大小要求和限制

FatFs 组件支持 FAT12、FAT16 和 FAT32 文件系统类型，并且可以选择支持 exFAT（参见 可选的 exFAT 支持）。

文件系统类型取决于卷上的簇数：

\[cluster\_count = data\_logical\_sectors / logical\_sectors\_per\_cluster\]

因此，最小分区大小取决于 FAT 元数据和至少一个数据簇所需的逻辑扇区数。对于下面使用的术语，请参见 术语表。

### 原始 FAT 文件系统大小

原始 FAT 文件系统的所需大小包括 FAT 文件系统元数据加上数据区：

\[fatfs\_size = (reserved + fats + root\_dir + data) × logical\_sector\_size\]

其中：

- `reserved` 是保留的逻辑扇区数

- `fats` 是所有 FAT 副本占用的逻辑扇区总数

- `root_dir` 是 FAT12/FAT16 上根目录占用的逻辑扇区数

- `data` 是数据区中的逻辑扇区数

FAT 区域取决于所选的 FAT 类型和簇数：

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，根目录在数据区之外，因此：

\[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 分区等介质。

### 带磨损均衡的 FAT 文件系统

对于带磨损均衡的 flash 分区，总大小为 原始 FAT 文件系统大小 加上磨损均衡元数据区：

\[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 文件系统大小，如 原始 FAT 文件系统大小 中所定义

最小支持的分区大小取决于 WL 配置：

条件 | 最小大小 | 说明
--- | --- | ---
`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 扇区。

有关更多详细信息，请参阅 文件系统注意事项。

备注

磨损均衡层通过在受管存储区域中均匀分布写入和擦除周期来保护底层 flash。因此，其有效性随该区域的大小而缩放。小分区会限制磨损分布，并可能显著降低保护效果，因此强烈不建议使用。有关更多信息，请参阅 磨损均衡。

## 优化

通过选择正确的配置选项和文件系统布局，可以针对不同的优先级调整 FatFs 行为。

影响行为的主要变量是：

- 缓冲区位置和大小（CONFIG_FATFS_ALLOC_PREFER_EXTRAM、CONFIG_FATFS_USE_DYN_BUFFERS、CONFIG_FATFS_PER_FILE_CACHE 和应用程序 I/O 缓冲区大小）。

- 磨损均衡逻辑扇区大小和模式（`CONFIG_WL_SECTOR_SIZE_*` 和 `CONFIG_WL_SECTOR_MODE_*`）。

- 同步策略（CONFIG_FATFS_IMMEDIATE_FSYNC）。

- 工作负载模式（事务大小、顺序访问与随机访问、读取与写入比率）。

### 优化 I/O 性能

对于面向吞吐量的工作负载：

- 除非一致性要求需要，否则保持禁用 CONFIG_FATFS_IMMEDIATE_FSYNC。

- 如果峰值速度是最高优先级，请禁用 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`。有关更广泛的速度指导，请参阅 最大化执行速度。

- 对于具有长反向查找的读取密集型工作负载，启用 CONFIG_FATFS_USE_FASTSEEK。

备注

有关性能优化场景下预期基准的参考，请参阅 storage/perf_benchmark。

### 优化内存使用

- 当减少 RAM 使用是优先事项时，禁用 CONFIG_FATFS_PER_FILE_CACHE 以使用单个共享缓存。

- 尽可能低地调整 `esp_vfs_fat_mount_config_t.max_files` （参见 挂载和使用 FatFs）；每个同时打开的文件都会增加 RAM 使用量。

- 如果启用了 CONFIG_FATFS_PER_FILE_CACHE，优先选择 `CONFIG_WL_SECTOR_SIZE_512` 以减少每文件缓存大小。

- 如果禁用了 CONFIG_FATFS_PER_FILE_CACHE，`CONFIG_WL_SECTOR_SIZE_4096` 可能是更好的权衡。

- 在支持外部 RAM 的目标上启用 CONFIG_FATFS_ALLOC_PREFER_EXTRAM 以减少内部 RAM 压力，但预计 I/O 性能会降低。

- 当 SFN (8.3) 文件名可接受时，考虑使用 `CONFIG_FATFS_LONG_FILENAMES = CONFIG_FATFS_LFN_NONE`。

- 如果需要长文件名，将 `CONFIG_FATFS_MAX_LFN` 减小到满足需求的最小值。

- 启用 CONFIG_FATFS_USE_DYN_BUFFERS，以便每个挂载的卷使用根据其实际扇区大小调整大小的缓冲区。

### 优化存储效率

为了最大化可用文件系统容量：

- 对于生成的只读镜像，考虑在 `fatfs_create_spiflash_image` 中使用 `ONE_FAT` 选项（参见 FatFs 分区生成器）以使用一个 FAT 而不是两个。

- 当最小化开销是优先事项时，考虑使用 `CONFIG_WL_SECTOR_SIZE_512`，因为在某些布局中，较小的扇区可以提高可用存储效率。

- 为分区留有余量。非常小的分区具有不成比例的高元数据和磨损均衡开销。

- 尽可能保持长文件名简短；长名称会消耗额外的目录条目。

- 对于只读数据，在适当的情况下考虑不带磨损均衡的原始 FAT 分区。

## 高级操作

当你使用 ESP-IDF 不直接支持的存储介质，或者当你需要访问底层 FatFs 库函数时，可能需要手动挂载。

### 可选的 exFAT 支持

上游 FatFs 库包括可选的 exFAT 支持，但 ESP-IDF 默认不启用它。

如果你的项目需要 exFAT，你必须在 fatfs 组件中手动修改 FatFs 配置，并将生成的配置集成到你的构建和验证流程中。ESP-IDF 不提供专门的 menuconfig 选项或标准的 exFAT 高级启用路径。

在启用 exFAT 之前，请审查可能适用于你的产品、分销模式和目标市场的任何许可或其他知识产权考虑因素。

### 架构概述

下图说明了 FatFs 的分层架构及其与 ESP-IDF 其他组件的关系：

    +---------------------------------------------------------------+
    |              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 卡               |
    +-------------------------------+-------------------------------+

### 手动挂载 FAT 文件系统分区

手动挂载 FAT 文件系统分区时，请遵循 挂载和使用 FatFs 中描述的便利封装函数使用的相同一般流程：向 FatFs 库注册文件系统，然后通过 VFS 挂载它。

1. 通过调用 `esp_vfs_fat_register()` 向 VFS 注册文件系统。这会分配 `FATFS` 结构，并设置 VFS 和 FatFs 库之间的接口。指定：

   > - 文件系统将挂载的路径前缀（例如 `"/sdcard"` 或 `"/spiflash"`）。

   > - FatFs 驱动器号。

   > - 一个接收指向 `FATFS` 结构的指针的变量。

2. 通过调用 `ff_diskio_register()` 为步骤 1 中使用的驱动器号注册磁盘 I/O 驱动程序。

3. 通过调用 `f_mount()` 为传递给 `esp_vfs_fat_register()` 的驱动器号挂载文件系统。

   如果目标逻辑驱动器上不存在文件系统，`f_mount()` 将失败并返回 `FR_NO_FILESYSTEM`。在这种情况下，使用 `f_mkfs()` 在驱动器上创建一个新的 FAT 文件系统，然后再次调用 `f_mount()`。

   对于 SD 卡，首先使用 `f_fdisk()` 创建分区表。有关详细信息，请参阅 FatFs 文档。

要卸载，请遵循以下一般步骤：

1. 通过使用相同的驱动器号和 NULL `FATFS*` 参数调用 `f_mount()` 来卸载文件系统。

2. 通过使用相同的驱动器号和 NULL `ff_diskio_impl_t*` 参数调用 `ff_diskio_register()` 来注销磁盘 I/O 驱动程序。

3. 通过使用挂载路径调用 `esp_vfs_fat_unregister_path()` 来删除 VFS 注册并释放在挂载过程的步骤 1 中分配的 `FATFS` 结构。

调用 FatFs 库提供的底层函数时，使用不带挂载点前缀的路径。例如，使用 `"/hello.txt"`。

### FatFs 磁盘 I/O 层

FatFs 提供 API 以在运行时注册磁盘 I/O 驱动程序。这些 API 构成了介质特定访问函数和 FatFs 库之间的接口。

磁盘 I/O 层使用 `ff_diskio_impl_t` 来提供介质访问函数。将此结构传递给 `ff_diskio_register()`。

ESP-IDF 为支持的存储介质提供便利封装函数。这些包括：

> - `ff_diskio_register_wl_partition()` 用于带磨损均衡的 SPI flash 分区。

> - `ff_diskio_register_raw_partition()` 用于 不带 磨损均衡的只读 SPI flash 分区。

> - `ff_diskio_register_sdmmc()` 用于 SDMMC 和 SPI 连接的 SD 卡。

有关 API 参考，请参见 磁盘 I/O API 参考。

## 术语表

术语 | 描述
--- | ---
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 个字符）。

## 应用程序示例

- storage/fatfs/getting_started 演示了使用 FatFs 在 SPI flash 上存储持久数据所需的最小设置，包括挂载文件系统、打开文件、执行基本的读写操作和卸载文件系统。

- storage/fatfs/fs_operations 演示了高级 FatFs 操作，包括读写文件、创建、移动和删除文件和目录，以及检查文件详细信息。

- storage/fatfs/ext_flash 演示了如何操作格式化为 FatFs 的外部 SPI flash，包括初始化 SPI 总线、配置 flash 芯片、将其注册为分区以及执行读写操作。

## 高级 API 参考

### Header File

- components/fatfs/vfs/esp_vfs_fat.h

- This header file can be included with:

  >     #include "esp_vfs_fat.h"

- This header file is a part of the API provided by the `fatfs` component. To declare that your component depends on `fatfs`, add the following to your CMakeLists.txt:

  >     REQUIRES fatfs

  > or

  >     PRIV_REQUIRES fatfs

### Functions

esp_err_t esp_vfs_fat_register(const esp_vfs_fat_conf_t *conf, FATFS **out_fs)

  Register FATFS with VFS component.

  This function registers given FAT drive in VFS, at the specified base path. Input arguments are held in esp_vfs_fat_conf_t structure. If only one drive is used, fat_drive argument can be an empty string. Refer to FATFS library documentation on how to specify FAT drive. This function also allocates FATFS structure which should be used for f_mount call.

  备注

  This function doesn't mount the drive into FATFS, it just connects POSIX and C standard library IO function with FATFS. You need to mount desired drive into FATFS separately.

  参数:

    - conf -- pointer to esp_vfs_fat_conf_t configuration structure

    - out_fs -- [out] pointer to FATFS structure which can be used for FATFS f_mount call is returned via this argument.

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_register was already called

    - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered

esp_err_t esp_vfs_fat_unregister_path(const char *base_path)

  Un-register FATFS from VFS.

  备注

  FATFS structure returned by esp_vfs_fat_register is destroyed after this call. Make sure to call f_mount function to unmount it before calling esp_vfs_fat_unregister_ctx. Difference between this function and the one above is that this one will release the correct drive, while the one above will release the last registered one

  参数:

    base_path -- path prefix where FATFS is registered. This is the same used when esp_vfs_fat_register was called

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS

esp_err_t esp_vfs_fat_sdmmc_mount(const char *base_path, const sdmmc_host_t *host_config, const void *slot_config, const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t **out_card)

  Convenience function to get FAT filesystem on SD card registered in VFS.

  This is an all-in-one function which does the following:

  - initializes SDMMC driver or SPI driver with configuration in host_config

  - initializes SD card with configuration in slot_config

  - mounts FAT partition on SD card using FATFS library, with configuration in mount_config

  - registers FATFS library with VFS, with prefix given by base_prefix variable

  This function is intended to make example code more compact. For real world applications, developers should implement the logic of probing SD card, locating and mounting partition, and registering FATFS in VFS, with proper error checking and handling of exceptional conditions.

  备注

  Use this API to mount a card through SDSPI is deprecated. Please call `esp_vfs_fat_sdspi_mount()` instead for that case.

  参数:

    - base_path -- path where partition should be registered (e.g. "/sdcard")

    - host_config -- Pointer to structure describing SDMMC host. When using SDMMC peripheral, this structure can be initialized using SDMMC_HOST_DEFAULT() macro. When using SPI peripheral, this structure can be initialized using SDSPI_HOST_DEFAULT() macro.

    - slot_config -- Pointer to structure with slot configuration. For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.

    - mount_config -- pointer to structure with extra parameters for mounting FATFS

    - out_card -- [out] pointer to the card information structure will be returned via this argument

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if any of the arguments is NULL

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called

    - ESP_ERR_NO_MEM if memory can not be allocated

    - ESP_FAIL if partition can not be mounted

    - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers

esp_err_t esp_vfs_fat_sdspi_mount(const char *base_path, const sdmmc_host_t *host_config_input, const sdspi_device_config_t *slot_config, const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t **out_card)

  Convenience function to get FAT filesystem on SD card registered in VFS.

  This is an all-in-one function which does the following:

  - initializes an SPI Master device based on the SPI Master driver with configuration in slot_config, and attach it to an initialized SPI bus.

  - initializes SD card with configuration in host_config_input

  - mounts FAT partition on SD card using FATFS library, with configuration in mount_config

  - registers FATFS library with VFS, with prefix given by base_prefix variable

  This function is intended to make example code more compact. For real world applications, developers should implement the logic of probing SD card, locating and mounting partition, and registering FATFS in VFS, with proper error checking and handling of exceptional conditions.

  备注

  This function try to attach the new SD SPI device to the bus specified in host_config. Make sure the SPI bus specified in `host_config->slot` have been initialized by `spi_bus_initialize()` before.

  参数:

    - base_path -- path where partition should be registered (e.g. "/sdcard")

    - host_config_input -- Pointer to structure describing SDMMC host. This structure can be initialized using SDSPI_HOST_DEFAULT() macro.

    - slot_config -- Pointer to structure with slot configuration. For SPI peripheral, pass a pointer to sdspi_device_config_t structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().

    - mount_config -- pointer to structure with extra parameters for mounting FATFS

    - out_card -- [out] Pointer to the card information structure will be returned via this argument. It is suggested to hold this handle and use it to unmount the card later if needed. Otherwise it's not suggested to use more than one card at the same time and unmount one of them in your application.

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if any of the arguments is NULL

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called

    - ESP_ERR_NO_MEM if memory can not be allocated

    - ESP_FAIL if partition can not be mounted

    - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers

esp_err_t esp_vfs_fat_sdcard_unmount(const char *base_path, sdmmc_card_t *card)

  Unmount an SD card from the FAT filesystem and release resources acquired using `esp_vfs_fat_sdmmc_mount()` or `esp_vfs_fat_sdspi_mount()`

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the card argument is unregistered

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called

esp_err_t esp_vfs_fat_sdcard_format_cfg(const char *base_path, sdmmc_card_t *card, esp_vfs_fat_mount_config_t *cfg)

  Format FAT filesystem with given configuration.

  备注

  This API should be only called when the FAT is already mounted.

  参数:

    - base_path -- Path where partition should be registered (e.g. "/sdcard")

    - card -- Pointer to the card handle, which should be initialised by calling `esp_vfs_fat_sdspi_mount` first

    - cfg -- Pointer to structure with extra parameters for formatting FATFS (only relevant fields are used). If NULL, the previous configuration will be used.

  返回:

    - ESP_OK

    - ESP_ERR_INVALID_STATE: FAT partition isn't mounted, call esp_vfs_fat_sdmmc_mount or esp_vfs_fat_sdspi_mount first

    - ESP_ERR_NO_MEM: if memory can not be allocated

    - ESP_FAIL: fail to format it, or fail to mount back

esp_err_t esp_vfs_fat_sdcard_format(const char *base_path, sdmmc_card_t *card)

  Format FAT filesystem.

  备注

  This API should be only called when the FAT is already mounted.

  参数:

    - base_path -- Path where partition should be registered (e.g. "/sdcard")

    - card -- Pointer to the card handle, which should be initialised by calling `esp_vfs_fat_sdspi_mount` first

  返回:

    - ESP_OK

    - ESP_ERR_INVALID_STATE: FAT partition isn't mounted, call esp_vfs_fat_sdmmc_mount or esp_vfs_fat_sdspi_mount first

    - ESP_ERR_NO_MEM: if memory can not be allocated

    - ESP_FAIL: fail to format it, or fail to mount back

esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char *base_path, const char *partition_label, const esp_vfs_fat_mount_config_t *mount_config, wl_handle_t *wl_handle)

  Convenience function to initialize FAT filesystem in SPI flash and register it in VFS.

  This is an all-in-one function which does the following:

  - finds the partition with defined partition_label. Partition label should be configured in the partition table.

  - initializes flash wear levelling library on top of the given partition

  - mounts FAT partition using FATFS library on top of flash wear levelling library

  - registers FATFS library with VFS, with prefix given by base_prefix variable

  This function is intended to make example code more compact.

  参数:

    - base_path -- path where FATFS partition should be mounted (e.g. "/spiflash")

    - partition_label -- label of the partition which should be used

    - mount_config -- pointer to structure with extra parameters for mounting FATFS

    - wl_handle -- [out] wear levelling driver handle

  返回:

    - ESP_OK on success

    - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl was already called

    - ESP_ERR_NO_MEM if memory can not be allocated

    - ESP_FAIL if partition can not be mounted

    - other error codes from wear levelling library, SPI flash driver, or FATFS drivers

esp_err_t esp_vfs_fat_spiflash_unmount_rw_wl(const char *base_path, wl_handle_t wl_handle)

  Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_rw_wl.

  参数:

    - base_path -- path where partition should be registered (e.g. "/spiflash")

    - wl_handle -- wear levelling driver handle returned by esp_vfs_fat_spiflash_mount_rw_wl

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl hasn't been called

esp_err_t esp_vfs_fat_spiflash_format_cfg_rw_wl(const char *base_path, const char *partition_label, esp_vfs_fat_mount_config_t *cfg)

  Format FAT filesystem with given configuration.

  备注

  This API can be called when the FAT is mounted / not mounted. If this API is called when the FAT isn't mounted (by calling esp_vfs_fat_spiflash_mount_rw_wl), this API will first mount the FAT then format it, then restore back to the original state.

  参数:

    - base_path -- Path where partition should be registered (e.g. "/spiflash")

    - partition_label -- Label of the partition which should be used

    - cfg -- Pointer to structure with extra parameters for formatting FATFS (only relevant fields are used). If NULL and mounted the previous configuration will be used. If NULL and unmounted the default configuration will be used.

  返回:

    - ESP_OK

    - ESP_ERR_NO_MEM: if memory can not be allocated

    - Other errors from esp_vfs_fat_spiflash_mount_rw_wl

esp_err_t esp_vfs_fat_spiflash_format_rw_wl(const char *base_path, const char *partition_label)

  Format FAT filesystem.

  备注

  This API can be called when the FAT is mounted / not mounted. If this API is called when the FAT isn't mounted (by calling esp_vfs_fat_spiflash_mount_rw_wl), this API will first mount the FAT then format it, then restore back to the original state.

  参数:

    - base_path -- Path where partition should be registered (e.g. "/spiflash")

    - partition_label -- Label of the partition which should be used

  返回:

    - ESP_OK

    - ESP_ERR_NO_MEM: if memory can not be allocated

    - Other errors from esp_vfs_fat_spiflash_mount_rw_wl

esp_err_t esp_vfs_fat_spiflash_mount_ro(const char *base_path, const char *partition_label, const esp_vfs_fat_mount_config_t *mount_config)

  Convenience function to initialize read-only FAT filesystem and register it in VFS.

  This is an all-in-one function which does the following:

  - finds the partition with defined partition_label. Partition label should be configured in the partition table.

  - mounts FAT partition using FATFS library

  - registers FATFS library with VFS, with prefix given by base_prefix variable

  备注

  Wear levelling is not used when FAT is mounted in read-only mode using this function.

  参数:

    - base_path -- path where FATFS partition should be mounted (e.g. "/spiflash")

    - partition_label -- label of the partition which should be used

    - mount_config -- pointer to structure with extra parameters for mounting FATFS

  返回:

    - ESP_OK on success

    - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_ro was already called for the same partition

    - ESP_ERR_NO_MEM if memory can not be allocated

    - ESP_FAIL if partition can not be mounted

    - other error codes from SPI flash driver, or FATFS drivers

esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char *base_path, const char *partition_label)

  Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_ro.

  参数:

    - base_path -- path where partition should be registered (e.g. "/spiflash")

    - partition_label -- label of partition to be unmounted

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_ro hasn't been called

esp_err_t esp_vfs_fat_info(const char *base_path, uint64_t *out_total_bytes, uint64_t *out_free_bytes)

  Get information for FATFS partition.

  参数:

    - base_path -- Base path of the partition examined (e.g. "/spiflash")

    - out_total_bytes -- [out] Size of the file system

    - out_free_bytes -- [out] Free bytes available in the file system

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_STATE if partition not found

    - ESP_FAIL if another FRESULT error (saved in errno)

esp_err_t esp_vfs_fat_create_contiguous_file(const char *base_path, const char *full_path, uint64_t size, bool alloc_now)

  Create a file with contiguous space at given path.

  备注

  The file cannot exist before calling this function (or the file size has to be 0) For more information see documentation for `f_expand` from FATFS library

  参数:

    - base_path -- Base path of the partition examined (e.g. "/spiflash")

    - full_path -- Full path of the file (e.g. "/spiflash/ABC.TXT")

    - size -- File size expanded to, number of bytes in size to prepare or allocate for the file

    - alloc_now -- True == allocate space now, false == prepare to allocate – see `f_expand` from FATFS

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if invalid arguments (e.g. any of arguments are NULL or size lower or equal to 0)

    - ESP_ERR_INVALID_STATE if partition not found

    - ESP_FAIL if another FRESULT error (saved in errno)

esp_err_t esp_vfs_fat_test_contiguous_file(const char *base_path, const char *full_path, bool *is_contiguous)

  Test if a file is contiguous in the FAT filesystem.

  参数:

    - base_path -- Base path of the partition examined (e.g. "/spiflash")

    - full_path -- Full path of the file (e.g. "/spiflash/ABC.TXT")

    - is_contiguous -- [out] True == allocate space now, false == prepare to allocate – see `f_expand` from FATFS

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if invalid arguments (e.g. any of arguments are NULL)

    - ESP_ERR_INVALID_STATE if partition not found

    - ESP_FAIL if another FRESULT error (saved in errno)

### Structures

struct esp_vfs_fat_conf_t

  Configuration structure for esp_vfs_fat_register.

  Public Members

  const char *base_path

    Path prefix where FATFS should be registered,

  const char *fat_drive

    FATFS drive specification; if only one drive is used, can be an empty string.

  size_t max_files

    Maximum number of files which can be open at the same time.

struct esp_vfs_fat_mount_config_t

  Configuration arguments for esp_vfs_fat_sdmmc_mount and esp_vfs_fat_spiflash_mount_rw_wl functions.

  Public Members

  bool format_if_mount_failed

    If FAT partition can not be mounted, and this parameter is true, create partition table and format the filesystem.

  int max_files

    Max number of open files.

  size_t allocation_unit_size

    If format_if_mount_failed is set, and mount fails, format the card with given allocation unit size. Must be a power of 2, between sector size and 128 * sector size. For SD cards, sector size is always 512 bytes. For wear_levelling, sector size is determined by CONFIG_WL_SECTOR_SIZE option.

    Using larger allocation unit size will result in higher read/write performance and higher overhead when storing small files.

    Setting this field to 0 will result in allocation unit set to the sector size.

  bool disk_status_check_enable

    Enables real ff_disk_status function implementation for SD cards (ff_sdmmc_status). Possibly slows down IO performance.

    Try to enable if you need to handle situations when SD cards are not unmounted properly before physical removal or you are experiencing issues with SD cards.

    Doesn't do anything for other memory storage media.

  bool use_one_fat

    Use 1 FAT (File Allocation Tables) instead of 2. This decreases reliability, but makes more space available (usually only one sector). Note that this option has effect only when the filesystem is formatted. When mounting an already-formatted partition, the actual number of FATs may be different.

### Macros

VFS_FAT_MOUNT_DEFAULT_CONFIG()

### Type Definitions

typedef PARTITION esp_vfs_fat_pdrv_part_t

typedef LBA_t esp_vfs_fat_drive_divide_arr_t[4]

  For example:

  `{100, 0, 0, 0}` will create a single partition with 100% of the drive space. `{50, 50, 0, 0}` will create two partitions, first with 50% of the drive space and second with the remaining 50%. `{0x10000000, 0x10000000, 0x10000000, 0}` will create three partitions, each with a size of 256 MiB, leaving remaining space non-allocated.

  备注

  When the value of item is less than or equal to 100, it specifies the partition size in percentage of the entire drive space. When it is larger than 100, it specifies number of sectors. The partition map table is terminated by a zero, 4th partition in MBR format or no remaining space for next allocation. If the specified size is larger than remaining space on the drive, the partition is truncated at end of the drive.

typedef esp_vfs_fat_mount_config_t esp_vfs_fat_sdmmc_mount_config_t

### Enumerations

enum vfs_fat_x_ctx_flags_t

  Values:

  enumerator FORMATTED_DURING_LAST_MOUNT

### 磁盘 I/O API 参考

void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t *discio_impl)

  Register or unregister diskio driver for given drive number.

  When FATFS library calls one of disk_xxx functions for driver number pdrv, corresponding function in discio_impl for given pdrv will be called.

  参数:

    - pdrv -- drive number

    - discio_impl -- pointer to ff_diskio_impl_t structure with diskio functions or NULL to unregister and free previously registered drive

struct ff_diskio_impl_t

  Structure of pointers to disk IO driver functions.

  See FatFs documentation for details about these functions

  Public Members

  DSTATUS (*init)(unsigned char pdrv)

    disk initialization function

  DSTATUS (*status)(unsigned char pdrv)

    disk status check function

  DRESULT (*read)(unsigned char pdrv, unsigned char *buff, uint32_t sector, unsigned count)

    sector read function

  DRESULT (*write)(unsigned char pdrv, const unsigned char *buff, uint32_t sector, unsigned count)

    sector write function

  DRESULT (*ioctl)(unsigned char pdrv, unsigned char cmd, void *buff)

    function to get info about disk and do some misc operations

void ff_diskio_register_sdmmc(unsigned char pdrv, sdmmc_card_t *card)

  Register SD/MMC diskio driver

  参数:

    - pdrv -- drive number

    - card -- pointer to sdmmc_card_t structure describing a card; card should be initialized before calling f_mount.

esp_err_t ff_diskio_register_wl_partition(unsigned char pdrv, wl_handle_t flash_handle)

  Register spi flash partition

  参数:

    - pdrv -- drive number

    - flash_handle -- handle of the wear levelling partition.

esp_err_t ff_diskio_register_raw_partition(unsigned char pdrv, const esp_partition_t *part_handle)

  Register spi flash partition

  参数:

    - pdrv -- drive number

    - part_handle -- pointer to raw flash partition.

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。