二、串行FLASH文件系统FatFs移植

经过上一节的分析,我们对文件系统有一定的理解了,这一节给大家介绍怎么把FatFs文件系统的这些代码移植到STM32S上,然后STM32利用这一些代码或者函数,以文件的格式对FLASH进行读写数据。

实则对diskio.c提供一些函数接口。

首先将 ff11a\src文件夹拷贝至user底下,重命名为fatFs,以方便我们后续操作

       

移植文件系统主要就是实现底层disk函数的具体功能

一、底层disk接口程序API配置

1、首先将diskio.c和ff.c包含到工程中,如图所示:

 2、添加完成之后,点击编译,报错找不到头文件,我们需要将diskio.c官方测试样例的三个头文件注释掉

 3、将diskio.c中的result = ATA_disk_status();、result = MMC_disk_status();和result = USB_disk_status();注释掉

4、注释完之后,再次编译,找不到get_fattime,我们需要手动将get_fattime()函数添加到文件中

//返回时间
DWORD get_fattime (void)
{
    //这个我不用 所以没有实现
    return 0;

}

5、可以发现diskio.h中官方已经定义了三个物理存储介质编号ATA、MMC和USB,diskio中的所有存储介质操作函数通过传入参数判断区分具体操作哪个存储介质,我用的SD卡和FLASH就将之前定义的宏注释掉,重新定义自己的宏,并将所有底层操作函数中所有的switch case中的原存储介质替换掉

6、首先我们来看disk_initialize函数,只有一个参数用于传入存储设备编号,编号用于区分操作的存储器,返回值返回当前的存储器状态。该函数用于初始化存储设备,并使设备进入读写可用的状态,初始化完之后用于返回一个状态值来告诉上层的调用者。

该函数用于被FatFs module(中间层)调用,不能直接使用,需要使用中间层的f_mount挂载函数来调用(我们初始化文件系统都通过使用f_mount来实现)

(1)首先将SPI_FLASH_Init(),函数添加到  case SPI_FLASH :中。

(2)接着要返回一个状态,但我们仅仅通过SPI_FLASH_Init()不知道是否初始化FLASH成功。因此我们通过SPI_FLASH_ReadID()函数通过判断读取设备号是否成功来判断,将SPI_FLASH_ReadID()放在“ DSTATUS disk_status (BYTE pdrv){}”状态读取函数中

/*-----------------------------------------------------------------------*/
/* 获取设备状态                                                          */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (BYTE pdrv		/* 物理编号 */
)
{DSTATUS status = STA_NOINIT;switch (pdrv) {case ATA:	/* SD CARD */break;case SPI_FLASH:      /* SPI Flash状态检测:读取SPI Flash 设备ID */if(sFLASH_ID == SPI_FLASH_ReadID()){/* 设备ID读取结果正确 */status &= ~STA_NOINIT;}else{/* 设备ID读取结果错误 */status = STA_NOINIT;;}break;default:status = STA_NOINIT;}return status;
}

 (4)如果我们不确定FLASH是否处于低功耗模式,我们需要在init后面添加SPI_Flash_WAKEUP()函数来确保FLASH处于正常功耗模式(因为FLASH在低功耗状态下,无法正常工作),所以disk_initializ函数的写法如下:

*-----------------------------------------------------------------------*/
/* 设备初始化                                                            */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (BYTE pdrv				/* 物理编号 */
)
{uint16_t i;DSTATUS status = STA_NOINIT;	switch (pdrv) {case ATA:	         /* SD CARD */break;case SPI_FLASH:    /* SPI Flash */ /* 初始化SPI Flash */SPI_FLASH_Init();/* 延时一小段时间 */i=500;while(--i);	/* 唤醒SPI Flash */SPI_Flash_WAKEUP();/* 获取SPI Flash芯片状态 */status=disk_status(SPI_FLASH);break;default:status = STA_NOINIT;}return status;
}

7、继续我们来配置disk_read()函数

disk_read()函数如下:

用于读取扇区(一个或者多个),第一个参数为设备号;第二个参数为读取扇区的起始地址,读取完数据后通过指针返回给上层,具体传入数组或指针由上层定义;第三个参数为开始的扇区号;第四个个参数为读取的扇区个数。

通过调用void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead);函数来实现扇区的读取。通过disk_read()中的sector扇区号来计算出ReadAddr起始地址,通过count扇区个数来算出NumByteToRead要读取多少个字节。一个扇区4096Byte(4KB),一个块有16个扇区,但是此处扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面6MB空间

通过调用SPI_FLASH_BufferRead()函数,并算出对应参数。默认读取成功,返回值为RES_OK
 

/*-----------------------------------------------------------------------*/
/* 读扇区:读取扇区内容到指定存储区                                              */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (BYTE pdrv,		/* 设备物理编号(0..) */BYTE *buff,		/* 数据缓存区 */DWORD sector,	/* 扇区首地址 */UINT count		/* 扇区个数(1..128) */
)
{DRESULT status = RES_PARERR;switch (pdrv) {case ATA:	/* SD CARD */break;case SPI_FLASH:/* 扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面6MB空间 */sector+=512;      SPI_FLASH_BufferRead(buff, sector <<12, count<<12);status = RES_OK;break;default:status = RES_PARERR;}return status;
}

8、接着配置disk_write()函数,注意使用写功能,必须将_FS_READONLY置0,否则只支持只读

/*-----------------------------------------------------------------------*/
/* 写扇区:见数据写入指定扇区空间上                                      */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
DRESULT disk_write (BYTE pdrv,			  /* 设备物理编号(0..) */const BYTE *buff,	/* 欲写入数据的缓存区 */DWORD sector,		  /* 扇区首地址 */UINT count			  /* 扇区个数(1..128) */
)
{uint32_t write_addr; DRESULT status = RES_PARERR;if (!count) {return RES_PARERR;		/* Check parameter */}switch (pdrv) {case ATA:	/* SD CARD */      break;case SPI_FLASH:/* 扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面6MB空间 */sector+=512;write_addr = sector<<12;    SPI_FLASH_SectorErase(write_addr);SPI_FLASH_BufferWrite((u8 *)buff,write_addr,count<<12);status = RES_OK;break;default:status = RES_PARERR;}return status;
}
#endif

9、再来配置disk_ioctl函数,

在初始化设备前,必须将存储设备进行格式化,建立好文件信息结构,从而才能实现对设备存储内容以文件格式进行读写。

第一个参数为设备号;第二个参数为cmd命令号,文件系统通过命令号来告诉底层,比如获取当前设备的容量,某个文件的大小等等(底层通过switch...case...来确定不同的返回值);第三个参数buff既可以作为输入也可以作为输出

/*-----------------------------------------------------------------------*/
/* 其他控制                                                              */
/*-----------------------------------------------------------------------*/#if _USE_IOCTL
DRESULT disk_ioctl (BYTE pdrv,		/* 物理编号 */BYTE cmd,		  /* 控制指令 */void *buff		/* 写入或者读取数据地址指针 */
)
{DRESULT status = RES_PARERR;switch (pdrv) {case ATA:	/* SD CARD */break;case SPI_FLASH:switch (cmd) {/* 扇区数量:1536*4096/1024/1024=6(MB) */case GET_SECTOR_COUNT:*(DWORD * )buff = 1536;		break;/* 扇区大小  */case GET_SECTOR_SIZE :*(WORD * )buff = 4096;break;/* 同时擦除扇区个数 */case GET_BLOCK_SIZE :*(DWORD * )buff = 1;break;        }status = RES_OK;break;default:status = RES_PARERR;}return status;
}
#endif

二 、FatFs 功能配置


ffconf.h 文件是FatFs 功能配置文件,我们可以对文件内容进行修改,使得FatFs 更符合
我们的要求。ffconf.h 对每个配置选项都做了详细的使用情况说明。下面只列出修改的配置,
其他配置采用默认即可。

1 #define _USE_MKFS 1
2 #define _CODE_PAGE 936
3 #define _USE_LFN 2
4 #define _VOLUMES 2
5 #define _MIN_SS 512
6 #define _MAX_SS 4096

1) _USE_MKFS:格式化功能选择,为使用FatFs 格式化功能,需要把它设置为1。
2) _CODE_PAGE:语言功能选择,并要求把相关语言文件添加到工程宏。为支持简
体中文文件名需要使用“936”,正如在图 26-7 的操作,我们已经把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。

三、FatFs 功能测试

移植操作到此,就已经把FatFs 全部添加到我们的工程了,这时我们编译功能,顺利编
译通过,没有错误。接下来,我们就可以使用编写图 26-5 中用户应用程序了。
主要的测试包括格式化测试、文件写入测试和文件读取测试三个部分,主要程序都在
main.c 文件中实现。

 *******************************************************************************/#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "ff.h"
#include "string.h"
/*********************************************************************************                              定义变量*******************************************************************************/
FATFS fs;													/* FatFs文件系统对象 */
FIL fnew;													/* 文件对象 */
FRESULT res_flash;                /* 文件操作结果 */
UINT fnum;            					  /* 文件成功读写数量 */
char fpath[100];                  /* 保存当前扫描路径 */
char readbuffer[512];             /*********************************************************************************                                任务函数*******************************************************************************/
/* 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 字节/扇区 1个扇区为4KB) */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_OPEN_ALWAYS|FA_WRITE|FA_READ );if ( res_flash == FR_OK ){/*  文件定位 */res_flash = f_lseek(&fnew,f_size(&fnew));if (res_flash == FR_OK){/* 格式化写入,参数格式类似printf函数 */f_printf(&fnew,"\n在原来文件新添加一行内容\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");      } }else{printf("!! 打开文件失败:%d\n",res_flash);printf("!! 或许需要再次运行“FatFs移植与读写测试”工程\n");}return res_flash;
}FILINFO fno;
/*** 文件信息获取*/
static FRESULT file_check(void)
{/* 获取文件信息 */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;
}/*** @brief  scan_files 递归扫描FatFs内的文件* @param  path:初始扫描路径* @retval result:文件系统的返回值*/
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; 
}
/*** @brief  主函数* @param  无* @retval 无*/
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){}
}/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/597253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes-网络

一. 前言 flannel两种容器跨主机通信的方案&#xff0c;其中UDP模式是IP in UDP&#xff0c;即三层报文封装在UDP数据包中通信&#xff1b;而vxlan模式则是MAC in UDP&#xff0c;即二层报文封装在UDP数据包中通信 flannel UDP模式和vxlan模式都对数据包做了封解包&#xff0c…

手把手教你在Ubuntu22上安装VideoRetalking

VideoReTalking是一种新系统&#xff0c;可以根据输入音频编辑真实世界的谈话头部视频的面孔&#xff0c;即使具有不同的情感&#xff0c;也能生成高质量和口型同步的输出视频。我们的系统将这个目标分解为三个连续的任务&#xff1a; &#xff08;1&#xff09;具有规范表情的…

SpringBoot实现登录拦截器

SpringBoot实现登录拦截器 对于管理系统或其他需要用户登录的系统&#xff0c;登录验证都是必不可少的环节&#xff0c;在SpringBoot开发的项目中&#xff0c;通过实现拦截器来实现用户登录拦截并验证。 1、SpringBoot实现登录拦截的原理 SpringBoot通过实现HandlerIntercep…

普中STM32-PZ6806L开发板(HAL库函数实现-访问多个温度传感器DS18B20)

简介 我们知道多个DS18B20的DQ线是可以被挂在一起的, 也就是一根线上可以访问不同的DS18B20而不会造成数据错乱, 怎么做到的&#xff0c;其实数据手册都有说到&#xff0c; 就是靠64-bit ROM code 进行识别, 也可以理解成Serial Number进行识别, 因为主要差异还是在Serial Numb…

本地git服务器的使用

最后总结一句&#xff0c;用gitlab最省事&#xff0c;管理权限最方便&#xff0c;别像下文一样整了。 Windows上使用&#xff1a; 首先要在windows开发机上生成密钥&#xff1a; 1.安装git&#xff0c;首先去git官网下载git&#xff0c;https://git-scm.com/downloads&#xff…

初学者SkyWalking详细使用文档

SkyWalking使用文档 下载地址&#xff1a;https://skywalking.apache.org/downloads/ 主要下载&#xff1a;skywalking apm&#xff08;tar&#xff09; 、agents(tar) 解压&#xff1a; &#xff08;可选操作&#xff09;&#xff1a; ​ apache-skywalking-apm-bin --&g…

chromium在中文用户名下无法编译的问题

新电脑没有太注意&#xff0c;起用户名的时候用了中文。 在编译chromium104的代码时&#xff0c;因为环境变量有中文导致编译失败&#xff1a; 因为我的电脑默认是使用gbk编码&#xff0c;而不是utf-8编码。 这个问题有三种解决办法&#xff1a; &#xff08;一&#xff09;把…

MySQL——事物

目录 一.发现问题 二.什么时事物 三.事务提交方式 四.事物的常规操作方式 五. 事务隔离级别 1.如何理解隔离性 2.隔离级别 3.查看与设置隔离性 4.读未提交【Read Uncommitted】 5.读提交【Read Committed】 6.可重复读【Repeatable Read】 7.串行化【serializabl…

云卷云舒:大型电信运营商应用软件健康度评估方法

大型电信运营商内均会自建云资源池&#xff0c;并基于云资源池构建自己上层应用软件资源&#xff0c;但是各类上层应用软件的故障频发也给运维工作带来了较大的压力&#xff0c;电信运营商急需一种较完善的方法实现对于应用软件的健康度评测&#xff0c;以进一步指导运维完成应…

unity C# 中一看就会的try-catch-finally、throw

文章目录 1、C# 异常处理原理&#xff1a;2、C# 异常处理实用案例&#xff08;简化版示例&#xff09;&#xff1a;3、throw关键字 C# 异常处理是一种用于捕获和处理程序运行时错误的机制&#xff0c;它允许程序在遇到不可预见或非正常条件时进行优雅地恢复或失败。C# 中的异常…

Rust 圣经 阅读 字符、布尔、单元类型

字符类型&#xff08;char&#xff09; Rust 的字符不仅仅是 ASCII &#xff0c;还包含所有的 Unicode 值&#xff0c;包括单个的中文、日文、表情符号等等。 Unicode 值的范围从 U0000 ~ UD7FF 和 UE000 ~ U10FFFF。 因为每个 Unicode 都是 4 个字节编码&#xff0c;所以字符…

知虾皮Shopee:东南亚最受欢迎的电子商务平台

在如今数字化时代&#xff0c;电子商务平台成为人们购物的首选方式。Shopee作为东南亚地区最受欢迎的电子商务平台&#xff0c;通过其多样化的商品、便捷的购物体验和创新的商业模式&#xff0c;迅速在该地区占据了重要地位。本文将详细介绍Shopee的特点和优势&#xff0c;以及…

设计模式 七大原则

1.单一职责原则 单一职责原则&#xff08;SRP&#xff1a;Single responsibility principle&#xff09;又称单一功能原则 核心&#xff1a;解耦和增强内聚性&#xff08;高内聚&#xff0c;低耦合&#xff09;。 描述&#xff1a; 类被修改的几率很大&#xff0c;因此应该专注…

CNN——VGG

1.VGG简介 论文下载地址&#xff1a;https://arxiv.org/pdf/1409.1556.pdf VGGNet 是由牛津大学视觉几何小组&#xff08;Visual Geometry Group, VGG&#xff09;提出的一种深层卷积网络结构&#xff0c;他们以 7.32% 的错误率赢得了 2014 年 ILSVRC 分类任务的亚军&#xff…

2024年MySQL学习指南(二),探索MySQL数据库,掌握未来数据管理趋势

文章目录 前言4. DDL- 操作数据库4.1 查询4.2 创建数据库4.3 删除数据库4.4 使用数据库 5. DDL- 操作数据表5.1 数据类型5.2 查询表5.3 创建表5.4 删除表5.5 修改表 6. 实战案例详解 前言 接上一篇文章【2024年MySQL学习指南&#xff08;一&#xff09;】 4. DDL- 操作数据库 …

2023-2024 年广东省职业院校技能大赛高职组 “软件测试”赛项竞赛规程

2023-2024 年广东省职业院校技能大赛&#xff08;高职组&#xff09; “软件测试”赛项竞赛规程 一、赛项信息 赛项名称&#xff1a;软件测试 赛项编号&#xff1a;GZ034 赛项组别&#xff1a;高职组 二、竞赛目标 软件是新一代信息技术的灵魂&#xff0c;是数字经济发展的基础…

LeetCode 每日一题 Day 32 ||递归单调栈

2487. 从链表中移除节点 给你一个链表的头节点 head 。 移除每个右侧有一个更大数值的节点。 返回修改后链表的头节点 head 。 示例 1&#xff1a; 输入&#xff1a;head [5,2,13,3,8] 输出&#xff1a;[13,8] 解释&#xff1a;需要移除的节点是 5 &#xff0c;2 和 3 。…

大数据 - Doris系列《二》- Doris安装(亲测成功版)

目录 &#x1f436;2.1 安装前准备 &#x1f959;1.设置系统最大文件打开句柄数 >启动一个程序的时候&#xff0c;打开文件的数量就是句柄数 &#x1f959;3.时钟同步 &#x1f959;4.关闭交换分区&#xff08;swap&#xff09; &#x1f436;2.2 安装FE &#x1f436…

论文悦读(7)——NVM文件系统之Trio(SOSP‘23)文件系统

TRIO&#xff08;SOSP23&#xff09; 1. 背景&#xff08;Background&#xff09;1.1 NVM Technologis1.2 File System Customization1.3 Userspace NVM File Systems 2. 观察与动机&#xff08;Observation & Motivation&#xff09;3. 设计与实现&#xff08;Design &…

JMeter 插件大全:详细介绍 Jmeter 常用插件

JMeter作为一个开源的接口性能测试工具&#xff0c;其本身的小巧和灵活性给了测试人员很大的帮助&#xff0c;但其本身作为一个开源工具&#xff0c;相比于一些商业工具&#xff08;比如 LoadRunner&#xff09;&#xff0c;在功能的全面性上就稍显不足。这篇博客&#xff0c;就…