基于STM32完成FATFS文件系统移植与运用--这是完全免费开源的FAT文件系统

Posted DS小龙哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于STM32完成FATFS文件系统移植与运用--这是完全免费开源的FAT文件系统相关的知识,希望对你有一定的参考价值。

一、环境介绍

主控MCU: STM32F103ZET6 

STM32程序开发IDE: keil5

STM32程序风格:  采用寄存器方式开发,注释齐全,执行效率高,方便移植

硬件包含:  一块STM32F103ZET6系统板、一个SPI接口的SD卡卡槽模块、一张SD卡

工程完整源码下载地址:  https://download.csdn.net/download/xiaolong1126626497/19687693

 

这篇文章主要演示FATFS文件系统如何移植到自己的工程,并完成文件的读写。

因为SD卡采用的是SPI模拟时序,所以,其他单片机一样可以照着移植,代码都可以复制粘贴的。

 

 

 

二、FATFS文件系统介绍

2.1 FATFS简介

FatFs 是一种完全免费开源的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C 语言编写,所以具有良好的硬件平台独立性,可以移植到 8051、 PIC、 AVR、 SH、 Z80、 H8、 ARM 等系列单片机上而只需做简单的修改。它支持 FATl2、 FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对 8 位单片机和 16 位单片机做了优化。

2.2 特点

  1.  Windows兼容的FAT文件系统
  2. 不依赖于平台,易于移植
  3. 代码和工作区占用空间非常小
  4.  多种配置选项
  5. 多卷(物理驱动器和分区)
  6.  多ANSI/OEM代码页,包括DBCS
  7. 在ANSI/OEM或Unicode中长文件名的支持
  8. RTOS的支持
  9. 多扇区大小的支持
  10. 只读,最少API,I/O缓冲区等等

2.3 移植性

fatfs模块是ANSI C(C89)编写的。 没有平台的依赖, 编译器只要符合ANSI C标准就可以编译。

fatf模块假设大小的字符/短/长8/16/32位和int是16或32位。 这些数据类型在integer.h文件中定义。这些数据类型在大多数的编译器中定义都符合要求。 如果现有的定义与编译器有任何冲突发生时,需要自己解决。

2.4 源码下载

下载地址:http://elm-chan.org/fsw/ff/00index_e.html

FATFS有两个版本,一个大版本,一个小版本。小版本主要用于8位机(内存小)使用。

下载图:

2.5 FATFS源码文件介绍

将下载的源码解压后可以得到两个文件夹: doc 和 src。 doc 里面主要是对 FATFS 的介绍(离线文档—英文和日文),而 src 里面才是我们需要的源码。

其中,与平台无关的是:

ffconf.h     FATFS配置文件

ff.h        应用层头文件

ff.c        应用层源文件

diskio.h    硬件层头文件

interger.h   数据类型定义头文件

option     可选的外部功能(比如支持中文等)

与平台相关的代码:

diskio.c     底层接口文件(需要用户提供)

FATFS 模块在移植的时候,我们一般只需要修改 2 个文件,即 ffconf.h 和 diskio.c。

FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们可以通过配置里面的一些选项,来满足自己的需求。

FATFS最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC 上读/写文件那样简单。

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

需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口 ( disk、I/O) 和供给文件创建修改时间的实时时钟。

 

三、 移植FATFS文件系统

移植之前,首先得准备一个能正常编译的工程,并且工程里有SD卡的驱动代码,提供了读写扇区这些函数才能进行FATFS文件系统的正常移植。

关于如何编写SD卡驱动,SD卡的时序介绍、命令介绍等知识点下篇文章再讲解。这篇文章重点是FATFS文件系统的移植过程。

3.1  新建工程

FATFS文件系统源码下载下来,解压之后,移植修改的步骤如下:

打开KEIL工程,添加FATFS文件源码:

加入.h文件主要是方便配。cc936.c 用于支持中文。

 

3.2  修改diskio.c文件

注释掉现在不需要的用到的文件,因为我们现在用的是SD卡,与USB,ATA,MMC卡没关系。

并加入一个新的宏 :

#define  SD  0

定义SD卡的物理驱动器号为0。 

修改 disk_status函数,该函数主要是用来获取磁盘状态。现在未用到,可以直接函数体内代码删除。

修改截图:

代码示例:

#include "diskio.h"           /* fatf底层API */

#include "sd.h"                      /* SD卡驱动头文件  */

/* 定义每个驱动器的物理驱动器号*/

#define SD    0

 

/*-----------------------------------------------------------------------*/

/* 获取设备(磁盘)状态                                                     */

/*-----------------------------------------------------------------------*/

 

DSTATUS disk_status (

        BYTE pdrv             /* 物理驱动识别 */

)

{

   return 0;  //该函数现在无需用到,直接返回0

}

修改disk_initialize函数,添加SD卡的初始化,其他不用到的代码直接删掉,该函数成功返回0,失败返回1。

修改截图:

代码示例:

/*-----------------------------------------------------------------------*/

/* 初始化磁盘驱动                                                        */

/*-----------------------------------------------------------------------*/

 

DSTATUS disk_initialize (

        BYTE pdrv                             /* 物理驱动识别 */

)

{

        DSTATUS stat;

        int result;

 

        switch (pdrv) {

        case SD :            //选择SD

                stat=SD_Init();   //初始化SD-用户自己提供

        }

        if(stat)return STA_NOINIT;  //磁盘未初始化

        return 0; //初始化成功

}

修改disk_read函数,加入SD卡读任意扇区的函数(需要用户自己提供),其他不用到的选项可以删掉。

修改代码如下:

/*-----------------------------------------------------------------------*/

/* 读扇区                                                                */

/*-----------------------------------------------------------------------*/

DRESULT disk_read (

        BYTE pdrv,            /* 物理驱动编号 - 范围0-9*/

        BYTE *buff,           /* 数据缓冲区存储读取数据 */

        DWORD sector,  /* 扇区地址*/

        UINT count             /* 需要读取的扇区数*/

)

{

        DRESULT res;

        int result;

        switch (pdrv) {

                case SD:

                  res=SD_Read_Data((u8*)buff,sector,count);  //SD扇区函数--用户提供

                  return res; //在此处可以判错误

        }

        return RES_PARERR;  //无效参数

}

修改disk_write 函数,添加写扇区函数:

代码:

/*-----------------------------------------------------------------------*/

/* 写扇区                                                                */

/*-----------------------------------------------------------------------*/

 

#if _USE_WRITE

DRESULT disk_write (

        BYTE pdrv,                      /* 物理驱动号*/

        const BYTE *buff,        /* 要写入数据的首地址 */

        DWORD sector,                 /* 扇区地址 */

        UINT count                        /* 扇区数量*/

)

{

        DRESULT res;

        int result;

 

        switch (pdrv) {

                case SD:

                        res=SD_Write_Data((u8*)buff,sector,count); //写入扇区

                  return res;

        }

        return RES_PARERR;  //无效参数

}

#endif

修改disk_ioctl 函数,填充ioctl命令功能。这些功能是标准的命令,在diskio.h有定义。

代码如下:

/*-----------------------------------------------------------------------*/

/* 其他函数                                              */

/*-----------------------------------------------------------------------*/

 

#if _USE_IOCTL

DRESULT disk_ioctl (

        BYTE pdrv,            /* 物理驱动号 */

        BYTE cmd,               /* 控制码  */

        void *buff               /* 发送/接收数据缓冲区地址 */

)

{

        DRESULT res;

        int result;

 

        switch (pdrv) {

                case SD:

                         switch(cmd)

                         {

                                 case CTRL_SYNC:      //等待写过程

                                         SD_CS(0);          //选中SD

                                         if(SD_Wait_Ready())result = RES_ERROR;/*等待卡准备好*/

                                     else res = RES_OK;     //成功

                                         SD_CS(1);            //释放SD

                        break;     

                                 

                         case GET_SECTOR_SIZE://获取扇区大小

                           *(DWORD*)buff = 512;

                        res = RES_OK;     //成功

                        break;     

                                 

                         case GET_BLOCK_SIZE:    //获取块大小

                                *(WORD*)buff = 8;      //块大小(扇区为单位),一块等于8个扇区

                         res = RES_OK;

                         break;

                                 

                         case GET_SECTOR_COUNT: //获取总扇区数量

                        *(DWORD*)buff = SD_Get_Sector_Count();

                        res = RES_OK;

                        break;

                                 

                        default:  //命令错误

                        res = RES_PARERR;

                        break;

                         }

                return res;

        }

        return RES_PARERR;  //返回状态

}

diskio.c 文件修改完整代码:

/*-----------------------------------------------------------------------*/

/* 低级别磁盘I / O模块框架fatf(C)ChaN)2014 

*存储控制模块fatf模块定义了一个API      */

/*-----------------------------------------------------------------------*/

 

#include "diskio.h"           /* fatf底层API */

#include "sd.h"                      /* SD卡驱动头文件  */

 

/* 定义每个驱动器的物理驱动器号*/

#define SD    0

 

/*-----------------------------------------------------------------------*/

/* 获取设备(磁盘)状态                                                     */

/*-----------------------------------------------------------------------*/

 

DSTATUS disk_status (

        BYTE pdrv             /* 物理驱动识别 */

)

{

   return 0;  //该函数现在无需用到,直接返回0

}

 

 

 

/*-----------------------------------------------------------------------*/

/* 初始化磁盘驱动                                                        */

/*-----------------------------------------------------------------------*/

 

DSTATUS disk_initialize (

        BYTE pdrv                             /* 物理驱动识别 */

)

{

        DSTATUS stat;

        int result;

 

        switch (pdrv) {

        case SD :           //选择SD

                stat=SD_Init();   //初始化SD-用户自己提供

        }

        if(stat)return STA_NOINIT;  //磁盘未初始化

        return 0; //初始化成功

}

 

 

/*-----------------------------------------------------------------------*/

/* 读扇区                                                                */

/*-----------------------------------------------------------------------*/

 

DRESULT disk_read (

        BYTE pdrv,            /* 物理驱动编号 - 范围0-9*/

        BYTE *buff,           /* 数据缓冲区存储读取数据 */

        DWORD sector,      /* 扇区地址*/

        UINT count             /* 需要读取的扇区数*/

)

{

        DRESULT res;

        int result;

 

        switch (pdrv) {

                case SD:

                        res=SD_Read_Data((u8*)buff,sector,count);  //SD扇区函数--用户提供

                  return res; //在此处可以判错误

        }

        return RES_PARERR;  //无效参数

}

 

 

 

/*-----------------------------------------------------------------------*/

/* 写扇区                                                                */

/*-----------------------------------------------------------------------*/

 

#if _USE_WRITE

DRESULT disk_write (

        BYTE pdrv,                      /* 物理驱动号*/

        const BYTE *buff, /* 要写入数据的首地址 */

        DWORD sector,                /* 扇区地址 */

        UINT count                       /* 扇区数量*/

)

{

        DRESULT res;

        int result;

 

        switch (pdrv) {

                case SD:

                        res=SD_Write_Data((u8*)buff,sector,count); //写入扇区

                  return res;

        }

        return RES_PARERR;  //无效参数

}

#endif

 

 

/*-----------------------------------------------------------------------*/

/* 其他函数                                              */

/*-----------------------------------------------------------------------*/

 

#if _USE_IOCTL

DRESULT disk_ioctl (

        BYTE pdrv,            /* 物理驱动号 */

        BYTE cmd,               /* 控制码  */

        void *buff               /* 发送/接收数据缓冲区地址 */

)

{

        DRESULT res;

        int result;

 

        switch (pdrv) {

                case SD:

                         switch(cmd)

                         {

                                 case CTRL_SYNC:      //等待写过程

                                         SD_CS(0);          //选中SD

                                         if(SD_Wait_Ready())result = RES_ERROR;/*等待卡准备好*/

                                       else res = RES_OK;     //成功

                                          SD_CS(1);          //释放SD

                             break;   

                                 

                                 case GET_SECTOR_SIZE://获取扇区大小

                                *(DWORD*)buff = 512;

                        res = RES_OK;     //成功

                        break;     

                                 

                                 case GET_BLOCK_SIZE:  //获取块大小

                                *(WORD*)buff = 8;      //块大小--一块等于8个扇区

                        res = RES_OK;

                        break;

                                 

                                 case GET_SECTOR_COUNT: //获取总扇区数量

                        *(DWORD*)buff = SD_Get_Sector_Count();

                        res = RES_OK;

                        break;

                                 

                                 default:  //命令错误

                        res = RES_PARERR;

                        break;

                         }

                return res;

        }

        return RES_PARERR;  //返回状态

}

#endif

 

 

//返回FATFS时间

//获得时间 

DWORD get_fattime (void)

{     

        return (DWORD)(2017-1980)<<25|    //

                                                    7<<21|    //

                                                   27<<16|    //

                                   12<<11|    //

                                    13<<5|    //

                                       14;    //

}

 

 

/*

Return Value

Currnet local time is returned with packed into a DWORD value. The bit field is as follows:

bit31:25

Year origin from the 1980 (0..127)

bit24:21

Month (1..12)

bit20:16

Day of the month(1..31)

bit15:11

Hour (0..23)

bit10:5

Minute (0..59)

bit4:0

Second / 2 (0..29)

*/

 

3.3 修改ffconf.h文件

需要注意的一些宏配置:

#define _CODE_PAGE  936   //采用中文GBK编码       (64)

#define    _USE_LFN     3     //动态的堆上工作             93行)

#define    _MAX_LFN   255   /*_USE_LFN选项开关LFN(长文件名)特性。

#define _VOLUMES      1     /* 支持的磁盘数量(逻辑驱动器)。 */   (142行)

#define    _MIN_SS                512                                  (165行)

#define    _MAX_SS              512   /*这些选项配置支持扇区大小的范围。(512,1024, 4096*/ 

#define _FS_NORTC         0    /*启用RTC时间功能*/   (202行)

#define _NORTC_MON     1

#define _NORTC_MDAY     1

#define _NORTC_YEAR       2015 //年 

/*需要实现:get_fattime()函数*/

 

ffconf.h 文件源码(讲解):

/*---------------------------------------------------------------------------/

/  FatFs - FAT文件系统模块配置文件  R0.11a (C)ChaN, 2015

/---------------------------------------------------------------------------*/

 

#define _FFCONF 64180       /* 版本识别*/

 

/*---------------------------------------------------------------------------/

/ 功能配置

/---------------------------------------------------------------------------*/

 

#define _FS_READONLY     0

/* 这个选项开关只读配置。(0:/写或1:只读)   

/只读配置删除编写API函数,f_write(),f_sync(),   

/ f_unlink(),f_mkdir(),f_chmod(),f_rename(),f_truncate(),f_getfree()   

/写和可选的功能. */

 

 

#define _FS_MINIMIZE        0

/*此选项定义删除一些基本的API函数极小化水平。  

/   

/ 0:所有基本功能都是激活的。  

/ 1:f_stat(),f_getfree(),f_unlink(),f_mkdir(),f_chmod(),f_utime(),   

/ f_truncate()f_rename()函数删除。  

/ 2:f_opendir(),f_readdir()f_closedir()中除了1。  

/ 3:f_lseek()函数删除除了2*/

 

 

#define    _USE_STRFUNC  1

/*这个选项开关字符串函数,f_gets(),f_putc(),f_puts()和 

/ f_printf()。  

/   

/ 0:禁用字符串函数。  

/ 1:启用没有LF-CRLF转换。  

/ 2:启用LF-CRLF(回车换行)转换。*/

 

 

#define _USE_FIND              0

/*这个选项开关过滤目录读取特性和相关功能,   

/ f_findfirst()f_findnext()(0:禁用或1:启用)*/

 

 

#define    _USE_MKFS         1

/* 这个选项开关f_mkfs()函数。(0:禁用或1:启用) */

 

 

#define    _USE_FASTSEEK 1

/* 这个选项开关快速寻求功能。(0:禁用或1:启用) */

 

 

#define _USE_LABEL           1

/*   磁盘卷标这个选项开关功能,f_getlabel()f_setlabel()。  

/(0:禁用或1:启用) */

 

 

#define    _USE_FORWARD 0

/*  这个选项开关f_forward()函数。(0:禁用或1:启用)   

/启用它,_FS_TINY需要设置为1. */

 

 

/*---------------------------------------------------------------------------/

/ 语言环境和名称空间配置

STM32+雷龙SD NAND(贴片SD卡)完成FATFS文件系统移植与测试

STM32移植FATFS文件系统最新版R0.14b

STM32 下 FatFs的移植,实现了擦写均衡,坏块管理,硬件 ECC,ECC纠错

STM32之独立版USB(Host)驱动+MSC+Fatfs移植

STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)

STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)

(c)2006-2024 SYSTEM All Rights Reserved IT常识