- 概述
1.1. 简介
FAT全称File Allocation Table,是一种由微软发明的文件系统。FAT文件系统考虑当时计算机性能有限,所以未被复杂化,此特性使它能用在嵌入式系统。
FatFs是FAT文件系统的开源实现。FatFs是用C语言编写的,符合ANSI C (C89)标准,并且软件架构中把存储设备的操作接口独立出来,使得FatFs与平台无关,非常易于移植。FatFs官方网站http://elm-chan.org/fsw/ff/00index_e.html 。
1.2. 目的
本文档主要描述我司对FatFs的自定义配置,以及FatFs移植方法。方便工程师快速地把FatFs移植到不同的机型。本文以FatFs R0.13b版本为例进行介绍。
- FatFs介绍
2.1. 配置项
FatFs在编译前配置需要支持的功能,所有的配置项保存在文件ffconf.h中。下表列出了FatFs所有选项的默认值以及我司修改后的值。其中标蓝的项是与默认值不一样的项。
配置项 含义 默认值 XGD配置值
FF_FS_READONLY 使能或禁用与写相关函数,0是使能写函数 0 0
FF_FS_MINIMIZE 基本API配置等级,0是最高等级,支持的API最多 0 0
FF_USE_STRFUNC 是否使能字符处理函数,0是禁用 0 0
FF_USE_FIND 使能或禁用在指定目录内搜索指定文件函数:f_findfirst和f_findnext,0是禁用 0 0
FF_USE_MKFS 是否使能f_mkfs函数创建文件系统,0是禁用 0 1
FF_USE_FASTSEEK 使能或禁用快速搜索功能,使能后,可以加快f_lseek、f_read和f_write函数执行,0是禁用 0 0
FF_USE_EXPAND 使能或禁用f_expand函数,该函数可以为文件分配连续数据区域,0是禁用 0 0
FF_USE_CHMOD 使能或禁用元数据控制函数:f_chmod和f_utime,0是禁用 0 0
FF_USE_LABEL 使能或禁用卷标签API函数:f_getlabel和f_setlabel,0是禁用 0 0
FF_USE_FORWARD 使能或禁用f_forward函数,0是禁用 0 0
FF_CODE_PAGE 规定目标系统使用的OEM代码,如果该代码设置的不正确,可能会引起文件打开失败(跟文件名称编码相关) 932 437
FF_USE_LFN 使能或禁用长文件名(LFN),0是禁用,1是使能并使用静态数组作为缓冲(非线程安全),2是使能并使用栈内存作为缓冲,3是使能并使用堆内存作为缓冲,使能后需要编译ffunicode.c。如果配置为0,文件和文件夹的名称最长只能支持8字节。 0 2
FF_MAX_LFN 长文件名工作缓冲区大小,可以为12~255字节。当禁用长文件名时,此选项无效 255 31
FF_LFN_UNICODE 使能或禁用Unicode。如果要使用Unicode(UTF16)字符串路径名,需要使能LFN和设置本选项为1。此选项还影响字符串I/O功能函数。如果禁用长文件名,此选项必须为0 0 0
FF_LFN_BUF 255 255
FF_SFN_BUF 12 12
FF_STRF_ENCODE 使能Unicode API函数,包括f_gets(),f_putc(), f_puts,f_printf(),0是禁用 3 0
FF_FS_RPATH 使能或禁用相对路径函数,0是禁用 0 0
FF_VOLUMES 配置可用卷的数目 1 1
FF_STR_VOLUME_ID 使能或禁用字符串卷标识,0是禁用 0 0
FF_VOLUME_STRS 逻辑驱动器的字符串卷标识 “RAM”,“NAND”,“CF”,“SD”,“SD2”,“USB”,“USB2”,“USB3” “RAM”,“NAND”,“CF”,“SD”,“SD2”,“USB”,“USB2”,“USB3”
FF_MULTI_PARTITION 使能或禁止多分区函数,0是禁用 0 0
FF_MIN_SS 定义扇区大小,有效值为512、1024、2048、4096,需要根据硬件配置来定义,可定义为Flash的页大小。FF_MIN_SS定义最小扇区大小,FF_MAX_SS定义最大扇区大小。当FF_MAX_SS > FF_MIN_SS,FatFs被配置为扇区大小可变的并且必须在函数disk_ioctl中实现GET_SECTOR_SIZE命令。 512 根据实际硬件配置,如NAND Flash页大小为2K,则可以定义为2048
FF_MAX_SS 512 根据实际硬件配置,强烈建议把FF_MIN_SS和FF_MAX_SS配置成相同的值
FF_USE_TRIM 使能或禁用ATA-TRIM函数,0是禁用 0 0
FF_FS_NOFSINFO 使能或禁用空闲簇计数和最后分配的簇计数,0是禁用 0 0
FF_FS_TINY 配置FatFs为正常模式或者微型(TINY)模式。配置为微型模式后,对内存需求变小,文件对象数据结构FIL会减少_MAX_SS字节。程序复用FATFS数据结构中的缓冲区代替FIL数据结构中去除掉的缓冲区,0是正常模式 0 0
FF_FS_EXFAT 使能或禁用exFAT文件系统,0是禁用 0 0
FF_FS_NORTC 使能或禁用时间戳函数。使能时间戳函数需要硬件RTC,并且需要提供底层函数get_fattime 0 0
FF_NORTC_MON 如果系统没有RTC,这些宏用来定义固定时间戳 1 1
FF_NORTC_MDAY 1 1
FF_NORTC_YEAR 2018 2018
FF_FS_LOCK 使能或禁用文件锁功能,控制重复打开文件和非法打开文件对象,>0表示可同时打开的文件和文件夹数量 0 64
FF_FS_REENTRANT 使能或禁用FatFs模块的可重入特性,0是禁用 0 1
FF_FS_TIMEOUT 设置超时时间,单位为系统时钟滴答周期,当宏_FS_REENTRANT=0时,本设置无效 1000 1000
FF_SYNC_t 定义同步对象类型,取决于OS,当宏FF_FS_REENTRANT=0时,本设置无效 HANDLE 根据实际平台配置
已配置功能:
- 支持全部基础接口
- 不支持字符处理接口
- 不支持搜索接口
- 支持f_mkfs接口
- 不支持多分区
- 支持长文件名(最长31字节)
- 支持同时打开在同一个分区上的多个文件
- 不支持exFAT
2.2. 资源需求
Fatfs本身并不需要其他的外设资源,只需要占用ROM和RAM。
下表是不同平台、不同配置,代码占用的空间统计:
其中Full表示支持全部接口,Min表示只支持最少的接口;R/W表示可支持文件系统的读写,R/O表示只读文件系统。
配置项中,文件名支持的编码也会影响代码大小,不同编码需要增加的代码空间如下所示:
以THM3100平台为例,使用Keil v5编译器,开启-O1优化选项,按照2.1节的自定义项进行配置,fatfs占用资源如下:
Code=9740 RO-data=1112 RW-data=8 ZI-data=1016
因此,FatFs需要ROM空间约11K字节,栈空间约1K字节。
2.3. 软件架构
整个软件框架如上图所示,应用程序只调用DDI接口,屏蔽底层不同文件系统的差异。FatFs内部也有分层,
- 对外是一系列的参考POSIX标准的API;
- 中间是文件系统的处理逻辑;
- 底下是disk IO API,这些API是由移植者实现,被FatFs调用,屏蔽不同存储设备的差异。
2.4. 关键数据结构
-
接口执行结果枚举
typedef enum {
FR_OK = 0, /* (0) Succeeded /
FR_DISK_ERR, / (1) A hard error occurred in the low level disk I/O layer /
FR_INT_ERR, / (2) Assertion failed /
FR_NOT_READY, / (3) The physical drive cannot work /
FR_NO_FILE, / (4) Could not find the file /
FR_NO_PATH, / (5) Could not find the path /
FR_INVALID_NAME, / (6) The path name format is invalid /
FR_DENIED, / (7) Access denied due to prohibited access or directory full /
FR_EXIST, / (8) Access denied due to prohibited access /
FR_INVALID_OBJECT, / (9) The file/directory object is invalid /
FR_WRITE_PROTECTED, / (10) The physical drive is write protected /
FR_INVALID_DRIVE, / (11) The logical drive number is invalid /
FR_NOT_ENABLED, / (12) The volume has no work area /
FR_NO_FILESYSTEM, / (13) There is no valid FAT volume /
FR_MKFS_ABORTED, / (14) The f_mkfs() aborted due to any problem /
FR_TIMEOUT, / (15) Could not get a grant to access the volume within defined period /
FR_LOCKED, / (16) The operation is rejected according to the file sharing policy /
FR_NOT_ENOUGH_CORE, / (17) LFN working buffer could not be allocated /
FR_TOO_MANY_OPEN_FILES, / (18) Number of open files > FF_FS_LOCK /
FR_INVALID_PARAMETER / (19) Given parameter is invalid */
} FRESULT; -
文件句柄结构体
typedef struct {
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) /
BYTE flag; / File status flags /
BYTE err; / Abort flag (error code) /
FSIZE_t fptr; / File read/write pointer (Zeroed on file open) /
DWORD clust; / Current cluster of fpter (invalid when fptr is 0) /
DWORD sect; / Sector number appearing in buf[] (0:invalid) /
#if !FF_FS_READONLY
DWORD dir_sect; / Sector number containing the directory entry (not used at exFAT) /
BYTE dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) /
#endif
#if FF_USE_FASTSEEK
DWORD cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) /
#endif
#if !FF_FS_TINY
BYTE buf[FF_MAX_SS]; / File private data read/write window */
#endif
} FIL;
2.5. 可调用接口
按照上述配置,FatFs可调用的接口如下:
-
打开文件
FRESULT f_open (
FIL* fp, /* [OUT] Pointer to the file object structure /
const TCHAR path, /* [IN] File name /
BYTE mode / [IN] Mode flags */
); -
关闭文件
FRESULT f_close (
FIL* fp /* [IN] Pointer to the file object */
); -
从文件读取内容
FRESULT f_read (
FIL* fp, /* [IN] File object /
void buff, /* [OUT] Buffer to store read data /
UINT btr, / [IN] Number of bytes to read /
UINT br /* [OUT] Number of bytes read */
); -
把内容写入文件
FRESULT f_write (
FIL* fp, /* [IN] Pointer to the file object structure /
const void buff, /* [IN] Pointer to the data to be written /
UINT btw, / [IN] Number of bytes to write /
UINT bw /* [OUT] Pointer to the variable to return number of bytes written */
); -
移动文件读写指针的位置
FRESULT f_lseek (
FIL* fp, /* [IN] File object /
FSIZE_t ofs / [IN] File read/write pointer */
); -
截取文件大小
FRESULT f_truncate (
FIL* fp /* [IN] File object */
); -
把缓冲区的数据强制写入存储设备
FRESULT f_sync (
FIL* fp /* [IN] File object */
); -
获取当前读写指针的位置
FSIZE_t f_tell (
FIL* fp /* [IN] File object */
); -
获取文件大小
FSIZE_t f_size (
FIL* fp /* [IN] File object */
); -
打开文件夹
FRESULT f_opendir (
DIR* dp, /* [OUT] Pointer to the directory object structure /
const TCHAR path /* [IN] Directory name */
); -
关闭文件夹
FRESULT f_closedir (
DIR* dp /* [IN] Pointer to the directory object */
); -
读取文件夹内容
FRESULT f_readdir (
DIR* dp, /* [IN] Directory object /
FILINFO fno /* [OUT] File information structure */
); -
读取文件或子文件夹信息
FRESULT f_stat (
const TCHAR* path, /* [IN] Object name /
FILINFO fno /* [OUT] FILINFO structure */
); -
删除文件或子文件夹
FRESULT f_unlink (
const TCHAR* path /* [IN] Object name */
); -
重命名文件或子文件夹
FRESULT f_rename (
const TCHAR* old_name, /* [IN] Old object name /
const TCHAR new_name /* [IN] New object name */
); -
创建文件夹
FRESULT f_mkdir (
const TCHAR* path /* [IN] Directory name */
); -
挂载分区
FRESULT f_mount (
FATFS* fs, /* [IN] Filesystem object /
const TCHAR path, /* [IN] Logical drive number /
BYTE opt / [IN] Initialization option */
); -
创建分区
FRESULT f_mkfs (
const TCHAR* path, /* [IN] Logical drive number /
BYTE opt, / [IN] Format options /
DWORD au, / [IN] Size of the allocation unit /
void work, /* [-] Working buffer /
UINT len / [IN] Size of working buffer */
); -
获取指定分区空闲簇的数量
FRESULT f_getfree (
const TCHAR* path, /* [IN] Logical drive number /
DWORD nclst, /* [OUT] Number of free clusters /
FATFS* fatfs /* [OUT] Corresponding filesystem object */
); -
移植方法
3.1. 存储设备操作接口
FatFs把存储设备相关的操作接口都定义在diskio.c文件中,在移植时,需要实现5个接口,包括disk_status、disk_initialize、disk_read、disk_write和disk_ioctl。 -
DSTATUS disk_initialize (BYTE pdrv);
功能:根据传入参数,初始化指定的存储设备,并返回设备状态
参数:pdrv存储设备ID
返回值:正常返回0,错误值包括STA_NOINIT、STA_NODISK等状态。 -
DSTATUS disk_status (BYTE pdrv);
功能:根据传入参数,返回不同存储设备的状态
参数:pdrv存储设备ID
返回值:正常返回0,错误值包括STA_NOINIT、STA_NODISK等状态。 -
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
功能:从存储设备读取一个扇区的数据,保存到buff。
参数:sector表示从第几个扇区开始读取,从0开始,对应实际的偏移位置为sector * FF_MAX_SS。
count表示读取几个扇区,实际读取的大小为count * FF_MAX_SS字节。
返回值:函数执行成功返回RES_OK,错误值包括RES_ERROR、RES_NOTRDY等。 -
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
功能:把buff中的数据写入到存储设备的指定位置。
参数:sector表示从第几个扇区开始写入,从0开始,对应实际的偏移位置为sector * FF_MAX_SS。
count表示读取几个扇区,实际读取的大小为count * FF_MAX_SS字节。
返回值:函数执行成功返回RES_OK,错误值包括RES_ERROR、RES_NOTRDY等。 -
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
功能:存储设备的控制命令。
注意:需要根据配置实现不同的命令。
-
CTRL_SYNC
实现条件:FF_FS_READONLY == 0则必需实现此命令
功能:阻塞写入缓冲区中的数据到存储器
参数:无 -
GET_SECTOR_COUNT
实现条件:FF_USE_MKFS == 1则必需实现此命令
功能:返回存储器的扇区总个数
参数:(DWORD *)data,返回扇区个数 -
GET_BLOCK_SIZE
实现条件:FF_USE_MKFS == 1则必需实现此命令
功能:返回一个擦除块共有几个扇区。这个命令主要是针对Flash存储器擦除时做数据对齐。返回的擦除块大小必需大于等于1且小于等于32768,并且只能是2的n次方。如果擦除块的大小未知或者是非Flash存储器,则返回1。
参数:(DWORD *)data,返回一个擦除块的大小,单位扇区
3.2. 多线程互斥
配置FF_FS_REENTRANT = 1后,FatFs可以支持同时打开同一个存储器中的多个文件(线程安全)。FatFs总是支持同时打开保存在两个不同存储器的文件(线程安全),不受FF_FS_REENTRANT选项的影响。f_mount、f_mkfs和f_fdisk三个存储器控制函数总是不能同时操作,无论是否配置FF_FS_REENTRANT选项。
为了实现线程安全,移植时需要
-
配置FF_SYNC_t
把FF_SYNC_t定义成当前平台互斥锁类型的指针,例如linux平台可定义为(pthread_mutex_t *)。 -
添加头文件
把互斥锁类型的头文件包含在ffconf.h中,例如linux平台需要包含pthread.h头文件。 -
实现ffsystem.c中关于互斥的接口
-
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj);
功能:创建一个互斥锁
参数:vol存储器编号
sobj返回互斥锁句柄
返回值:返回1表示成功,0表示失败 -
int ff_req_grant (FF_SYNC_t sobj);
功能:申请互斥锁
参数:sobj互斥锁句柄
返回值:返回1表示成功,0表示失败 -
void ff_rel_grant (FF_SYNC_t sobj);
功能:释放互斥锁
参数:sobj互斥锁句柄 -
int ff_del_syncobj (FF_SYNC_t sobj);
功能:销毁一个互斥锁
参数:sobj互斥锁句柄
返回值:返回1表示成功,0表示失败
3.3. 文件名长度
默认支持的文件和文件夹名称长度为8字节。如果需要支持大于8字节长度的名称,需要配置FF_USE_LFN。
配置FF_USE_LFN 为非0值后,需要增加编译ffunicode.c文件。
配置FF_USE_LFN = 3后,需要实现ffsystem.c中的动态内存申请函数ff_memalloc和释放函数ff_memfree。