当前位置: 首页 > news >正文

FATFS文件系统

一、文件系统简介

文件系统:为了存储和管理数据而建立的一种组织结构

FAT(File Alloction Table,文件分配表)

          使用文件系统前,需先对存储设备进行格式化,擦除原来的数据,在存储设备上建立一个文件分配表和目录。

FAT32文件系统布局:

   系统引导扇区:引导程序,以及文件系统信息(扇区字节数/每簇扇区数/保留扇区数等)

文件分配表:记录文件存储中簇与簇之间连接的信息

根目录:存在所有文件和子目录信息(文件名/文件夹名/创建时间/文件大小)

数据区:文件等数据存放地方,占用大部分的磁盘空间

  FAT文件系统用“簇” 作为数据单元,一个“簇”由一组(2的整数次幂)连续的扇区组成,而一个扇区的大小为512字节。

  所有的簇从2开始进行编号,每个簇都有自己的地址编号,用户文件和数据都存储在簇中。

FATFS层次结构图:
① 底层接口(Low level device controls)

        存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟,需要我们根据平台和存储介质编写移植代码

FATFS模块(FatFs Module)

        实现了FAT文件读/写协议.FATFS模块提供的是ff.cff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去。

③ 应用层(Application)

使用者无需理会FATFS的内容结构和复杂的FAT协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_openf_readf_writef_close等,就可以像在PC机上读/写文件。

FATFS文件结构:

FATFS文件系统的移植需要修改2个文件,ffconf.hdiskio.c

文件名

功能名

说明

ffconf.h

FATFS模块配置文件

根据需求来配置

ff.h

FATFS和应用模块共用的包含文件

不需要修改

ff.c

FATFS模块源码(文件系统API)

不需要修改

diskio.h

FATFSdisk I/O模块共用的包含文件

不需要修改

diskio.c

FATFSdisk I/O模块接口层文件

与硬件相关代码

ffunicode.c

FATFS所支持的字体代码转换表

不需要修改

ffsystem.c

FATFSOS相关函数示例代码

没用到

ffcong.h:

FF_FS_READONLY:文件系统只读,默认为0,如果置1那么FatFs会关闭所有能修改文件的函数,大大减少FatFs的体积。

FF_FS_MINIMIZE:文件系统最小化,这个配置也可以减少FatFs的体积,它主要是通过关闭一些不常用的函数实现的;默认为0,即所有基础函数全开;置1的时候会关闭f_stat、f_getfree、f_unlink、f_mkdir、f_truncate和f_rename;置2的时候在上面的基础上再关闭f_opendir、f_readdir、f_closedir函数;置3的时候在上面的基础上再关闭f_lseek函数。

FF_USE_MKFS:文件系统格式化,默认为0,如果需要支持格式化可以置1开启这个功能。

FF_CODE_PAGE:文件系统语言,通过这个可以修改文件系统支持的语言,默认为437(英语),简体中文对应936,繁体中文对应950,语言全开就置0。

FF_USE_LFN:长文件名支持,默认为0,即不支持;这个配置一个有3种选项,区别在于文件名的储存方式;置1时,文件名储存在内存的BSS段中,此时是线程不安全的;置2时,文件名储存在栈中;置3时,文件名储存在堆中,这种方式是最推荐的。

FF_MAX_LFN:文件名长度,这个是和上面的配置对应的,如果没有使能长文件名支持,那么可以忽略该配置,文件名的长度最大可以设置为255字节。

FF_VOLUMES:储存介质数量,默认为1,如果单片机挂载了多于1种储存介质并且都有挂载文件系统,那么可以根据需要设置。

FF_MULTI_PARTITION:多分区支持,默认为0,如果文件系统需要支持多分区可以开启该配置,开启后需要用户创建分区表。

FF_MIN_SS和FF_MAX_SS:最小最大扇区大小,默认都为512,一般的存储介质扇区大小都是512字节,如果有不同可以根据储存芯片参数修改。

FF_FS_NORTC:时间戳支持,默认为0,即支持时间戳。

FF_FS_LOCK:文件锁支持,默认为0,它可以控制文件系统同时可以开启多少文件,一般建议设置成1,即同时只能开启一个文件。

FF_FS_REENTRANT:可重入支持,默认为0,单片机中有操作系统的话建议开启,它可以防止操作系统对文件系统进行异常操作。

配置项

配置项说明

设定值

系统配置

FF_FS_TINY

配置使用FATFS为正常模式还是Tiny模式

0

FF_FS_EXFAT

使用或禁用exFAT文件系统(使能exFAT需使能长文件名)

1

功能函数配置

FF_FS_READONLY

使能或禁止与写相关函数,即配置只读

0

FF_USE_MKFS

使能或禁用f_mkfs函数,即是否使能格式化

1

FF_USE_FASTSEEK

使能快速搜索功能,加快f_lseek/read/write函数执行

1

FF_USE_LABEL

使能或禁止支持磁盘盘符读取与设置函数

1

FF_USE_STRFUNC

设置是否支持字符串类操作

1

FF_STRF_ENCODE

设置字符串I/O函数读写文件字符编码

0

命名空间和本地环境配置

FF_CODE_PAGE

设置语言类型,简体中文设置为963

963

FF_USE_LFN

使能或禁止长文件名,取值范围(0~3),存储地方不同

3

磁盘配置

FF_VOLUMES

设置FATFS支持的逻辑设备数目

3

FF_MAX_SS

设置最大扇区大小

512

diskio.c需要实现的函数:

disk_initialize初始化磁盘驱动器

disk_status获取磁盘状态

disk_read从磁盘驱动器读扇区

disk_write从磁盘驱动器写扇区

disk_ioctl控制设备实现指定功能,用于辅助FATFS中其他API

get_fattime获取当前时间

ffconf.h文件中FF_FS_NORTC宏为0时需要实现

二、基于GD32F427移植FATFS文件系统

移植步骤:

 (1)移植FATFS源代码

FatFs的官网:FatFs - Generic FAT Filesystem Module

将下载好的源文件,一直到工程 文件夹中

 

(2)将GD例程中的sdcard源码移植到工程中的存放硬件驱动的文件夹中

(3) 修改diskio.c中各个函数

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2025        */
/*-----------------------------------------------------------------------*/
/* 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"			/* Basic definitions of FatFs */
#include "diskio.h"		/* Declarations FatFs MAI *//* Example: Declarations of the platform and disk functions in the project */
#include "sdcard.h"/* Example: Mapping of physical drive number for each drive */
#define SD_CARD  	0	/* Map FTL to physical drive 0 */
#define SD_CARD_BLOCK_SIZE 1
extern sd_card_info_struct sd_cardinfo;/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/DSTATUS disk_status (BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{switch (pdrv) {case SD_CARD :return 0;}return STA_NOINIT;}/*-----------------------------------------------------------------------*/
/* 功能:初始化存储介质
/* 参数:pdrv,物理硬盘号
/* 说明:因为本例程只有一个存储介质,且将其定义为0,可以认为sdcard定义为盘符0的存储介质                                                  */
/*-----------------------------------------------------------------------*/DSTATUS disk_initialize (BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{DSTATUS stat;switch (pdrv) {case SD_CARD :stat &= ~STA_NOINIT;return 0;}return STA_NOINIT;	
}/*-----------------------------------------------------------------------*/
/* 功能:读数据
/* 参数:pdrv,物理硬盘号 
/* 参数:buff,指向要读取数据的指针
/* 参数:sector,待读取的起始扇区号
/* 参数:count,待读取的扇区数
/* 说明:当只需要读一个扇区时,调用单块读函数
/*      当需要读多个扇区时,调用多块读函数,提高效率/* 注意:官方提供的驱动函数中第二个参数为读取的地址,所以  扇区号 ✖ 每个扇区大小 = 地址                                                       */
/*-----------------------------------------------------------------------*/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 */
)
{if((buff == NULL) || (count == 0)) {return RES_PARERR;}DRESULT res;sd_error_enum SD_stat = SD_OK;switch (pdrv) {case SD_CARD :if(count > 1) {SD_stat = sd_multiblocks_read((uint32_t *)buff, sector * sd_cardinfo.card_blocksize,sd_cardinfo.card_blocksize, count);} else{SD_stat = sd_block_read((uint32_t *)buff, sector * sd_cardinfo.card_blocksize,sd_cardinfo.card_blocksize);}if(SD_stat == SD_OK) {res = RES_OK ;} else {res = RES_ERROR ;} return res;}return RES_PARERR;
}/*-----------------------------------------------------------------------*/
/* 功能:写数据
/* 参数:pdrv,物理硬盘号 
/* 参数:buff,指向要写入数据的指针
/* 参数:sector,待写入的起始扇区号
/* 参数:count,待写入的扇区数
/* 说明:当只需要写一个扇区时,调用单块写函数
/*      当需要写多个扇区时,调用多块写函数,提高效率/* 注意:官方提供的驱动函数中第二个参数为写入的地址,所以  扇区号 ✖ 每个扇区大小 = 地址
/*-----------------------------------------------------------------------*/#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 */
)
{if((buff == NULL) || (count == 0)) {return RES_PARERR;}DRESULT res;sd_error_enum SD_stat = SD_OK;switch (pdrv) {case SD_CARD :if(count > 1) {SD_stat = sd_multiblocks_write((uint32_t *)buff, sector * sd_cardinfo.card_blocksize,sd_cardinfo.card_blocksize, count);} else {SD_stat = sd_block_write((uint32_t *)buff, sector * sd_cardinfo.card_blocksize,sd_cardinfo.card_blocksize);}if(SD_stat == SD_OK) {res = RES_OK ;}else{res = RES_ERROR ;}return res;}return RES_PARERR;
}#endif/*-----------------------------------------------------------------------*/
/* 功能:获取底层IO信息
/* 参数:pdrv,物理硬盘号
/* 参数:cmd,控制命令,不同命令返回不同的数据
/* 参数:buff,指向数据缓存的指针                                              */
/*-----------------------------------------------------------------------*/DRESULT disk_ioctl (BYTE pdrv,		/* Physical drive nmuber (0..) */BYTE cmd,		/* Control code */void *buff		/* Buffer to send/receive control data */
)
{DRESULT res;switch (pdrv) {case SD_CARD :switch(cmd) {/*获取扇区数*/case GET_SECTOR_COUNT:*(DWORD *)buff = sd_cardinfo.card_capacity / (sd_cardinfo.card_blocksize);break;/*获取扇区大小*/case GET_SECTOR_SIZE:*(WORD *)buff = sd_cardinfo.card_blocksize;break;/*获取块大小*/case GET_BLOCK_SIZE:*(DWORD *)buff = SD_CARD_BLOCK_SIZE;break;}res = RES_OK;return res;}return RES_PARERR;
}/*-----------------------------------------------------------------------*/
/* 功能:获取时间戳                                               */
/*-----------------------------------------------------------------------*/DWORD get_fattime(void)
{return 0;
}

(5)修改ffconf.h文件(可选)

三、测试用例

#include "main.h"
#include "ff.h"
#include "diskio.h"
#include <stdio.h>
#include <string.h>/* 定义测试文件名 */
#define TEST_FILE_NAME "test.txt"
#define TEST_DIR_NAME "test_dir"/* 全局变量 */
FATFS fs;         // 文件系统对象
FIL file;         // 文件对象
FRESULT fr;       // 文件操作结果
UINT bw, br;      // 写入/读取的字节数
BYTE buffer[512]; // 数据缓冲区
sd_error_enum sd_error;
uint16_t i = 5;
sd_card_info_struct sd_cardinfo;void check_root_files() {DIR dir;FILINFO fno;FRESULT fr;fr = f_opendir(&dir, ""); // 打开根目录if(fr == FR_OK) {printf("根目录文件列表:\r\n");while(1) {fr = f_readdir(&dir, &fno);if(fr != FR_OK || fno.fname[0] == 0) break;// 打印所有文件/文件夹(包括短文件名)printf("- %s %s\r\n", fno.fname, (fno.fattrib & AM_DIR) ? "[目录]" : "[文件]");}f_closedir(&dir);} else {printf("读取根目录失败: %d\r\n", fr);}
}/*!\brief    main function\param[in]  none\param[out] none\retval     none
*/
int main(void)
{// debug_init();bsp_uart_init();printf("fatfs demo\r\n");/* initialize the card */do {sd_error = sdcard_init();} while((SD_OK != sd_error) && (--i));printf("Starting FATFS tests...\r\n");/* 1. 挂载文件系统 */fr = f_mount(&fs, "0:", 1);if(fr != FR_OK){printf("f_mount failed: %d\r\n", fr);return 0;}printf("File system mounted successfully\r\n");/* 2. 创建并写入文件 */fr = f_open(&file, TEST_FILE_NAME, FA_WRITE | FA_CREATE_ALWAYS);if(fr == FR_OK){printf("File %s created successfully\r\n", TEST_FILE_NAME);/* 填充测试数据 */strcpy((char*)buffer, "Hello, FATFS on GD32F427VGT6!\r\nThis is a test file.\r\n");/* 写入数据到文件 */fr = f_write(&file, buffer, strlen((char*)buffer), &bw);if(fr == FR_OK){printf("Wrote %d bytes to file\r\n", bw);}else{printf("f_write failed: %d\r\n", fr);}/* 关闭文件 */f_close(&file);}else{printf("f_open for write failed: %d\r\n", fr);}/* 3. 读取文件内容 */fr = f_open(&file, TEST_FILE_NAME, FA_READ);if(fr == FR_OK){printf("Reading from %s:\r\n", TEST_FILE_NAME);/* 读取文件内容 */fr = f_read(&file, buffer, sizeof(buffer)-1, &br);if(fr == FR_OK){buffer[br] = '\0'; // 添加字符串结束符printf("Read %d bytes:\r\n%s\r\n", br, buffer);}else{printf("f_read failed: %d\r\n", fr);}/* 关闭文件 */f_close(&file);}else{printf("f_open for read failed: %d\r\n", fr);}/* 4. 创建目录 */fr = f_mkdir(TEST_DIR_NAME);if(fr == FR_OK){printf("Directory %s created successfully\r\n", TEST_DIR_NAME);}else if(fr == FR_EXIST){printf("Directory %s already exists\r\n", TEST_DIR_NAME);}else{printf("f_mkdir failed: %d\r\n", fr);}/* 5. 在子目录中创建文件 */char subfile[32];sprintf(subfile, "%s/subfile.txt", TEST_DIR_NAME);fr = f_open(&file, subfile, FA_WRITE | FA_CREATE_ALWAYS);if(fr == FR_OK){printf("File %s created successfully\r\n", subfile);strcpy((char*)buffer, "This file is in a subdirectory\r\n");fr = f_write(&file, buffer, strlen((char*)buffer), &bw);if(fr == FR_OK){printf("Wrote %d bytes to %s\r\n", bw, subfile);}else{printf("f_write to %s failed: %d\r\n", subfile, fr);}f_close(&file);}else{printf("f_open for %s failed: %d\r\n", subfile, fr);}/* 6. 删除文件 */
//    fr = f_unlink(TEST_FILE_NAME);
//    if(fr == FR_OK)
//    {
//        printf("File %s deleted successfully\r\n", TEST_FILE_NAME);
//    }
//    else
//    {
//        printf("f_unlink failed: %d\r\n", fr);
//    }//check_root_files();/* 7. 卸载文件系统 */fr = f_mount (NULL, "0:", 0);if(fr == FR_OK){printf("File system unmounted successfully\r\n");}else{printf("f_mount unmount failed: %d\r\n", fr);}printf("FATFS tests completed\r\n");while(1) {}
}

http://www.lryc.cn/news/606129.html

相关文章:

  • 从“救火”到“先知”:润建曲尺运维大模型如何重构网络运维价值链
  • 科研快报 |无人机+AI:广东防控基孔热背后的技术革命
  • 大疆无人机开发:MQTT 赋能机场系统集成的Java实战之旅
  • 九识智能与星逻智能达成战略合作,共推“无人车 + 无人机”空地一体巡检升级
  • 5G 单兵终端 + 无人机:消防应急场景的 “空 - 地” 救援协同体系
  • 无人机光伏巡检缺陷检出率↑32%:陌讯多模态融合算法实战解析
  • Lombok 字段魔法:用 @FieldDefaults 解锁“隐身+锁死”双重特效
  • php session 和 jwt 区别和使用场景
  • Java试题-选择题(2)
  • sqli-labs:Less-13关卡详细解析
  • 数据大集网:引领精准获客新时代的优质平台
  • 智慧医院导航系统:基于GPS+蓝牙ibeacon多源融合定位与deepseek•AI导诊问答的设计与实现
  • Linux 时钟同步配置:基础管理与 chrony 工具应用
  • 多架构镜像整合全攻略:在Docker中实现单一镜像支持同时支持amd64和arm64架构
  • hive新增列之后插入新数据时,新列为NULL的解决办法
  • CentOS 7 编译 Redis 6.x 完整教程(解决 GCC 版本不支持 C11)
  • 告别物业思维:科技正重构产业园区的价值坐标系
  • AR智能巡检:工业4.0时代的降本增效利器
  • [人工智能-综述-17]:AI革命:重塑职业版图,开启文明新篇
  • 数据集归一化
  • 机器学习之逻辑回归(Logistic Regression)
  • 视觉图像处理中级篇 [2]—— 外观检查 / 伤痕模式的原理与优化设置方法
  • 【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step5—Nginx安装
  • Qt 常用控件 - 3
  • vue-seamless-scroll 与 echarts 三联水球图循环滚动的渲染难题-出现短暂空白
  • iOS高级开发工程师面试——其他
  • Linux大页内存导致服务内存不足
  • Java——方法
  • 基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析实践技术应用
  • EEG手工特征提取总结