二、串行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.hqwc.cn/news/321917.html

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

相关文章

企业数字化转型,应该先从哪开始?

近期有位朋友在后台私信我&#xff0c;说看了我之前写的数字化转型文章&#xff0c;觉得不错&#xff0c;便想咨询我&#xff1a;企业做数字化转型&#xff0c;到底应该先从哪儿开始&#xff1f; 首先&#xff0c;我认为这位朋友提出这个问题非常好&#xff0c;而且是一个非常…

解析《个人信息保护法》实施以来主要的变化

文章目录 前言一、二十一部配套的立法二、数据入表三、跨境规则转向四、未成年个人信息保护五、数据交易六、监管创新七、执法全覆盖八、地方聚焦场景执法九、个人信息保护诉讼十、个人信息保护公益诉讼十一、包容审慎十二、双清单上线十三、外部独立监督机构十四、个性化推荐便…

初学者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…

如何精选WordPress插件

WordPress的强大功能大多得益于其众多插件。正确选择插件可以让你的网站功能强大、运行平稳&#xff0c;而错误的选择则可能导致网站变慢甚至出现安全漏洞。这篇文章将指导你如何在众多可选的插件中作出明智的选择。 明确需求 在浏览WordPress的插件目录或其他市场之前&#…

类和对象之拜访对象村

类和对象 1.面向对象的初步认知 ————&#xff08;进入对象村&#xff09;1.1为什么称为对象村1.2面向对象和面向过程 2.类的定义和使用————&#xff08;这个陌生人是谁&#xff1f;&#xff09;2.1简单认识类2.2类的定义格式2.3定义一个狗类 3.类的实例化之new一个“对…

Databend 的算力可扩展性

作者&#xff1a;尚卓燃&#xff08;PsiACE&#xff09; 澳门科技大学在读硕士&#xff0c;Databend 研发工程师实习生 Apache OpenDAL(Incubating) Committer PsiACE (Chojan Shang) GitHub 对于大规模分布式数据处理系统&#xff0c;为了更好应对数据、流量、和复杂性的增长…

关于无人机上层控制的PID算法的思考

一、前言 背景介绍&#xff1a;PID虽然出现了很多年&#xff0c;但是目前工业界还是把PID作为主流的控制算法&#xff08;尽管学术界有很多非常时尚的控制算法&#xff0c;包括鲁邦控制&#xff0c;神经网络控制等等&#xff09;&#xff0c;PID的算法在于其不需要对系统进行复…

Python Selenium如何下载网页中的图片到本地?(Base64编码的图片下载)

前言&#xff1a; 在网页上&#xff0c;图片有时会以Base64编码的形式嵌入在HTML中&#xff0c;而不是作为单独的文件提供。这种方式的优点是可以减少HTTP请求的数量&#xff0c;因为图片数据直接包含在HTML中&#xff0c;不需要额外的请求来获取图片文件。这对于小图片…

双击shutdown.bat关闭Tomcat报错:未设置关闭端口~

你们好&#xff0c;我是金金金。 场景 当我startup.bat启动tomcat之后&#xff0c;然后双击shutdown.bat关闭&#xff0c;结果报错了~ 排查 看报错信息很明显了&#xff0c;未配置关闭端口&#xff0c;突然想起来了我在安装的时候都选的是默认的配置&#xff0c;我还记得有这…

鸿蒙南向开发—OpenHarmony技术编译构建框架

概述 OpenHarmony编译子系统是以GN和Ninja构建为基座&#xff0c;对构建和配置粒度进行部件化抽象、对内建模块进行功能增强、对业务模块进行功能扩展的系统&#xff0c;该系统提供以下基本功能&#xff1a; 以部件为最小粒度拼装产品和独立编译。支持轻量、小型、标准三种系…

pandas保存style到excel文件中

更多pandas style用法请参考&#xff1a;https://pandas.liuzaoqi.com/doc/chapter8/style.html 示例程序 import numpy as np import pandas as pd# 示例数据 dataframe pd.DataFrame({"date": pd.date_range("2024-01-01", "2024-02-01"),&…

软件测试/测试开发丨函数式编程学习笔记

一.高阶函数 高阶函数&#xff1a;既然变量可以指向函数&#xff0c;函数的参数能接收变量&#xff0c;那么一个函数就可以接收另一个函数作为参数&#xff0c;这种函数就称之为高阶函数。 1. map/reduce map() : 函数接收两个参数&#xff0c;一个是函数&#xff0c;一个是…