Stm32文件系统FATFS
参考资料主要是原子和野火两家的讲解。
1.FATFS简介:
适合嵌入式小型单片机,是一个 独立 的软件层文件系统,我们只需要将底层硬件的读取函数移植到FATFS提供的向下的接口(Media Access Interface),完成之后,就可以像电脑一样使用文件的操作函数(FATFS提供的向上的供我们使用的API函数 (Application Interface) )。
FAFTS中的函数参数介绍中的,IN表示该参数是传入数值;OUT表示,该参数是介质用于存放需要传出数据的载体。
1)初始化磁盘:
2)系统结构:
2.FATFS文件系统
1)组要组成:(需要添加的文件)
-
integer.h: 数值类型的宏定义 -
ffconf.h: FATFS 模块配置文件( 可根据我们需要改变宏,进行对应的裁剪 ) -
ff.c : FATFS 模块的核心文件,我们不需要改动; -
ff.h :FATFS 和应用模块公用的包含文件( 存放着函数的声明,移植成功后当我们使用后,只需要将其include即可 ) -
diskio.c : FATFS 和 disk I/O 模块接口层文件(就是 我们移植时重点要修改的东西 ,我们需要将底层的操作函数提供给它的接口) -
cc936.c:主要存放了Unicode(国标码)与GBK相互转换的数组,用于支持中文。
2)需要我们提供的底层接口:
要实现的函数有如下图:(但可根据自己所需要的东西选择性实现,但以下五个是一定要的,其中所谓的可选择,是指disk_ioctl函数中部分命令可不实现)
主要是在diskio.c中的五个函数的实现:
3.ffconf.h的配置
-
_VOLUMES : 用于设置 FATFS 支持的逻辑设备数目. -
_MAX_SS:扇区缓冲的最大值,一般设置为 512(SD卡中) ,FLASH (常用4096); 注意:_MAX_SS的大小,要与diskio_ioctl(GET_SECTOR_SIZE)中单次读取扇区大小一致,若不一致,极其容易导致内存溢出,HARDFAULT. -
_USE_MKFS:这个用来定时是否使能格式化,设置为 1,允许格式化。 -
_FS_MINIMISE :对文件系统裁剪的宏. -
_FS_TINY :是否使用微系统。 -
_FS_READONLY :是否只读. -
_USE_STRFUNC :是否使能格式化操作。 -
_USE_FASTSEEK :是否使能快速定位功能。 -
_USE_LABEL : 置是否支持磁盘盘符(磁盘名字)读取与设置。 -
_CODE_PAGE :设置哪种语言类型,936GBK简体中文,932日文等。
4.FATFS系统API的调用
1)FATFS系统的对象(定义的结构体)
(对于结构体的成员细节,简单看看就好)
关于该结构体比较多,也比价复杂;需要注意的是其中最后一项定义了一个缓存数组 BYTE win[_MAX_SS] ,如果是512 ( MAX_SS )字节一个扇区的话,这个数组非常大。
若定义为本地变量的话,极其容易导致栈溢出;所以通常定义为全局变量(该结构体是物理磁盘的文件结构的结构体,通常一个物理磁盘只需一个即可),原子的exfuns.c 中使用的是动太内存分配的方式。
-
FRESULT 多个函数的返回类型(ff.h定义的一个枚举变量,实际是u8的枚举)我们常使用来判断API函数是否成功运行并实现预期效果,该枚举如下如下: typedef enum {
FR_OK = 0,
FR_DISK_ERR,
FR_INT_ERR,
FR_NOT_READY,
FR_NO_FILE,
FR_NO_PATH,
FR_INVALID_NAME,
FR_DENIED,
FR_EXIST,
FR_INVALID_OBJECT,
FR_WRITE_PROTECTED,
FR_INVALID_DRIVE,
FR_NOT_ENABLED,
FR_NO_FILESYSTEM,
FR_MKFS_ABORTED,
FR_TIMEOUT,
FR_LOCKED,
FR_NOT_ENOUGH_CORE,
FR_TOO_MANY_OPEN_FILES,
FR_INVALID_PARAMETER
} FRESULT;
-
FIL 文件对象:(同样,建议为全局或者是动态内存分配) typedef struct {
FATFS* fs;
WORD id;
BYTE flag;
BYTE err;
DWORD fptr;
DWORD fsize;
DWORD sclust;
DWORD clust;
DWORD dsect;
#if !_FS_READONLY
DWORD dir_sect;
BYTE* dir_ptr;
#endif
#if _USE_FASTSEEK
DWORD* cltbl;
#endif
#if _FS_LOCK
UINT lockid;
#endif
#if !_FS_TINY
BYTE buf[_MAX_SS];
#endif
} FIL;
-
FILINFO 文件信息对象(与文件对象不同,是另外定义的一个结构体) 同样,建议全局或者是动态内存分配。 typedef struct {
DWORD fsize;
WORD fdate;
WORD ftime;
BYTE fattrib;
TCHAR fname[13];
#if _USE_LFN
TCHAR* lfname;
UINT lfsize;
#endif
} FILINFO;
2)API函数使用:
-
磁盘驱动器初始化(磁盘挂载函数):初始化0盘,1盘等等,取决于path(自己决定,不过常与ff内定义的驱动器的宏一致,0-9). FRESULT f_mount(FATFS* fs ,
const TCHAR * ,
BYTE opt
)
-
格式化磁盘,在物理磁盘上建立FAFTS文件系统的架构(目录、文件分配表等) FRESULT f_mkfs (const TCHAR* path,
BYTE sfd,
UINT au
)
注意:格式化后需要重新挂载磁盘,所以需要执行两步: 1?? 取消挂载 ?f_monut?(?NULL,“0:”,1),即将空设备挂到我们的逻辑磁盘上(相当于清空逻辑磁盘)2?? 重新挂载设备? f_mount( &fs ,"0: " ,1). -
打开文件函数:与c语言相同,注意第三个参数,表示以什么方式(权限)打开。 当我们需要多种权限时,可以将第三个参数以多个可选参数相| (与)的形式写入 FRESULT f_open (
FIL* fp,
const TCHAR* path,
BYTE mode
)
-
写文件函数 FRESULT f_write (
FIL* fp,
const void *buff,
UINT btw,
UINT* bw
)
-
读文件函数 FRESULT f_read (
FIL* fp,
void* buff,
UINT btr,
UINT* br
)
-
光标重定位函数(常在读写函数后使用,因为读写时光标会移动) FRESULT f_lseek (
FIL* fp,
DWORD ofs
)
-
文件关闭函数 (注意:f_open使用后一定要f_close,因为在有些系统中,只有在f_close时才将文件缓冲区的数据写到物理磁盘中) FRESULT f_close (FIL *fp)
-
获取文件大小的函数 FSIZE_t f_size (FIL* fp );
-
指定格式写入文件函数(与printf函数相同,转义字符也相同,注意第一个参数为写入文件对象) int f_printf (
FIL* fp,
const TCHAR* fmt,
...
)
-
删测文件(文件夹)函数 FRESULT f_unlink ( const TCHAR* path)
-
有关文件夹的打开,创建,关闭函数(与上面操作文件没有什么不同,注意参数类型,为dir类(宏定义的结构体)) FRESULT f_opendir (
DIR* dp,
const TCHAR* path
);
FRESULT f_mkdir (
const TCHAR* path
);
FRESULT f_closedir (
DIR* dp
);
-
值得注意的是读取文件夹的函数 (注意:参数类型FILFINO) (注意它每一次调用会返回该文件夹中下一个文件的FILFINO) FRESULT f_readdir (
DIR* dp,
FILINFO* fno
);
可以使用该函数实现对一个文件夹扫描所有文件的函数:(可以参考官网的递归函数进行扫描的方法)[参考](D:\typora\local notebook\stm32\stm32正点原子图片解码使用简介.md)
5.支持长(中文)文件名
-
1?? 将cc936.c文件加入FATFS文件分组中(加入工程) -
2?? 将文件ffconf.h 中的三个宏: #define _USE_LFN 3
#define _MAX_LFN 255
#define _CODE_PAGE 936
但cc936.c中有两个大的转换数组,将会使代码烧录变慢,可以使用像原子一样,将cc936.c写在磁盘中读出。
|