Fatfs文件系统常用函数
本文将介绍Fatfs文件系统的常用函数,学过C语言文件函数的小伙伴们就可以跳过了,因为两者之间基本没有什么区别:
返回值枚举:
下文提到的大多数函数都会返回FRESULT枚举,可以通过返回值来调试程序,找出程序出错的原因,FRESULT枚举如下:
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 parameter error */
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 > _FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
f_mount:
挂载和取消挂载文件系统
FRESULT f_mount (
FATFS* fs, /* [IN] File system object */
const TCHAR* path, /* [IN] Logical drive number */
BYTE opt /* [IN] Initialization option */
);
fs: 文件系统句柄(注意区别于文件句柄),要提前定义一个FATFS类型变量,然后把它的
地址传给此参数
path: 这个参数决定了你要挂载哪个文件系统,在下文将统一叫它“逻辑驱动器”如果已经在diskio.c文件中有了如下定义:
/* Definitions of physical drive number for each drive */
#define ATA 2 /* Example: Map ATA harddisk to physical drive 0 */
#define MMC 3 /* Example: Map MMC/SD card to physical drive 1 */
#define USB 4 /* Example: Map USB MSD to physical drive 2 */
#define SD_CARD 0
#define FLASH_SPI 1
这个时候如果把path传入"1:"那就表示要挂载FLASH_SPI 这个文件系统,当然前提是你必须提前移植好了FLASH_SPI
opt: 给0会取消挂载path指定的文件系统,给1会挂载path指定的文件系统
f_mkfs:
格式化文件系统
FRESULT f_mkfs (
const TCHAR* path, /* Logical drive number */
const MKFS_PARM* opt, /* Format options */
void* work, /* Pointer to working buffer (null: use heap memory) */
UINT len /* Size of working buffer [byte] */
)
path : 逻辑驱动器
sfd :分区规则,一般给0即可(给1貌似是不会创建文件分配表,被称为超级软盘(SFD)格式化)
au :以字节数或扇区数指定分配单元(聚集)的大小。当值介于 1 到 128 之间时,它指定扇区数。当值为 >= _MIN_SS 时,它指定字节数。如果给出了任何无效值(零或非 2 的幂),则根据卷大小自动确定簇大小。(所以一般给0即可)
注意!!!:格式化文件系统之后,必须使用f_mount取消挂载,再重新挂载。
f_open:
打开文件(指定文件权限,完成文件句柄初始化)
FRESULT f_open (
FIL* fp, /* [OUT] Pointer to the file object structure */
const TCHAR* path, /* [IN] File name */
BYTE mode /* [IN] Mode flags */
);
*fp: 文件句柄地址
path:逻辑驱动器
mode :文件模式(权限),详见下表
参数 | 描述 |
---|---|
FA_READ | 读权限 |
FA_WRITE | 写权限 |
FA_OPEN_EXISTING | 如果文件存在,则打开;否则打开失败,返回FR_NO_FILE |
FA_OPEN_ALWAYS | 如果文件存在,则打开,如果不存在则创建一个文件并打开 |
FA_CREATE_NEW | 创建一个文件,如果文件存在,则创建失败,返回FR_EXIST |
FA_CREATE_ALWAYS | 创建一个文件,如果文件存在,则覆盖原文件 |
f_write:
写文件
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 */
);
*fp:文件句柄地址
*buff:要写入的数据地址(字符数组首地址,普通数组首地址或者字符串),这个参数应该传入const类型的实参
btw:要写入的字节数
*bw:传入一个指针,函数调用之后,将把成功写入的字节数传入该指针指向的变量,可以用来检查是否成功写入
f_printf:
int f_printf (
FIL* fp, /* [IN] File object */
const TCHAR* fmt, /* [IN] Format stirng */
...
);
此函数不必多说了,用法与printf函数一摸一样,只不过多了第一个参数*fp而已.
f_read:
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 */
);
* fp: 文件句柄地址
* buff:存放读取结果的数组
btr:要读取的字节数(通常和f_size配合使用)
* br:成功读取到的字节数
补充:在此说明一下br究竟有什么用,想象一下这样的场景:你要读取一个文件,文件很大,buff每次只能存放256个字节,那就只能每次读取256个字节咯!所以你不得不多次读取文件,但是如何确定读取到文件末尾了呢?读取到文件末尾,f_read就不会再读取了,所以此时br必定小于btr,因此,在多次读取的时候,可以用br来判断是否读取到文件末尾。
f_size:
传入文件句柄地址,返回该文件内容包含的字节数
f_lseek:
改变文件指针(光标)的位置
这个函数很简单,但是很不简单,新手经常碰到写入失败,读取失败,很大一部分原因就是不能正确的理解文件指针位置导致的,所以不要小看这个函数
FRESULT f_lseek (
FIL* fp, /* [IN] File object */
DWORD ofs /* [IN] File read/write pointer */
);
* fp:文件句柄地址
ofs 文件指针新的位置
f_getfree:
FRESULT f_getfree (
const TCHAR* path, /* [IN] Logical drive number */
DWORD* nclst, /* [OUT] Number of free clusters */
FATFS** fatfs /* [OUT] Corresponding file system object */
);
path:
指向指定逻辑驱动器的空终止字符串的指针。空字符串表示默认驱动器。
*nclst:
指向 DWORD 变量的指针,用于存储剩余簇的数量。
文件系统句柄的csize成员表示每个簇含有几个扇区,可以搭配两者使用来计算剩余空间;而n_fatent成员表示总空间的个数+2,所以还可以通过总空间减去剩余空间来计算已用空间
补充:一个扇区多少个字节,这个由ffconf.h配置文件的_MAX_SS宏决定,比如我的是4096,那就表示一个扇区4096个字节即4KB
** fatfs:
文件系统指针的指针(其实我不太明白为什么这里要用二级指针,感觉完全没必要啊,可能我这个菜鸟还不知道大佬的别有用心吧)
参考官方例程:
FATFS *fs;
DWORD fre_clust, fre_sect, tot_sect;
/* Get volume information and free clusters of drive 1 */
res = f_getfree("1:", &fre_clust, &fs);
/* Get total sectors and free sectors */
tot_sect = (fs->n_fatent - 2) * fs->csize;
fre_sect = fre_clust * fs->csize;
/* Print the free space (assuming 512 bytes/sector) */
printf("%10lu KiB total drive space.\n%10lu KiB available.\n",
tot_sect / 2, fre_sect / 2);
f_close:
关闭文件,不用多说了,文件使用完之后,肯定要关闭吧,
FRESULT f_close (
FIL* fp /* [IN] Pointer to the file object */
);
f_opendir:
打开一个已存在的目录,并创建一个目录对象,如果不存在指定目录,则返回FR_NO_PATH
FRESULT f_opendir (
DIR* dp, /* [OUT] Pointer to the directory object structure */
const TCHAR* path /* [IN] Directory name */
);
*dp:创建的目录对象存放在此指针
* path:目录名字
DIR结构体定义如下:
typedef struct {
FATFS* fs; /*指向文件系统的指针(禁止更改*/
WORD id; /* 文件系统的逻辑驱动器ID(禁止更改) */
WORD index; /* 当前读/写索引号*/
DWORD sclust; /* Table start cluster (0:Root dir) */
DWORD clust; /* Current cluster */
DWORD sect; /* Current sector */
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
#if _FS_LOCK
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
#endif
#if _USE_LFN
WCHAR* lfn; /* 指向长文件名的工作数组 */
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
#endif
#if _USE_FIND
const TCHAR* pat; /* 指向匹配路径的指针 */
#endif
} DIR;
f_closedir:
关闭打开的目录对象。函数成功后,目录对象将不再有效,可以丢弃。
FRESULT f_closedir (
DIR* dp /* [IN] Pointer to the directory object */
);
f_mkdir:
在指定路径创建目录
FRESULT f_mkdir (
const TCHAR* path /* [IN] Directory name */
);
* path:指定路径和新文件的名字
f_unlink:
删除文件
FRESULT f_unlink (
const TCHAR* path /* [IN] Object name */
);
f_rename:
移动,重命名文件或子目录
FRESULT f_rename (
const TCHAR* old_name, /* [IN] Old object name */
const TCHAR* new_name /* [IN] New object name */
)
old_name :旧文件名(包括路径)
new_name:新文件名(包括路径)
f_stat:
检查文件或子目录是否存在,如果存在,则将文件的信息储存在fno指针指向的FILINFO结构体中
FRESULT f_stat (
const TCHAR* path, /* [IN] Object name */
FILINFO* fno /* [OUT] FILINFO structure */
);
*path:指定文件或子目录路径
*fno: 存储文件信息的FILINFO结构体指针
FILINFO结构体如下:
typedef struct {
DWORD fsize; /* File size */ //文件(夹)大小(
WORD fdate; /* Last modified date */ //文件(夹)上次修改日期
WORD ftime; /* Last modified time */ //文件(夹)上次修改时间
BYTE fattrib; /* Attribute */ //文件(夹)权限
TCHAR fname[13]; /* Short file name (8.3 format) */ //文件(夹)名
#if _USE_LFN
TCHAR* lfname; /* Pointer to the LFN buffer */ //长文件(夹)名
UINT lfsize; /* Size of LFN buffer in TCHAR */ //长文件(夹)大小
#endif
} FILINFO;
fattrib成员的所有位的含义如下:
/* File attribute bits for directory entry */
#define AM_RDO 0x01 /* Read only */
#define AM_HID 0x02 /* Hidden */
#define AM_SYS 0x04 /* System */
#define AM_VOL 0x08 /* Volume label */
#define AM_LFN 0x0F /* LFN entry */
#define AM_DIR 0x10 /* Directory */ //文件夹
#define AM_ARC 0x20 /* Archive */ //档案文件(压缩文件)
#define AM_MASK 0x3F /* Mask of defined bits */
f_readdir:
输入目录对象,将目录中第一个文件(夹)信息存储在参数二指针指向的地址;如果再次调用,则输出目录中第二个文件(夹)信息,以此类推
FRESULT f_readdir (
DIR* dp, /* [IN] Directory object */
FILINFO* fno /* [OUT] File information structure */
);
DIR和FILINFO结构体如下:文章来源:https://www.toymoban.com/news/detail-753528.html
typedef struct {
FATFS* fs; /*指向文件系统的指针(禁止更改*/
WORD id; /* 文件系统的逻辑驱动器ID(禁止更改) */
WORD index; /* 当前读/写索引号*/
DWORD sclust; /* Table start cluster (0:Root dir) */
DWORD clust; /* Current cluster */
DWORD sect; /* Current sector */
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
#if _FS_LOCK
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
#endif
#if _USE_LFN
WCHAR* lfn; /* 指向长文件名的工作数组 */
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
#endif
#if _USE_FIND
const TCHAR* pat; /* 指向匹配路径的指针 */
#endif
} DIR;
typedef struct {
DWORD fsize; /* File size */ //文件(夹)大小(
WORD fdate; /* Last modified date */ //文件(夹)上次修改日期
WORD ftime; /* Last modified time */ //文件(夹)上次修改时间
BYTE fattrib; /* Attribute */ //文件(夹)权限
TCHAR fname[13]; /* Short file name (8.3 format) */ //文件(夹)名
#if _USE_LFN
TCHAR* lfname; /* Pointer to the LFN buffer */ //长文件(夹)名
UINT lfsize; /* Size of LFN buffer in TCHAR */ //长文件(夹)大小
#endif
} FILINFO;
这个函数通常会被封装成一个递归函数来实现类似Linux系统中的ls命令功能.
官方给出的递归函数如下:文章来源地址https://www.toymoban.com/news/detail-753528.html
FRESULT scan_files (
char* path /* Start node to be scanned (also used as work area) */
)
{
FRESULT res;
FILINFO fno;
DIR dir;
int i;
char *fn; /* This function assumes non-Unicode configuration */
#if _USE_LFN
static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */
fno.lfname = lfn;
fno.lfsize = sizeof lfn;
#endif
res = f_opendir(&dir, path); /* Open the directory */
if (res == FR_OK) {
i = strlen(path);
for (;;) {
res = f_readdir(&dir, &fno); /* Read a directory item */
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
if (fno.fname[0] == '.') continue; /* Ignore dot entry */
#if _USE_LFN
fn = *fno.lfname ? fno.lfname : fno.fname;
#else
fn = fno.fname;
#endif
if (fno.fattrib & AM_DIR) { /* It is a directory */
sprintf(&path[i], "/%s", fn);
res = scan_files(path);
path[i] = 0;
if (res != FR_OK) break;
} else { /* It is a file. */
printf("%s/%s\n", path, fn);
}
}
f_closedir(&dir)
}
return res;
}
到了这里,关于Fatfs文件系统常用函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!