STM32外部Flash移植FATFS笔记
FatFs是面向小型嵌入式系统的一种通用的FAT文件系统。它完全是由AISI C语言编写并且完全独立于底层的I/O介质。因此它可以很容易地不加修改地移植到其他的处理器当中,如8051、 PIC、 AVR、 SH、 Z80、H8、 ARM等。 FatFs支持FAT12、 FAT16、 FAT32等格式。
目录
下载FatFs源码
认识FATFS源码结构
FATFS文件系统移植步骤
1. 定义自己的设备类型编号
2. 修改DSTATUS disk_initialize 方法,初始化W25Q128设备
3. 完成DSTATUS disk_status 函数
4.对芯片的读取操作,完善DRESULT disk_read操作
5. 完成对芯片的写操作,完善DRESULT disk_write
6. 完成一些杂项操作DRESULT disk_ioctl
7. 完善系统要求的get_fattime方法
ffconf.h 的配置
应用层调用
验证结果
参考代码
下载FatFs源码
FatFs - Generic FAT Filesystem Module
认识FATFS源码结构
文件 |
说明 |
备注 |
ffsystem.c |
FatF用户提供的操作系统相关函数的示例代码 |
|
ffunicode.c |
文件系统支持的语言编码 |
不需要修改 |
ffconf.h |
文件系统配置项 |
根据需求修改 |
ff.c |
FatFs核心文件,文件管理的实现 |
不需要修改 |
diskio.c |
包含底层存储介质的操作函数, |
集成驱动文件 |
函数 |
条件(ffconf.h) |
备注 |
disk_status |
总是需要 |
底层设备驱动函数 |
disk_write |
FF_FS_READONLY == 0 |
|
disk_ioctl (GET_SECTOR_COUNT) |
FF_USE_MKFS == 1 |
|
disk_ioctl (GET_SECTOR_SIZE) |
FF_MAX_SS != _MIN_SS |
|
disk_ioctl (CTRL_TRIM) |
FF_USE_TRIM == 1 |
|
ff_convert |
FF_USE_LFN != 0 |
FF_CODE_PAGE 936 添加中文文件名支持 |
ff_cre_syncobj |
FF_FS_REENTRANT == 1 |
FatFs 统支持可重入配置,需要多任务系 (一般不需要) |
ff_mem_alloc |
FF_USE_LFN == 3 |
长文件名支持,缓冲区设置在堆 空间(一般设置_USE_LFN = 2) |
图中的六个函数位于diskio.c文件中,再加上我们需要适当的修改宏定义,位于ffconf.h中。所以实际上我们在进行文件系统移植的时候,只需要修改ffconf.h和diskio.c两个文件。
FATFS文件系统移植步骤
1. 定义自己的设备类型编号
2. 修改DSTATUS disk_initialize 方法,初始化W25Q128设备
DSTATUS disk_initialize (BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{DSTATUS stat;u16 i;switch (pdrv) {case DEV_FLASH :W25Q128_Init();// 延时一小段时间i=500;while(--i); stat = disk_status(pdrv);printf("init stat = %d\r\n",stat);return stat;// case DEV_MMC :
// result = MMC_disk_initialize();// // translate the reslut code here// return stat;// case DEV_USB :
// result = USB_disk_initialize();// // translate the reslut code here// return stat;}return STA_NOINIT;
}
这里调用W25Q128的初始化函数,之后调用 disk_status 检测设备是否初始化成功
3. 完成DSTATUS disk_status 函数
上图是官方对disk_status()函数的解释。该函数只有一个形参,就是设备号用于标识不同的设备,在只有一个设备的情况下,设备号默认为0。
该函数的返回值有:
STA_NOINIT:标识该设备未初始化成功,未进入就绪状态。
STA_NODISK:FatFs不引用此标志。
STA_PROTECT:表示该设备已进行了写保护。如果设置了STA_NODISK,则无效。
0:表示该设备初始化成功。
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/DSTATUS disk_status (BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{DSTATUS stat;u16 FLASH_ID;switch (pdrv) {case DEV_FLASH:FLASH_ID = Read_Manufacturer();if(FLASH_ID == NM25Q128) {stat = 0;} else{stat = STA_NOINIT;}return stat;// case DEV_MMC :
// result = MMC_disk_status();// // translate the reslut code here// return stat;// case DEV_USB :
// result = USB_disk_status();// translate the reslut code here// return stat;}return STA_NOINIT;
}
检测W25Q128是否能够读取芯片的厂商ID,这个ID是固定的,如果能够读取出来就返回0,代表成功,否则返回初始化失败。这样当系统层在调用的时候会给出对应的错误提示。
4.对芯片的读取操作,完善DRESULT disk_read操作
形参:
第一个形参:要操作的设备号,在本次移植工程中,可选项是 DEV_FLASH(0)
第二个形参:读取到的数据的缓冲区。
第三个形参:要读取的扇区的个数。
返回值:
RES_OK (0): 读数据正常
RES_ERROR: 读取操作期间发生了不可恢复的硬错误。
RES_PARERR: 输入了无效参数
RES_NOTRDY: 设备还没初始化
函数值调用:
f_read()---->disk_read()
DRESULT disk_read (BYTE pdrv, /* Physical drive nmuber to identify the drive */BYTE *buff, /* Data buffer to store read data */LBA_t sector, /* Start sector in LBA */UINT count /* Number of sectors to read */
)
{DRESULT res;switch (pdrv) {case DEV_FLASH :/* 扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面14MB空间 */sector+=512; W25Q128_Read((u8 *)buff, sector * FLASH_SECTOR_SIZE, count * FLASH_SECTOR_SIZE);res = RES_OK;return res;// case DEV_MMC :
// // translate the arguments here// result = MMC_disk_read(buff, sector, count);// // translate the reslut code here// return res;// case DEV_USB :
// // translate the arguments here// result = USB_disk_read(buff, sector, count);// // translate the reslut code here// return res;}return RES_PARERR;
}
这里前面sector+=512 是偏移2M字节,是根据秉火的教程,Flash前2M空间会放一些其他的内容,用户可以直接进行扇区的数据操作,不使用文件系统存放的一些数据。
w25q128 一个扇区4096byte,16个扇区一个Block,256个block是总共的存储空间。
所以2M( 2048 * 1024 / 4096 = 512 sectors) 每次系统在读的时候,进行这么多偏移,把前2M 的空间就不操作了。
5. 完成对芯片的写操作,完善DRESULT disk_write
形参:
第一个形参:要操作的设备号,在本次移植工程中,可选项是SD_CARD(0)或SPI_FLASH(1),只不过后续程序处理部分SD_CARD没进行处理。
第二个形参:写数据的缓冲区。 要写入的数据大小为扇区大小 × 计数字节。
第三个形参:写操作的起始扇区。比如要从第三个扇区写操作,那么该参数值就是3。
第四个形参:写操作的扇区个数。也就是写操作要进行操作几个扇区。
返回值:
RES_OK (0): 写数据正常
RES_ERROR: 写操作期间发生了不可恢复的硬错误。
RES_WRPRT: 设备进行了写保护。
RES_PARERR: 输入了无效参数
RES_NOTRDY: 设备还没初始化
函数值调用:
f_write()---->disk_write()
#if FF_FS_READONLY == 0DRESULT disk_write (BYTE pdrv, /* Physical drive nmuber to identify the drive */const BYTE *buff, /* Data to be written */LBA_t sector, /* Start sector in LBA */UINT count /* Number of sectors to write */
)
{DRESULT res = RES_PARERR;if (!count) {return RES_PARERR; /* Check parameter */}switch (pdrv) {case DEV_FLASH :/* 扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面14MB空间 */sector+=512;W25Q128_Sector_Erase(sector * FLASH_SECTOR_SIZE);W25Q128_Write((u8 *)buff,sector * FLASH_SECTOR_SIZE,count * FLASH_SECTOR_SIZE);res = RES_OK;return res;// case DEV_MMC :
// // translate the arguments here// result = MMC_disk_write(buff, sector, count);// // translate the reslut code here// return res;// case DEV_USB :
// // translate the arguments here// result = USB_disk_write(buff, sector, count);// // translate the reslut code here// return res;}return RES_PARERR;
}#endif
这里 sectors+=512 也是对文件系统上层的的操作进行偏移,保留前面512字节地址,和写操作对应。
另外,这里write 方法有个条件编译,我们需要设置对应的宏 FF_FS_READONLY == 0 才能使用write操作。
在进行写操作之前一定要先进行擦除操作。在W25Q128_Write((u8*)buff,sector*4096,count*4096);中含有了擦除操作,最开始没有增加擦除方法的时候,默认提示格式化系统成功,但是后面用电脑进行USB读取Flash时候,提示未格式化,也就是没有格式化成功。所以这里还是把擦除操作加上了。可以屏蔽擦除扇区一行测试。
这里默认我们给返回写成功。也可以对写操作进行判断,增加返回值。如果某次写错误,也可能会在系统层面提示成功。这里需要注意。根据实际情况来编码。
6. 完成一些杂项操作DRESULT disk_ioctl
DRESULT disk_ioctl 函数用于控制特定于设备的功能和除通用读/写之外的其他功能。
DRESULT disk_ioctl (BYTE pdrv, /* Physical drive nmuber (0..) */BYTE cmd, /* Control code */void *buff /* Buffer to send/receive control data */
)
{DRESULT res;if (pdrv == DEV_FLASH) {switch (cmd) {/* 扇区数量:3584*4096/1024/1024=14(MB) */case GET_SECTOR_COUNT:*(DWORD * )buff = 3584; // 这个值来源于前面512扇区给了文件系统表,后面的才是可以使用的空间 w25q128 4096 扇区 - 512 扇区 = 3584break;/* 扇区大小 */case GET_SECTOR_SIZE :*(WORD * )buff = 4096;break;/* 同时擦除扇区个数 */case GET_BLOCK_SIZE :*(DWORD * )buff = 1;break; }res = RES_OK;} else {res = RES_PARERR;}return res;
}
怎么理解这里的cmd参数呢,有以下命令
调用disk_ioctl函数以控制设备的特定功能和除通用读/写之外的其他功能。比如通过发送命令来获取该设备的扇区大小、内存大小等相关信息。
形参:
第一个形参:要操作的设备号,在本次移植工程中,可选项是SD_CARD(0)或SPI_FLASH(1),只不过后续程序处理部分SD_CARD没进行处理。
第二个形参:控制命令号。命令号,通过发送命令控制flash;比如查询每个扇区的字节数等等
第三个形参:数据缓冲区。既有可能输出也有可能输入。因为输入的命令可能会带有参数;发送命令后,需要接收返回来的数据信息。
返回值:
RES_OK (0): 此次 操作正常
RES_ERROR: 有错误产生
RES_PARERR: 输入的命令或参数是无效的
RES_NOTRDY: 设备还没初始化
这里我们只关心
GET_SECTOR_COUNT:
告诉文件系统,我们的Flash 有多少个可以操作的扇区,这个值来源于前面512扇区给了文件系统,后面的才是可以使用的空间 w25q128 4096 扇区 - 512 扇区 = 3584 个扇区
GET_SECTOR_SIZE:
获取每一个扇区的大小,一个扇区就是4096 字节。
GET_BLOCK_SIZE:
以闪存介质扇区为单位的擦除块大小
该函数的使用必须首先开启宏,使得“FF_USE_MKFS == 1”
7. 完善系统要求的get_fattime方法
DWORD get_fattime (void)
{return (DWORD)(2022 - 80) << 25 |(DWORD)(10 + 1) << 21 |(DWORD)9 << 16 |(DWORD)20 << 11 |(DWORD)20 << 5 |(DWORD)0 >> 1;
}
这个方法是我们在对文件操作的时候,会产生一个时间戳,这里我写死了,可以使用RTC完善这里的具体时间参数。
OK diskio修改完成。
ffconf.h 的配置
#define FF_FS_READONLY 0
要有写功能必须设置为0
#define FF_USE_MKFS 1
如果存储芯片不存在文件系统,我们需要对其进行格式化,这里配置为1 使能格式化操作。对应的是f_mkfs()函数
#define FF_CODE_PAGE 936
添加中文支持
#define FF_USE_LFN 2
长文件名存储空间设置,缓冲区设置在堆 空间(一般设置_USE_LFN = 2)
#define FF_VOLUMES 1
我的代码中只移植了Flash的文件系统,所以只有一个设备DEV_FLASH,所以这里定义为1 ,如果还有其他的设备定义,这可以修改。
#define FF_MAX_SS 4096
这里是可以操作的扇区的大小,因为Flash的一个扇区是4096个字节,所以一次可以操作最大设置为4096.这里和 disk_ioctl() 方法里的 GET_SECTOR_SIZE 对应。
最基本的配置就是这些了。
应用层调用
首先挂载文件系统,如果没有文件系统则对底层设备进行格式化,就会创建一个文件系统 ,挂在的“0:”代表DEV_FLASH 定义的数值,: 是必须要写的。
printf("****** 这是一个SPI FLASH 文件系统实验 ******\r\n");//在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化//初始化函数调用流程如下//f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()res_flash = f_mount(&fs,"0:",1);printf("res_flash = %d\r\n",res_flash);if(res_flash == FR_NO_FILESYSTEM){printf("FLASH还没有文件系统,即将进行格式化...\r\n");/* 格式化 */res_flash=f_mkfs("0:",0,work,sizeof(work));if(res_flash == FR_OK){printf("》FLASH已成功格式化文件系统。\r\n");/* 格式化后,先取消挂载 */res_flash = f_mount(NULL,"0:",1); /* 重新挂载 */ res_flash = f_mount(&fs,"0:",1);}else{LED_RED = 0;printf("《《格式化失败。》》\r\n");while(1);}} else if(res_flash!=FR_OK){printf("!!外部Flash挂载文件系统失败。(%d)\r\n",res_flash);printf("!!可能原因:SPI Flash初始化不成功。\r\n");while(1);}else{printf("》文件系统挂载成功,可以进行读写测试\r\n");}
写操作,这里使用了长文件名,并且文件名是中文。并且开启宏 FF_FS_READONLY == 0
/*------------------- 文件系统测试:写测试 --------------------------*/
printf("\r\n****** 即将进行文件写入测试... ******\r\n");
res_flash = f_open(&fnew, "0:新建文本文档abc.txt",FA_CREATE_ALWAYS | FA_WRITE);
if(res_flash == FR_OK )
{printf("》打开/创建新建文本文档abc.txt文件成功,向文件写入数据。\r\n");/* 将指定存储区内容写入到文件内 */res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);if(res_flash == FR_OK){printf("文件写入成功,写入字节数据:%d\r\n",fnum);delay_ms(1);printf("向文件写入的数据为:\r\n%s\r\n",WriteBuffer); delay_ms(1); }else{printf("!!文件写入失败:(%d)\n",res_flash);} /* 不再读写,关闭文件 */f_close(&fnew);
}
else
{ LED_RED = 0;printf("!!打开/创建文件失败。\r\n");
}
文件读测试。
/*------------------- 文件系统测试:读测试 --------------------------*/printf("****** 即将进行文件读取测试... ******\r\n");res_flash = f_open(&fnew, "0:新建文本文档abc.txt",FA_OPEN_EXISTING | FA_READ); if(res_flash == FR_OK){LED_GRREN = 0;printf("》打开文件成功。\r\n");res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum); if(res_flash==FR_OK){printf(" 文件读取成功,读到字节数据:%d\r\n",fnum);printf(" 读取得的文件数据为:\r\n %s \r\n", ReadBuffer); }else{printf("!!文件读取失败:(%d)\r\n",res_flash);} }else{LED_RED = 0;printf("!!打开文件失败(%d)。\r\n",res_flash);}/* 不再读写,关闭文件 */f_close(&fnew);
/* 不再使用文件系统,取消挂载文件系统 */
f_mount(NULL,"0:",1);
第一个参数NULL,代表取消挂载文件系统。
第二个参数“0:”代表取消挂载的设备
第三个参数 1 立即取消挂载
验证结果
备注,以上是在原子F103精英板子上进行测试
参考代码
参考代码地址
通过USB验证Flash文件
STM32外部Flash移植FATFS笔记相关推荐
- STM32CUBEMX(13)--SPI,W25Q128外部Flash移植
STM32CUBEMX--SPI,W25Q128外部Flash移植 概述 视频教学 完整代码下载 硬件准备 选择芯片型号 配置时钟源 配置时钟树 串口配置 SPI配置 接线方式 生成工程设置 生成代码 ...
- 【FatFs】基于STM32 SD卡移植FatFs文件系统
相关文章 <[SDIO]SDIO.SD卡.FatFs文件系统相关文章索引> 1.前言 FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统.它完全是由 ANSI C 语 ...
- stm32外部中断_STM32学习笔记 | 电源管理及低功耗设计要点
一款好的电子产品,都需要认真考虑电源管理的问题,电池供电的产品更应该注意低功耗的实现. STM32电源介绍 嵌入式开发直播课 - STM32 USART串口的应用 - 创客学院直播室www.make ...
- w806 w25q128 spi flash 移植fatfs
一:spi初始化 hspi.Instance = SPI;hspi.Init.Mode = SPI_MODE_MASTER;hspi.Init.CLKPolarity = SPI_POLARITY_L ...
- RT-Thread--外部 flash 挂载 fatfs 文件系统
外部 flash 挂载 fatfs 文件系统 /** Copyright (c) 2006-2018, RT-Thread Development Team** SPDX-License-Identi ...
- STM32移植FatFS文件系统最新版R0.14b
STM32移植FatFS文件系统 目录 一.前言 二.硬件及软件准备 三.移植FatFS文件到工程文件夹下 四.将移植文件添加到工程中 五.修改"diskio.c"文件 六.配置& ...
- 串行FLASH文件系统FatFs介绍并在STM32F1上移植
先对内存存储有一个理解,比如在FALSH中存储数据时,已知在STM32F1 开发板上都有自带有一个外部 FLASH(W25Q128.128Mbit=16MByte,即16M内存),FLAS ...
- 【单片机笔记】基于STM32F103C8的 USB 外部flash虚拟U盘
学习stm32已经很长时间了,但是一直没有过多的学习stm32的USB部分,因为实际工作还是用的比较少.说起USB那就有的说了,因为USB的功能很强大,这里主要重点记录一下STM32的USB部分,这个 ...
- H750移植rt_thread操作系统完整工程分享,包括外部FLASH分散加载文件
一.移植注意事项 1.在运行外部FLASH存储的代码之前首先要初始化QSPI进入内存映射模式,参考代码: //QSPI进入内存映射模式(执行QSPI代码必备前提,为了减少引入的文件, //除了GPIO ...
最新文章
- 【kuangbin专题】计算几何_半平面交
- Codeforces 167B Wizards and Huge Prize(概率dp)
- linux下为PHP扩展安装memcache模块
- 2-2 用Python爬取银河演员网上的演员参演电影的信息进行抓取
- 电工结业试卷_电工技术基础结业考试试卷
- 小程序功能模块-优客娱乐视频1.0.5源码
- Servlet规范之Filter工作原理
- 安装操作系统的过程图解
- 机器学习实战(九)K-means(K-均值)
- 秃友进销存标准版内存注册机 Cracked.By.HackWm.
- teraterm linux环境,linux ssh telnet TeraTerm终端中文显示乱码解决方法
- Mac上的PowerPoint保存时出现“连同字体保存”——“某些字体无法随演示文稿一起保存。仍然要保存演示文稿吗?”
- vmware mac安装教程 | 不能全屏的终极原因
- 互联网科普贴-阿里巴巴国际站是什么
- 软件测试个人求职简历该怎么写?一个优质软件测试工程师简历
- Windows光标选中字符切换到输入字符快捷键
- 微信小程序例子——获取用户登录信息
- Cocostudio学习笔记(2) Button + CheckBox
- 线阵相机的优势是什么?如何选择线阵相机?
- 【工具】iOS代码混淆工具-iOS源码混淆