串行FLASH文件系统FatFs---转自野火论坛

 

为了支持长文件名,需要用到FATFS源码中的cc936.c的两个函数ff_convert,ff_wtoupper;这里面直接用了两个大数组(127KB)来做unicode转gbk(OEM)的对照表,这两个对照表太大,太占用内部flash,所以,一般将它们做成一个.bin的文件,叫做UNIGBK.BIN,将这个文件烧录到外部FLASH中,然后需要转换的时候,读外部FLASH中的这个文件内容。

下面是转自野火论坛的一篇FATFS的移植教材,有一定的参考价值。

 

本章参考资料:《00index_e.html》,这是FatFs官方的编译好的HTML文档,里面有FatFs所有函数的介绍和函数的应用示例,学习FatFs看这个官方的文档即可。

261 FatFs参考资料

25.1 文件系统

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

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

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

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

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

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

25.2 FatFs文件系统简介

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

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

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

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

25.2.1 FatFs的目录结构

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

251 FatFs文件目录

25.2.2 FatFs帮助文档

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

252 doc文件夹的文件目录

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

25.2.3 FATFS源码

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

253 src文件夹的文件目录

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

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

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

00readme.txt说明了当前目录下 diskio.c diskio.hff.cff.hinteger.h的功能。

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

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

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

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

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

    ffconf.h:这个头文件包含了对FatFs功能配置的宏定义,通过修改这些宏定义就可以裁剪FatFs的功能。如需要支持简体中文,需要把ffconf.h中的_CODE_PAGE 的宏改成936并把上面的cc936.c文件加入到工程之中。

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

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

25.3 FatFs文件系统移植实验

25.3.1 FatFs程序结构图

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

254 FatFs程序结构图

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

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

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

25.3.2 硬件设计

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

25.3.3 FatFs移植步骤

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

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

255 拷贝FatFs源码到工程

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

256 添加FatFS文件到工程

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

257 添加FATFS路径到工程选项

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

25.3.4 FatFs底层设备驱动函数

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

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

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

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

251 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中的函数接口与SPI Flash芯片驱动连接起来。总共有五个函数,分别为设备状态获取(disk_status)、设备初始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)

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

宏定义

代码清单 251 物理编号宏定义

1 /* 为每个设备定义一个物理编号 */

2 #define ATA 0 // 预留SD卡使用

3 #define SPI_FLASH 1 // 外部SPI Flash

这两个宏定义在FatFs中非常重要,FatFs是支持多物理设备的,必须为每个物理设备定义一个不同的编号。

SD卡是预留接口,在讲解SDIO接口相关章节后会用到,可以实现使用读写SD卡内文件。

设备状态获取

代码清单 252设备状态获取

1 DSTATUS disk_status (

2 BYTE pdrv /* 物理编号 */

3 )

4 {

5

6 DSTATUS status = STA_NOINIT;

7

8 switch (pdrv) {

9 case ATA: /* SD CARD */

10 break;

11

12 case SPI_FLASH:

13 /* SPI Flash状态检测:读取SPI Flash 设备ID */

14 if (sFLASH_ID == SPI_FLASH_ReadID()) {

15 /* 设备ID读取结果正确 */

16 st

你可能感兴趣的:(c/c++,运维,数据结构与算法)