小工具知识网

当前位置: 首页 > 行业知识 > STM32进阶笔记——FATFS文件系统(上)

行业知识

STM32进阶笔记——FATFS文件系统(上)

2026-11-06 12:25:09 来源:互联网转载

  ���专栏争取每周三更新直到更新完成,期待大家的订阅关注,欢迎互相学习交流。

  本文需要一些SD卡的前置知识,后续文章会介绍,这里先介绍一下FATFS文件系统。关于FATFS的文章分为上下两篇,上篇主要介绍什么是FAT文件系统以及FATFS的移植,下篇主要介绍FATFS的一些API函数。

目录

  • 一、FATFS文件系统简介
    • 1.1 FATFS引入
    • 1.2 FATFS特点
    • 二、FATFS文件系统移植
      • 2.1 FATFS源码文件简介
      • 2.2 FATFS重要配置选项
      • 2.3 FATFS移植步骤
        • 2.3.1 disk_initialize函数
        • 2.3.2 disk_status函数
        • 2.3.3 disk_read函数
        • 2.3.4 disk_write函数
        • 2.3.5 disk_ioctl函数
        • 2.3.6 get_fattime函数

          一、FATFS文件系统简介

          1.1 FATFS引入

            通常我们买来一张全新的SD卡时,如果我们想把它通过读卡器插入到电脑使用,我们需要先对其格式化,这是因为Windows系统使用的是FAT文件系统,我们格式化SD卡,实际也就是在SD卡中建立一个FAT文件系统,这样我们的电脑才能识别它。

            FAT是一个专门为小型的嵌入式系统设计的,完全用标准C语言编写,完全免费开源的文件系统。FAT具有良好的硬件平台独立性,可以移植到C51、PIC、ARM等单片机上,而且只需要做一些简单的修改,使用起来非常方便。

          1.2 FATFS特点

            FATFS有以下特点

          • Windows 兼容的 FAT 文件系统
          • 与平台无关,移植简单
          • 代码量少、效率高
          • 支持多卷(物理驱动器或分区,最多 10 个卷)
          • 多个 ANSI/OEM 代码页包括 DBCS
          • 支持长文件名、ANSI/OEM 或 Unicode
          • 支持 RTOS
          • 支持多种扇区大小
          • 只读、最小化的 API 和 I/O 缓冲区等

              这里简单说明一下几个名词,仅供参考,大家可以自行搜索了解更多详细内容。

              上面提到的卷指的是是硬盘上的存储空间,一个硬盘包括好多卷,一卷也可以跨越许多磁盘。卷又分为很多种,比如启动卷、跨区卷、带区卷等。此外还有簇,指的是磁盘文件存储管理的最小单位。

              Unicode(Universal Multiple-Octet Coded Character Set)简称UCS,中文意思是统一码,或者叫万国码,可以简单理解为他就是一个数字映射表,每一个数字代表不同的字符或文字。

            二、FATFS文件系统移植

            2.1 FATFS源码文件简介

              下面我们介绍一下如何移植FATFS。首先我们需要下载到它的源码,有需要的友友可以私信获取。下载完成后打开其中的“src”文件夹,其中是我们需要的源码。我们在移植FATFS模块的时候,一般只需要修改ffconf.h 和 diskio.c这两个文件。

            • option文件夹是一些可选的外部C文件,包含了多语言支持需要的文件和转换函数,移植时我们通常会选择cc936.c文件,用来支持简体中文,它包含了简体中文GBK和Unicode互相转换的功能函数。
            • diskio.c文件是FATFS移植的最关键文件,它为文件系统提供了最底层的接口返回问函数。
            • diskio.h里面包含了FATFS用到的宏定义以及diskio.c文件内与底层硬件接口相关的函数声明。
            • 00history.txt介绍了当前FATFS的版本更新情况。
            • 00readme.txt介绍了文件夹中各个文件的功能。
            • integer.h文件中是一些数据类型定义。
            • ff.c文件时FATFS的核心,里面包含了FATFS的各个模块程序,时文件管理的实现方法,我们在移植时通常不需要对这个文件进行修改。
            • ffconf.h是FATFS的配置文件,其中包含了对FATFS功能配置的宏定义。我们通过修改这些宏定义就可会议实现对FATFS功能的裁剪,达到自己想要的效果。

              2.2 FATFS重要配置选项

                上面在介绍源码文件时提到,FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们这里再介绍一些重要的配置选项。

              • _FS_TINY该宏定义时设置使用的是标准模式还是微小模式,我们通常设置为0,使用标准模式。
              • _FS_READONLY该宏定义是选择是否使用只读模式,通常我们选择可读可写,设置为0。
              • _FS_MINIMIZE该宏定义是选择是否裁剪掉一些函数,通常选择不裁剪,设置为0。
              • _USE_STRFUNC该宏定义是设置是否支持字符串类操作,通常选择支持,设置为1。
              • _USE_MKFS该宏定义是设置是否使用格式化,我们选择使用,设置为1。
              • _USE_FASTSEEK该宏定义用来设定是否是能快速定位,通常选择使用,设置为1。
              • _USE_LABEL该宏定义用来设置是否支持磁盘盘符读取与设置,通常选择支持,设置为1。使能后我们就可以通过相关函数来读取或者设置磁盘的名字。
              • _CODE_PAGE该宏定义用来设置语言类型,通常我们选择简体中文,设置为936(也就是c936.c文件)。
              • _USE_LFN该宏定义用来设置是否支持长文件名,取值范围是0到3。0表示不支持长文件名,1~3表示支持长文件名,但是存储的地方不一样。通常设置为3,可以通过ff_memalloc函数来动态分配长文件名的存储区域。
              • _MAX_LFN该宏定义用来设置允许的最大文件名长度,我们设置为最大值255。
              • _LFN_UNICODE该宏定义用来设置是否使用FATFS的字符编码,通常设置为不使用,设置为0。
              • _VOLUMES该宏定义用来设置FATFS支持的逻辑涉笔数量。
              • _MAX_SS该宏定义用来设置山区缓冲的最大值,一般设置为512。

                2.3 FATFS移植步骤

                  想要将FATFS移植到STM32实际并不复杂,主要分为三步

                • 下载FATFS源码
                • 将源码添加到Keil工程
                • 编写一些底层的接口函数

                    通常最后一步我们需要编写六个底层的接口函数,下面我们来详细地介绍一下它们。

                  2.3.1 disk_initialize函数

                  • 函数功能:初始化磁盘。
                  • 函数原型:DSTATUS disk_initialize(BYTE pdrv);
                  • 输入参数:prdrv是要初始化的逻辑驱动器号,也就是盘符,取值范围是0~9。
                  • 返回值:返回一个盘符状态作为结果。
                  • 注意事项:应用程序不应该在FATFS活动时调用此函数,否则卷上的FAT结构可能会损坏。
                    //初始化磁盘DSTATUS disk_initialize (BYTE pdrv/* Physical drive nmuber to identify the drive */){u8 res=0;    switch(pdrv){case SD_CARD://SD卡res=SD_Init();//SD卡初始化   break;case EX_FLASH://外部flashW25QXX_Init();FLASH_SECTOR_COUNT=2048*12;//W25Q1218,前12M字节给FATFS占用  break;default:res=1; } if(res)return  STA_NOINIT;else return 0; //初始化成功 } 

                    2.3.2 disk_status函数

                    • 函数功能:返回当前磁盘驱动器的状态。
                    • 函数原型:DSTATUS disk_status (BYTE pdrv);
                    • 输入参数:pdrv是要确认的逻辑驱动器号,也就是盘符,取值范围是0~9。
                    • 返回值:有如下几个返回值
                      #define STA_NOINIT0x01/* Drive not initialized */#define STA_NODISK0x02/* No medium in the drive */#define STA_PROTECT0x04/* Write protected */

                        FATFS只使用前两个返回值。

                      //获得磁盘状态DSTATUS disk_status (BYTE pdrv/* Physical drive nmuber to identify the drive */){ return RES_OK;} 

                      2.3.3 disk_read函数

                      • 函数功能:从磁盘上读取扇区。
                      • 函数原型:DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
                      • 输入参数:pdrv:驱动器逻辑号;

                        buff:指向存储读取数据字节数组的指针;

                        sector:指定起始扇区的逻辑块上的地址;

                        count:指定要读取的扇区数;

                      • 返回值:返回值结构体如下
                        /* Results of Disk Functions */typedef enum {RES_OK = 0,/* 0: Successful */RES_ERROR,/* 1: R/W Error */RES_WRPRT,/* 2: Write Protected */RES_NOTRDY,/* 3: Not Ready */RES_PARERR/* 4: Invalid Parameter */} DRESULT;
                        //读扇区//pdrv:磁盘编号0~9//*buff:数据接收缓冲首地址//sector:扇区地址//count:需要读取的扇区数DRESULT disk_read (BYTE pdrv,/* Physical drive nmuber to identify the drive */BYTE *buff,/* Data buffer to store read data */DWORD sector,/* Sector address in LBA */UINT count/* Number of sectors to read */){u8 res=0;     if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误  switch(pdrv){case SD_CARD://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 EX_FLASH://外部flashfor(;count>0;count--){W25QXX_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;   }

                        2.3.4 disk_write函数

                        • 函数功能:向磁盘写入一个或多个扇区。
                        • 函数原型:DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
                        • 输入参数:pdrv:要操作的逻辑驱动器号;

                          buff:指向要写入的数组的指针;

                          sector:指定起始扇区逻辑块上的地址;

                          count:指定要写入的扇区数,取值范围是1~128;

                        • 返回值:与上面的读函数为同一个结构体。
                          //写扇区//pdrv:磁盘编号0~9//*buff:发送数据首地址//sector:扇区地址//count:需要写入的扇区数#if _USE_WRITEDRESULT disk_write (BYTE pdrv,/* Physical drive nmuber to identify the drive */const BYTE *buff,/* Data to be written */DWORD sector,/* Sector address in LBA */UINT count/* Number of sectors to write */){u8 res=0;      if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误  switch(pdrv){case SD_CARD://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 EX_FLASH://外部flashfor(;count>0;count--){    W25QXX_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

                          2.3.5 disk_ioctl函数

                          • 函数功能:控制设备指定特性和一些除了读写外的杂项功能。
                          • 函数原型:DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
                          • 输入参数:pdrv:要操作的逻辑驱动器号;

                            cmd:命令代码;

                            buff:指向参数缓冲区的指针,取决于命令代码,不使用时指向一个NULL指针;

                          • 返回值:与上面的读/写函数为同一个结构体。
                          • 注意事项:

                              这里贴一下一些命令的宏定义

                            /* Command code for disk_ioctrl fucntion *//* Generic command (Used by FatFs) */#define CTRL_SYNC0/* Complete pending write process (needed at _FS_READONLY == 0) */#define GET_SECTOR_COUNT1/* Get media size (needed at _USE_MKFS == 1) */#define GET_SECTOR_SIZE2/* Get sector size (needed at _MAX_SS != _MIN_SS) */#define GET_BLOCK_SIZE3/* Get erase block size (needed at _USE_MKFS == 1) */#define CTRL_TRIM4/* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) *//* Generic command (Not used by FatFs) */#define CTRL_POWER5/* Get/Set power status */#define CTRL_LOCK6/* Lock/Unlock media removal */#define CTRL_EJECT7/* Eject media */#define CTRL_FORMAT8/* Create physical format on the media *//* MMC/SDC specific ioctl command */#define MMC_GET_TYPE10/* Get card type */#define MMC_GET_CSD11/* Get CSD */#define MMC_GET_CID12/* Get CID */#define MMC_GET_OCR13/* Get OCR */#define MMC_GET_SDSTAT14/* Get SD status *//* ATA/CF specific ioctl command */#define ATA_GET_REV20/* Get F/W revision */#define ATA_GET_MODEL21/* Get model name */#define ATA_GET_SN22/* Get serial number */
                            //其他表参数的获得//pdrv:磁盘编号0~9//ctrl:控制代码//*buff:发送/接收缓冲区指针#if _USE_IOCTLDRESULT disk_ioctl (BYTE pdrv,/* Physical drive nmuber (0..) */BYTE cmd,/* Control code */void *buff/* Buffer to send/receive control data */){DRESULT res;       if(pdrv==SD_CARD)//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==EX_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;}#endif

                            2.3.6 get_fattime函数

                            • 函数功能:获取当前时间。
                            • 返回值:返回以双字封装的当前时间,具体格式这里不再详细描述了。
                            • 注意事项:get_fattime函数必须返回一个合法的时间,如果系统不支持实时时钟,可以返回0。

                                通过上述步骤,我们就完成了对于FATFS的移植,FATFS 提供了很多 API 函数,这些函数 FATFS 的自带介绍文件里面都有详细的介绍。这里需要注意的是,在使用FATFS的时候,必须先通过f_mount函数注册一个工作区,才能开始后续 API 的使用。

上一篇:给网络怎么更改服务器?

下一篇:成员服务器怎么降级?