文章目录
- @[toc]
- 官方文档介绍-FatFs
- 前提知识准备
- 一、核心编程语言:C语言(优先级★★★★★)
- 二、嵌入式系统基础(优先级★★★★★)
- 三、存储介质原理(优先级★★★★)
- 四、文件系统基础概念(优先级★★★★)
- 五、底层驱动开发能力(优先级★★★★)
- 六、辅助调试与配置能力(优先级★★★)
- 七、可选但加分的知识(优先级★★)
- 学习路径建议
- 一、**簇、FAT表、目录项详解**
- **1. 簇 (Cluster) - 存储的基本单位**
- **什么是簇?**
- **比喻:仓库的储物柜**
- **实际示例**
- **簇的特点**
- **2. FAT表 (File Allocation Table) - 簇的地图导航**
- **什么是FAT表?**
- **比喻:快递追踪系统**
- **FAT表项的值**
- **文件簇链示例**
- **FAT表在磁盘上的位置**
- **3. 目录项 (Directory Entry) - 文件的身份证**
- **什么是目录项?**
- **比喻:图书馆的图书卡片**
- **目录项的详细结构(32字节)**
- **属性字节 (DIR_Attr) 详解**
- **目录项示例**
- **三者的协同工作流程**
- **场景:读取文件"DATA.BIN"**
- **场景:创建新文件**
- **FatFs中对应的操作**
- **实际调试示例**
- **常见问题**
- **Q: 为什么我的4KB文件显示占用8KB?**
- **Q: 删除文件后数据还在吗?**
- **Q: 如何查看文件的真实簇链?**
- 二、**FAT32 磁盘布局**
- **关键结构详解**
- **1. 引导扇区 (DBR - DOS Boot Record)**
- **2. FAT 表 (File Allocation Table)**
- **3. 目录项结构 (32字节)**
- **4. 长文件名支持 (LFN)**
- **5. 簇的寻址**
- **6. 文件簇链示例**
- **与 FatFs 的关联**
- **FatFs 内部结构**
- **常用 FatFs 函数与 FAT32 的关系**
- **FAT32 限制**
- **学习建议**
- 三、实际应用
- 📁 FatFs核心设计:模块化与可移植性
- 🛠️ 如何使用FatFs:核心工作流程
- 🔄 FatFs与其他嵌入式文件系统对比
- 💡 关键概念与使用要点
- 📚 学习与移植建议
- FAT12 / FAT16 / FAT32 含义
- 一句话理解
- FAT12 / FAT16 / FAT32 的区别
- 通俗类比
- 嵌入式开发关键点
- 举个场景
文章目录
- @[toc]
- 官方文档介绍-FatFs
- 前提知识准备
- 一、核心编程语言:C语言(优先级★★★★★)
- 二、嵌入式系统基础(优先级★★★★★)
- 三、存储介质原理(优先级★★★★)
- 四、文件系统基础概念(优先级★★★★)
- 五、底层驱动开发能力(优先级★★★★)
- 六、辅助调试与配置能力(优先级★★★)
- 七、可选但加分的知识(优先级★★)
- 学习路径建议
- 一、**簇、FAT表、目录项详解**
- **1. 簇 (Cluster) - 存储的基本单位**
- **什么是簇?**
- **比喻:仓库的储物柜**
- **实际示例**
- **簇的特点**
- **2. FAT表 (File Allocation Table) - 簇的地图导航**
- **什么是FAT表?**
- **比喻:快递追踪系统**
- **FAT表项的值**
- **文件簇链示例**
- **FAT表在磁盘上的位置**
- **3. 目录项 (Directory Entry) - 文件的身份证**
- **什么是目录项?**
- **比喻:图书馆的图书卡片**
- **目录项的详细结构(32字节)**
- **属性字节 (DIR_Attr) 详解**
- **目录项示例**
- **三者的协同工作流程**
- **场景:读取文件"DATA.BIN"**
- **场景:创建新文件**
- **FatFs中对应的操作**
- **实际调试示例**
- **常见问题**
- **Q: 为什么我的4KB文件显示占用8KB?**
- **Q: 删除文件后数据还在吗?**
- **Q: 如何查看文件的真实簇链?**
- 二、**FAT32 磁盘布局**
- **关键结构详解**
- **1. 引导扇区 (DBR - DOS Boot Record)**
- **2. FAT 表 (File Allocation Table)**
- **3. 目录项结构 (32字节)**
- **4. 长文件名支持 (LFN)**
- **5. 簇的寻址**
- **6. 文件簇链示例**
- **与 FatFs 的关联**
- **FatFs 内部结构**
- **常用 FatFs 函数与 FAT32 的关系**
- **FAT32 限制**
- **学习建议**
- 三、实际应用
- 📁 FatFs核心设计:模块化与可移植性
- 🛠️ 如何使用FatFs:核心工作流程
- 🔄 FatFs与其他嵌入式文件系统对比
- 💡 关键概念与使用要点
- 📚 学习与移植建议
- FAT12 / FAT16 / FAT32 含义
- 一句话理解
- FAT12 / FAT16 / FAT32 的区别
- 通俗类比
- 嵌入式开发关键点
- 举个场景
官方文档介绍-FatFs
前提知识准备
学习FatFs文件系统是嵌入式开发中高频的技能需求(FatFs是轻量级、可移植的FAT文件系统,广泛用于MCU/单片机场景),其前置知识需围绕嵌入式基础、存储原理、编程核心能力三大维度展开,以下是分层次的必备知识清单,附学习优先级和应用关联:
一、核心编程语言:C语言(优先级★★★★★)
FatFs源码完全由标准C编写,且嵌入式场景下的移植/使用高度依赖C语言核心特性,需掌握:
- 基础语法:变量、数据类型(尤其是
uint8_t/uint16_t/uint32_t等嵌入式常用类型)、分支/循环、函数定义与调用; - 核心特性:
- 指针(一级/二级指针):FatFs大量使用指针操作缓冲区、文件目录项、FAT表;
- 结构体/枚举/联合体:FatFs的核心对象(如
FIL文件结构体、DIR目录结构体、FATFS卷结构体、FRESULT错误码枚举)均基于这些类型; - 宏定义/条件编译:FatFs的配置文件
ffconf.h全靠宏控制功能(如FF_FS_READONLY、FF_FS_MINIMIZE),需理解宏的定义与展开; - 函数指针:FatFs底层驱动接口(如
disk_ioctl)会用到函数指针实现通用适配;
- 内存操作:
memcpy/memset/memcmp等内存操作函数(FatFs内部大量用于数据读写/校验),以及嵌入式场景下的静态/动态内存分配(避免堆溢出)。
二、嵌入式系统基础(优先级★★★★★)
FatFs几乎只用于嵌入式场景,需掌握嵌入式开发的核心底层能力:
- MCU外设操作:
- 通信总线:SPI/I2C/SDIO(对接SD卡、SPI Flash)、USB Host(对接U盘)——FatFs的存储介质依赖这些总线驱动;
- GPIO/定时器:辅助存储介质的硬件初始化(如SD卡的片选引脚、Flash的复位引脚);
- 中断与并发:
- 中断处理基础:文件读写时需避免中断打断关键操作(如SD卡扇区读写),需懂中断屏蔽/恢复;
- RTOS基础(可选但高频):若在FreeRTOS/RT-Thread等系统中使用FatFs,需掌握互斥锁/信号量(防止多线程同时操作文件导致数据损坏);
- 内存管理:
- 嵌入式内存布局(栈/堆/全局区):FatFs的缓冲区(如文件读写缓存)需合理分配内存;
- 静态内存分配:嵌入式场景通常禁用
malloc/free,需用静态数组替代动态内存(FatFs支持静态配置缓冲区);
- 字节序(大小端):FAT文件系统规定数据存储为小端序,MCU若为大端(如部分PowerPC),需掌握字节序转换(FatFs提供
BYTE_SWAP宏适配,但需理解原理)。
三、存储介质原理(优先级★★★★)
FatFs需挂载具体的存储介质,必须理解介质的底层读写逻辑:
- 核心存储介质特性:
介质类型 关键知识点 SD卡/MMC卡 扇区概念(512字节/扇区,FatFs的最小操作单位)、SDIO/SPI协议、卡初始化流程、扇区读写指令; SPI Flash(如W25Q) 页/扇区/块的区别(擦除单位是块,读写单位是页)、擦写寿命、SPI Flash的读写/擦除指令; U盘(USB Host) USB枚举流程、大容量存储设备(MSC)协议、U盘的扇区读写接口; eMMC/NAND Flash 坏块管理、ECC校验(FatFs本身不处理坏块,需底层驱动适配); - 通用概念:
- 块设备(Block Device):FatFs仅支持块设备(按固定大小扇区读写),需区分“字符设备”(如串口)与块设备的差异;
- 读写时序:存储介质的读写有严格时序要求(如SD卡的CMD指令响应),需理解底层驱动的“等待就绪”逻辑。
四、文件系统基础概念(优先级★★★★)
无需深入FAT协议细节,但需掌握通用文件系统的核心逻辑,理解FatFs的设计目标:
- FAT文件系统核心术语:
- 卷(Volume):一个存储介质(如一张SD卡)对应一个FatFs卷(
FATFS结构体); - 簇(Cluster):FAT文件系统的最小分配单位(由多个扇区组成),FatFs通过簇管理文件空间;
- 文件分配表(FAT表):记录簇的使用状态(空闲/已分配/坏簇),是FatFs读写文件的核心依据;
- 目录项(Directory Entry):存储文件/目录的元信息(名称、大小、起始簇、时间戳);
- 卷(Volume):一个存储介质(如一张SD卡)对应一个FatFs卷(
- FAT格式差异:FAT12/FAT16/FAT32的区别(FatFs支持全系列,需知道
ffconf.h中FF_FS_FAT32的配置逻辑); - 文件操作通用流程:打开→读写→关闭(FatFs的API完全遵循这一逻辑,如
f_open/f_read/f_write/f_close)。
五、底层驱动开发能力(优先级★★★★)
FatFs的核心是“可移植”,其与硬件的解耦依赖底层驱动接口,需掌握:
- FatFs底层接口(diskio.c):需实现5个核心函数(FatFs的“硬件抽象层”),必须理解每个函数的作用:
disk_initialize:存储介质初始化(如SD卡初始化);disk_status:获取介质状态(就绪/未就绪);disk_read:从扇区读取数据(FatFs读文件的底层依赖);disk_write:向扇区写入数据(FatFs写文件的底层依赖);disk_ioctl:控制指令(如获取扇区数、擦除扇区、同步缓存);
- 驱动调试能力:能通过串口打印调试底层读写是否正常(如读取SD卡的MBR主引导记录、FAT表),定位“介质未就绪”“读写失败”等问题。
六、辅助调试与配置能力(优先级★★★)
- FatFs配置文件(ffconf.h):理解核心配置项的含义,按需裁剪功能(减少ROM/RAM占用):
FF_FS_READONLY:是否只读(仅读场景可开启,节省代码);FF_FS_MINIMIZE:功能裁剪级别(0-3,级别越高功能越少);FF_USE_STRFUNC:是否启用字符串操作(如f_puts/f_gets);FF_VOLUMES:支持的卷数量(如同时挂载SD卡+SPI Flash需设为2);
- 错误码解析:FatFs的返回值(
FRESULT枚举,如FR_DISK_ERR、FR_NOT_READY、FR_NO_FILE)是调试的核心,需熟记常见错误码的含义; - 编译与链接:嵌入式编译器(Keil/STM32CubeIDE/GCC)的基本使用,能将FatFs源码加入工程,解决链接时的“未定义函数”(通常是底层驱动未实现)。
七、可选但加分的知识(优先级★★)
- FAT协议细节:如MBR分区表结构、FAT表的映射规则、长文件名(LFN)的存储方式(FatFs支持长文件名,需理解其编码逻辑);
- 低功耗/异常处理:文件读写中断后的恢复逻辑(如掉电前同步文件缓存);
- 跨平台移植经验:理解FatFs的“硬件无关层”与“硬件相关层”分离设计,能快速适配不同MCU(如STM32、ESP32、51单片机)。
学习路径建议
- 先巩固C语言指针/结构体 + 嵌入式外设驱动(如SD卡SPI驱动);
- 理解FAT文件系统的核心概念(扇区/簇/FAT表);
- 从
ffconf.h最简配置入手(只读、单卷、禁用长文件名),先实现“读取SD卡中某文件”的基础功能; - 逐步开启写功能、长文件名、多卷,结合RTOS实现多线程安全的文件操作。
核心逻辑:FatFs的学习本质是“C语言+嵌入式驱动+文件系统概念”的融合,重点不是背API,而是理解“FatFs如何通过通用接口适配不同存储介质,如何基于FAT协议管理文件”。
一、簇、FAT表、目录项详解
1. 簇 (Cluster) - 存储的基本单位
什么是簇?
- 定义:簇是文件系统中存储分配的最小单位
- 组成:一个簇 = N个连续的扇区(通常N=1,2,4,8,…)
- 大小:格式化时决定,常见的有4KB(8个512字节扇区)
比喻:仓库的储物柜
仓库(硬盘) → 储物柜区(簇区) 每个储物柜(簇)有固定大小(如4KB) 即使只存1字节文件,也要占用整个储物柜实际示例
// 假设簇大小 = 4KB(8个扇区)文件"log.txt"大小=1.2KB 但实际占用空间=4KB(一个整簇) 文件"photo.jpg"大小=15KB 实际占用空间=16KB(4个簇,15/4向上取整)簇的特点
- 编号从2开始:簇0和簇1有特殊用途
- 物理位置:
簇号 → 物理扇区需要转换计算 - 浪费问题:小文件会产生"内部碎片"
2. FAT表 (File Allocation Table) - 簇的地图导航
什么是FAT表?
- 定义:一个簇号映射表,记录每个簇的状态和链接关系
- 作用:跟踪文件占用了哪些簇,以及这些簇的顺序
比喻:快递追踪系统
文件 = 一批要配送的货物 每个簇 = 一个配送站点 FAT表 = 配送路线图,告诉你: "当前站点的下一站是哪里?" "货物是否已送达?"FAT表项的值
0x00000000空闲簇,可以分配0x00000002有效簇号,指向下一簇0x0FFFFFF7坏簇,不可用0x0FFFFFF8-0x0FFFFFFF文件结束标记文件簇链示例
文件"report.doc"占用簇:5 → 8 → 12 → EOF FAT表内容: 索引 | 值 | 含义 -----|----------|---------------- 5 | 0x00000008 → 去簇8 8 | 0x0000000C → 去簇12 12 | 0x0FFFFFFF → 文件结束 其他 | 0x00000000 → 空闲FAT表在磁盘上的位置
扇区0-31: 引导区 扇区32-543: FAT1(主表) 扇区544-1055: FAT2(备份表) 扇区1056+: 数据区(簇从这里开始)3. 目录项 (Directory Entry) - 文件的身份证
什么是目录项?
- 定义:32字节的记录,存储文件的元数据信息
- 位置:位于目录文件的内容中(目录也是一种特殊文件)
比喻:图书馆的图书卡片
目录项 = 图书索引卡片 包含:书名(文件名)、位置(簇号)、大小、借阅时间等目录项的详细结构(32字节)
// 实际内存布局(小端序)字节0-10:DIR_Name[11]// "FILENAMEEXT"(8.3格式)字节11:DIR_Attr// 属性字节字节12-21:保留/时间信息 字节22-23:DIR_FstClusHI// 起始簇高16位字节24-25:DIR_WrtTime// 最后写入时间字节26-27:DIR_WrtDate// 最后写入日期字节28-29:DIR_FstClusLO// 起始簇低16位字节30-33:DIR_FileSize// 文件大小(字节)属性字节 (DIR_Attr) 详解
位0 (0x01): 只读 → 不能修改 位1 (0x02): 隐藏 → dir命令不显示 位2 (0x04): 系统文件 → 操作系统使用 位3 (0x08): 卷标 → 磁盘标签 位4 (0x10): 子目录 → 这是一个目录 位5 (0x20): 存档 → 已备份/可修改 位6-7: 保留 长文件名条目:属性=0x0F(特殊标记)目录项示例
文件: "README.TXT" (6KB) 属性: 存档(0x20) 起始簇: 0x0005 (簇5) 大小: 6144字节 十六进制表示: 52 45 41 44 4D 45 20 20 54 58 54 20 00 00 00 00 00 00 00 00 00 00 88 6D 75 54 00 00 05 00 00 00 00 00 18 00 00 00三者的协同工作流程
场景:读取文件"DATA.BIN"
1. 查找目录项(在目录文件中搜索) - 找到"DATA.BIN"的目录项 - 从中提取:起始簇号=5,文件大小=10240字节 2. 使用FAT表遍历簇链 - 读取簇5 → FAT[5]=8 - 读取簇8 → FAT[8]=12 - 读取簇12 → FAT[12]=0x0FFFFFFF(结束) 3. 读取实际数据 - 簇5物理地址 = 数据区起始 + (5-2)*每簇扇区数 - 读取簇5、8、12的内容场景:创建新文件
1. 在目录中找空闲目录项(第一个字节=0xE5或0x00) 2. 在FAT表中找空闲簇(FAT项=0x00000000) 3. 填充目录项:文件名、属性、起始簇号、大小等 4. 在FAT表中标记该簇为"已使用"FatFs中对应的操作
// 打开文件(内部操作)f_open(&fil,"test.txt",FA_READ);// 1. FatFs查找当前目录,找到"test.txt"的目录项// 2. 读取目录项,获取起始簇号和大小// 3. 初始化文件对象的当前位置信息// 读取文件f_read(&fil,buffer,1024,&br);// 1. 根据当前位置计算在哪个簇// 2. 读取该簇的数据// 3. 如果读到簇末尾,查FAT表找下一簇// 4. 更新文件位置指针// 写入文件f_write(&fil,data,512,&bw);// 1. 如果当前簇已满,分配新簇// 2. 更新FAT表,建立簇链// 3. 更新目录项中的文件大小实际调试示例
假设你想查看文件"CONFIG.CFG"的存储情况:
// 1. 找到目录项(假设在根目录)根目录起始簇=BPB_RootClus(假设=2)读取簇2,搜索"CONFIG CFG"// 2. 解析目录项起始簇号=(DIR_FstClusHI<<16)|DIR_FstClusLO=5文件大小=1500字节// 3. 跟踪FAT链FAT[5]=8// 还有数据FAT[8]=0x0FFFFFFF// 文件结束// 4. 结论:文件占用簇5和簇8簇5:0-4095字节(实际只用前1500字节) 簇8:未使用(因为1500<4096,只用一个簇就够了)// 这里似乎矛盾,说明FAT链有问题或文件大小变了常见问题
Q: 为什么我的4KB文件显示占用8KB?
A: 可能因为簇大小=8KB,或者文件系统有额外的元数据。
Q: 删除文件后数据还在吗?
A: 还在!删除只是:
- 目录项第一个字节改为0xE5(标记删除)
- FAT表中相应簇标记为空闲
- 实际数据直到被覆盖前都保留
Q: 如何查看文件的真实簇链?
A: 可以使用WinHex等工具,或自己写代码解析FAT。
二、FAT32 磁盘布局
┌─────────────────────────────────────────────────────────────────┐ │ FAT32 磁盘结构 │ ├──────────────┬──────────────┬──────────────┬────────────────────┤ │ 保留区域 │ FAT1 │ FAT2 │ 数据区域 │ │ (包含MBR/DBR) │ (文件分配表)│ (备份FAT) │ (簇区域) │ ├──────────────┼──────────────┼──────────────┼────────────────────┤ │ 扇区0 │ FAT表区域 │ FAT表备份 │ 簇2, 簇3, ... │ │ (引导扇区) │ │ │ (文件/目录数据) │ └──────────────┴──────────────┴──────────────┴────────────────────┘关键结构详解
1. 引导扇区 (DBR - DOS Boot Record)
位于第 0 扇区,包含文件系统参数:
typedefstruct{uint8_tBS_jmpBoot[3];// 跳转指令uint8_tBS_OEMName[8];// OEM名称uint16_tBPB_BytsPerSec;// 每扇区字节数 (通常512)uint8_tBPB_SecPerClus;// 每簇扇区数uint16_tBPB_RsvdSecCnt;// 保留扇区数 (FAT32通常为32)uint8_tBPB_NumFATs;// FAT表数量 (通常2)uint32_tBPB_FATSz32;// 每个FAT表大小(扇区数)uint32_tBPB_RootClus;// 根目录起始簇号// ... 还有其他字段}FAT32_BPB;2. FAT 表 (File Allocation Table)
- 作用:记录簇的分配状态和文件簇链
- FAT32表项:32位(实际只用28位)
- 特殊值:
0x00000000:空闲簇0x00000002-0x0FFFFFEF:下一簇号0x0FFFFFF7:坏簇0x0FFFFFF8-0x0FFFFFFF:文件结束
3. 目录项结构 (32字节)
typedefstruct{uint8_tDIR_Name[11];// 8.3文件名 (大写)uint8_tDIR_Attr;// 文件属性uint8_tDIR_NTRes;// NT保留uint8_tDIR_CrtTimeTenth;// 创建时间(10ms)uint16_tDIR_CrtTime;// 创建时间uint16_tDIR_CrtDate;// 创建日期uint16_tDIR_LstAccDate;// 最后访问日期uint16_tDIR_FstClusHI;// 起始簇号高16位uint16_tDIR_WrtTime;// 最后修改时间uint16_tDIR_WrtDate;// 最后修改日期uint16_tDIR_FstClusLO;// 起始簇号低16位uint32_tDIR_FileSize;// 文件大小(字节)}DIR_ENTRY;文件属性位 (DIR_Attr):
0x01: 只读0x02: 隐藏0x04: 系统文件0x08: 卷标0x10: 子目录0x20: 存档0x0F: 长文件名条目
4. 长文件名支持 (LFN)
FAT32 通过特殊目录项存储长文件名:
- 属性字节 =
0x0F - 每个 LFN 条目存储 13 个 Unicode 字符
- 通过校验和与短名关联
- 逆序排列在短名前
5. 簇的寻址
- 簇号从 2 开始(0 和 1 保留)
- 物理扇区地址计算:
数据区起始扇区 = 保留扇区数 + (FAT表数 × FAT表大小) 簇的扇区地址 = 数据区起始扇区 + (簇号 - 2) × 每簇扇区数
6. 文件簇链示例
假设文件占用簇 5、8、10:
FAT表内容: 索引 值(16进制) 含义 ... 5 0x00000008 → 下一簇是8 8 0x0000000A → 下一簇是10 10 0x0FFFFFFF → 文件结束与 FatFs 的关联
在 FatFs 中,这些概念对应为:
FatFs 内部结构
// FatFs 的 FAT32 结构体typedefstruct{FATFS*fs;// 文件系统对象DWORD sclust;// 起始簇号DWORD clust;// 当前簇号DWORD sect;// 当前扇区UINT index;// 扇区内偏移}FIL;常用 FatFs 函数与 FAT32 的关系
f_mount()- 读取引导扇区,初始化文件系统信息f_open()- 查找目录项,获取起始簇号f_read()/f_write()- 通过 FAT 表遍历簇链f_lseek()- 计算簇内偏移,可能触发簇链跳转f_truncate()- 修改 FAT 表释放簇
FAT32 限制
- 最大文件大小:4GB - 1 字节(由于 32 位文件大小字段)
- 最大分区大小:理论 2TB,实际通常 32GB-2TB
- 最大文件数:受限于 FAT 表大小和目录结构
- 性能问题:碎片化严重时,文件访问会变慢
学习建议
- 先从 FatFs API 开始:不要一开始就深入二进制格式
- 理解关键概念:簇、FAT表、目录项
- 使用调试工具:如 WinHex 查看实际磁盘结构
- 动手实验:创建小文件,观察 FAT 表和目录项变化
三、实际应用
FatFs(File Allocation Table File System)是一个专为嵌入式系统设计的、兼容FAT格式的开源文件系统模块。它不依赖操作系统,能让你在W25Q128这类存储芯片上实现类似电脑U盘的文件管理。
📁 FatFs核心设计:模块化与可移植性
FatFs采用清晰的分层架构,便于移植到不同硬件:
| 架构层级 | 组件名称 | 核心职责 | 你需要做什么 |
|---|---|---|---|
| 应用层 | 用户程序 | 调用FatFs API(如f_open, f_write) | 编写业务逻辑代码 |
| 中间层 | FatFs核心 (ff.c, ff.h) | 实现FAT文件系统逻辑(目录、文件读写) | 通常无需修改 |
| 物理层 | 磁盘I/O层 (diskio.c, diskio.h) | 提供底层存储介质的读写、控制接口 | 重点移植部分,需对接W25Q128驱动 |
这种设计的精髓在于:你只需实现底层diskio.c中的几个函数(如disk_read,disk_write)来驱动W25Q128,上层的文件操作(创建、读写文件)FatFs都已为你准备好。
🛠️ 如何使用FatFs:核心工作流程
典型的FatFs操作遵循以下流程,这是一个在SD卡或W25Q128上创建并写入文件的简化示例:
// 0. 初始化你的硬件(SPI、GPIO等)spi_init();// 1. 注册工作区(挂载)FATFS fs;// 文件系统对象FRESULT fr=f_mount(&fs,"0:",1);// "0:"对应diskio.c中的驱动器编号// 2. 创建并打开文件FIL fil;fr=f_open(&fil,"0:/log.txt",FA_CREATE_ALWAYS|FA_WRITE);// 3. 写入数据UINT bw;chardata[]="Hello, W25Q128 with FatFs!\n";fr=f_write(&fil,data,sizeof(data)-1,&bw);// 4. 关闭文件并卸载f_close(&fil);f_unmount("0:");🔄 FatFs与其他嵌入式文件系统对比
了解FatFs的定位有助于你做出选择:
| 特性对比 | FatFs | LittleFS | SPIFFS |
|---|---|---|---|
| 主要特点 | FAT标准兼容,通用性强 | 专为掉电安全和磨损均衡优化 | 专为SPI NOR Flash设计,极度轻量 |
| 优点 | 与PC兼容性好,文档丰富,功能全面 | 防掉电损坏,动态磨损均衡 | 内存占用极小,设计简洁 |
| 缺点 | 掉电保护较弱,需自行处理均衡 | 代码比FatFs稍复杂 | 非标准格式,社区支持相对少 |
| 适用场景 | 需要与PC交换数据、功能要求全面的应用 | 频繁断电或需高可靠性的IoT设备 | 资源极度紧张的小容量SPI Flash |
💡 关键概念与使用要点
FAT卷与驱动器号
FatFs通过“驱动器号”(如0:,1:)来标识不同的物理设备。在你的diskio.c中,需要将这些编号映射到具体的W25Q128芯片实例。可裁剪性
FatFs通过ffconf.h配置文件提供大量选项。你可以根据需求关闭不用的功能(如长文件名、多分区),以节省宝贵的ROM和RAM空间。重要:确保底层驱动稳定
一切文件操作都建立在disk_read/disk_write稳定可靠的基础上。对于W25Q128,你需要特别注意:- SPI时序:确保在Quad SPI模式下时序正确。
- 擦除管理:NOR Flash写入前必须先擦除(变为0xFF)。FatFs不负责Flash的擦除特性,你需要在
disk_write函数中自行管理擦除操作(通常是整个扇区擦除)。 - 磨损均衡:W25Q128每个扇区约10万次擦写寿命。FatFs本身不提供完善的磨损均衡策略,若频繁写同一区域(如日志),需在应用层设计策略或考虑LittleFS。
📚 学习与移植建议
- 官方资源:访问FatFs官网,阅读
00readme.txt和doc目录下的文档,这是最权威的信息源。 - 移植起点:以官方提供的“
diskio.c模板”为起点进行修改,重点关注disk_status、disk_initialize、disk_read、disk_write、disk_ioctl这五个函数。 - 调试:先确保底层W25Q128的读写擦除操作绝对可靠,再挂载FatFs。使用
f_mkfs创建文件系统是很好的第一步测试。
总结来说,FatFs是连接你的嵌入式应用与W25Q128这类存储芯片的强大桥梁。理解其分层架构和移植关键点,你就能在设备上高效管理文件。
FAT12 / FAT16 / FAT32 含义
一句话理解
FatFs 是嵌入式单片机用的开源C语言文件系统库;这句话意思:只要你的SD卡 / SPI Flash / U盘 格式化成 FAT12、FAT16、FAT32,FatFs 就能识别、读写里面的文件,和Windows电脑互相兼容。
FatFs 本身不是硬件驱动!
它只负责文件逻辑(打开、读写、创建文件、目录);底层读扇区、写扇区需要你自己写底层disk驱动。
FAT12 / FAT16 / FAT32 的区别
名字里的数字 =文件分配表FAT里,簇编号占用多少bit,决定能管理多大存储:
- FAT12(12bit)
- 老式软盘,容量很小,最大约32MB;现在极少用。
- FAT16(16bit)
- 老U盘、小存储卡;分区上限约2GB;单个文件最大2GB。
- FAT32(32bit)
- 最常用!普通SD卡、U盘默认格式;分区最大2TB,单个文件上限4GB(这是FAT32经典限制)。
通俗类比
文件系统 = 仓库的账本:
- FAT表就是账本,记录每一块数据(簇)放在哪里、是不是空闲;
- FAT12/FAT16/FAT32是三种版本的账本格式;
- FatFs 就是一个能看懂这三种账本的“仓库管理员”,在单片机上帮你新建文件、读写数据、删除文件;
- 把SD卡插到电脑Windows格式化,选FAT32,插到单片机,FatFs就能直接识别这个卡上的文件。
嵌入式开发关键点
- FatFs 自动识别卡是FAT12/FAT16/FAT32,你代码不用手动指定格式;
- FatFs新版还可选开启 exFAT(支持大于4GB单文件);
- 可以用FatFs的
f_mkfs()函数,直接在单片机里把存储介质格式化成FAT12/FAT16/FAT32,不用电脑格式化; - 代码只需要实现底层
disk_read、disk_write这两个读写扇区函数,上层f_open、f_write等API直接调用。
举个场景
STM32+SD卡,移植FatFs:
- SD卡电脑格式化FAT32;
- 单片机上电,FatFs挂载SD卡;
- 调用
f_open("log.txt", FA_CREATE_ALWAYS | FA_WRITE)创建日志文件,写采集数据; - 写完拔卡插到电脑,Windows能直接打开
log.txt。