Fatfs文件系统常用函数

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结构体如下:

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命令功能.
官方给出的递归函数如下:

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;
}


  • 4
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GuiStar_李什么恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值