详细了解并学习FatFS文件系统的基本原理

最近做的s     pi   flash,本打算弄个文件系统,由于之前用过了JFFS、YAFFS和TrueFFS,代码量都相当的大,这次想找款代码量不那么吓人的,学习一下,听说配置会相对复杂一些。选来选去,最终选定了FatFS,代码量足够的小,最新的R0.09版本只有1个.c文件(当然,还有一个底层的要自己写,op     ti   on文件夹里的无视),老点版本就更小了。而且更新很频繁,用户量也够大,就选定它了。尽管最后由于硬件和项目原因未能实际的移植它到vxWorks,但学过的还是要记录下。

 详细了解并学习FatFS文件系统的基本原理_设计制作_存储技术

在这里http://elm-chan.org/fsw/ff/00index_e.html下载源码,只有800多K,小的可怜,还可以下载示例程序,有A     VR   、Win32、     lpc   等多平台已实现的方案。打开看src文件夹,一个op  TI on文件夹、00readme.txt、diskio.h、ff.c、ff.h、ffconf.h和in     te   rger.h。移植时需要修改的文件主要包括ffconf.h和interger.h,后者是在它的定义与目标平台上的有冲突,或者用的不习惯时修改的。

在做具体修改之前,先大概阅读下FatFS的源代码,可以先读integer.h,了解所用的数据类型,然后是ff.h,了解文件系统所用的数据结构和各种函数声明,再就是diskio.h,了解与介质相关的数据结构和操作函数。ff.c这个文件相对较大,可以在最后将所实现的函数大致扫描一遍,之后根据用户应用层程序调用函数的次序仔细阅读相关代码。各个文件都可以直接用记事本打开查阅,非常方便。ff.h中的几个结构体十分重要,列举如下,首先是最基础的文件系统结构体:

view pl     ai   ncopy to clipboardprint?

/* File system object structure (FATFS) */

typedef struct {

BYTE    fs_type;        /* FAT子类型,一般在mount时用,置0表示未挂载*/

BYTE    drv;            /* 物理驱动号,一般为0*/

BYTE     csi   ze;          /* 每个簇的扇区数目(1,2,4...128) */

BYTE    n_fats;         /* 文件分配表的数目(1,2) */

/*FAT文件系统依次为:引导扇区、两个文件分配表、根目录区和数据区*/

BYTE    wflag;          /*     标记   文件是否被改动过,为1时要回写*/

BYTE    fsi_flag;       /* 标记文件系统信息是否被改动过,为1时要回写*/

WORD    id;             /* 文件系统挂载ID */

WORD    n_rootdir;      /* 根目录区入口(目录项)的个数(用于FAT12/16)*/

#if _MAX_SS != 512

WORD    ssize;          /* 每扇区的字节数(用于扇区大于512Byte的flash) */

#endif

#if _FS_REENTRANT

_SYNC_t sobj;           /* 允许重入,即定义同步对象,用在  TI ny中*/

#endif

#if !_FS_READONLY

DWORD   last_clust;     /* 最后一个被分配的簇*/

DWORD   free_clust;     /* 空闲簇的个数*/

DWORD   fsi_sector;     /* 存放fsinfo的扇区(用于FAT32) */

#endif

#if _FS_RPATH

DWORD   cdir;           /* 允许相对路径时用,存储当前目录起始簇(0:root)*/

#endif

DWORD   n_fatent;       /* FAT入口数(簇的数目 + 2)*/

DWORD   fsize;          /* 每个FAT所占扇区*/

DWORD   fatbase;        /* FAT起始扇区*/

DWORD   dirbase;        /* 根目录起始扇区(FAT32:Cluster#) */

DWORD   database;       /* 数据目录起始扇区*/

DWORD   winsect;        /* 当前缓冲区中存储的扇区号*/

BYTE    win[_MAX_SS];   /* 单个扇区缓存*/

} FATFS;

/* File system object structure (FATFS) */typedef struct {BYTEfs_type;/* FAT子类型,一般在mount时用,置0表示未挂载*/BYTEdrv;/* 物理驱动号,一般为0*/BYTEcsize;/* 每个簇的扇区数目(1,2,4...128) */BYTEn_fats;/* 文件分配表的数目(1,2) *//*FAT文件系统依次为:引导扇区、两个文件分配表、根目录区和数据区*/BYTEwflag;/* 标记文件是否被改动过,为1时要回写*/BYTEfsi_flag;/* 标记文件系统信息是否被改动过,为1时要回写*/WORDid;/* 文件系统挂载ID */WORDn_rootdir;/* 根目录区入口(目录项)的个数(用于FAT12/16)*/#if _MAX_SS != 512WORDssize;/* 每扇区的字节数(用于扇区大于512Byte的flash) */#endif#if _FS_REENTRANT_SYNC_tsobj;/* 允许重入,即定义同步对象,用在  TI ny中*/#endif#if !_FS_READONLYDWORDlast_clust;/* 最后一个被分配的簇*/DWORDfree_clust;/* 空闲簇的个数*/DWORDfsi_sector;/* 存放fsinfo的扇区(用于FAT32) */#endif#if _FS_RPATHDWORDcdir;/* 允许相对路径时用,存储当前目录起始簇(0:root)*/#endifDWORDn_fatent;/* FAT入口数(簇的数目 + 2)*/DWORDfsize;/* 每个FAT所占扇区*/DWORDfatbase;/* FAT起始扇区*/DWORDdirbase;/* 根目录起始扇区(FAT32:Cluster#) */DWORDdatabase;/* 数据目录起始扇区*/DWORDwinsect;/* 当前缓冲区中存储的扇区号*/BYTEwin[_MAX_SS];/* 单个扇区缓存*/} FATFS;

然后是与之相关的文件和文件夹结构体,附上具体注释:

view plaincopy to clipboardprint?

/* File object structure (FIL) */

typedef struct {

FATFS*  fs;             /* 所在的fs指针*/

WORD    id;             /* 所在的fs挂载编号*/

BYTE    flag;           /* 文件状态*/

BYTE     pad   1;           /* 不知道含义,也未见程序使用*/

DWORD   fptr;           /* 文件读写指针*/

DWORD   fsize;          /* 大小*/

DWORD   sclust;         /* 文件起始簇(fsize=0时为0) */

DWORD   clust;          /* 当前簇*/

DWORD   dsect;          /* 当前数据扇区*/

#if !_FS_READONLY

DWORD   dir_sect;       /* 包含目录项的扇区 */

BYTE*   dir_ptr;        /* Ponter to the directory entry in the window */

#endif

#if _USE_FASTSEEK

DWORD*  cltbl;          /*指向簇链接映射表的指针*/

#endif

#if _FS_SHARE

UINT    lockid;         /* File lock ID (index of file semaphore table) */

#endif

#if !_FS_  TI NY

BYTE    buf[_MAX_SS];   /* File data read/write buffer */

#endif

} FIL;

/* File object structure (FIL) */typedef struct {FATFS*fs;/* 所在的fs指针*/WORDid;/* 所在的fs挂载编号*/BYTEflag;/* 文件状态*/BYTEpad1; /* 不知道含义,也未见程序使用*/DWORDfptr;/* 文件读写指针*/DWORDfsize;/* 大小*/DWORDsclust;/* 文件起始簇(fsize=0时为0) */DWORDclust;/* 当前簇*/DWORDdsect;/* 当前数据扇区*/#if !_FS_READONLYDWORDdir_sect;/* 包含目录项的扇区 */BYTE*dir_ptr;/* Ponter to the directory entry in the window */#endif#if _USE_FASTSEEKDWORD*cltbl;/*指向簇链接映射表的指针*/#endif#if _FS_SHAREUINTlockid;/* File lock ID (index of file semaphore table) */#endif#if !_FS_TINYBYTEbuf[_MAX_SS];/* File data read/write buffer */#endif} FIL;

下面是目录的:

view plaincopy to clipboardprint?

/* Directory object structure (DIR) */

typedef struct {

FATFS*  fs;             /* 同上*/

WORD    id;

WORD    index;          /* 当前读写索引号 */

DWORD   sclust;         /* 文件数据区开始簇*/

DWORD   clust;          /* 当前簇*/

DWORD   sect;           /* 当前扇区*/

BYTE*   dir;            /* 扇区缓存中当前SFN入口指针,SFN含义未知,猜测和LFN类似,与文件名相关*/

BYTE*   fn;             /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */

#if _USE_LFN

WCHAR*  lfn;            /* Pointer to the LFN working buffer */

WORD    lfn_idx;        /* Last matched LFN index number (0xFFFF:No LFN) */

#endif

} DIR;

/* Directory object structure (DIR) */typedef struct {FATFS*fs;/* 同上*/WORDid;WORDindex;/* 当前读写索引号 */DWORDsclust;/* 文件数据区开始簇*/DWORDclust;/* 当前簇*/DWORDsect;/* 当前扇区*/BYTE*dir;/* 扇区缓存中当前SFN入口指针,SFN含义未知,猜测和LFN类似,与文件名相关*/BYTE*fn;/* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */#if _USE_LFNWCHAR*lfn;/* Pointer to the LFN working buffer */WORDlfn_idx;/* Last matched LFN index number (0xFFFF:No LFN) */#endif} DIR;

其他类似f_mount、f_open等接口API就不细说了,在挂载的时候其实真正起作用的是chk_mounted函数,在这里才会将挂载分区的相关信息分配到FatFS结构体中;还有一个get_fat函数,也比较重要,在f_open和许多目录操作的函数中都有用到,而且FAT入口这个表达也十分晦涩,而它又调用了一个move_window的函数,也是十分晦涩难懂,可能是我英语太烂的缘故吧。实际上,move_window的作用是改变文件系统的当前工作扇区,如果要迁移到的是当前扇区,直接返回,如果不是,就将原扇区写回,若是FAT表,还要写进备份区。

熟悉了代码结构后,现在开始作修改了,首先修改ffconf.h文件配置与硬件相关的文件系统特性,然后自己添加一套底层操作即可。先看ffconf.h,里面定义了很多宏,可以根据自己需要一一配置:

先看功能配置:

_FS_TINY:文件系统为标准的还是微型的,默认为标准的(0);

_FS_READONLY:文件系统是否为只读,默认为可读写(0),若只读则f_write、f_sync、 f_unlink、f_mkdir、f_chmod、f_rename、f_truncate和f_getfree不可用;

_FS_     MI   NIMIZE:裁剪文件系统的功能,默认为全部功能(0),若为1、2则会移除大部分链接、目录等功能;

_USE_STRFUNC:是否允许     字符   串操作,默认为不允许(0),这个看个人需求,一般情况下设置为1即可,如果工作在windows下,为保证文件兼容性(如换行符’\n’和回车符’\r’)建议将此项设置为2;

_USE_MKFS:是否允许使用f_mkfs函数,默认为0,用于创建文件夹,建议开启;

_USE_FORWARD:用于允许f_forward函数,只有开启tiny文件系统时才用到,该函数用于将读写的数据立即转存到数据流中,以节省     RAM   空间;

_USE_FASTSEEK:是否开启快速索引,默认为0,开启后,会使用FIL结构体中的cltbl元素来加快搜索;

_CODE_PAGE:指定目标系统使用的OEM代码页,默认为日语(932),改为936简体中文;OEM是什么意思呢?在OS编码中,unicode是一种双字节字符编码,无论中文还是英文,或者其他语言统一到2个字节,它与现有的任何编码(ASCII,GB等)都不兼容。WindowsNT(2000)的内核即使用该编码,所有数据进入内核前转换成UNICODE,退出内核后在转换成版本相关的编码(通常称为OEM,在简体中文版下即为GB);

_USE_LEN、_MAX_LEN、_LFN_UNICODE:这三个的意思不是很清楚,但是确定是与长文件名有关的,不建议开启,否则又要多加函数,麻烦;

_FS_RPATH:是否允许相对路径,让我选择就不开启,否则逻辑变得复杂不说,代码量也变多了一些;

再看硬件相关配置:

_VOLUMES:磁盘(flash)逻辑卷数,默认为1,不建议修改;

_MAX_SS:扇区大小,默认512Byte,最大可设置4096Byte;

_MULTI_PARTITION:分区选项,默认为0,即一个分区,若想要多分区可自行设置;

_USE_ERASE:是否允许扇区擦除,默认为0,若开启则要在disk_ioctl函数中添加擦除命令代码;

最后是文件系统配置:

_WORD_ACCESS:数据递进格式,默认为0,即以字节为单位递进,兼容性更强,若你的系统最新单位为字(2Byte),则可设为1;

_FS_REENTRANT、_FS_TIMEOUT、_SYNC_t:这三个选项与文件系统是否允许重入有关,所直白点,就是能否被多线程同时访问,像     RTOS   中,一般建议开启,_SYNC_t可定义为对应OS中的操作对象,windows下为HANDLE,     uCos   中为OS_EVENT,vxWorks中为SEMAPHORE。另外,开启后还需要添加ff_req_grant、ff_rel_grant和ff_del_syncobj三个函数,实际上实现的功能就是申请互斥量、释放互斥量和删除互斥量的意思,可以定义OS封装即可;

_FS_SHARE:和上面的类似,表示文件系统最大允许同时打开多少文件,默认为0,即只能打开一个。

在配置这些选项的时候,可以根据定义阅读ff.c文件中的相关代码,基本上能对整体的结果有了了解,完成了ffconf.h后,再就是编写底层接口了,在新一点的FatFs中,并未提供函数接口模版,可以下老版的拷过来,也可以打开doc文件夹下的帮助文档00index_e.htm文件,里面有底层函数接口的格式及各个参数的描述。至于底层驱动,我只做过spi flash的,这个可以参考我上一篇文章。需要注意的是,底层读写函数中的参数sector指的是扇区的序号,需要自己换算成驱动接口中的字节位置。

到这里,移植基本完成了,如果你的文件系统出现LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))有问题(数据异常终止DATA ABORT exception之类的)的情况,请百度搜索“转一篇比较详细介绍FatFS文件系统移植的文章”就可以搞定了,那里有详细的解决办法。



4
81
0
59

相关资讯

  1. 1、《小舍得》中6个黄金配角,个个都是叫不出名字的老戏骨2136
  2. 2、专访《哥斯拉》导演:一封写给斯皮尔伯格的情书3826
  3. 3、赌王千金原是前度?TVB力捧小生自爆:ex梦中情人807
  4. 4、2021五一档混战,只有张艺谋的《悬崖之上》赢了?3797
  5. 5、和徐峥合作的三位以黄毛造型出现的明星,如今个个都走红了2429
  6. 6、《菊花台》爱情版海报曝光致敬经典电影4140
  7. 7、周星驰2019新片确定为《喜剧之王》续作,能否重现辉煌?1085
  8. 8、“国民闺女”晋升“国民女神”,关晓彤获得这个称号顺理成章啊2809
  9. 9、《司藤》导演戏瘾太足,疯狂给自己加戏!喜欢演戏的导演都有谁?1483
  10. 10、43岁林志玲的未修图照曝光,这下相信她老了2930
全部评论(0)
我也有话说
0
收藏
点赞
顶部