【文件系统】嵌入式文件系统Fatfs简介
Fatfs
1.Fatfs简介
FatFs(File Allocation Table File System)是一个专为小型嵌入式系统设计的通用FAT文件系统模块。它完全由ANSI C语言编写,独立于硬件平台,因此具有很好的可移植性。FatFs支持FAT12、FAT16和FAT32文件系统,可以用于各种嵌入式平台,包括但不限于8051、PIC、AVR、SH、Z80、H8、ARM等系列单片机。
FatFs的主要特点包括:
- 多平台支持:由于使用ANSI C编写,FatFs可以在多种操作系统上运行,如Linux、Android、MacOS和Windows。
- 配置灵活性:通过
ffconf.h
配置文件,可以开启或关闭各种功能,以适应不同的应用需求。 - 支持多种文件系统:支持FAT12、FAT16和FAT32,以及exFAT格式。
- 支持多卷:可以支持多个存储媒介,最多可达10个卷。
- 支持长文件名:支持ANSI/OEM或Unicode编码的长文件名。
- 支持RTOS:适合在实时操作系统(RTOS)中使用。
- 多种扇区大小支持:支持多种扇区大小,适应不同的存储设备。
- 只读和最小化API:提供只读模式、最小化的API和I/O缓冲区配置,以满足不同应用的需求。
FatFs模块层次结构包括底层接口(包括存储媒介读写接口disk I/O
和实时时钟),中间层FATFS模块(实现FAT文件读写协议),以及最顶层的应用层(提供一系列应用接口函数,如f_open
、f_read
、f_write
和f_close
等)。
FatFs广泛应用于嵌入式系统和物联网设备中,如智能家居、工业控制、消费电子等领域,特别是在需要访问可移动存储介质(如SD卡、USB闪存驱动器等)的应用中。
2.FatFs特性
- 兼容 DOS/Windows 的 FAT/exFAT 文件系统。
- 独立于平台。易于移植。
- 程序代码和工作区占用空间极小。
- 支持多种配置选项:
- ANSI/OEM 或 Unicode 长文件名。
- exFAT 文件系统、64 位 LBA 和 GPT,适用于大型存储设备。
- RTOS 的线程安全
- 多卷 (物理驱动器和分区)
- 可变扇区大小
- 多个代码页,包括 DBCS。
- 只读、可选 API、I/O 缓冲区等。
3.FatFs接口
3.1 FatFs应用接口
如下图所示,FatFs 为应用程序提供各种文件系统功能。
- 文件访问
- f_open - 打开/创建一个文件
- f_close - 关闭一个打开的文件
- f_read - 从文件中读取数据
- f_write - 写入数据到文件中
- f_lseek - 移动读/写指针,扩展尺寸
- f_truncate - 截断文件大小
- f_sync - 刷新缓存数据
- f_forward - 将数据转发到数据流
- f_expand - 为文件分配一个连续块
- f_gets - 读取字符串
- f_puts - 写一个字符
- f_printf - 写入格式化字符串
- f_tell - 获取当前读/写指针
- f_eof - 文件结束测试
- f_size - 获取尺寸
- f_error - 测试错误
- 路径访问
- f_opendir - 打开一个路径
- f_closedir - 关闭一个路径
- f_readdir - 读取目录项
- f_findfirst - 打开一个目录,读取匹配的第一个项目
- f_findnext - 阅读匹配的下一个项目
- 文件和路径管理
- f_stat - 检查文件或子目录是否存在
- f_unlink - 删除文件或子目录
- f_rename - 重命名/移动文件或子目录
- f_chmod - 更改文件或子目录的属性
- f_utime - 更改文件或子目录的时间戳
- f_mkdir - 创建子目录
- f_chdir - 更改当前目录
- f_chdrive - 更改当前驱动器
- f_getcwd - 读取当前目录和驱动器
- 卷管理和系统配置
- f_mount - 注册/取消注册卷的工作区
- f_mkfs - 在逻辑驱动器上创建 FAT 卷
- f_fdisk - 在物理驱动器上创建分区
- f_getfree - 获取卷上的可用空间
- f_getlabel - 获取卷标
- f_setlabel - 设置卷标签
- f_getlabel - 获取卷标签
- f_setcp - 设置活动代码页
3.2存储媒介访问接口
由于 FatFs 模块是独立于平台和存储介质的文件系统层,因此它与存储卡、硬盘和任何类型的存储设备等物理设备完全分离。 存储设备控制模块不是 FatFs 模块的一部分,需要由实现者提供。 FatFs 通过下图所示的简单媒体访问接口控制存储设备。 一些平台的实现示例也可从下载中获取。
- 存储设备控制
- disk_status - 获取设备状态
- disk_initialize - 初始化设备
- disk_read - 读取数据
- disk_write - 写入数据
- disk-ioctl - 控制设备相关功能
- 实时时间时钟
- get_fattime - 获取当前时间
4.FatFs移植
4.1 FatFs源码下载
R0.15下载地址:
FatFs - Generic FAT Filesystem Module (elm-chan.org)
4.2 FatFs移植
将FatFs源码添加到工程中,然后修改diskio.c,用于打通文件系统的软件部分与存储硬件驱动:
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "EN25Q128.h"
#include "malloc.h"
#include "sdio_sdcard.h"
#include "system.h"
#include "usart.h"
/* Definitions of physical drive number for each drive */
#define DEV_FLASH 1 /* Example: Map MMC/Flash to physical drive 1 */
#define DEV_SD 0 /* Example: Map SD to physical drive 3 */
#define FLASH_SECTOR_SIZE 512
//对于EN25Q128
//前12M字节给fatfs用,12M字节后,用于存放字库,字库占用3.09M. 剩余部分给自己用
u16 FLASH_SECTOR_COUNT=2048*12; //EN25Q128,前12M字节给FATFS占用
#define FLASH_BLOCK_SIZE 8 //每个BLOCK有8个扇区
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
u8 res=0;
switch (pdrv) {
case DEV_FLASH :
while(EN25Q128 != EN25QXX_Init()) //初始化EN25Q128
{
printf("EN25Q128 Init Error!\r\n");
}
FLASH_SECTOR_COUNT=2048*12;//EN25Q1218,前12M字节分配给FATFS
break;
case DEV_SD :
while(res = SD_Init())//检测不到SD卡
{
printf("SD Card Error!\r\n");
}
break;
default:
res = 1;
break;
}
if(res)return STA_NOINIT;
else return 0; //初始化成功
}
/*-----------------------------------------------------------------------*/
/* 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 */
)
{
u8 res=0;
if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误
switch(pdrv)
{
case DEV_SD://SD卡
res=SD_ReadDisk(buff,sector,count);
while(res)//读出错
{
SD_Init(); //重新初始化SD卡
res=SD_ReadDisk(buff,sector,count);
//printf("sd rd error:%d\r\n",res);
}
break;
case DEV_FLASH://外部flash
for(;count>0;count--)
{
EN25QXX_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
sector++;
buff+=FLASH_SECTOR_SIZE;
}
res=0;
break;
default:
res=1;
}
//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
if(res==0x00)return RES_OK;
else return RES_ERROR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT 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 */
)
{
u8 res=0;
if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误
switch(pdrv)
{
case DEV_SD://SD卡
res=SD_WriteDisk((u8*)buff,sector,count);
while(res)//写出错
{
SD_Init(); //重新初始化SD卡
res=SD_WriteDisk((u8*)buff,sector,count);
//printf("sd wr error:%d\r\n",res);
}
break;
case DEV_FLASH://外部flash
for(;count>0;count--)
{
EN25QXX_Write((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
sector++;
buff+=FLASH_SECTOR_SIZE;
}
res=0;
break;
default:
res=1;
}
//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
if(res == 0x00)return RES_OK;
else return RES_ERROR;
}
#endif
/*-----------------------------------------------------------------------*/
/* 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;
if(pdrv==DEV_SD)//SD卡
{
switch(cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(DWORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = SDCardInfo.CardBlockSize;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = SDCardInfo.CardCapacity/512;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
}else if(pdrv==DEV_FLASH) //外部FLASH
{
switch(cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = FLASH_SECTOR_SIZE;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = FLASH_BLOCK_SIZE;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = FLASH_SECTOR_COUNT;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
}else res=RES_ERROR;//其他的不支持
return res;
}
DWORD get_fattime(void)
{
return 0;
}
// 动态分配内存
void *ff_memalloc(UINT size)
{
return (void*)mymalloc(SRAM_IN,size);
}
void ff_memfree(void *mf)
{
myfree(SRAM_IN,mf);
}
再增加一个fatfs_app.c文件用于为文件系统对象申请内存,创建对象
#ifndef _fatfs_app_H
#define _fatfs_app_H
#include "system.h"
#include "ff.h"
typedef struct
{
uint8_t type[6]; //后缀6个字节
uint8_t name[100]; //路径和文件名字100个字节(支持25个汉字大小名字)
} FileNameTypeDef;
extern FATFS *fs[FF_VOLUMES];
extern FIL *file;
extern FIL *ftemp;
extern UINT br,bw;
extern FILINFO fileinfo;
extern DIR dir;
extern u8 *fatbuf;//SD卡数据缓存区
//f_typetell返回的类型定义
//根据表FILE_TYPE_TBL获得.在exfuns.c里面定义
#define T_BIN 0X00 //bin文件
#define T_LRC 0X10 //lrc文件
#define T_NES 0X20 //nes文件
#define T_TEXT 0X30 //.txt文件
#define T_C 0X31 //.c文件
#define T_H 0X32 //.h文件
#define T_WAV 0X40 //WAV文件
#define T_MP3 0X41 //MP3文件
#define T_APE 0X42 //APE文件
#define T_FLAC 0X43 //FLAC文件
#define T_BMP 0X50 //bmp文件
#define T_JPG 0X51 //jpg文件
#define T_JPEG 0X52 //jpeg文件
#define T_GIF 0X53 //gif文件
#define T_AVI 0X60 //avi文件
#define TYPE_BIN (0X00)
#define TYPE_LRC (0X10)
#define TYPE_GAME (0X20)
#define TYPE_TEXT (0X30)
#define TYPE_MUSIC (0X40)
#define TYPE_PICTURE (0X50)
u8 FATFS_Init(void); //申请内存
u8 FATFS_GetFree(u8 *drv,u32 *total,u32 *free); //得到磁盘总容量和剩余容量
#endif
#include "fatfs_app.h"
#include "malloc.h"
#include "string.h"
#define FILE_MAX_TYPE_NUM 7 // 最多FILE_MAX_TYPE_NUM个大类
#define FILE_MAX_SUBT_NUM 4 // 最多FILE_MAX_SUBT_NUM个小类
// 文件类型列表
u8* const FILE_TYPE_TBL[FILE_MAX_TYPE_NUM][FILE_MAX_SUBT_NUM] =
{
{"BIN"},
{"LRC"},
{"NES"}, //NES文件
{"TXT","C","H"}, //文本文件
{"WAV","MP3","APE","FLAC"},//支持的音乐文件
{"BMP","JPG","JPEG","GIF"},//图片文件
{"AVI"}, //视频文件
};
///////////////////////////////公共文件区,使用malloc的时候////////////////////////////////////////////
// 公共文件区,使用malloc的时候
FATFS *fs[FF_VOLUMES]; // 逻辑磁盘工作区.
FIL *file; // 文件1
FIL *ftemp; // 文件2.
UINT br,bw; // 读写变量
FILINFO fileinfo; // 文件信息
DIR dir; // 目录
u8 *fatbuf; // SD卡数据缓存区
// SD卡数据缓存区
///////////////////////////////////////////////////////////////////////////////////////
//为exfuns申请内存
//返回值:0,成功
//1,失败
u8 FATFS_Init(void)
{
u8 i;
for(i = 0;i < FF_VOLUMES;i++)
{
fs[i] = (FATFS*)mymalloc(SRAM_IN,sizeof(FATFS)); // 为磁盘i工作区申请内存
if(!fs[i])
break;
}
file = (FIL*)mymalloc(SRAM_IN,sizeof(FIL)); // 为file申请内存
ftemp = (FIL*)mymalloc(SRAM_IN,sizeof(FIL)); // 为ftemp申请内存
fatbuf = (u8*)mymalloc(SRAM_IN,512); // 为fatbuf申请内存
if(i == FF_VOLUMES&&file&&ftemp&&fatbuf) return 0; // 申请有一个失败,即失败.
else return 1;
}
//得到磁盘剩余容量
//drv:磁盘编号("0:"/"1:")
//total:总容量 (单位KB)
//free:剩余容量 (单位KB)
//返回值:0,正常.其他,错误代码
u8 FATFS_GetFree(u8 *drv,u32 *total,u32 *free)
{
FATFS *fs1;
u8 res;
u32 fre_clust=0, fre_sect=0, tot_sect=0;
//得到磁盘信息及空闲簇数量
res =(u32)f_getfree((const TCHAR*)drv, (DWORD*)&fre_clust, &fs1);
if(res==0)
{
tot_sect=(fs1->n_fatent-2)*fs1->csize; //得到总扇区数
fre_sect=fre_clust*fs1->csize; //得到空闲扇区数
#if FF_MAX_SS!=512 //扇区大小不是512字节,则转换为512字节
tot_sect*=fs1->ssize/512;
fre_sect*=fs1->ssize/512;
#endif
*total=tot_sect>>1; //单位为KB
*free=fre_sect>>1; //单位为KB
}
return res;
}
编写测试程序:
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "tftlcd.h"
#include "malloc.h"
#include "sdio_sdcard.h"
#include "EN25Q128.h"
#include "ff.h"
#include "fatfs_app.h"
int main()
{
u8 i=0;
u32 free_sd,total_sd,free_mmc,total_mmc;
u8 res=0;
SysTick_Init(168);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(9600);
TFTLCD_Init(); //LCD初始化
my_mem_init(SRAM_IN); //初始化内部内存池
FATFS_Init(); //为fatfs相关变量申请内存
FRONT_COLOR=RED;//设置字体为红色
LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,16,"PRECHIN STM32F4");
LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"Fatfs Test");
LCD_ShowString(10,50,tftlcd_data.width,tftlcd_data.height,16,"www.prechin.net");
res = f_mount(fs[0],"0:",1); //挂载SD卡
if(res!=FR_OK)
{
//LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"Flash Disk Format Error "); //格式化失败
printf(" fs[0] res = %d\n",res);
delay_ms(1000);
return res;
}
res = f_mount(fs[1],"1:",1); //挂载FLASH.
if(res!=FR_OK)
{
//LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"Flash Disk Format Error "); //格式化失败
printf("fs[1] res = %d\n",res);
delay_ms(1000);
return res;
}
FRONT_COLOR=BLUE; //设置字体为蓝色
//检测SD卡成功
printf("SD Card & EN25Q128 OK!\r\n");
LCD_ShowString(10,100,tftlcd_data.width,tftlcd_data.height,16,"SD Card & EN25Q128 OK ");
LCD_Fill(10,80,tftlcd_data.width,80+16,WHITE); //清除显示
while(FATFS_GetFree("0:",&total_sd,&free_sd)) //得到SD卡的总容量和剩余容量
{
LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"SD Card Fatfs Error!");
delay_ms(200);
led2=!led2;
}
while(FATFS_GetFree("1:",&total_mmc,&free_mmc)) //得到SD卡的总容量和剩余容量
{
LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"flash Fatfs Error!");
delay_ms(200);
led2=!led2;
}
LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"FATFS OK!");
LCD_ShowString(10,100,tftlcd_data.width,tftlcd_data.height,16,"SD Total Size: MB");
LCD_ShowString(10,120,tftlcd_data.width,tftlcd_data.height,16,"SD Free Size: MB");
LCD_ShowString(10,140,tftlcd_data.width,tftlcd_data.height,16,"FLASH Total Size: MB");
LCD_ShowString(10,160,tftlcd_data.width,tftlcd_data.height,16,"FLASH Free Size: MB");
LCD_ShowNum(10+8*14,100,total_sd>>10,5,16); //显示SD卡总容量 MB
LCD_ShowNum(10+8*14,120,free_sd>>10,5,16); //显示SD卡剩余容量 MB
LCD_ShowNum(10+8*14,140,total_mmc>>10,5,16); //显示MMC卡总容量 MB
LCD_ShowNum(10+8*14,160,free_mmc>>10,5,16); //显示MMC卡剩余容量 MB
while(1)
{
i++;
if(i%10==0)
{
led1=!led1;
}
delay_ms(10);
}
}
参考资料:
Fatfs中的doc目录