STM32 串行FLASH文件系统FatFs

目录

一、Windows系统为例

二、文件系统的结构与特性

为什么要应用文件分配表?

三、FatFs文件系统

1- FatFs 文件系统源码介绍

2- FatFs在程序中的关系网

四、配置FatFs移植程序

1-配置diskio.c文件

2-配置移植程序

3-其余问题汇总


把文件系统FatFs移植到串行FLASH中。能够将文档(txt、bmp、docx等格式文件)存入FLASH中(W25Q64芯片)。

文件系统,就是对数据进行管理的方式,可有效地管理存储介质(磁盘、U盘等等)。

使用文件系统时,它为了存储和管理数据,在存储介质建立了一些组织结构,这些结构包括操作系统引导区、目录和文件。目录:FLASH芯片自己建立了一个目录,可以知道自己存储空间里面的数据有什么,地址在哪里。

FatFs是在嵌入式里面应用非常广泛的文件系统,就是一个代码库,这个代码库能对FLASH进行操作,以便于管理FLASH这个存储介质中的文件。

注意:本实验中可能会丢掉原来FLASH中存储的数据,导致液晶屏显示会出现中文乱码,但后面会有重新刷FLASH的教程。

一、Windows系统为例

常见的windows下的文件系统格式包括FAT32、NTFS、exFAT。在使用文件系统前,要先对存储介质进行格式化。格式化时会在存储介质上新建一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址,剩余空间。

Windows操作系统为了便于用户对磁盘的管理。加入了磁盘分区的概念,即将一块磁盘逻辑划分为几块,它会把磁盘的分区信息记录到硬盘分区表中。下图中的分区表就相当于目录,指向了基本分区所在的位置。 

二、文件系统的结构与特性

使用文件系统时,数据都以文件的形式存储。写入新文件时,先在目录中创建一个文件索引,它指示了文件存放的物理地址,再把数据存储到该地址中。当需要读取数据时,可以从目录中找到该文件的索引,进而在相应的地址中读取出数据。具体还涉及到逻辑地址、簇大小(类似于扇区,最小删除单位)、不连续存储等一系列辅助结构或处理过程。

文件系统的存在使存取数据时,不再是简单地向某物理地址直接读写,而是要遵循它的读写格式。如经过逻辑转换,一个完整的文件可能被分开成多段存储到不连续的物理地址,使用目录或链表的方式来获知下一段的位置。

文件系统的空间示意图:

存储了A.TXT,B.TXT,C.TXT文件  簇可以理解为扇区。

其中,目录表示意图如下:

 记录了文件的开始簇位置、大小等信息

开始簇:该文件的起始位置。

文件分配表(存储在FLASH的第0扇区)               

 第一个扇区指向目录,第二个扇区指向第三个扇区文件。我们根据目录项中指定的A.txt 的首簇为2,然后找到文件分配表的第2簇记录,上面登记的是3,就能确定下一簇是3。找到文件分配表的第3簇记录,上面登记的是4,就能确定下一簇是4......直到指到第11簇,发现下一个指向是FF,就是结束。文件便读取完毕。

为什么要应用文件分配表?

这是我们原本的文件分配表,B.txt文件是从12-65的,我们要删除B文件插入D文件,但是D文件比B文件大,12-65写不开,我们可以从65指向87(空余空间)接着进行存储。

 目录表和文件分配表在存储介质格式化的时候就已经创建好。

三、FatFs文件系统

底层是如何写入、擦除、读取字节的程序在FatFs文件系统中是没有的,这些程序是留给用户自己去写,想用在FLASH上就写FLASH相关的操作程序;想用在EEPROM上就写EEPROM相关的操作程序(EEPROM存储空间较小,所以没有用文件系统去管控),想用在SD卡上就写SD卡相关的操作程序。

利用前面写好的SPI Flash芯片驱动,把FatFs文件系统代码移植到工程之中,就可以利用文件系统的各种函数,对SPI Flash芯片以“文件”格式进行读写操作了。

1- FatFs 文件系统源码介绍

FatFs文件系统源码:doc文件夹里面是一些使用帮助文档;在src是FatFs文件系统的源代码。

integer.h:文件中包含了一些数值类型定义(typedef)。

diskio.c:包含底层存储介质的操作函数,这些函数需要用户自己实现,主要添加底层驱动函数。

ff.c:    FatFs核心文件,文件管理的实现方法。该文件独立于底层介质操作文件的函数,利用这些函数实现文件的读写。

cc936.c:本文件在option目录下,是简体中文支持所需要添加的文件,包含了简体中文的GBK和Unicode相互转换功能函数。

除diskio.c文件外,其他文件都是可以直接移植到其他存储介质中去使用的。为了使用文件系统,只需要理解integer.h及diskio.c文件并会调用ff.c文件中的函数就可以了。

2- FatFs在程序中的关系网

用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到f_mount()、f_open()、f_write()、f_read()就可以实现文件的读写操作。FatFs组件中只需要修改ffconf.h(修改宏定义)和diskio.c两个文件。

函数

条件(ffconf.h)

备注

disk_status
disk_initialize
disk_read

总是需要

底层设备驱动函数

disk_write
get_fattime
disk_ioctl (CTRL_SYNC)

_FS_READONLY == 0

支持写、读文档,为1不支持写入,只读

disk_ioctl (GET_SECTOR_COUNT)
disk_ioctl (GET_BLOCK_SIZE)

_USE_MKFS == 1

格式化(目录表等写进去)

disk_ioctl (GET_SECTOR_SIZE)

_MAX_SS != _MIN_SS

获取最大最小的缓冲 配置扇区大小

disk_ioctl (CTRL_TRIM)

_USE_TRIM == 1

ff_convert
ff_wtoupper

_USE_LFN != 0

Unicode支持,为支持简体中文,添加cc936.c到工程即可

ff_cre_syncobj
ff_del_syncobj
ff_req_grant
ff_rel_grant

_FS_REENTRANT == 1

FatFs可重入配置,需要多任务系统支持(一般不需要)

ff_mem_alloc
ff_mem_free

_USE_LFN == 3

长文件名支持,缓冲区设置在堆空间(一般设置_USE_LFN = 2 )

四、配置FatFs移植程序

1-配置diskio.c文件

1. DSTATUS disk_status 函数

 用于获取底层设备(FLASH)的状态。

 形参:设备号

 返回值:STA_NOINIT:指示设备尚未初始化,尚未准备好工作。

               STA_NODISK:驱动器中没有介质

               STA_PROTECT:写保护状态

/* Disk Status Bits (DSTATUS) */

#define STA_NOINIT		0x01	/* Drive not initialized */
#define STA_NODISK		0x02	/* No medium in the drive */
#define STA_PROTECT		0x04	/* Write protected */

 2.disk_initialize 函数

    初始化所用的设备

    形参与返回值与上面函数相同

3.disk_read 函数

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address in LBA */
	UINT count		/* Number of sectors to read */
)

形参:设备号,上层ff.c文件调用此函数从FLASH(设备号的设备)读出来的数据存储在*buff(指针指向的数组缓冲区)中,需要读取FLASH(设备)哪个扇区,要读取多少个扇区。

返回值:RES_OK (0)    函数成功。

              RES_ERROR   读取操作期间发生不可恢复的硬错误。

              RES_PARERR  传入的形参有错误。

              RES_NOTRDY 设备尚未初始化。

4.disk_write 函数

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector address in LBA */
	UINT count			/* Number of sectors to write */
)

形参:设备号,要写入数据的指针(数组),要写入的扇区的地址,写入多少个扇区。

返回值与上面的read函数一样。

5.disk_ioctl 函数

控制设备特定的功能和其他功能,而不是一般的读/写。

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)

形参:设备号;命令;输入或输出一个指针(依据命令而定)

返回值与上面的read函数一样。

常用的命令:

GET_SECTOR_COUNT:存储设备有多大 该命令是DWORD类型

GET_SECTOR_SIZE:每个扇区有多大 配置要支持的扇区大小范围,当_MAX_SS大于_MIN_SS时,FatF被配置为可变扇区大小。我们应该更改ffconf.h里面的:_MAX_SS宏定义为4096,因为FLASH 每个扇区是 4096 个字节。该命令是WORD类型

GET_BLOCK_SIZE:每个block有多大 文件系统以block为单位进行擦除 该命令是DWORD类型。

switch(cmd)//对传入的命令进行分析
{
	case GET_SECTOR_COUNT://存储设备有多大
		*(DWORD*)buff = 2048-514;//有2048个扇区
	break;
		
	case GET_SECTOR_SIZE://每个扇区有多大
		*(WORD*)buff = 4096;//每个扇区4096个字节
	break;
			
	case GET_BLOCK_SIZE://每个block有多大
		*(DWORD*)buff = 1;//文件系统以block为单位进行擦除
	break;
}
res = RES_OK;
return res;

这些程序是底层的程序,在使用文件系统的时候最好不要直接调用,我们一般使用f_mount函数来调用这些底层函数,实现底层程序与上层程序之间的隔离。

2-配置移植程序

1.f_mount 函数

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			/* 0:Do not mount (delayed mount), 1:Mount immediately */
)

形参:文件系统指针;字符串类型的路径(也就是我们宏定义的设备的号码);安装选项:0:现在不挂载(要在第一次访问时挂载),1:强制挂载并检查它是否准备好工作,一般配置为1。

2.f_mkfs 函数

格式化,创建文件系统

形参:选择要格式化的文件系统;第二个参数为0(硬盘);每个存储单元(扇区)的大小。

3.f_open 函数

打开文件函数

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 */
)

形参:文件指针;创建的文件名,这里要包含创建文件所在的目录(1: 代表着是在FLASH下);模式(对该文件的权限)

权限如下:

其中:FA_CREATE_ALWAYS:创建一个新文件。如果文件存在,它将被截断并覆盖。

4. f_write 函数

往文件里面写入数据。

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 */
)

形参:文件指针;要写入的内容;要写入多少个字节;返回值,返回写入了多少个字节。

5.f_close 函数

要关闭哪个文件。

FRESULT f_close (
	FIL *fp		/* Pointer to the file object to be closed */
)

形参:文件指针

6.f_read 函数

读取文件内容。

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 */
)

形参:文件指针;你要读取的内容存储在哪里(指针);要读取多少个字节;真正读回来多少个字节

3-其余问题汇总

如何设置长文件名?

在ffconf.h文件中我们把下面这个宏定义改为1,并且要添加cc936.c文件,这样我们文件的文件名也可以用中文来命名。

为何进行地址的偏移

这里用的flash芯片有8M字节,每个扇区4096字节,我们把前面2M字节空出用于其他(野火板子自带的内容),后面6M字节给FatFS使用。那么,需要将所有的地址都偏移2M,即512个扇区的空间。

在我们写完这个程序之后,我们可以与官方例程中的“USB—外部FLASH模拟U盘”程序结合,将这个程序下载到板子里面之后,再下载官方的那个程序,板子要用数据线与电脑相连,这样FLASH就可以当作U盘使用,我们写的文件也会在里面体现。

猜你喜欢

转载自blog.csdn.net/ChenWenHaoHaoHao/article/details/128394420
今日推荐