第25章 串行FLASH文件系统FatFs

25.1 文件系统

即使读者可能不了解文件系统,读者也一定对“文件”这个概念十分熟悉。数据在PC上是以文件的形式储存在磁盘中的,这些数据的形式一般为ASCII码或二进制形式。在上一章我们已经写好了QSPI Flash芯片的驱动函数,我们可以非常方便的在QSPI Flash芯片上读写数据。如需要记录本书的书名“零死角玩转STM32-F7系列”,可以把这些文字转化成ASCII码,存储在数组中,然后调用QSPI_FLASH_BufferWrite函数,把数组内容写入到QSPI Flash芯片的指定地址上,在需要的时候从该地址把数据读取出来,再对读出来的数据以ASCII码的格式进行解读。

但是,这样直接存储数据会带来极大的不便,如难以记录有效数据的位置,难以确定存储介质的剩余空间,以及应以何种格式来解读数据。就如同一个巨大的图书馆无人管理,杂乱无章地存放着各种书籍,难以查找所需的文档。想象一下图书馆的采购人员购书后,把书籍往馆内一扔,拍拍屁股走人,当有人来借阅某本书的时候,就不得不一本本地查找。这样直接存储数据的方式对于小容量的存储介质如EEPROM还可以接受,但对于QSPI Flash芯片或者SD卡之类的大容量设备,我们需要一种高效的方式来管理它的存储内容。

这些管理方式即为文件系统,它是为了存储和管理数据,而在存储介质建立的一种组织结构,这些结构包括操作系统引导区、目录和文件。常见的windows下的文件系统格式包括FAT32、NTFS、exFAT。在使用文件系统前,要先对存储介质进行格式化。格式化先擦除原来内容,在存储介质上新建一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址,剩余空间。

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

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

上一章的SPI Flash芯片驱动只完成了向物理地址写入数据的工作,而根据文件系统格式的逻辑转换部分则需要额外的代码来完成。实质上,这个逻辑转换部分可以理解为当我们需要写入一段数据时,由它来求解向什么物理地址写入数据、以什么格式写入及写入一些原始数据以外的信息(如目录)。这个逻辑转换部分代码我们也习惯称之为文件系统。

25.2  FatFs文件系统简介

上面提到的逻辑转换部分代码(文件系统)即为本章的要点,文件系统庞大而复杂,它需要根据应用的文件系统格式而编写,而且一般与驱动层分离开来,很方便移植,所以工程应用中一般是移植现成的文件系统源码。

FatFs是面向小型嵌入式系统的一种通用的FAT文件系统。它完全是由AISI C语言编写并且完全独立于底层的I/O介质。因此它可以很容易地不加修改地移植到其他的处理器当中,如8051、PIC、AVR、SH、Z80、H8、ARM等。FatFs支持FAT12、FAT16、FAT32等格式,所以我们利用前面写好的QSPI Flash芯片驱动,把FatFs文件系统代码移植到工程之中,就可以利用文件系统的各种函数,对QSPI Flash芯片以“文件”格式进行读写操作了。

FatFs文件系统的源码可以从fatfs官网下载:

http://elm-chan.org/fsw/ff/00index_e.html

25.2.1  FatFs的目录结构

在移植FatFs文件系统到开发板之前,我们先要到FatFs的官网获取源码,最新版本为R0.11a,官网有对FatFs做详细的介绍,有兴趣可以了解。解压之后可看到里面有  doc 和 src 这两个文件夹,见图 251。doc 文件夹里面是一些使用帮助文档; src 才是FatFs文件系统的源码。

 

图 25-1  FatFs文件目录

25.2.2 FatFs帮助文档

打开  doc 文件夹,可看到如图 25-2的文件目录:

 

图 25-2 doc文件夹的文件目录

其中 en 和 ja 这两个文件夹里面是编译好的html文档,讲的是FATFS里面各个函数的使用方法,这些函数都是封装得非常好的函数,利用这些函数我们就可以操作SPI Flash芯片。有关具体的函数我们在用到的时候再讲解。这两个文件夹的唯一区别就是 en 文件夹下的文档是英文的,ja 文件夹下的是日文的。img文件夹包含en和ja文件夹下文件需要用到的图片,还有四个名为app.c文件,内容都是FatFs具体应用例程。00index_e.html和00index_j.html是一些关于FATFS的简介,至于另外两个文件可以不看。

25.2.3  FATFS源码

打开 src 文件夹,可看到如图 25-3的文件目录:

 

图 25-3 src文件夹的文件目录

option 文件夹下是一些可选的外部c文件,包含了多语言支持需要用到的文件和转换函数。

diskio.c文件是FatFs移植最关键的文件,它为文件系统提供了最底层的访问QSPI Flash芯片的方法,FatFs有且仅有它需要用到与QSPI Flash芯片相关的函数。diskio.h定义了FatFs用到的宏,以及diskio.c文件内与底层硬件接口相关的函数声明。

00history.txt介绍了FatFs的版本更新情况。

00readme.txt说明了当前目录下 diskio.c 、diskio.h、ff.c、ff.h、integer.h的功能。

src文件夹下的源码文件功能简介如下:

  1. integer.h:文件中包含了一些数值类型定义。
  2. diskio.c:包含底层存储介质的操作函数,这些函数需要用户自己实现,主要添加底层驱动函数。
  3. ff.c: FatFs核心文件,文件管理的实现方法。该文件独立于底层介质操作文件的函数,利用这些函数实现文件的读写。 
  4. cc936.c:本文件在option目录下,是简体中文支持所需要添加的文件,包含了简体中文的GBK和Unicode相互转换功能函数。
  5. ffconf.h:这个头文件包含了对FatFs功能配置的宏定义,通过修改这些宏定义就可以裁剪FatFs的功能。如需要支持简体中文,需要把ffconf.h中的_CODE_PAGE 的宏改成936并把上面的cc936.c文件加入到工程之中。

建议阅读这些源码的顺序为:integer.h --> diskio.c --> ff.c 。

阅读文件系统源码ff.c文件需要一定的功底,建议读者先阅读FAT32的文件格式,再去分析ff.c文件。若仅为使用文件系统,则只需要理解integer.h及diskio.c文件并会调用ff.c文件中的函数就可以了。本章主要讲解如何把FATFS文件系统移植到开发板上,并编写一个简单读写操作范例。

25.3 FatFs文件系统移植实验

25.5.1 FatFs程序结构图

移植FatFs之前我们先通过FatFs的程序结构图了解FatFs在程序中的关系网络,见图 25-4。

 

图 25-4 FatFs程序结构图

用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到f_mount()、f_open()、f_write()、f_read()就可以实现文件的读写操作。

FatFs组件是FatFs的主体,文件都在源码src文件夹中,其中ff.c、ff.h、integer.h以及diskio.h四个文件我们不需要改动,只需要修改ffconf.h和diskio.c两个文件。

底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。我们使用QSPI Flash芯片作为物理设备,在上一章节已经编写好了QSPI Flash芯片的驱动程序,这里我们就直接使用。

25.3.2 硬件设计

FatFs属于软件组件,不需要附带其他硬件电路。我们使用QSPI Flash芯片作为物理存储设备,其硬件电路在上一章已经做了分析,这里就直接使用。

25.3.3 FatFs移植步骤

上一章我们已经实现了QSPI Flash芯片驱动程序,并实现了读写测试,为移植FatFs方便,我们直接拷贝一份工程,我们在工程基础上添加FatFs组件,并修改main函数的用户程序即可。

1)先拷贝一份QSPI Flash芯片测试的工程文件(整个文件夹),并修改文件夹名为“QSPI—FatFs文件系统”。将FatFs源码中的src文件夹整个文件夹拷贝一份至“QSPI—FatFs文件系统\USER\”文件夹下并修改名为“FATFS”,见图 25-5。

图 25-5 拷贝FatFs源码到工程

2)使用KEIL软件打开工程文件(..\QSPI—FatFs文件系统\Project\RVMDK(uv5)\ BH-F767.uvprojx),并将FatFs组件文件添加到工程中,需要添加有ff.c、diskio.c和cc936.c三个文件,见图 25-6。

 

图 25-6  添加FatFS文件到工程

3)添加FATFS文件夹到工程的include选项中。打开工程选项对话框,选择“C/C++”选项下的“Include Paths”项目,在弹出路径设置对话框中选择添加“FATFS”文件夹,见图 25-7。

 

图 25-7 添加FATFS路径到工程选项

4)如果现在编译工程,可以发现有两个错误,一个是来自diskio.c文件,提示有一些头文件没找,diskio.c文件内容是与底层设备输入输出接口函数文件,不同硬件设计驱动就不同,需要的文件也不同;另外一个错误来自cc936.c文件,提示该文件不是工程所必需的,这是因为FatFs默认使用日语,我们想要支持简体中文需要修改FatFs的配置,即修改ffconf.h文件。至此,将FatFs添加到工程的框架已经操作完成,接下来要做的就是修改diskio.c文件和ffconf.h文件。

25.3.4 FatFs底层设备驱动函数

FatFs文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已。表 25-1为FatFs移植时用户必须支持的函数。通过表 25-1我们可以清晰知道很多函数是在一定条件下才需要添加的,只有前三个函数是必须添加的。我们完全可以根据实际需求选择实现用到的函数。

前三个函数是实现读文件最基本需求。接下来三个函数是实现创建文件、修改文件需要的。为实现格式化功能,需要在disk_ioctl添加两个获取物理设备信息选项。我们一般只有实现前面六个函数就可以了,已经足够满足大部分功能。

为支持简体中文长文件名称需要添加ff_convert和ff_wtoupper函数,实际这两个已经在cc936.c文件中实现了,我们只要直接把cc936.c文件添加到工程中就可以了。

后面六个函数一般都不用。如真有需要可以参考syscall.c文件(src\option文件夹内)。

表 25-1 FatFs移植需要用户支持函数

函数

条件(ffconf.h)

备注

disk_status
disk_initialize
disk_read

总是需要

底层设备驱动函数

disk_write
get_fattime
disk_ioctl (CTRL_SYNC)

_FS_READONLY == 0

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 )

底层设备驱动函数是存放在diskio.c文件,我们的目的就是把diskio.c中的函数接口与QSPI Flash芯片驱动连接起来。总共有五个函数,分别为设备状态获取(disk_status)、设备初始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)。

接下来,我们对每个函数结合QSPI Flash芯片驱动做详细讲解。

设备状态获取

代码清单 25-1设备状态获取

1 DSTATUS TM_FATFS_FLASH_SPI_disk_status(BYTE lun)

 2 {

 3     FLASH_DEBUG_FUNC();

 4     if (sFLASH_ID == QSPI_FLASH_ReadID()) {   /*检测FLASH是否正常工作*/

 5         return TM_FATFS_FLASH_SPI_Stat &= ~STA_NOINIT;  /* Clear STA_NOINIT flag */

 6     } else {

 7         return TM_FATFS_FLASH_SPI_Stat |= STA_NOINIT;

 8     }

 9 }  

TM_FATFS_FLASH_SPI_disk_status函数只有一个参数lun,没有使用。对于QSPI Flash芯片,我们直接调用在QSPI_FLASH_ReadID()获取设备ID,然后判断是否正确,如果正确,函数返回正常标准;如果错误,函数返回异常标志。

设备初始化

代码清单 25-2 设备初始化

1 DSTATUS TM_FATFS_FLASH_SPI_disk_initialize(BYTE lun)

 2 {

 3     GPIO_InitTypeDef GPIO_InitStruct;

 4

 5     /* 使能 QSPI 及 GPIO 时钟 */

 6     QSPI_FLASH_CLK_ENABLE();

 7     QSPI_FLASH_CLK_GPIO_ENABLE();

 8     QSPI_FLASH_BK1_IO0_CLK_ENABLE();

 9     QSPI_FLASH_BK1_IO1_CLK_ENABLE();

10     QSPI_FLASH_BK1_IO2_CLK_ENABLE();

11     QSPI_FLASH_BK1_IO3_CLK_ENABLE();

12     QSPI_FLASH_CS_GPIO_CLK_ENABLE();

13

14     //设置引脚

15     /*!< 配置 QSPI_FLASH 引脚: CLK */

16     GPIO_InitStruct.Pin = QSPI_FLASH_CLK_PIN;

17     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

18     GPIO_InitStruct.Pull = GPIO_NOPULL;

19     GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;

20     GPIO_InitStruct.Alternate = QSPI_FLASH_CLK_GPIO_AF;

21

22     HAL_GPIO_Init(QSPI_FLASH_CLK_GPIO_PORT, &GPIO_InitStruct);

23

24     /*!< 配置 QSPI_FLASH 引脚: IO0 */

25     GPIO_InitStruct.Pin = QSPI_FLASH_BK1_IO0_PIN;

26     GPIO_InitStruct.Alternate = QSPI_FLASH_BK1_IO0_AF;

27     HAL_GPIO_Init(QSPI_FLASH_BK1_IO0_PORT, &GPIO_InitStruct);

28

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值