细说STM32单片机FatFS文件系统基础知识和函数详解

目录

一、FatFS概述

1、FatFS的作用

(1)用户应用程序

(2)FatFS通用程序

(3)FatFS底层设备控制

(4)存储介质和RTC

2、文件系统的一些基本概念

(1)文件系统

(2)FAT卷

(3)扇区

(4)簇

(5)数据存储形式

3、FatFS的功能特点和参数

4、FatFS的文件组成

(1)用户应用程序

(2)FatFS应用接口

(3)FatFS通用硬件接口

(4)具体硬件访问层

(5)具体硬件

5、FatFS的基本数据类型定义

二、 FatFS的应用程序接口函数

1、卷管理和系统配置相关函数

(1)函数f_mkfs()

(2)函数f_mount()

(3)函数f_getfree()

2、文件和目录管理相关函数

(1)函数f_stat()

(2)函数f_chmod()

(3)函数f_utime()

3、目录访问相关函数

(1)打开和关闭目录

(2)函数f_readdir()

(3)查找目录下匹配的项

4、文件访问相关函数

(1)函数f_open()

(2)函数f_write()

(3)函数f_read()

(4)读写字符串的函数f_puts()和f_gets()

(5)文件内读写指针的移动

(6)函数f_sync()

三、FatFS的存储介质访问函数


        FatFS(FAT File System)是一个适用于嵌入式系统的文件系统管理工具,可用于管理SD卡、U盘、Flash存储器等存储介质上的文件。FatFS已经作为中间件集成到了STM32 MCU固件库中,在CubeMX中,可启用FatFS管理SD卡、U盘等存储介质,生成的代码自动完成了针对具体存储介质的FatFS移植,使用起来非常方便。

一、FatFS概述

1、FatFS的作用

        文件系统是管理存储介质上的文件的软件系统,例如,Windows系统使用的文件系统是FAT32或NTFS。在Windows、Linux等平台上,用C++等高级语言编写程序进行文件读写操作时,只需通过文件名和文件操作函数进行文件读写,而不用关心文件是如何写入存储介质或如何从存储介质读取的,也不用关心文件存储在哪个扇区等底层的问题。

        在没有文件系统的嵌入式系统中,用户读写数据时就需要进行底层的操作。例如,在一个SPI接口的Flash(以下简称为SPI-Flash)存储芯片W25Q128中,它有16M字节存储空间,分为256个块、4096个扇区和65536个页。通过SPI接口的HAL驱动程序和W25Q128的指令读写数据时,用户必须指定起始绝对地址和读写数据的长度。

        在存储介质容量不太大,数据存储结构比较简单时,用户还可以通过这种底层操作进行数据管理,但是当存储容量很大,存储的数据结构比较复杂时,这种底层操作的管理难度就非常大了。例如,在16GB的SD卡上读写数据时,用户就难以直接通过扇区地址来管理数据了,而应该在嵌入式系统中引入类似于计算机上的文件系统,以文件为对象管理存储介质上的数据

        FatFS是一个用于嵌入式系统的文件管理系统,它可以在SD卡、U盘、Flash芯片等存储介质上创建FAT或exFAT文件系统,它提供应用层接口函数,用户可以通过它直接对文件进行数据读写操作和管理。FatFS是用ANSI C语言编写的,文件操作的软件模块与底层硬件访问层完全分离,能方便地移植到各种处理器平台和存储介质。使用FatFS的嵌入式系统软硬件结构如图所示,其中各个部分的作用如下。

 

(1)用户应用程序

        用户应用程序通过FatFS的通用接口API函数进行文件系统的操作,例如,用函数f_open()打开一个文件,用函数f_write()向文件写入数据,用函数f_close()关闭一个文件。这些操作与在计算机上用C语言编程读写文件类似,只需知道文件名,而这个文件在SD卡或Flash存储芯片上如何存储,如何写入和读取,则是FatFS底层的事情。

(2)FatFS通用程序

        这些是与硬件无关的用于文件系统管理的一些通用操作API函数,包括文件系统的创建和挂载、目录操作和文件操作等函数。这些通用函数是面向应用程序的接口,实现这些函数的功能需要调用底层的硬件访问操作,所以这些通用函数是用户应用程序与底层硬件之间的桥梁。

(3)FatFS底层设备控制

        这部分是FatFS与底层存储设备通信的一些功能函数,包括读写存储介质的函数disk_read()和disk_write()、获取时间戳的函数get_fattime()等。例如,一个系统使用了SPI接口的Flash芯片存储文件,当应用程序调用f_write()向一个文件写入数据时,到了设备控制层,就是执行disk_write(),通过SPI接口向Flash芯片写入数据。FatFS底层设备控制部分是与硬件密切相关的,FatFS的移植主要就是实现这部分的几个函数。

(4)存储介质和RTC

        存储介质就是用于存储文件的各种硬件设备,如Flash存储芯片、SD卡、U盘、扩展的SRAM等。在一个嵌入式设备上,用户可以使用FatFS管理多个存储设备,例如,可以同时使用SPI-Flash存储芯片和SD卡。RTC用于获取当前时间,在创建文件、修改文件后,保存文件时需要一个当前时间作为文件的时间戳信息。时间信息可以通过读取RTC来获得,可以使用MCU片上的RTC,也可以使用外接的RTC,如果对文件的时间信息不敏感,不使用RTC时间也没问题。

2文件系统的一些基本概念

(1)文件系统

        FatFS可以在存储介质上创建和管理FAT(File Allocation Table)文件系统或exFAT (ExtendedFile  Allocation Table)文件系统,这两种都是Microsoft公司定义的标准文件系统。

        经过不断发展,FAT文件系统现在有3种:FAT12、FAT16和FAT32。其中,FAT16的单个分区容量不能超过2GB;FAT32的单个分区容量不能超过2TB,单个文件大小不能超过4GB。FAT文件系统是向后兼容的,即FAT32兼容FAT16和FAT12。

        exFAT是Microsoft继FAT16/FAT32之后开发的一种文件系统,它更适用于基于闪存的存储器,如SD卡、U盘等,而不适用于机械硬盘。所以,exFAT广泛应用于嵌入式系统、消费电子产品和固态存储设备中。exFAT文件系统允许单个文件大小超过4GB单个分区大小和单个文件大小几乎没有限制。

(2)FAT卷

        一个FAT文件系统称为一个逻辑卷(logical volume)或逻辑驱动器(logical drive),如计算机上的C盘、D盘。一个FAT卷包括如下的3个或4个区域(Area),每个区域占用1个或多个扇区,并按如下的顺序排列。

  • 保留区域,用于存储卷的配置数据。
  • FAT区域,用于存储数据区的分配表。
  • 根目录区域,在FAT32卷上没有这个区域。
  • 数据区域,存储文件和目录的内容。

(3)扇区

        扇区(sector)是存储介质上读写数据的最小单元。一般的扇区大小是512字节,FatFS支持512字节、1024字节、2048字节和4096字节等几种大小的扇区。存储设备上的每个扇区有一个扇区编号,从设备的起始位置开始编号的称为物理扇区号(physical sector number),也就是扇区的绝对编号。另外,从卷的起始位置开始相对编号也是可以的,这称为扇区号(sector number)。W25Q16的扇区是4096B,4KB。

(4)

        一个卷的数据区分为多个簇(cluster),一个簇包含1个或多个扇区,数据区就是以簇为单位进行管理的。簇越小,文件占用空间越小,但速度越慢。反之亦然。一个卷的FAT类型就是由其包含的簇的个数决定的,由簇的个数就可以判断卷的FAT类型。FAT的具体类型如下。

  • FAT12卷,簇的个数≤4085。
  • FAT16卷,4086≤簇的个数≤65525。
  • FAT32卷,簇的个数≥65526。

(5)数据存储形式

        FAT文件系统使用的是小端字节序(little endian),所以,如果处理器使用的是大端字节序(big endian),那么读取FAT文件系统的文件,就需要进行字节序的转换。另外,字(word)数据也不一定是边界对齐的,所以在读写FAT文件系统的文件时,最好使用字节数组的形式,逐个字节进行读写。

3FatFS的功能特点和参数

        FatFS是在存储介质上创建FAT/exFAT文件系统,并管理文件的软件系统,它有以下特性和限制。

  • 支持FAT文件系统和exFAT文件系统。
  • 支持长文件名和Unicode。
  • 对于RTOS是线程安全的。
  • 最多可以管理10个卷,可以是多个存储介质或多个分区。
  • 支持不同大小的扇区,扇区大小可以是512字节、1024字节、2048字节或4096字节。
  • 同时打开的文件个数:无限制,只受限于内存大小。
  • 最小卷大小:128个扇区。
  • 最大卷大小:FAT中是4G个扇区,exFAT中几乎是无限制的。
  • 最大单个文件大小:FAT卷中是4GB,exFAT中几乎是无限制的。
  • 簇大小限制:FAT卷中一个簇最大128个扇区,exFAT卷中是16MB。

4FatFS的文件组成

        FatFS已经作为一个中间件集成到了STM32 MCU固件库中,可以在CubeMX中启用和配置FatFS。CubeMX中的FatFS支持外部SRAM、SD卡、U盘和用户定义存储器(如SPI-Flash存储器)。除了用户定义存储器,CubeMX生成的代码能自动完成对所选存储介质的FatFS移植,如SD卡和U盘,所以在STM32Cube开发方式中使用FatFS很方便。

         一个应用系统使用FatFS管理某个具体的存储介质上的文件系统时,又可以具体划分为如下图所示的分层结构。图中以SPI-Flash存储芯片W25Q128为例。图中的箭头表示调用关系,系统各层的功能描述如下。

 

(1)用户应用程序

        包括CubeMX自动生成的FatFS初始化程序和用户编写的文件系统访问程序。CubeMX自动生成的代码在进行FatFS的初始化时,会调用文件f_gen_drv.h中的一个函数FATFS_LinkDriver(),其功能就是将一个或多个逻辑驱动器链接到FatFS管理的驱动器列表里。

        用户编写的文件系统访问程序就是使用文件ffh中提供的API函数进行文件系统操作,例如,用函数f_open()打开文件,用函数f_write()向文件写入数据等。

(2)FatFS应用接口

        这是面向用户应用程序的编程接口,提供文件操作的API函数,这些API函数与具体的存储介质和处理器类型无关。文件ff.h和ff.c中定义和实现了这些API函数。文件ffconf.h是FatFS的配置文件,用于定义FatFS的一些参数,以便进行功能裁剪。

(3)FatFS通用硬件接口

        这是实现存储介质访问(Disk IO)的通用接口。文件diskio.h/.c中定义和实现了Disk IO的几个基本函数,包括disk_initialize()、disk_read()、disk_ioctl()等。在CubeMX生成的代码中,文件diskio.c中的这些Disk IO函数实际上是调用具体硬件访问层实现的具体器件的Disk IO函数。文件diskio.h/.c中的代码是不允许修改的,以往直接手工编程移植FatFS时,都是直接在文件diskio.c中实现具体器件的Disk IO函数。

        文件ff_gen_drv.h定义了驱动器和驱动器列表管理的一些类型和函数。这个文件里最主要的一个函数是FATS_LinkDriver()。主程序进行FatFS初始化时,会调用这个函数,用于将一个驱动器链接到FatFS管理的驱动器列表里。而驱动器是在器件的Disk IO实现文件里定义的对象,驱动器实现了针对具体存储介质的Disk IO函数。

        另外,还有一个文件integer.h,这个文件定义了FatFS用到的基本数据类型。

(4)具体硬件访问层

        假设一个存储介质只建立一个逻辑分区,那么一个存储介质就是一个驱动器,例如,一个SD卡或一个U盘。驱动器需要实现自己的Disk IO函数,驱动器会通过函数指针,将diskio.h中定义的通用Disk IO函数指向器件的Disk IO函数。器件的Disk IO函数是在一个特定的文件里实现的,而这些Disk IO函数又会调用器件的驱动程序或硬件接口的HAL驱动程序。

        例如,使用SPI-Flash存储芯片W25Q128作为存储介质时,需要在一个文件中实现SPI-Flash器件访问的Disk IO函数,这些Disk IO函数要用到W25Q128的驱动程序,而W25Q128的驱动程序又要用到HAL库中SPI接口的驱动程序。

(5)具体硬件

        具体硬件就是用于存储文件的存储介质。CubeMX的FatFS模式设置中支持的存储介质,包括外部SRAM、SD卡、U盘和用户定义(User-defined)器件。在嵌入式设备中,一个存储介质一般只有一个分区。

        如果使用外部SRAM、SD卡或U盘作为存储介质,CubeMX生成的代码会自动生成完整的FatFS移植代码,除了一个获取时间戳的函数需要用户编程实现,用户无须自己再编程实现器件的Disk IO函数,直接使用FatFS的API函数进行文件管理即可,所以使用起来很方便

        如果选择用户定义器件作为存储介质,CubeMX会生成FatFS移植的代码框架,用户需要自己编写器件的Disk IO函数。例如,SPI-Flash存储芯片W25Q128就是用户定义器件。

5、FatFS的基本数据类型定义

        FatFS的文件integer.h对基本数据类型重新定义了符号,这是为了便于在不同的处理器平台上移植。文件integer.h中针对嵌入式平台定义的基本数据类型符号如下:

/*-------------------------------------------*/
/* Integer type definitions for FatFs module */
/*-------------------------------------------*/

#ifndef _FF_INTEGER
#define _FF_INTEGER

#ifdef _WIN32 /* FatFs development platform */

#include <windows.h>
#include <tchar.h>
typedef unsigned __int64 QWORD;


#else /* Embedded platform */

/* These types MUST be 16-bit or 32-bit */
typedef int INT;
typedef unsigned int UINT;

/* This type MUST be 8-bit */
typedef unsigned char BYTE;

/* These types MUST be 16-bit */
typedef short SHORT;
typedef unsigned short WORD;
typedef unsigned short WCHAR;

/* These types MUST be 32-bit */
typedef long LONG;
typedef unsigned long DWORD;

/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
typedef unsigned long long QWORD;

#endif

#endif

        在STM32 MCU上,INT和LONG是32位整数,UINT和DWORD是32位无符号整数,QWORD是64位无符号整数。

二、 FatFS的应用程序接口函数

        FatFS的应用程序接口函数在文件ff.h中定义,用于FAT文件系统的操作,函数可以分为几个大类。

1卷管理和系统配置相关函数

        卷管理和系统配置相关函数见。这些函数的返回值类型都是FRESULT。

函数名

函数功能

函数原型

f_mount

注册或注销一个卷,使用一个
卷之前必须调用此函数并挂载
文件系统

f_mount(FATFS*fs,const TCHAR*path,BYTE opt)

f_mkfs

在一个逻辑驱动器上创建FAT
卷,也就是进行格式化

f_mkfs(const TCHAR*path,BYTE opt,DWORD au,void*
work,UINT len)

f_fdisk

在一个物理驱动器上创建分区

f_fdisk(BYTE pdrv,const DWORD*szt,void*work)

f_getfree

返回一个卷上剩余簇的个数

f_getfree(const TCHAR*path,DWORD*nclst,FATFS**fatfs)

f_getlabel

获取一个卷的标签

f_getlabel(const TCHAR*path,TCHAR*label,DWORD*vsn)

f_setlabel

设置一个卷的标签

f_setlabel(const TCHAR*label)

        FatFS对文件系统是以卷为单位进行管理的,一个卷就相当于计算机上的一个逻辑分区,如C盘和D盘。在嵌入式设备中,一般一个存储介质只有一个卷,不需要用函数f_fdisk()进行分区,例如,一个SD卡是一个卷,一个SPI-Flash存储芯片是一个卷。在FatFS中,卷的编号默认是用字符串“0:”“1:”“2:”等表示的,其中的数字表示卷的编号。

        要管理一个卷上的文件系统,首先需要用函数f_mount()注册这个卷,如果这个函数的返回值为FR_NO_FILESYSTEM,就表示卷上还没有文件系统,需要用函数f_mkfs()对卷进行格式化。注册一个卷之后,用户才能进行创建文件、打开文件、读写文件数据等操作。

(1)函数f_mkfs()

        函数f_mkfs()用于在一个卷上创建文件系统,也就是进行格式化。函数f_mkfs()的原型定义如下。通常,在各参数的注释中,[IN]表示输入参数,[OUT]表示输出参数,输出参数就是传递指针用于获取返回数据。

FRESULT f_mkfs (
    const TCHAR* path, 	/*[IN]逻辑驱动器号*/
    BYTE opt, 			/*[IN]格式化选项*/
    DWORD au, 		    /*[IN]分配单元(簇)大小,单位:字节*/
    void* work,		    /*[IN]用于格式化的工作缓冲区指针*/
    UINT len			/*[IN]工作缓冲区大小,单位:字节*/
); /* Create a FAT volume */

        参数path是一个字符串,就是逻辑驱动器号(也就是卷号),如“0:”表示第1个卷,“1:”表示第2个卷。如果是空字符串,则表示默认卷,如果系统中只有一个卷,则可以使用空字符串。

        参数opt是格式化选项,也就是要创建的文件系统类型,可设置的选项宏定义如下:

/*Format options(2nd argument of f_mkfs)*/
#define FM_FAT		0x01		//FAT文件系统,自动设置为FAT12或FAT16
#define FM_FAT32	0x02		//FAT32文件系统
#define FM_EXFAT	0x04		//exFAT文件系统
#define FM_ANY	    0x07		//任意文件系统,前面3种的按位或,自动根据卷大小和簇大小来决定
#define FM_SFD		0x08		//只有单个分区时可以指定此选项,SFD表示super-floppy disk

        参数opt的默认值是FM_ANY,也就是根据卷的总大小和簇的大小自动设置文件系统格式,一个文件系统是FAT12、FAT16或FAT32,完全由其簇的个数决定。一般情况下,32GB以下的卷可以使用FM_FAT选项,32GB以上的一般使用FM_FAT32选项。

        参数au是簇的大小,以字节为单位。一个卷的数据区是以簇为单位进行管理的,一个簇包含1个或多个扇区,扇区是存储介质上读写数据块的最小单元。

        参数work是格式化操作时的一个工作缓冲区,这个缓冲区大小必须是簇的1倍以上大小。缓冲区越大,格式化操作越快。

        参数len是缓冲区work的大小,以字节为单位。

        函数的返回值类型是FRESULT,这是FatFS中定义的函数返回值枚举类型。函数操作正常时,返回值为FR_OK,其他返回值表示了各种信息,具体见注释。FatFS的API函数返回值一般都是FRESULT类型。

/* File function return code (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 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 > _FS_LOCK */
    FR_INVALID_PARAMETER   /* (19) Given parameter is invalid */
} FRESULT;

        例如,以开发板上的SPI-Flash芯片W25Q16为例,它的总容量为16Mbit(2Mbyte,256byte/page),一个扇区大小为1024字节,若设置簇大小为2个扇区,则可以用FM_FAT格式化选项。假设系统中只有这一个存储介质使用FatFS进行管理,则对其进行格式化的示意代码如下:

BYTE workBuffer[1024];		//工作缓冲区
res=f_mkfs("0:",FM_FAT,2*1024,workBuffer,1024);

(2)函数f_mount()

        FatFS要求每个逻辑驱动器(FAT卷)有一个文件系统对象(file system object)作为工作区域,在对这个逻辑驱动器进行文件操作之前,需要用函数f_mount()对逻辑驱动器和文件系统对象进行注册。只有注册了逻辑驱动器,才可以使用FatFS的应用接口API函数进行操作。

        函数f_mount()的原型定义如下:

/*-----------------------------------------------------------------------*/
/* Mount/Unmount a Logical Drive */
/*-----------------------------------------------------------------------*/

FRESULT f_mount (
    FATFS* fs, 			 /* Pointer to the file system object (NULL:unmount)*/
    const TCHAR* path,	 /* Logical drive number to be mounted/unmounted */
    BYTE opt			 /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
)

        参数fs是一个FATFS结构体指针,表示文件系统对象,若这个参数为NULL,则表示卸载(unmount)这个逻辑驱动器。

        参数path是逻辑驱动器号,例如“0:”,如果是空字符串,则表示默认的逻辑驱动器号。

        参数opt是模式选项。设置为1时,表示强制立刻挂载;设置为0时,表示延后挂载,在后续进行文件操作时自动挂载。延后挂载时,此函数总是返回FR_OK。

        调用函数f_mount()的示例代码如下。返回值为FR_NO_FILESYSTEM时,表示存储介质还没有文件系统,需要调用f_mkfs()函数进行格式化,格式化成功后,需要先卸载,然后再次挂载,示意代码如下:

FATFS fs;    						            //文件系统对象
FRESULT res =f_mount(&fs,"0:",1);               //立刻挂载文件系统
if(res == FR_NO_FILESYSTEM)		                //不存在文件系统,未格式化
{
    BYTE workBuffer[1024];			                //工作缓冲区
    res=f_mkfs("0:",FM_FAT,1024,workBuffer,1024);	//簇大小=4096字节
    if(res == FR_OK)				                //格式化成功
    {
        res = f_mount(NULL,"0:",1)	;               //先卸载
        res = f_mount(&fs,"0:",1);	                //再次挂载文件系统
    }
}

(3)函数f_getfree()

        FAT文件系统的数据区是以簇为单位进行管理的,函数f_getfree()返回一个逻辑驱动器上剩余簇的个数,其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Get Number of Free Clusters */
/*-----------------------------------------------------------------------*/

FRESULT f_getfree 
(
    const TCHAR* path, 	/* Path name of the logical drive number */
    DWORD* nclst, 		/* Pointer to a variable to return number of free clusters */
    FATFS** fatfs 		/* Pointer to return pointer to corresponding file system object */
)

        参数path是驱动器号,如“0:”。参数nclst是输出参数,是剩余簇的个数,使用传递地址的方式获取返回值。参数fatfs是输出参数,指向返回的文件系统对象。

        FatFS的API函数的返回值一般都是FRESULT类型,表示函数执行是否成功或错误类型。要返回的数据使用指针型输出参数,返回的结果写入指针指向的变量或对象。

        FATFS结构体是表示文件系统的结构体,它的一些成员变量存储了一个逻辑驱动器上FAT文件系统的一些参数。结构体FATFS的完整定义见源代码,比较有用的几个参数如下。

  • BYTE fs_type,分区类型,1=FAT12,2=FAT16,3=FAT32,4=exFAT。
  • WORD csize,簇的大小,即一个簇的扇区个数,至少是1个扇区。
  • WORD ssize,扇区大小,可为512字节、1024字节、2048字节或4096字节。
  • DWORD free_clst,剩余的簇个数,必须执行函数f_getfree()后,才会更新这个值。
  • DWORD n_fatent,卷的条目(item)个数,等于簇的个数加2。

        调用函数f_getfree()获取逻辑驱动器相关信息的示意代码如下:

FATFS *fs;
//DWORD fre_clust;
FRESULT res = f_getfree("0:",&fre_clust,&fs);
if(res == FR_OK)
{
    DWORD tot_sect = (fs->n_fatent -2)*fs->csize;	//总的扇区个数
    DWORD fre_sect = free_clst*fs->csize;	        //剩余的扇区个数=剩余簇个数*每个簇的扇区个数
    DWORD freespace = (fre_sect*fs->ssize)/1024;	//剩余空间大小,单位:KB
}

2文件和目录管理相关函数

        文件和目录管理相关函数见这些函数的主要功能包括创建目录、删除文件或目录、改变当前工作目录、检查文件或目录是否存在等。这些函数的返回值类型都是FRESULT,所以在函数原型表示中省略了返回值类型。

函数名

函数功能

函数原型

f_stat

检查一个文件或目录是否存在

f_stat(const TCHAR*path,FILINFO*fno)

f_unlink

删除一个文件或目录

f_unlink(const TCHAR*path)

f_rename

重命名或移动一个文件或目录

f_rename(const TCHAR*path_old,const TCHAR*path_new)

f_chmod

改变一个文件或目录的属性

f_chmod(const TCHAR*path,BYTE attr,BYTE mask);

f_utime

改变一个文件或目录的时间戳

f_utime(const TCHAR*path,const FILINFO*fno)

f_mkdir

创建一个新的目录

f_mkdir(const TCHAR*path);

f_chdir

改变当前工作目录

f_chdir(const TCHAR*path);

f_chdrive

改变当前驱动器

f_chdrive(const TCHAR*path)

f_getcwd

获取当前驱动器的当前工作目录

f_getcwd(TCHAR*buff,UINT len)

  • 删除文件时,不能删除具有只读属性的文件或目录,不能删除非空目录或当前目录,不能删除打开的文件或目录。
  • 重命名文件时,不能操作打开的文件或目录。
  • 参数_FS_RPATH≥1表示使用相对路径,此时才有函数f_chdir()。
  • 参数_FS_RPATH≥1且_VOLUMES≥2时,也就是使用相对路径,且卷的个数大于1时,才有函数f_chdrive()。
  • 参数_FS_RPATH≥2时,才有函数f_getcwd()。

(1)函数f_stat()

        函数f_stat()用于检查一个文件或目录是否存在,并且返回其文件信息,其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Get File Status */
/*-----------------------------------------------------------------------*/

FRESULT f_stat (
    const TCHAR* path,	 /* Pointer to the file path */
    FILINFO* fno		 /* Pointer to file information to return */
)

        输出参数fno是FILINFO结构体类型的指针,用于保存返回的文件信息。结构体FILINFO的定义如下,可以返回文件大小、最后修改的时间和日期、文件属性等信息:

/* File information structure (FILINFO) */

typedef struct {
    FSIZE_t fsize;                 /* File size */
    WORD fdate;                    /* Modified date */
    WORD ftime;                    /* Modified time */
    BYTE fattrib;                  /* File attribute */
    #if _USE_LFN != 0
        TCHAR altname[13];         /* Alternative file name */
        TCHAR fname[_MAX_LFN + 1]; /* Primary file name */
    #else
        TCHAR fname[13];           /* File name */
    #endif
} FILINFO;

        函数的返回值为FR_OK表示文件或目录存在;为FR_NO_FILE表示文件不存在;为FR_NO_PATH表示目录不存在;还可能有其他返回值,具体请查阅FRESULT的枚举值。

        使用f_stat()检查一个文件,并获取其参数的示例代码如下:

FILINFO fno;
FRESULT fr = f_stat("0:/readme.txt",&fno);
if(fr == FR_OK)
{
    printf("File size(bytes)= %d. \r\n",fno.fsize);
}

(2)函数f_chmod()

        函数f_chmod()用于改变一个文件或目录的属性,其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Change Attribute */
/*-----------------------------------------------------------------------*/

FRESULT f_chmod (
    const TCHAR* path, /* Pointer to the file path */
    BYTE attr,         /* Attribute bits */
    BYTE mask          /* Attribute mask to change */
)

        文件或目录的属性用一个字节数据表示,各个位代表不同的属性。属性位为1表示具有相应的属性,否则就表示没有这个属性。文件ff.h定义了属性位的宏定义。

#define	AM_RDO	0x01		//只读,Read only
#define	AM_HID	0x02		//隐藏,Hidden
#define	AM_SYS	0x04		//系统,System
#define	AM_DIR	0x10		//目录,Directory
#define	AM_ARC	0x20		//归档,Archive

        参数attr是要置位的属性位,是这些属性位的按位或运算,例如,要将一个文件的属性设置为只读和隐藏,需要将attr的值设置为AM_RDO|AM_HID。参数mask是要修改的属性位的掩码,mask中为1的位会被置0。例如,要将文件readme.txt设置为只读属性,清除文件的隐藏属性,其他属性不变,需要执行如下代码:

f_chmod("readme.txt",AM_RDO,AM_RDO|AM_HID);

(3)函数f_utime()

        函数f_utime()用于改变一个文件或目录的时间戳数据,其原型定义如下:

函数f_utime()用于改变一个文件或目录的时间戳数据,其原型定义如下:
/*-----------------------------------------------------------------------*/
/* Change Timestamp */
/*-----------------------------------------------------------------------*/

FRESULT f_utime (
    const TCHAR* path, /* Pointer to the file/directory name */
    const FILINFO* fno /* Pointer to the time stamp to be set */
)

        其中的结构体FILINFO,其成员变量fdate表示日期,ftime表示时间,这两个成员变量都是WORD类型变量,也就是uint16_t类型。fdate的日期数据格式如下表。其中,年份是与1980(年)的差值。

数据位

数据范围

表示意义

15:9位

共7位,数据范围为0~127

年份,实际年份是1980加上这个值,如39表示2019年

8:5位

共4位,有效范围为1~12

月份,表示1到12月

4:0位

共5位,有效范围为1~31

日期,表示1到31日

        ftime的时间数据格式如下表。其中,最低5位的最大值也只有31,有效数据范围是0~29,不能逐一表示0~59s,将此数值乘以2之后得到的才是秒数据。 

数据位

数据范围

表示意义

15:11位

共5位,数据范围为0~23

小时,表示0~23时

10:5位

共6位,有效范围为0~59

分钟,表示0~59分

4:0位

共5位,有效范围为0~29

秒/2,将这个值乘以2之后是秒,表示0~58s

         使用函数f_utime()修改一个文件的日期时间的示意代码如下:

FILINFO fno;
//日期2019-12-13
WORD date = (2019-1980)<<9;
WORD month = 12<<5;
fno.fdate = date | month | 13;
//时间14:32:15
WORD time = 14<<11;
WORD minute = 32<<5;
WORD sec = 15>>1;		    //除以2
fno.ftime = time | minute | sec;
f_utime("readme,txt",&fno);	//修改文件的时间戳

3目录访问相关函数

        使用目录访问函数可以打开一个目录,然后在这个目录下查找匹配的文件或目录。这些函数的返回值类型都是FRESULT。

函数名

函数功能

函数原型

f_opendir

打开一个已存在的目录

f_opendir(DIR*dp,const TCHAR*path)

f_closedir

关闭一个打开的目录

f_closedir(DIR*dp)

f_readdir

读取目录下的一个项

f_readdir(DIR*dp,FILINFO*fno)

f_findfirst

在目录下查找第一个匹配项

f_findfirst(DIR*dp,FILINFO*fno,const TCHAR*path,

const TCHAR*pattern)

f_findnext

查找下一个匹配项

f_findnext(DIR*dp,FILINFO*fno)

        使用这些函数,可以列出一个目录下的特定类型文件。例如,一个嵌入式的图像抓拍设备自动将抓拍的图片以“cap*.jpg”的形式命名并保存到SD卡上(其中,*表示数字编号),就可以使用目录访问相关函数列出这些文件。系统复位后,可以查找编号最大的文件,然后在新编号的基础上保存新的图片文件。

(1)打开和关闭目录

        函数f_opendir()用于打开一个已存在的目录,其原型定义如下:

FRESULT f_opendir (
	DIR* dp,			/* Pointer to directory object to create */
	const TCHAR* path	/* Pointer to the directory path */
)

        输入参数path是目录名称;输出参数dp是一个DIR结构体类型指针,是这个函数创建的一个目录对象,表示打开的目录。函数f_closedir()用于关闭一个打开的目录。两个函数的使用示例代码如下:

DIR dir;
FRESULT res =f_opendir(&dir,"0:");	//打开根目录
f_closedir(&dir);					//关闭目录

        结构体DIR包含了目录的一些信息,在文件ff.h中定义

(2)函数f_readdir()

        函数f_readdir()用于读取目录下的一个项(文件或目录),其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Read Directory Entries in Sequence */
/*-----------------------------------------------------------------------*/

FRESULT f_readdir (
    DIR* dp,     /* Pointer to the open directory object */
    FILINFO* fno /* Pointer to file information to return */
)

        输入参数dp是DIR类型的指针,是已经打开的目录对象;输出参数fno是FILINFO类型的指针,存储了读取的一个项(文件或目录)的信息。

        连续调用函数f_readdir()可以依次读取目录下的项,包括文件和子目录,不包括“.”和“..”项。当一个目录下的所有项被读取完之后,参数fno的成员变量fname变成以null结尾的空字符串。

        在调用f_readdir()时,如果参数fno设置为NULL,就从头开始读取。

(3)查找目录下匹配的项

        函数f_findfirst()和f_findnext()可以用于读取目录下某些特定的文件或子目录,例如,找出目录下文件名与“*.txt”匹配的所有文件。函数f_findfirst()的原型定义如下:

/*-----------------------------------------------------------------------*/
/* Find First File */
/*-----------------------------------------------------------------------*/

FRESULT f_findfirst (
    DIR* dp,             /* Pointer to the blank directory object */
    FILINFO* fno,        /* Pointer to the file information structure */
    const TCHAR* path,   /* Pointer to the directory to open */
    const TCHAR* pattern /* Pointer to the matching pattern */
)
/*-----------------------------------------------------------------------*/
/* Find Next File */
/*-----------------------------------------------------------------------*/

FRESULT f_findnext (
    DIR* dp,             /* Pointer to the open directory object */
    FILINFO* fno         /* Pointer to the file information structure */
)

        其中,参数path是要打开的目录名称,参数pattern是匹配模式字符串。匹配模式字符串可以使用字符‘?’或‘*’,问号用于匹配一个字符,星号用于匹配任意长度的字符串。

        输出参数dp是打开的目录对象指针,输出参数fno是找到的第一个匹配项的文件信息指针。如果没有找到匹配项,fno为NULL,函数返回值不为FR_OK。

        函数f_findnext()用于在上次执行f_findfirst()或ffindnext()之后,继续查找下一个匹配文件。如果找到了匹配项,函数f_findnext()的返回值为FR_OK,匹配项的文件信息存储在变量fno中;如果没有找到匹配项,函数f_findnext()返回的fno->fname为空字符串。

        例如,查找并显示根目录下所有txt文件的示例代码如下:

DIR dir;		//搜索的目录对象
FILINFO fno;	//文件信息
FRESULT fr = f_findfirst(&dir,&fno,"","*.txt");	//查找第一个匹配文件
while(fr == FR_OK && fno.fname[0])
{
    printf("fno.fname: %s \r\n",fno.fname);
    fr = f_findnext(&dir,&fno);	//查找下一个匹配项
}
f_closedir(Gdir);   			//关闭目录

4文件访问相关函数

        文件访问包括打开文件、读取数据、写入数据、关闭文件等操作,这些函数与计算机上C语言访问文件的函数是类似的。在函数原型中若没有写返回值类型,函数返回值类型就是FRESULT。

函数名

函数功能

函数原型

f_open

打开一个文件

f_open(FIL*fp,const TCHAR*path,BYTE mode)

f_close

关闭一个打开的文件

f_close(FIL*fp)

f_read

从文件读取数据

f_read(FIL*fp,void*buff,UINT btr,UINT*br)

f_write

将数据写入文件

f_write(FIL*fp,const void*buff,UINT btw,UINT*bw)

f_lseek

移动读写操作的指针

f_lseek(FIL*fp,FSIZE_t ofs)

f_truncate

截断文件

f_truncate(FIL*fp)

f_sync

将缓存的数据写入文件

f_sync(FIL*fp)

f_forward

读取数据直接传给数据流设备

f_forward(FIL*fp,UINT(*func)(const BYTE*,UINT),
UINT btf,UINT*bf)

f_expand

为文件分配连续的存储空间

f_expand(FIL*fp,FSIZE_t szf,BYTE opt)

f_gets

从文件读取一个字符串

TCHAR *f_gets(TCHAR *buff,int len,FIL*fp)

f_putc

向文件写入一个字符

int f_putc(TCHAR c,FIL*fp);

f_puts

向文件写入一个字符串

intf puts(const TCHAR *str,FIL*cp)

f_printf

用格式写入字符串

int f_printf(FIL*fp,const TCHAR *str,..);

f_tell

获取当前的读写指针

#define f_tell(fp)  ((fp)->fptr)

f_eof

检测是否到文件尾端

#define f_eof(fp)  (int)(fp)->fptr == (fp)->obj.objsize))

f_size

获取文件大小,单位:字节

#define f_size(fp)  (fp)->obj.objsize)

f_error

检测是否有错误,返回0表示无
错误

#define f_error(fp)  (fp)->err)

        要访问一个文件,需要先用函数f_open()打开或新建一个文件,然后用f_read()、f_write()等函数进行文件读写操作,操作完成后,要用函数f_close()关闭文件。

(1)函数f_open()

        函数f_open()用于打开或新建一个文件,其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Open or Create a File */
/*-----------------------------------------------------------------------*/

FRESULT f_open (
    FIL* fp,             /* Pointer to the blank file object */
    const TCHAR* path,   /* Pointer to the file name */
    BYTE mode            /* Access mode and file open mode flags */
)

        其中,参数path是要打开或新建文件的文件名,如果成功打开或新建了文件,会返回一个FIL结构体类型指针fp,在后续的文件读写操作里,用这个文件对象表示所操作的文件。参数mode是文件访问模式,是一些宏定义的位运算组合,这些模式的宏定义如下:

/* Get logical drive */
mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND;
res = find_volume(&path, &fs, mode);
#define	FA_READ			    0x01		//读取模式
#define	FA_WRITE			0x02		//写入模式,FA_READ |FA_WRITE表示可读可写
#define	FA_OPEN_EXISTING    0x00		//要打开的文件必须已存在,否则函数失败
#define	FA_CREATE_NEW	    0x04		//新建文件,如果文件已存在,函数失败并返回FR_EXIST
#define	FA_CREATE_ALWAYS	0x08		//总是新建文件,如果文件已存在,会覆盖现有文件
#define	FA_OPEN_ALWAYS	    0x10		//打开一个已存在的文件,如果不存在就创建新文件
#define	FA_OPEN_APPEND	    0x30		//与FA_OPEN_ALWAYS相同,只是读写指针定位在文件尾端

        在使用函数f_open()成功打开一个文件,并完成文件的数据读写操作后,需要用函数f_close()关闭文件,否则,修改的数据可能保存不到文件的存储介质上,或使文件损坏。

(2)函数f_write()

        函数f_write()用于向一个以可写方式打开的文件写入数据,其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Write File */
/*-----------------------------------------------------------------------*/

FRESULT f_write (
    FIL* fp,             /* Pointer to the file object */
    const void* buff,    /* Pointer to the data to be written */
    UINT btw,            /* Number of bytes to write */
    UINT* bw             /* Pointer to number of bytes written */
)

        参数fp是用函数f_open()打开文件时返回的文件对象指针。文件对象结构体FIL有一个DWORD类型的成员变量fptr,称为文件读写位置指针,用于表示文件内当前读写位置。文件打开后,这个读写位置指针指向文件顶端。函数f_write()在当前的读写位置指针处向文件写入数据,写入数据后,读写位置指针会自动向后移动。

        参数buff是待写入的数据缓冲区指针,参数btw是待写入数据的字节数。输出参数bw是完成操作后,实际写入文件的数据字节数,如果*bw<btw,表明缓冲区buff内的数据没有全部写入文件,可能是存储介质满了。

        举个例子,用函数f_open()新建一个文件,然后向文件写入一些数据,写完数据后,用f_close()关闭文件,代码如下:

void TestWriteBinFile(TCHAR *filename,uint32_t pointCount,uint32_t sampFreq)
{
    FIL file;
    FRESULT res = f_open(&file,filename,FA_CREATE_ALWAYS | FA_WRITE);
    if(res == FR_OK)
    {
        UINT bw=0;								        //实际写入字节数
        f_write(&file,&pointCount,sizeof(uint32_t),&bw);//数据点个数
	    f_write(&file,&sampFreq,sizeof(uint32_t),&bw);	//采样频率
        uint32_t value = 1000;
        for(uint16_t i=0;i<pointCount;i++,value++)
            f_write(&file,&value,sizeof(uint32_t),&bw);
    }
    f_close(&file);
}

(3)函数f_read()

        在使用函数f_open()打开文件时,如果模式参数中带有FA_READ,打开文件后就可以用函数f_read()读取数据。其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Read File */
/*-----------------------------------------------------------------------*/

FRESULT f_read (
    FIL* fp,    /* Pointer to the file object */
    void* buff, /* Pointer to data buffer */
    UINT btr,   /* Number of bytes to read */
    UINT* br    /* Pointer to number of bytes read */
)

        函数f_read()会从文件的当前读写位置处读取btr个字节的数据,然后将读出的数据保存到缓冲区buff里,将实际读取的字节数返回到变量*br里。读出数据后,文件的读写指针会自动向后移动。与前面的函数TestWriteBinFile()对应的,从一个文件读取数据的代码如下:

void TestReadBinFile(TCHAR*filename)
{
    FIL file;
    FRESULT res = f_open(&file,filename,FA_READ);
    if(res == FR_OK)
    {
        UINT bw = 0;								    //实际读取字节数
        uint32_t pointCount,sampFreq;
        f_read(&file,&pointCount,sizeof(uint32_t),&bw);	//数据点个数
	    f_read(&file,&sampFreq,sizeof(uint32_t),&bw);	//采样频率
        uint32_t value;
        for(uint16_t i=0;i<pointCount;i++)
        f_read(&file,&value,sizeof(uint32_t),&bw);
    }
    f_close(&file);
}

        二进制文件按照设定的顺序写入各种数据,读取时,也必须按照规定的顺序读取,这就是二进制文件的存储格式定义。

        f_write()和f_read()是通用的数据读写函数,可以读写任何类型的数据,但不太适合于读写字符串数据。例如,用下面的代码向文件写入一个字符串:

TCHAR str[]="ADC1";	//以'\0'结束的字符串
UINT bw;
f_write(&file,str,1+sizeof(str),&bw);

        字符串str是以结束符10结束的字符串,sizeof(str)的值是st的字符个数,不包含结束符\0',所以在使用函数f_write()写入字符串时,写入的字节数是1+sizeof(str),也就是实际写入了5字节。

        在这种情况下,用函数f_read()读取字符串时会遇到问题。例如,读取前面写入的字符串“ADC1”的代码如下:

TCHAR str[20];	//缓冲区,预留较大空间
UINT br,btr=5;	//字符串实际有5个字符,包含结束符'\0'
f_read(&file,str,btr,&br);

        使用函数f_read()时,需要指定读取数据的字节数,这里已知字符串长度为5字节,所以指定为5。如果字符串长度不是固定的,采用函数f_read()读取字符串时,就需要采用一些处理方法了。例如,将字符串长度作为一个uint16_t数据写在字符串前面,先读取这个字符串长度数据,然后作为函数f_read()的参数用于读取字符串。

(4)读写字符串的函数f_puts()和f_gets()

        FatFS提供了两个函数,专门用于字符串数据的读写。函数f_puts()用于写入一个字符串,其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Put a string to the file */
/*-----------------------------------------------------------------------*/

int f_puts (
    const TCHAR* str,   /* Pointer to the string to be output */
    FIL* fp             /* Pointer to the file object */
)

        其中,str是以'0'作为结束符的字符串指针,fp是文件对象指针。如果写入成功,函数返回实际写入的字节数,否则返回-1。这个函数的使用示例代码如下:

TCHAR str[]="Line1:Hello,FatFS\n";		//字符串必须有换行符'\n'
f_puts(str,&file);						//不会写入结束符'\0'

        必须在字符串str末尾加上换行符'n¹,因为使用函数f_puts()将字符串写入文件时,不会将字符串的结束符'0写入文件。

        函数f_gets()用于读取一个字符串,它通过换行符'n'判断一个字符串的结束,在读出的字符串末尾会自动添加结束符'0'。其原型定义如下:

/*-----------------------------------------------------------------------*/
/* Get a string from the file */
/*-----------------------------------------------------------------------*/

TCHAR* f_gets (
    TCHAR* buff, /* Pointer to the string buffer to read */
    int len,     /* Size of string buffer (characters) */
    FIL* fp      /* Pointer to the file object */
)

        参数buff是保存读出字符串的缓冲区,len是buff的长度,fp是文件对象指针。这个函数的使用示例代码如下:

TCHAR str[100];
f_gets(str,100,&file);

        这里的第二个参数值100是指缓冲区str的长度,而不是实际读出的字符串的长度。

        缓冲区str的长度要足够大,能容纳一次读取的一行字符串。

        当FatFS的全局宏定义_USE_STRFUNC的值为2时,换行符为"Irln"。

(5)文件内读写指针的移动

        文件对象结构体FIL的成员变量fptr称为文件读写指针,用于存储文件读写的当前位置,是一个DWORD类型的变量。在文件刚打开时,fptr的值为0,也就是指向文件顶端。在使用函数f_write()或f_read()读写数据时,文件读写指针会自动移动。

        还有几个可以用于文件读写指针操作的函数,宏函数f_tell()返回文件读写指针的当前值,函数f_lseek()直接将文件读写指针移到文件内的某个绝对位置,函数f_eof()可以判断文件读写指针是否到文件末尾了。函数f_lseek()的原型定义如下:

/*-----------------------------------------------------------------------*/
/* Seek File R/W Pointer */
/*-----------------------------------------------------------------------*/

FRESULT f_lseek (
    FIL* fp,     /* Pointer to the file object */
    FSIZE_t ofs  /* File pointer from top of file */
)

        参数fp是文件对象指针;参数ofs是相对于文件顶端的偏移量,也就是文件内的绝对位置,单位为字节。所以,函数f_lseek()只能将文件读写指针定位到一个绝对位置,但是可以联合函数f_tell()实现相对移动,示例代码如下:

res =f_lseek(fp,0);			    //移到文件开头位置
res =f_lseek(fp,f_size(fp));	//移到文件末尾
res =f_lseek(fp,f_tell(fp)+100);//从当前位置后移100字节
res =f_lseek(fp,f_tell(fp)-20);	//从当前位置前移20字节

        函数f_eof()用于判断文件读写指针是否到达了文件末尾,例如,读取一个全是字符串的文本文件的内容:

FIL file;
FRESULT res = f_open(&file,"readme.txt",FA_READ);
if(res == FR_OK)
{
    TCHAR str[100];
    while(!f_eof(&file))
    {
        f_gets(str,100,&file);	//读取1个字符串
        //
    }
}

(6)函数f_sync()

        函数f_sync()用于在写入文件时,将缓存的数据保存到物理文件里。函数f_sync()的功能与函数f_close()的类似,只是文件继续处于打开状态,可以继续写入。

        函数f_sync()一般用于长时间打开一个文件进行写操作的场景,例如,操作一个数据记录文件,隔几分钟才写入一个数据点。如果在使用函数f_close()之前出现异常,缓存的数据没有写入实际文件,就可能导致数据丢失。使用函数f_sync()就可以将缓存的数据写入实际文件,降低数据丢失的风险。

三、FatFS的存储介质访问函数

        将FatFS移植应用于SD卡、U盘、SRAM、Flash芯片等不同存储介质时,用户需要针对具体的存储介质提供底层硬件访问接口。这些硬件层的访问接口在文件diskio.h中定义,FatFS的移植主要就是针对存储介质的硬件层访问程序的移植。

        FatFS已经尽量简化了硬件访问层函数的定义,并且使用了统一的函数接口,移植时只需重新实现这些函数即可。文件diskio.h中定义的需要移植的几个硬件层访问相关函数见下表。

函数名

函数功能

disk_status

获取驱动器当前状态,例如,是否已完成初始化

disk_initialize

设备初始化,就是硬件接口的初始化

disk_read

读取一个或多个扇区的数据

disk_write

将数据写入一个或多个扇区,只有_USE_WRITE==1,才需要实现这个函数

disk_ioctl

设备IO控制,例如获取扇区个数、扇区大小等参数。只有_USE_IOCTL==1,才需要
实现这个函数

get_fattime

获取当前时间作为文件的修改时间,可以通过RTC获取当前时间,也可以不实现这个
函数

        表中的前3个函数是必须实现的。存储设备数据读写的最小单位是1个扇区,函数disk_read()用于从存储设备将一个或多个扇区的数据读取到缓冲区,函数disk_write()用于将一个缓冲区的数据写入一个或多个扇区。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wenchm

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

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

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

打赏作者

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

抵扣说明:

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

余额充值