一文读懂FATFS文件系统
FATFS是一个专为小型嵌入式系统设计的开源FAT文件系统模块,采用标准C语言编写,具有良好的硬件平台独立性。它支持FAT12/16/32格式,具有多存储媒介支持、独立缓冲区等特点,可轻松移植到8051、ARM等单片机。移植时主要修改ffconf.h、diskio.c和ffsystem.c三个文件,通过配置选项支持中文文件名、RTC时间戳等功能。底层驱动需实现SD卡初始化、读写扇区等接口函数。FA
一、FATFS文件系统简介
FatFs 是一种完全免费开源的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C 语言编写,所以具有良好的硬件平台独立性,可以移植到 8051、 PIC、 AVR、 SH、 Z80、 H8、 ARM 等系列单片机上而只需做简单的修改。它支持 FATl2、 FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对 8 位单片机和 16 位单片机做了优化。
二、FATFS文件系统特点
1. Windows兼容的FAT文件系统
2. 不依赖于平台,易于移植
3. 代码和工作区占用空间非常小
4. 多种配置选项
5. 多卷(物理驱动器和分区)
6. 多ANSI/OEM代码页,包括DBCS
7. 在ANSI/OEM或Unicode中长文件名的支持
8. RTOS的支持
9. 多扇区大小的支持
10.只读,最少API,I/O缓冲区等等

三、FATFS文件系统移植性介绍
fatfs模块是ANSI C(C89)编写的。 没有平台的依赖, 编译器只要符合ANSI C标准就可以编译。
fatf模块假设大小的字符/短/长8/16/32位和int是16或32位。 这些数据类型在integer.h文件中定义。这些数据类型在大多数的编译器中定义都符合要求。 如果现有的定义与编译器有任何冲突发生时,需要自己解决。
四、文件系统源码文件与框架介绍
下载地址:http://elm-chan.org/fsw/ff/00index_e.html
将下载的源码解压后可以得到两个文件夹: documents 和 source。documents 里面主要是对 FATFS 的介绍(离线文档—英文和日文),而 source 里面才是我们需要的源码。
在source目录下,与平台无关的是:
ffconf.h FATFS配置文件,用于配置文件系统
lff.h 应用层头文件
lff.c 应用层源文件
ldiskio.h 硬件层头文件
lffunicode.c 语言编码配置文件(比如支持中文等)
ffsystem.c 系统配置文件(堆空间分配、操作系统配置等等)
与平台相关的代码
diskio.c 底层接口文件(用户需要根据自己的硬件进行修改、提供底层代码)
FATFS 模块在移植的时候,我们一般只需要修改 3 个文件,即 ffconf.h 、 diskio.c 、ffsystem.c。
FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们可以通过配置里面的一些选项,来满足自己的需求。

最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC上读/写文件那样简单。
中间层 FATFS 模块,实现了 FAT 文件读/写协议。 FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口(disk、I/O)和供给文件创建修改时间的实时时钟。
五、 FATFS文件系统移植
下面移植的工程里,底层媒介采用SD卡,所以在移植之前,需要先写好SD卡的底层驱动代码。、1. 软件采用KEIL5
2. CPU采用STM32F103ZET6
3. SD卡采用金士顿的8GB卡

(1) 配置是否支持格式化函数: f_mkfs函数
#define FF_USE_MKFS1
(2) 配置文件系统编码: 配置为936可以支持创建中文文件夹
#define FF_CODE_PAGE936
-
支持的编码如下:
|
/ 437 - 美国 / 720 - 阿拉伯语 / 737 - 希腊文 / 771 - KBL / 775 - 波罗的海 / 850 - 拉丁文1 / 852 - 拉丁文2 / 855 - 西里尔文 / 857 - 土耳其语 / 860 - 葡萄牙语 / 861 - 冰岛语 / 862 - 希伯来文 / 863 - 加拿大法语 / 864 - 阿拉伯文 / 865 - 北欧 / 866 - 俄语 / 869 - 希腊文2 / 932 - 日文(DBCS) / 936 - 简体中文(DBCS) / 949 - 韩文(DBCS) / 950 - 繁体中文(DBCS) |
配置支持长文件名称: 在堆空间上创建动态缓冲区
#define FF_USE_LFN3
#define FF_USE_LFN3
#define FF_MAX_LFN255
/* FF_USE_LFN切换对LFN的支持(长文件名).
/ 0:禁用LFN。 FF_MAX_LFN无效。
/ 1:在BSS上启用带有静态工作缓冲区的LFN。 始终不是线程安全的。
/ 2:在STACK上使用动态工作缓冲区启用LFN。
/ 3:在HEAP上启用带有动态工作缓冲区的LFN。
如果选择在堆空间上创建动态缓冲区,就需要使用ffsystem.c文件里的动态空间分配与释放函数。
需要在ffsystem.c文件里加入#include <stdlib.h>头文件,用于支持malloc/free函数。
如果支持了malloc/free函数,需要修改STM32启动文件的堆空间范围,如果空间范围大小不够,会导致malloc函数调用失败。
配置支持系统RTC实时时钟
#define FF_FS_NORTC0 //支持自己设定时间
#define FF_NORTC_MON1
#define FF_NORTC_MDAY1
#define FF_NORTC_YEAR2018
/*选项FF_FS_NORTC切换时间戳函数。 如果系统没有
/不需要任何RTC功能或有效时间戳,设置FF_FS_NORTC = 1以禁用
/时间戳功能。 FatFs修改的每个对象都有一个固定的时间戳
/由当地时间的FF_NORTC_MON,FF_NORTC_MDAY和FF_NORTC_YEAR定义。
/要启用时间戳功能(FF_FS_NORTC = 0),需要使用get_fattime()函数
/添加到项目中以读取当前时间形式的实时时钟。FF_NORTC_MON,
/FF_NORTC_MDAY和FF_NORTC_YEAR无效。
/这些选项对只读配置无效(FF_FS_READONLY = 1)。*/
在get_fattime()函数里,可以将工程里的RTC时间加入进去,给文件创建函数提供时间。
get_fattime()函数在ff.h里进行了声明,用户自己需要在diskio.c文件里实现该函数。
get_fattime函数实现示例: (如果本工程里有RTC时间,可以将时间替换为正确的RTC结构里的时间)。
DWORD get_fattime(void)
{
return (DWORD)(2019-1980)<<25| //年
4<<21| //月
26<<16| //日
14<<11| //时
39<<5| //分
14; //秒
}
六、修改diskio.c文件
(1). 完善SD卡初始化函数
DSTATUS disk_initialize (BYTE pdrv
/* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case DEV_SD :
printf("SD卡初始化!\n");
stat = SDCardDeviceInit();
//替换为自己工程里的SD卡初始化函数
return stat;
}
return STA_NOINIT;
}
(2). 完善读扇区函数
DRESULT disk_read (BYTE pdrv,
/* Physical drive nmuber to identify the drive */
BYTE *buff,
/* Data buffer to store read data */
DWORD sector,
/* Start sector in LBA */
UINT count
/* Number of sectors to read */
) {
switch (pdrv) {
case DEV_SD :
SDCardReadData(buff, sector, count);
//替换为自己工程里的SD卡读扇区函数
return RES_OK;
}
return RES_PARERR;
}
(3). 完善写扇区函数
DRESULT disk_write (BYTE pdrv,
/* Physical drive nmuber to identify the drive */
const BYTE *buff,
/* Data to be written */
DWORD sector,
/* Start sector in LBA */
UINT count
/* Number of sectors to write */
) {
switch (pdrv) {
case DEV_SD :
SDCardWriteData((u8*)buff, sector, count);
//替换为自己工程里的SD卡写扇区函数
return RES_OK;
}
return RES_PARERR;
}
(4). 完善ioctl控制函数 : 如果要调用f_mkfs函数就需要实现下面函数
DRESULT disk_ioctl (BYTE pdrv,
/* Physical drive nmuber (0..) */
BYTE cmd,
/* Control code */
void *buff
/* Buffer to send/receive control data */
) {
DRESULT res=0;
switch (pdrv) {
case DEV_SD : switch(cmd) {
case GET_SECTOR_COUNT:
*(u32*)buff=GetSDCardSectorCount();
//SD卡的扇区数量
res=RES_OK;
break;
case GET_SECTOR_SIZE:
//每个扇区是512字节
*(u32*)buff=512;
res=RES_OK;
break;
case GET_BLOCK_SIZE:
// 8个扇区=一个块
*(u32*)buff=8;
res=RES_OK;
break;
}
return RES_OK;
}
return RES_PARERR;
}
七、FATFS文件系统函数介绍
7.1 f_open函数介绍
f_open函数打开一个文件并创建一个文件对象。 文件对象用于对文件的后续读/写操作以标识。
FRESULT f_open (
FIL* fp, /*指向空白文件对象的指针*/
const TCHAR* path, / *指向文件名的指针* /
BYTE mode / *访问模式和文件打开模式标志* /
)
7.2 模式标志位:
模式标志,指定文件的访问类型和打开方法。 它由以下标志的组合指定。
FA_READ
指定对象的读访问权限。 可以从文件中读取数据。
FA_WRITE
指定对象的写访问权。 数据可以写入文件。 与FA_READ结合用于读写访问。
FA_OPEN_EXISTING
打开文件。 如果文件不存在,则函数将失败。 (默认)
FA_CREATE_NEW
创建一个新文件。 如果文件存在,则函数将失败并显示FR_EXIST。
FA_CREATE_ALWAYS
创建一个新文件。 如果文件存在,则会被截断并覆盖。
FA_OPEN_ALWAYS
如果文件存在,则打开该文件。 如果没有,将创建一个新文件。
FA_OPEN_APPEND
与FA_OPEN_ALWAYS相同,但读/写指针设置为文件末尾。
7.3 f_read函数介绍
该函数开始从读/写指针指向的位置读取文件中的数据。 读/写指针随读取的字节数而增加。 函数成功后,应检查* br以检测文件的结尾。 在* br <btr的情况下,这意味着读/写指针在读操作期间到达文件的末尾。
FRESULT f_read(
FIL * fp, / * [IN]文件对象* /
void * buff,/ * [OUT]用于存储读取数据的缓冲区* /
UINT btr, / * [IN]要读取的字节数* /
UINT * br / * [OUT]读取的字节数* /
);
7.4 f_write函数介绍
该函数开始在读/写指针指向的位置将数据写入文件。 读/写指针随着写入的字节数而增加。
FRESULT f_write
(
FIL * fp, / * [IN]指向文件对象结构的指针* /
const void * buff,/ * [IN]指向要写入的数据的指针* /
UINT btw, / * [IN]要写入的字节数* /
UINT * bw / * [OUT]指向变量的指针,返回写入的字节数* /
);
7.5 f_close函数介绍
f_close函数关闭打开的文件对象。 如果文件已更改,则文件的缓存信息将写回底层存储设备。 函数成功后,文件对象不再有效,可以将其丢弃。
FRESULT f_close(
FIL * fp / * [IN]指向文件对象的指针* /
);
7.6 f_lseek函数介绍
打开文件对象中的文件读/写指针指向要在下一个读/写操作中读/写的数据字节。它以读/写的字节数为前提。 f_lseek函数移动文件读/写指针,而不对文件进行任何读/写操作。
在写入模式下指定超出文件大小的偏移量时,文件大小将扩展为指定的偏移量。扩展区域中的文件数据未定义,因为在此过程中没有数据写入文件。这适合于快速地将数据区域预分配给文件以进行快速写入操作。当需要将连续数据区域分配给文件时,请改用f_expand函数。在f_lseek函数成功之后,应检查当前的读/写指针,以确保读/写指针已被移动更正。
FRESULT f_lseek(
FIL * fp, / * [IN]文件对象* /
FSIZE_t ofs / * [IN]偏移文件读/写指针的偏移量,只能从文件头开始偏移* /
);
更多推荐



所有评论(0)