STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

时间:2024-03-13 13:53:56

直接操作SD卡存储单元,在实际应用中是不现实的。SD卡一般用来存放文件,所以都需要加载文件系统到里面。类似于串行spi flash芯片,移植FatFs文件系统到SD卡内。
对于FatFs文件系统的介绍和具体移植过程,参考前面的文章。本章重点放在SD卡与FatFs接口函数编写上。与串行flash的FatFs文件系统移植例程相比,FatFs文件系统部分的代码只有diskio.c文件有所不同,其他的不用修改,所以一个简易的移植方法是利用原来工程进行修改。下面讲解利用原来工程实现SD卡的FatFs文件系统。

1、FatFs移植步骤

上一章我们已经完成了SD卡驱动程序以及进行了简单的读写测试。该工程有很多东西是现在可以使用的,所以我们先把上一章的工程文件完整的拷贝一份,并修改文件夹名称为“SDIO-FatFs移植与读写测试”,如果此时使用MDK软件打开该工程,应该是编译无错误并实现上一章的测试功能。

接下来,我们到串行flash文件系统移植工程文件的“user”文件夹下拷贝“FATFS”整个文件夹到现在工程文件的“user”文件夹下。该文件夹是FatFs文件系统的所有代码文件,在串行flash移植FatFs文件系统时我们对部分文件做了修改,这里主要是想保留之前的配置,而不是使用FatFs官方源码还需要重新配置。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

现在就可以使用MDK软件打开该工程,并把FatFs相关文件添加进工程内。
STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

添加文件之后还必须添加相关路径。
STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

操作到这里,工程文件结构就算完整了,接下来就是修改文件代码了。有2个文件需要修改,diskio.c文件和main.c文件。main.c文件可以参考"SPI FLASH文件系统移植”工作的main.c文件,只是做了小细节修改而已。这里重点讲解diskio.c文件,也是整个移植的重点。

2、FatFs接口函数

FatFs文件系统与存储设备的连接函数在diskio.c文件中,主要有5个函数需要我们编写。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

FatFs支持同时挂载多个存储设备,通过定义为不同编号以区别。SD卡一般定义为编号0,编号1预留给串行flash芯片使用。使用宏定义方式给出SD卡块大小,方便修改。
实际上,SD卡块大小一般都是设置为512字节的,不管是标准SD卡还是高容量SD卡。
disk_status函数要求返回存储设备的当前状态,对于SD卡一般返回SD卡插入状态,这里直接返回正常状态。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

该函数用于初始化存储设备,一般包括相关GPIO初始化、外设环境初始化、中断配置等。对于SD卡,直接调用SD_Init函数实现对SD卡初始化,如果函数返回SD_OK说明SD卡正确插入,并且控制器可以与之正常通信。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

disk_read函数用于从存储设备指定地址开始读取一定数量的数据到指定存储区内
对于SD卡,最重要是使用SD_ReadMultiBlocks函数读取多块数据到存储区。这里需要注意的地方是SD卡数据操作是使用DMA传输的,并设置数据尺寸为32位大小,为实现数据正确传输,要求存储区是4字节对齐。在某些情况下。在某些情况下,FatFs提供的buff地址不是4字节对齐,这会导致DMA数据传输失败,所以为保证数据传输正确,可以先判断存储区地址是否是4字节对齐,如果存储区地址已经是4字节对齐,无需其他处理,直接使用SD_ReadMultiBlocks函数执行多块读取即可。如果判断得到地址不是4字节对齐,则先申请一个4字节对齐的临时缓冲区,即局部数组变量scratch,通过定义为DWORD类型可以使得其自动4字节,scratch所占的总存储空间也是一个块大小,这样把一个块数据读取到scratch内,然后把scratch存储区内容拷贝到buff地址空间上就可以了

SD_ReadMultiBlocks函数用于从SD卡内读取多个块数据,它有4个形参,分别为存储区地址指针、起始块地址、块大小以及块数量。为保证数据传输完整,还需要调用SD_WaitReadOperation函数和SD_GetStatus函数检测和保证传输完成。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

disk_write函数用于向存储设备指定地址写入指定数量的数据。对于SD卡,执行过程与disk_read函数非常相似,也必须先检测存储区地址是否是4字节对齐,如果是4字节对齐则直接调用SD_WriteMultiBlocks函数完成多块数据写入操作。如果不是4字节对齐,申请一个4字节对齐的临时缓冲区,先把待写入的数据拷贝到该临时缓冲区内,然后才写入到SD卡

SD_WriteMultiBlocks函数是想SD卡写入多个块数据,它有四个形参,分别为存储区地址指针、起始块地址、块大小以及块数量,它与SD_ReadMultiBlocks函数执行相互过程。
最后也是需要使用相关函数保证数据写入完整才退出disk_write函数。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

disk_ioctl函数有3个形参,pdrv为设备物理编号,cmd为控制指令,包括发出同步信号、获取扇区数目、获取扇区大小、获取擦除块数量等指令,buff为指令对应的数据指针。

对于SD卡,为支持格式化功能,需要用到获取扇区数量(GET_SECTOR_COUNT)指令和获取块尺寸(GET_BLOCK_SIZE)。另外,SD卡扇区大小为512字节,串行flash芯片一般设置扇区大小为4096字节,所以需要用到获取扇区大小(GET_SECTOR_SIZE)指令。所以需要用到获取扇区大小指令(GET_SECTOR_SIZE)。

至此,基于SD卡的FatFs文件系统移植就已经完成了,最重要就是diskio.c文件中5个函数的编写。接下来就编写FatFs基本的文件操作检测移植代码是否可以正确执行。

3、FatFs功能测试

主要的测试包括格式化测试、文件写入测试和文件读取测试三个部分,主要程序都在main.c文件中实现。

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

FATFS是在ff.h文件中定义的一个结构体类型,针对的对象是物理设备,包含了物理设备的物理编号、扇区大小等信息,一般我们都需要为每个物理设备定义一个FATFS变量。

FIL也是在ff.h文件中定义的一个结构体类型,针对的对象是文件系统内具体的文件,包含了文件很多基本属性,比如文件大小、路径、当前读写地址等。如果需要在同一时间打开多个文件进行读写,才需要定义多个FIL变量,不然一般定义一个FIL变量即可。

FRESULT也是在ff.h文件中定义的一个枚举类型,作为FatFs函数的返回值类型,主要管理FatFs运行中出现的错误。总共有19种错误类型,包括物理设备读写错误、找不到文件、没有挂载工作空间等错误。这在实际编程中非常重要,当有错误出现时我们要停止文件读写,通过返回值我们可以快速定位到错误发生的可能地点。如果运行没有错误才返回FR_OK。

fnum是个32位无符号整形变量,用来记录实际读取或写入数据的变量

ReadBuffer和WriteBuffer 分别对应读取和写入数据缓存区,都是8位无符号整形数组

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

STM32F1开发指南笔记45----基于SD卡的文件系统FatFs

首先,调用BL8782_PDN_INIT函数禁用WIFI模块,接下来初始化RGB彩灯和调试串口,用来指示程序进程。

FatFs的第一步工作就是使用f_mount函数挂载工作区。f_mount函数有3个形参,第一个参数是指向FATFS变量指针,如果赋值为NULL可以取消物理设备挂载。第二个参数为逻辑设备编号,使用设备根路径表示,与物理设备编号挂钩,定义SD卡物理编号为0,所以这里使用“0:”。第三个参数可选0或1,1表示立即挂载,0表示不立即挂载,延迟挂载。f_mount函数会返回一个FRESULT类型值,指示运行情况。

如果f_mount函数返回值为FR_NO_FILESYSTEM,说明SD卡没有FAT文件系统。我们就必须对SD卡进行格式化处理。使用f_mkfs函数可以实现格式化操作。f_mkfs函数有3个形参,第一个参数为逻辑设备编号。第二个参数可选0或1(0表示设备为硬盘,1表示设备为软盘)。第三个参数指定扇区大小,如果为0,表示通过disk_ioctl函数获取。
格式化成功后需要先取消挂载原来设备,再重新挂载设备。

在设备正常挂载后,就可以进行文件读写操作了。使用文件之前,必须使用f_open函数打开文件,不再使用文件必须使用f_close函数关闭文件,这也跟电脑端操作文件步骤类似。
f_open函数有3个形参,第一个参数为文件对象指针。第二个参数为目标文件,包含绝对路径的文件名称和后缀名。第三个参数为访问文件模式选择,可以是打开已经存在的文件模式、读模式、写模式、新建模式、总是新建模式等的或运行结果。比如对于写测试,使用FA_CREATE_ALWAYS和FA_WRITE组合模式,就是总是新建文件并进行写模式。
f_close函数用于不再对文件进行读写操作关闭文件,f_close函数只要一个形参,为文件对象指针。f_close函数运行可以确保缓冲区完全写入到文件内。

成功打开文件之后就可以使用f_write函数和f_read函数对文件进行写操作和读操作。
这2个函数用到的参数是一致的,只不过一个是数据写入,一个是数据读取。f_write函数第一个形参为文件对象指针,使用与f_open函数一致即可。第二个参数为待写入数据的首地址,对于f_read函数就是用来存放读出数据的首地址。第三个参数为写入数据的字节数,对于f_read函数就是欲读取数据的字节数。第四个参数为32位无符号整形指针,这里使用fnum变量地址赋值给它,在运行读写操作函数后,fnum变量指示成功读取或写入的字节个数。

最后,不再使用文件系统时,使用f_mount函数取消挂载。

操作步骤:
挂载文件系统——打开文件——读写操作——关闭文件——取消挂载
f_mount() —— f_open() —— f_read()/f_write() —— f_close() ——f_mount(NULL)