提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
文章目录
前言
一、Fatfs是什么?
二、W25Q64是什么
三、使用步骤
1.引入库
2.移植文件系统
文件简单读写
总结
本文章为个人学习记录,主要内容为在stm32使用FatFs文件系统访问W25Q64
提示:以下是本篇文章正文内容,下面案例可供参考
W25Q64是一直Flash存储设备,使用SPI通讯,具有32768可编程页,每页256字节,用“页编程”每次可编程256个字节,每次写入内容都需要进行擦除工作,可以使用“扇区擦除”、“块擦除”、“整片擦除”擦除,擦除其实是给芯片里的储存单元都置1,因为写入数据时芯片只能把储存单元上的1置为0,不能把0置1........(略)
8MB=128块(64KB) 1块=16扇区(4KB) 1扇区=16页(256B)
先把下载的Fatfs的文件导入工程中:
我们重点需要关注的几个文件:
diskio.c:主要用于与底层设备通讯(底层驱动函数(Flash的通信函数)需要自己实现)
此文件有几个宏
可根据你挂载再stm32修改
这里#define SPI_FLASH 就是W25Q64的宏定义
//挂载设备
#define DEV_SD 0
#define SPI_FLASH 1
这里需要根据挂载的设备数量,修改ffconf.h里的FF_VOLUMES宏 ,这里表示设备有2台
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations存储设备数
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 2
/* Number of volumes (logical drives) to be used. (1-10) */
设备初始化函数(输入参数为挂在设备宏)
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive *//*输入设备号*/
)
{
DSTATUS stat;
switch (pdrv)
{
case DEV_SD :
return stat;
case SPI_FLASH :
SPI_Flash_Init(); //初始化
/*唤醒略*/
return disk_status(SPI_FLASH); /*获取设备状态,初始化成功则返回RES_OK*/
default:
break;
}
return STA_NOINIT;
}
只需要switch里的选项修改成你的挂载设备,然后在选项里添加响应的函数或代码即可
这里添加为 Flash的GPIO 及SPI初始化函数
/********************************************************
函数名 :SPI_Flash_Init
函数功能 :SPI的初始化配置
函数参数 :
函数返回 :无
*********************************************************/
void SPI_Flash_Init(void)
获取设备状态
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv)
{
case DEV_SD :
return stat;
case SPI_FLASH :
if(Flash_Read_ID()==Flash_ID) //如果获取的设备ID是对的
{
stat=RES_OK; //返回一个RES_OK表示成功
return stat;
}
break;
default:
break;
}
return STA_NOINIT;
}
引用函数:
/********************************************************
函数名 :Flash_Read_ID
函数功能 :返回Flash的设备ID
函数参数 :
函数返回 :读取从机的数据
*********************************************************/
uint32_t Flash_Read_ID(void)
读取数据
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
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_SD :
return res;
case SPI_FLASH :
Flash_Read_Data(buff,sector*4096,count*4096); /*1个扇区4K(4096)byte
每个扇区地址相隔4096byte所以需要*4096 */
return RES_OK;
}
return RES_PARERR;
}
调用函数
/********************************************************
函数名 :Flash_Read_Data
函数功能 :读取Flash内部数据
函数参数 :参数
@Read_Buff:用于接收数据的数组
@addr:读取哪个位置(地址)的数据例: 设备第0个字节地址为0,第1页地址为1*256,第二个扇区地址为2*4096
@Byte_length:读取数据的长度 单位字节读1个扇区需要1*4096
函数返回 :无
*********************************************************/
void Flash_Read_Data(uint8_t*Read_Buff,uint32_t addr,uint32_t Byte_length)
写数据函数(需要在ffconf.h把 FF_FS_READONLY设置为0才能使用)
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0 //需要在ffconf.h把 FF_FS_READONLY设置为0才能使用
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written *//*写入Flash的数据*/
LBA_t sector, /* Start sector in LBA *//*起始扇区*/
UINT count /* Number of sectors to write *//*写扇区的数量*/
)
{
DRESULT res;
int result;
switch (pdrv) {
case DEV_SD :
return res;
case SPI_FLASH :
Flash_Scetor_Erese(sector*4096);//写之前需要擦除扇区4k
Flash_Write_Buffs((uint8_t*)buff,sector*4096,count*4096); //向Flash写入数据
return RES_OK;
}
return RES_PARERR;
}
#endif
调用函数
/********************************************************
函数名 :Flash_Scetor_Erese
函数功能 :扇区擦除(4kbyte)
函数参数 :addr:擦除的扇区地址
函数返回 :无
*********************************************************/
void Flash_Scetor_Erese(uint32_t addr)
/********************************************************
函数名 :Flash_Write_Buffs
函数功能 :自动转页写入
函数参数 :参数
@Write_Buff:用于发送数据的数组
@addr:写哪个位置(地址)的数据
@Byte_length:写数据的长度
函数返回 :无
*********************************************************/
void Flash_Write_Buffs(uint8_t*Write_Buff,uint32_t addr,uint32_t Byte_length)
调用disk_ioctl函数以控制设备特定的功能和通用读/写以外的杂项功能
(这玩意好像是告诉我们上层程序一些信息的)
他在diskio.h里定义了一些命令宏
下面为一些标准命令(机翻)
程序
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code *//*命令*/
void *buff /* Buffer to send/receive control data *//**/
)
{
DRESULT res;
int result;
switch (pdrv)
{
case DEV_SD :
return res;
case SPI_FLASH :
switch (cmd)
{
case GET_SECTOR_COUNT://设备的大小(这里是有多少个扇区)
*(DWORD*)buff=2048; //返回类型根据你数据的大小决定好像,详细在ff.h
break;
case GET_SECTOR_SIZE://扇区大小 4096个字节
*(WORD*)buff=4096;
break;
case GET_BLOCK_SIZE://擦除块扇区大小 一次擦除一个扇区
*(WORD*)buff=1;
break;
default:
break;
}
// Process of the command for the MMC/SD card
}
return RES_OK;
}
相关宏设置
在ffconf.h里把把扇区最大范围FF_MAX_SS 修改成4096
#define FF_MIN_SS 512
#define FF_MAX_SS 4096
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
/* 这组选项配置要支持的扇区大小范围。 (512,
/ 1024、2048 或 4096) 对于大多数系统、通用存储卡和
/ 硬盘,但板载闪存和一些可能需要更大的值
/ 光学介质类型。 当 FF_MAX_SS 大于 FF_MIN_SS 时,配置 FatFs
/对于可变扇区大小模式和disk_ioctl()函数需要实现
/GET_SECTOR_SIZE 命令。 */
返回时间
DWORD get_fattime (void)
{
return 0;
}
不用直接返回0
使用看官网
媒体接口层好像讲完了。
应用程序接口层
介绍函数
f_mount挂载工作缓冲区
f_mount 函数有三个形参,第一个参数是 指向 FATFS 变量指针,如果赋值为 NULL 可以取消物理设备挂载。第二个参数为逻辑设备编号, 使用设备根路径表示,与物理设备编号挂钩,前面我们定义 SPI Flash 芯片 物理编号为 1 ,所以这里使用“1:”。第三个参数可选 0 或 1 , 1 表示立即挂载, 0 表示不立即挂载,延迟挂载。 f_mount 函数会返回一个 FRESULT 类型值,指示运行情况。
FATFS Flash_f; //需要较大空间所以定义为全局变量,存放在静态区
int main()
{
f_mount(&Flash_f,"1:",1);
}
运行会返回一个 FRESULT 类型值,挂载失败会返回非0的置,
其中返回FR_NO_FILESYSTEM,说明没有 FAT 文件系统,
这时需要我们格式化Flash,往芯片写入文件系统(会清除掉原本芯片上的数据)
初始化媒体设备
f_mkfs
f_mkfs有3个参数
path:储存设备号 (这里用的是Flash)字符串“x:” x是你宏定义的编号,“1:”
opt:格式选择 好像0是默认
work: 指向用于格式化过程的工作缓冲区的指针
FF_USE_LFN==3的话会在函数里使用malloc申请空间
len:工作缓冲区大小 *FF_USE_LFN尽量设置为1申请在静态区否则容易内存溢出
FATFS Flash_f; //需要较大空间所以定义为全局变量,存放在静态区
FIL WenJian;
BYTE work[FF_MAX_SS]; //工作缓冲区
int main()
{
FRESULT a; //保存函数返回状态
USART1_Config(); //使用串口通信
a=f_mount(&Flash_f,"1:",1); //挂载设备
printf("%d\n",a);
//如果Flash没有写入文件系统,就格式化
if(a==FR_NO_FILESYSTEM)
{
a=f_mkfs("1:",0,work,FF_MAX_SS); //初始化设备建立文件系统
printf("f_mkfs=%d\n",a);
/*初始化后需要重新挂载*/
f_mount(NULL,"1:",1); //取消挂载
f_mount(&Flash_f,"1:",1); //重新挂载
}
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include
#include "bsp_spi.h"
#include "ff.h"
FATFS Flash_f; //需要较大空间所以定义为全局变量,存放在静态区
FIL WenJian;
uint16_t Dt;
uint8_t Buffs[4096]={0};
const uint8_t WrtieBuffs[]={"欢迎使用野火stm32f1开发板"};
void Delay(uint32_t time)
{
for(;time!=0;time--);
}
UINT bW,bR;
BYTE work[FF_MAX_SS];
int main()
{
FRESULT a; //保存函数返回状态
USART1_Config(); //使用串口通信
a=f_mount(&Flash_f,"1:",1); //挂载设备
printf("%d\n",a);
//如果Flash没有写入文件系统,就格式化
if(a==FR_NO_FILESYSTEM)
{
a=f_mkfs("1:",0,work,FF_MAX_SS); //初始化设备建立文件系统
printf("f_mkfs=%d\n",a);
/*初始化后需要重新挂载*/
f_mount(NULL,"1:",1); //取消挂载
f_mount(&Flash_f,"1:",1); //重新挂载
}
a=f_open(&WenJian,"1:cbd.txt",FA_OPEN_ALWAYS|FA_WRITE|FA_READ); //打开/创建文件
printf("f_open=%d\n",a);
if(a==FR_OK)
{
a=f_write (&WenJian,WrtieBuffs,sizeof(WrtieBuffs),&bW); //写内容
printf("f_write=%d bW=%d\n",a,bW);
if(a==FR_OK)
{
f_lseek(&WenJian,0); //设置光标位置
a=f_read (&WenJian,Buffs,f_size(&WenJian),&bR); //读取文件内容
printf("f_read=%d bW=%d\n",a,bR);
if(a==FR_OK)
{
printf("\n内容:%s",Buffs); //读取成功就打印
}
}
f_close(&WenJian); //关闭文件
}
while(1)
{
}
}
不更了