2、FatFs (Rev-R0.09)
2.1 实验描述及工程文件清单
实验描述:MicroSD卡文件系统 FATFS R0.07C 测试实验。在MicroSD卡里面创建一个DEMO.TXT文本文件,在文件里面写入字符串“感谢您选用野火STM32开发板 !然后通过串口将这些内容打印在电脑的超级终端上。这个更新版本的代码增加了简体中文和长文件名的支持,采用SDIO的4bit+DMA模式。并图解了文件系统移植的全部过程。
硬件连接:PC12-SDIO-CLK:CLK PC10-SDIO-D2 :DATA2 PC11-SDIO-D3:CD/DATA3PD2-SDIO-CMD :CMDPC8-SDIO-D0:DATA0 PC9-SDIO-D1:DATA1
用到的库文件:startup/start_stm32f10x_hd.cCMSIS/core_cm3.cCMSIS/system_stm32f10x.cFWlib/stm32f10x_gpio.cFWlib/stm32f10x_rcc.cFWlib/stm32f10x_usart.cFWlib/ stm32f10x_sdio.cFWlib/ stm32f10x_dma.cFWlib/ misc.c
用户编写的文件:USER/main.cUSER/stm32f10x_it.cUSER/usart1.cUSER/ sdio_sdcard.c
文件系统文件: ff9/diskio.cff9/ff.cff9/cc936.c
野火STM32开发板 MicroSD卡硬件原理图:
2.2 实验简介
本实验是在上一讲《SDIO(4bit + DMA)》的基础上讲解的,只有上一讲的实验成功了,文件系统才能跑起来。有关卡的底层的初始化,这里不再详述,这里着重讲解文件系统的移植和文件系统的应用。这个文档是更新版本,采用的文件系统的版本是目前最新的 R0.09。 文件系统的源码可以从fatfs官网下载https://elm-chan.org/fsw/ff/00index_e.html
2.3 FatFS文件系统简介
FAFFS是面向小型嵌入式系统的一种通用的FAT文件系统。FATFS完全是由AISI C语言编写并且完全独立于底层的I/O介质。因此它可以很容易地不加修改地移植到其他的处理器当中,如8051、PIC、AVR、SH、Z80、H8、ARM等。FATFS支持FAT12、FAT16、FAT32等格式,所以我们利用前面写好的SDIO驱动,把FATFS文件系统代码移植到工程之中,就可以利用文件系统的各种函数,对已格式化的SD卡进行读写文件了。
本实验是将FATFS移植到野火STM32开发板中,CPU为STM32F103VET6,是采用ARM公司最新内核ARMV7的一款单片机。
2.4 移植前的工作
2.4.1 分析FATFS的目录结构
在移植FATFS文件系统之前,我们先要到FAT的官网获取源码,版本为R0.07C。解压之后可看到里面有 doc 和 src 这两个文件夹。doc 文件夹里面是一些使用文档, src 里面是文件系统的源码。
打开 doc 文件夹,可看到如下文件目录:
其中 en 和 ja 这两个文件夹里面是编译好的html文档,讲的是FATFS里面各个函数的使用方法,这些函数就如LINUX下的系统调用,是封装得非常好的函数,利用这些函数我们就可以操作我们的MicroSD卡了。有关具体的函数我们在用到的时候再讲解。这两个文件夹的唯一区别就是 en 文件夹下的文档是英文的,ja 文件夹下的是日文的。偏偏就是没中文的,真狗血呀。00index_e.html是一些关于FATFS的英文简介,updates.txt是FATFS的更新信息,至于其他几个文件可以不看。
打开 src 文件夹,可看到如下目录:
option 文件夹下是一些可选的外部c文件,包含了多语言支持需要用到的文件和转换函数。
00readme.txt 说明了当前目录下 diskio.c 、diskio.h、ff.c、ff.h、integer.h的功用、涉及了FATFS的版权问题( 是自由软件 ),还讲到了FATFS的版本更新信息。
integer.h:是一些数值类型定义
diskio.c : 底层磁盘的操作函数,这些函数需要用户自己实现
ff.c : 独立于底层介质操作文件的函数,完全由ANSI C编写
cc936.c :简体中文支持所需要添加的文件,包含了简体中文的GBK和转换函数。
只要添加进来就行,这个文件不需要修改。
ffconf.h:这个头文件包含了对文件系统的各种配置,如需要支持简体中文要把_CODE_PAGE 的宏改成936并把上面的cc936.c文件加入到工程之中
建议阅读这些源码的顺序为:integer.h -> diskio.c -> ff.c 。
关于具体源码的分析不是我能力所及呀,大家就自己研究吧。我的主要工作是带领大家把这个文件系统移植到我们的开发板上,让这个文件系统先跑起来,这样才是硬道理呀。文件系统工作起来了的话,源码的分析那自然是大家的活啦。
2.5 开始移植
首先我们要获取一个完全没有修改过的文件系统源码,然后在 10-MicroSD卡这个文件夹下的实验代码下移植,这个实验代码实现的是卡的底层的块操作。注意,我们在移植这个文件系统的过程中会尽量保持文件系统源码的纯净,尽量做到在修改最少量的源码的情况下移植成功。
首先将 integer.h、diskio.h、diskio.c、ff.h、ff.c添加到工程目录下的USER文件夹下,如下截图:
然后并回到MDK界面下将diskio.c 、ff.c 这两个文件添加到USER目录下,如
下截图:
因为我们要用到这两个c文件,所以我们在main.c中将这两个c文件对应的头文件diskio.h 、ff.h 包含进来,如下截图:
好嘞,下面我们开始编译,这时会出现如下错误:
意思是说 FALSE 跟 TRUE这两个变量已经定义过了,为什么会出现这个错误呢?因为在 integer.h和我们的M3库头文件stm32f10x.h中都定义了这两个变量,所以就产生了重复定义:
integer.h
stm32f10x.h
怎么解决呢,很简单,只要搞掉一个即可,但要去掉哪个呢?我们考虑到外面的M3库很多文件都包含了stm32f10x.h这个头文件,假如是修改stm32f10x.h的话工作量会非常大,鉴于此就只能委屈integer.h了,修改如下,将它注释掉:
好嘞,修改之后,我们再编译下,接着又出现如下错误:
意思是说在diskio.h、ff.c中 BOOL 、FALSE、TRUE没定义。刚刚才把人家注释掉了,不报错才怪。
解决方法如下:
1、将integer.h中有关BOOL的那句注释掉,注释掉也没太大关系,因为注释掉的不会怎么用到:
2、在ff.c文件的开头重新定义一个布尔变量,取名为 bool,与stm32f10x.h中的名字一样:
同时在 ff.c的第585行做如下修改:
现在我们再编译下,发现既没警告也没错误:
到这里我们算是把文件系统移植成功了,接下来的任务就是调用文件系统的函数来操作我们的卡了。其实这里的移植是非常非常简单的,要是你学过LINUX的话,那里面的UBOOT移植,系统移植,那才叫人头疼,就光是目录里面的文件夹都几千个,更别说是找到要修改的源代码了,刚接触的话绝对叫你吐血,就连下载个交叉编译器都涉及到移植。
2.6实验代码分析
FATFS是独立于底层介质的应用函数库,对底层介质的操作都要交给用户去实现,其仅仅是提供了一个函数接口而已,函数为空,要用户添加代码。
这几个函数的原型如下,在diskio.c中定义:
/* Inidialize a Drive */
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0..) */
)
/* Return Disk Status */
DSTATUS disk_status (
BYTE drv /* Physical drive nmuber (0..) */
)
/* Read Sector(s) */
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
/* Write Sector(s) */
#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
/* Miscellaneous Functions */
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
这些函数都是操作底层介质的函数,都需要用户自己实现,然后FATFS的应用函数就可以调用这些函数来操作我们的卡了。关于这些底层介质函数是如何实现的,请参考源码,这里就不贴出来了。
在diskio.c的最后我们还得提供了获取时间的函数,因为ff.c中调用了这个函数,而FATFS库又没有给出这个函数的原型,所以需要用户实现,不然会编译出错,函数体为空即可(也可以为它加载STM32的RTC驱动):
实现好底层介质的操作函数之后,我们就可以回到应用层了,下面我们从main函数开始看起。有关系统初始化和串口初始化这部分请参考前面的教程,这里不再详述。
首先我们调用函数disk_initialize( 0 ); 将我们的底层硬件初始化好,这一步非常重要,如果不成功的话,接下来什么都干不了。
f_open( &fsrc , "0:/Demo.TXT" , FA_CREATE_NEW | FA_WRITE); 将在刚刚开辟的工作区的盘符0下打开一个名为 Demo.TXT的文件,以只写的方式打开,如果文件不存在的话则创建这个文件。并将Demo.TXT这个文件关联到 fsrc 这个结构指针,以后我们操作文件就是通过这个结构指针来完成的。
f_write(&fsrc, textFileBuffer, sizeof(textFileBuffer), &br); 将缓冲区的数据写到刚刚打开的Demo.TXT文件中。写完之后调用f_close(&fsrc);。关闭文件,
f_open(&fsrc, "0:/Demo.TXT", FA_OPEN_EXISTING | FA_READ);以只读的方式打开刚刚的文件。
f_read( &fsrc, buffer, sizeof(buffer), &br ); 将文件的内容读到缓冲区,然后调用printf(" %s ", buffer);将数据打印到电脑的超级终端。
最后调用f_close(&fsrc);关闭文件。当被打开的文件操作完成之后都要调用f_close();将它关闭,就像一块动态分配的内存在用完之后都要调用free()来将它释放。
这里涉及到了FATFS文件系统库函数的操作,如果你学过LINUX系统调用的话,操作这些函数将是非常简单,没有学过的话也没太大的关系,因为FATFS源码目录doc这个文件夹中提供了每个应用函数的用法,如 f_mount():
2.7 实验现象
将野火STM32开发板供电(DC5V),插上JLINK,插上串口线(两头都是母的交叉线),插上MicroSD卡( 野火用的是1G,4G的也已经测试通过 ),打开超级终端,配置超级终端为115200 8-N-1,将编译好的程序下载到开发板,即可看到超级终端打印出如下信息:
|