第 26 章 串行 FLASH 文件系统 FatFs

26.1 文件系统

26.2 FatFs 文件系统简介

2.1 FatFs 的目录结构
2.2 FatFs 帮助文档
2.3 FATFS 源码

src 文件夹下的源码文件功能

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

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

函数。

(3) ffff.c:FatFs 核心文件,文件管理的实现方法。该文件独立于底层介质操作文件的函数,利用

这些函数实现文件的读写。

(4) cc936.c:本文件在 option 目录下,是简体中文支持所需要添加的文件,包含了简体中文的

GBK 和 Unicode 相互转换功能函数。

(5) ffffconf.h: 这个头文件包含了对 FatFs 功能配置的宏定义,通过修改这些宏定义就可以裁剪

FatFs 的功能。如需要支持简体中文,需要把 ffffconf.h 中的 _CODE_PAGE 的宏改成 936 并把

上面的 cc936.c 文件加入到工程之中。

26.3 FatFs 文件系统移植实验

3.1 FatFs 程序结构图

(1) 用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到 f_mount()、

f_open()、f_write()、f_read() 就可以实现文件的读写操作。

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

(3) 底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。

(4) 我们使用SPI Flash 芯片作为物理设备,在上一章节已经编写好了 SPI Flash 芯片的驱动程序,这里我们就直接使用。

3.2 硬件设计
3.3 FatFs 移植步骤
3.4 FatFs 底层设备驱动函数

(1) 宏定义。

(2) 设备状态获取disk_status()。

(3) 设备初始化disk_initialize()。

(4) 读取扇区disk_read()。

(5) 扇区写入disk_write()。

(6) 其他控制disk_ioctl()。

(7) 时间戳获取get_fattime()。

3.5 FatFs 功能配置

(1)_USE_MKFS:格式化功能选择,为使用 FatFs 格式化功能,需要把它设置为 1。

(2)_CODE_PAGE:语言功能选择,并要求把相关语言文件添加到工程宏。为支持简体中文文件名

需要使用“936”,正如在图添加 FatFS 文件到工程 的操作,我们已经把 cc936.c 文件添加到工程

中。

(3)_USE_LFN:长文件名支持,默认不支持长文件名,这里配置为 2,支持长文件名,并指定使

用栈空间为缓冲区。

(4)_VOLUMES:指定物理设备数量,这里设置为 2,包括预留 SD 卡和 SPI Flash 芯片。

(5)_MIN_SS 、_MAX_SS:指定扇区大小的最小值和最大值。SD 卡扇区大小一般都为 512 字节,

SPI Flash 芯片扇区大小一般设置为 4096 字节,所以需要把 _MAX_SS 改为 4096。

3.6 FatFs 功能测试

(1)FatFs 的第一步工作就是使用 f_mount 函数挂载工作区。

(2)如果 f_mount 函数返回值为 FR_NO_FILESYSTEM,说明没有 FAT 文件系统,比如新出厂的 SPI

Flash 芯片就没有 FAT 文件系统。我们就必须对物理设备进行格式化处理。格式化成功后需要先取消挂载原来设备,再重新挂载设备。

(3)在设备正常挂载后,就可以进行文件读写操作了。

(4)f_close 函数用于不再对文件进行读写操作关闭文件,f_close 函数只要一个形参,为文件对象指

针。f_close 函数运行可以确保缓冲区完全写入到文件内。

(5)成功打开文件之后就可以使用 f_write 函数和 f_read 函数对文件进行写操作和读操作。

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

26.4 FatFs 功能使用实验

4.1 硬件设计

4.2 软件设计

(1)FatFs 多项功能测试

(2)文件信息获取

(3)路径扫描

(4)主函数

main.c

#include "bsp_led.h"
#include "./flash/bsp_SPI_Flash.h"
#include "bsp_usart.h"
#include "bsp_led.h"
#include "ff.h"		
#include "string.h"
#include "stdio.h"

/*自添*/
char fpath[100];
FATFS fs; /* FatFs 文件系统对象 */
FIL fnew; /* 文件对象 */
FRESULT res_flash; /* 文件操作结果 */
UINT fnum; /* 文件成功读写数量 */
BYTE ReadBuffer[1024]= {0}; /* 读缓冲区 */
BYTE textFileBuffer[] = /* 写缓冲区 */
"欢迎使用野火 STM32 开发板 今天是个好日子,新建文件系统测试文件\r\n";
	/*声明*/
static FRESULT miscellaneous(void);
static FRESULT file_check(void) ;
static FRESULT scan_files (char* path);
extern int f_printf (
	FIL* fp,			/* Pointer to the file object */
	const TCHAR* fmt,	/* Pointer to the format string */
	...					/* Optional arguments... */
);

int main(void) 
{ 
/* 初始化调试串口,一般为串口 1 */
USART_Config();
printf("******** 这是一个 SPI FLASH 文件系统实验 *******\r\n");
 //在外部 SPI Flash 挂载文件系统,文件系统挂载时会对 SPI 设备初始化
res_flash = f_mount(&fs,"1:",1);
if (res_flash!=FR_OK) {
 printf("!!外部 Flash 挂载文件系统失败。(%d)\r\n",res_flash);
 printf("!!可能原因:SPI Flash 初始化不成功。\r\n");
 while (1);
 } else {
 printf("》文件系统挂载成功,可以进行测试\r\n");
 }

 /* FatFs 多项功能测试 */
 res_flash = miscellaneous();
 printf("\n*************** 文件信息获取测试 **************\r\n");
 res_flash = file_check();


 printf("***************** 文件扫描测试 ****************\r\n");
 strcpy(fpath,"1:");
 scan_files(fpath);


 /* 不再使用文件系统,取消挂载文件系统 */
 f_mount(NULL,"1:",1);

 /* 操作完成,停机 */
 while (1) {
 }
 }

	/* FatFs 多项功能测试 */
static FRESULT miscellaneous(void) {
DIR dir;
FATFS *pfs;
DWORD fre_clust, fre_sect, tot_sect;
 printf("\n*************** 设备信息获取 ***************\r\n");
/* 获取设备信息和空簇大小 */
 res_flash = f_getfree("1:", &fre_clust, &pfs);

 /* 计算得到总的扇区个数和空扇区个数 */
 tot_sect = (pfs->n_fatent - 2) * pfs->csize;
 fre_sect = fre_clust * pfs->csize;

 /* 打印信息 (4096 字节/扇区) */
 printf("》设备总空间:%10lu KB。\n》可用空间: %10lu KB。\n",
 tot_sect *4, fre_sect *4);
 printf("\n******** 文件定位和格式化写入功能测试 ********\r\n");
 res_flash = f_open(&fnew, "1:FatFs 功能测试文件.txt",FA_CREATE_ALWAYS|FA_WRITE|FA_READ );
 res_flash=f_write(&fnew,textFileBuffer,sizeof(textFileBuffer),&fnum);
 if ( res_flash == FR_OK )
 {
 /* 文件定位,定位到文件的末尾 */

res_flash = f_lseek(&fnew,f_size(&fnew)-1);
printf("%d",res_flash);
 if (res_flash == FR_OK)
 {
 /* 格式化写入,参数格式类似 printf 函数 */
	 f_printf(&fnew,"在原来文件新添加一行内容\n");
	 f_printf(&fnew,"》设备总空间:%10lu KB。\n》可用空间;%10lu KB。\n",tot_sect *4, fre_sect *4);
 /* 文件定位到文件起始位置 */
 res_flash = f_lseek(&fnew,0);
 /* 读取文件所有内容到缓存区 */
 res_flash = f_read(&fnew,ReadBuffer,f_size(&fnew),&fnum);
 if (res_flash == FR_OK)
 {
 printf("》文件内容:\n%s\n",ReadBuffer);
 }
 }
 f_close(&fnew);

 printf("\n********** 目录创建和重命名功能测试 **********\r\n");
 /* 尝试打开目录 */
 res_flash=f_opendir(&dir,"1:TestDir");
 if (res_flash!=FR_OK)
 {
 /* 打开目录失败,就创建目录 */
 res_flash=f_mkdir("1:TestDir");
 }
 else
 {
 /* 如果目录已经存在,关闭它 */
 res_flash=f_closedir(&dir);
 /* 删除文件 */
 f_unlink("1:TestDir/testdir.txt");
 }
 if (res_flash==FR_OK)
 {
 /* 重命名并移动文件 */
 res_flash=f_rename("1:FatFs 功能测试文件.txt",
 "1:TestDir/testdir.txt");
 if (res_flash==FR_OK)
 {
 printf("》重命名并移动文件操作成功\n");
 }
 else
 {
 printf("》重命名并移动文件操作失败:%d\n",res_flash);
 }
 }
 }
 else
 {
 printf("!! 打开文件失败:%d\n",res_flash);
 printf("!! 或许需要再次运行“FatFs 移植与读写测试”工程\n");
 }
 return res_flash;
 }
	/*文件信息获取*/
static FRESULT file_check(void)  { 
	static FILINFO fno;
 /* 获取文件信息,必须确保文件存在 */
res_flash=f_stat("1:TestDir/testdir.txt",&fno);
if (res_flash==FR_OK) {
printf("“testdir.txt”文件信息:\n");
printf("》文件大小: %ld(字节)\n", fno.fsize);
 printf("》时间戳: %u/%02u/%02u, %02u:%02u\n",
 (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,
 fno.ftime >> 11, fno.ftime >> 5 & 63);
 printf("》属性: %c%c%c%c%c\n\n",
 (fno.fattrib & AM_DIR) ? 'D' : '-', // 是一个目录
 (fno.fattrib & AM_RDO) ? 'R' : '-', // 只读文件
 (fno.fattrib & AM_HID) ? 'H' : '-', // 隐藏文件
 (fno.fattrib & AM_SYS) ? 'S' : '-', // 系统文件
 (fno.fattrib & AM_ARC) ? 'A' : '-'); // 档案文件
 }
 return res_flash;
 }
/*路径扫描*/
static FRESULT scan_files (char* path)
{ 
FRESULT res; //部分在递归过程被修改的变量,不用全局变量
FILINFO fno;
DIR dir;
int i;
char *fn; // 文件名
 #if _USE_LFN
 /* 长文件名支持 */
 /* 简体中文需要 2 个字节保存一个“字”*/
 static char lfn[_MAX_LFN*2 + 1];
 fno.lfname = lfn;
 fno.lfsize = sizeof(lfn);
 #endif
 //打开目录
 res = f_opendir(&dir, path);
 if (res == FR_OK) {
 i = strlen(path);
 for (;;) {
 //读取目录下的内容,再读会自动读下一个文件
 res = f_readdir(&dir, &fno);
 //为空时表示所有项目读取完毕,跳出
 if (res != FR_OK || fno.fname[0] == 0) break;
 #if _USE_LFN
 fn = *fno.lfname ? fno.lfname : fno.fname;
 #else
 fn = fno.fname;
 #endif
 //点表示当前目录,跳过
 if (*fn == '.') continue;
 //目录,递归读取
 if (fno.fattrib & AM_DIR) {
 //合成完整目录名
 sprintf(&path[i], "/%s", fn);
 //递归遍历
 res = scan_files(path);
 path[i] = 0;
 //打开失败,跳出循环
 if (res != FR_OK)
 break;
 } else {
 printf("%s/%s\r\n", path, fn); //输出文件名
 /* 可以在这里提取特定格式的文件路径 */
 }//else
 } //for
 }
 return res;
 }

void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}
/*********************************************END OF FILE**********************/

其他配置:由于代码过长,不能全篇展示。特附

在这里插入图片描述

如出现问题可看:https://blog.youkuaiyun.com/C_say_easy_to_me/article/details/125623523?spm=1001.2014.3001.5502

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

入世浮尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值