FATFS 分析

  • 格式:pdf
  • 大小:111.84 KB
  • 文档页数:15

FatFS源代码阅读分析(一)一、概述1、目的在移植之前,先将源代码大概的阅读一遍,主要是了解文件系统的结构、各个函数的功能和接口、与移植相关的代码等等。

2、准备工作在官方网站下载了0.07c版本的源代码,利用记事本进行阅读。

二、源代码的结构1、源代码组成源代码压缩包解压后,共两个文件夹,doc是说明,src里就是代码。

src文件夹里共五个文件和一个文件夹。

文件夹是option,还有00readme.txt、diskio.c、diskio.h、ff.c、ff.h、integer.h。

对比网上的文章,版本已经不同了,已经没有所谓的tff.c和tff.h了,估计现在都采用条件编译解决这个问题了,当然文件更少,可能编译选项可能越复杂。

2、00readme.txt的说明Low level disk I/O module is not included in this archive because the FatFs module is only a generic file system layer and not depend on any specific storage device.You have to provide a low level disk I/O module that written to control your storage device.主要是说不包含底层IO代码,这是个通用文件系统可以在各种介质上使用。

我们移植时针对具体存储设备提供底层代码。

接下来做了版权声明-可以自由使用和传播。

然后对版本的变迁做了说明。

3、源代码阅读次序先读integer.h,了解所用的数据类型,然后是ff.h,了解文件系统所用的数据结构和各种函数声明,然后是diskio.h,了解与介质相关的数据结构和操作函数。

再把ff.c和diskio.c两个文件所实现的函数大致扫描一遍。

最后根据用户应用层程序调用函数的次序仔细阅读相关代码。

三、源代码阅读1、integer.h头文件这个文件主要是类型声明。

以下是部分代码。

typedef int INT;typedef unsigned int UINT;typedef signed char CHAR;都是用typedef做类型定义。

移植时可以修改这部分代码,特别是某些定义与你所在工程的类型定义有冲突的时候。

2、ff.h头文件以下是部分代码的分析#include"integer.h"使用integer.h的类型定义#ifndef_FATFS#define_FATFS0x007C版本号007c,0.07c#define_WORD_ACCESS0//如果定义为1,则可以使用word访问。

中间有一些看着说明很容易弄清楚意思。

这里就不例举了。

#define_CODE_PAGE936#define_FS_REENTRANT0//如果要支持文件系统可重入,必须加入几个函数。

#define_TIMEOUT1000#define_SYNC_t HANDLE#elif_CODE_PAGE==936#define_DF1S0x81#define_DF1E0xFE#define_DS1S0x40#define_DS1E0x7E#define_DS2S0x80#define_DS2E0xFE接下来很大一部分都是与语言相关的因素,略过。

三个宏判断是否大写、小写、数字。

#define IsUpper(c)(((c)>='A')&&((c)<='Z'))#define IsLower(c)(((c)>='a')&&((c)<='z'))#define IsDigit(c)(((c)>='0')&&((c)<='9'))FatFS源代码阅读分析(二)继续分析ff.h文件内容。

#if_DF1S双字节编码相关的设定,暂时不理会它。

#if_MULTI_PARTITION//该变量定义为1时,支持一个磁盘的多个分区。

typedef struct_PARTITION{BYTE pd;BYTE pt;}PARTITION;Extern const PARTITION Drives[];//如果支持分区,则声明变量Drivers#define LD2PD(drv)(Drives[drv].pd)#define LD2PD(drv)(drv)#define LD2PT(drv)0#if_MAX_SS==512//一般扇区长度取512字节。

#define SS(fs)512U#if_LFN_UNICODE&&_USE_LFNtypedef WCHAR XCHAR;XCHAR是文件名的码型所用。

#elsetypedef char XCHAR;#endiftypedef struct_FATFS_{BYTE fs_type;BYTE drive;/*对应实际驱动号01---*/BYTE csize;/*每个簇的扇区数目*/先查一下簇的含义:应该是文件数据分配的基本单位。

BYTE n_fats;FAT文件系统依次应该是:引导扇区(即逻辑扇区0)、文件分配表两个(FAT)、根目录区(FDT)和数据区。

BYTE wflag;//文件是否改动的标志,为1时要回写。

WORD id;WORD n_rootdir;#if_FS_REENTRANT_SYNC_t sobj;#endif#if_MAX_SS!=512WORD s_size;#endif#if!_FS_READONLY//文件为可写BYTE fsi_flag;//文件需要回写的标志DWORD last_clust;DWORD free_clust;DWORD fsi_sector;#endif#if_FS_RPATHDWORD cdir;//目前的扇区在win[]里面,这个win[]数组暂时还不知道含义。

BYTE win[_MAX_SS];//这是一个win[512]数组,存储着一个扇区,好像作为扇区缓冲使用。

}FATFS;typedef struct_DIR_{FATFS*fs;指向相应文件系统对象。

WORD id;/*文件系统加载ID*/ WORD index;目前读写索引代码DWORD sclust;文件数据区开始簇DWORD clust;目前处理的簇DWORD sect;目前簇里对应的扇区BYTE*dir;BYTE*fn;#if_USE_LFNWCHAR*lfn;指向长文件名缓冲。

WORD lfn_idx;#endif}DIR;typedef struct_FIL_{FATFS*fs;WORD id;BYTE flag;文件状态标志BYTE csect;扇区偏移DWORD fptr;读写指针DWORD fsize;DWORD org_clust;文件开始簇DWORD curr_clust;当前簇DWORD dsect;文件当前扇区#if!_FS_READONLYDWORD dir_sect;该文件目录项对应所在的扇区BYTE*dir_ptr;#endif#if!_FS_TINYBYTE buf[_MAX_SS];文件读写缓冲#endif}FIL;typedef struct_FILINFO_{DWORD fsize;WORD fdate;WORD ftime;BYTE fattrib;char fname[13];#if_USE_LFNXCHAR*lfname;int lfsize;#endif}FILINFO;这个结构主要描述文件的状态信息,包括文件名13个字符(8+.+3+\0)、属性、修改时间等。

FatFS源代码阅读分析(三)接下来是函数的定义,先大概浏览一遍。

FRESULT f_mount(BYTE,FATFS*);//加载文件系统,BYTE参数是ID,后一个是文件系统定义。

FRESULT f_open(FIL*,const XCHAR*,BYTE);//打开文件,第一个参数是文件信息结构,第二个参数是文件名,第三是文件打开模式FRESULT f_read(FIL*,void*,UINT,UINT*);//文件读取函数,参数1为文件对象(文件打开函数中得到),参数2为文件读取缓冲区,参数3为读取的字节数,参数4意义不清晰,等读到源代码就清楚了。

FRESULT f_write(FIL*,const void*,UINT,UINT*);//写文件,参数跟读差不多FRESULT f_lseek(FIL*,DWORD);//移动文件的读写指针,参数2应该是移动的数目。

FRESULT f_close(FIL*);FRESULT f_opendir(DIR*,const XCHAR*);打开目录,返回目录对象FRESULT f_readdir(DIR*,FILINFO*);读取目录,获得文件信息FRESULT f_stat(const XCHAR*,FILINFO*);FRESULT f_getfree(const XCHAR*,DWORD*,FATFS**);FRESULT f_truncate(FIL*);FRESULT f_sync(FIL*);将缓冲区数据写回文件FRESULT f_unlink(const XCHAR*);删除目录中的一个文件FRESULT f_mkdir(const XCHAR*);FRESULT f_chmod(const XCHAR*,BYTE,BYTE);FRESULT f_utime(const XCHAR*,const FILINFO*);FRESULT f_rename(const XCHAR*,const XCHAR*);FRESULT f_forward(FIL*,UINT(*)(const BYTE*,UINT),UINT,UINT*);这个函数还要提供一个回调函数。

FRESULT f_mkfs(BYTE,BYTE,WORD);FRESULT f_chdir(const XCHAR*);改变当前目录FRESULT f_chdrive(BYTE);应该说基本能明白这些函数用于干什么。

#if_USE_STRFUNCint f_putc(int,FIL*);int f_puts(const char*,FIL*);int f_printf(FIL*,const char*,...);char*f_gets(char*,int,FIL*);#define f_eof(fp)(((fp)->fptr==(fp)->fsize)?1:0)#define f_error(fp)(((fp)->flag&FA__ERROR)?1:0)#if_FS_REENTRANT//如果定义了重入,则需要实现以下四个函数BOOL ff_cre_syncobj(BYTE,_SYNC_t*);创建同步对象BOOL ff_del_syncobj(_SYNC_t);删除同步对象BOOL ff_req_grant(_SYNC_t);申请同步对象void ff_rel_grant(_SYNC_t);释放同步对象。