FatFs文件系统的移植

来源:互联网 发布:淘宝美图用什么软件 编辑:程序博客网 时间:2024/05/22 12:22

因为需要,又不想自己写,所以就移植了一个文件系统。


说下我的硬件和开发工具:接成
TRUE IDE 模式下的CF卡(也就是相当于一块硬盘了),三星S3C2440的ARM9,开发工具是很老很老的D版的ADS1.2。


我在网上看到的嵌入式系统上面常用的文件系统有UCOSII公司的
UC/FS,支持CF卡,硬盘,SD/MMC卡,还有NAND FLASH等等,比较多,不过是商用的,需要银子的,有周立功的用于教学用(为什么说是用于教学用的,呵呵,等下就说)的ZLG/FS,还找到了开源、免费的两个,其中一个叫做 efsl ,另一个叫做 FatFs

    现在先不考虑版权的问题,选择一个比较合适的文件系统。第一个UC/FS文件系统没得什么说的,UCOSII那个公司开发的,稳定性,兼容性应该都不会差。第二个是ZLG/FS。周立功的很多的开发板上面都送了这个文件系统的源代码的,在网上找到一个现成的读写硬盘的,只是是基于LPC2200系列的处理器的。第三个是efsl,是一个开源的项目,免费,只需要提供读扇区和写扇区 2个函数。第四个是FatFs,跟efsl一样,也是一个开源的项目,移植的时候比efsl多几个简单的函数。


    这里补充一下CF卡和硬盘的简单的资料,CF卡有三种模式,其中有一个叫TRUE IDE,接成这个模式以后,就跟他的模式名字一样,他就是一个硬盘,对他进行读写,也就相当于对一个硬盘进行读写。当引脚OE(好像是叫OE,具体参考 CF卡文档)在上电的时候检测到拉低,那么CF卡就进入TRUE IDE模式。读写硬盘的时候,在只写一次LBA,只发送一个命令(读或者写)的情况下,最多可以读或者写256个扇区(当然也可以读一个扇区,读或者写多少个扇区在扇区计数器count里面),其中,发一个读或者写命令,读或者写256个扇区所需要的时间,比分256次去读写这些扇区所需要的时间要短得多,效率要高得多,我现在需要的是一个读写的速度比较快,效率比较高的文件系统,因此,底层的读写扇区必须要每写一个命令就可以读写多个扇区,读写扇区的函数必须要有扇区计数器(前面的count)这个参数,才可能满足要求


    UC/FS也是在网上搜了个代码,看了下,很标准的几个层,什么硬件层,文件系统层,API层,等等(具体参见UC/FS的文档),跟UCOSII一个公司的,稳定性应该不错,需要提供的函数也是读扇区,写扇区等等几个。但是底层的读写扇区的函数不需要提供扇区计数器count这个参数,也就是说,这个文件系统不能在只写一个读或者写命令的情况下,读或者写多个扇区,本来效感觉不错的一个文件系统,效率就大大的降低了。


    然后看了下efls这个文件系统,开源的项目,免费的项目,好东西,移植也很简单,同样移植的时候也是提供读写扇区等几个函数,但是面临的跟UC/FS同样的问题,每次读写的时候也只能读写一个扇区。

 

    绝望之余看到了周立功的文件系统,大概看了下(没有仔细阅读源代码),硬件驱动上面能够在发一次读命令的情况下,读写多个扇区,而且感觉上比较简单,同样,层次也很清楚,移植需要做的事情也是修改后面的读写扇区等等几个函数。于是就开干了。功夫不负苦心人,过了几天,CF卡能够读写了,拿到电脑上面看写的数据,没问题。从CF卡里面读文件出来,打印到超级终端,也没有问题,以为就万事OK了,想了下,我们需要的,最关心的,第一是速度,然后就开始测试速度,不测不知道,一测吓一跳!太“快”了,TMD,才5,6个K Bytes 每秒!!!!!(我的驱动已经测试了,上M字节每秒的)   于是跟踪到写里面去,发现一个很,十分,非常严重的问题:ZLG/FS提供了读一个字节的函数,忘了叫做啥,这里暂时叫 ReadOneByte(***),然后读多个字节,或者说读大块字节的函数用的是啥,呵呵,

for(i=0;i < N ; i++) ReadOneByte(***),这种机制,不慢才怪事!!!于是伤心的抛弃了ZLG/FS,这东东,学习还是可以的,商用的话,差太远了!!!


    我那点东西,文件系统可以不上,但是必须有个文件存储协议,或者说叫做自己的文件系统,自己写个简单的存储协议,试过,很麻烦。但是如果上文件系统,自己写的话,写要累死人的,写出来的不一定效率就高,速度就快,所以,还是在网上漫无目的的找,觉得应该有效率很高的文件系统的。


    还是那句话,功夫不负苦心人,终于让我找到了,也就是现在所用的,FatFs,开源,免费,高效!(说一下这里几个文件系统都有的一个缺点,由于微软的 FAT版权的问题,FatFs,ZLG/FS,efsl都只支持 DOS 8.3 文件名,即8个字节的文件名,一个”.“,然后3个字节的扩展名,我找到的那个UC/FS也不支持,不知道在更新的版本里面支持不,看哪天有空了,把那个 FatFs改下,让他支持,呵呵)。FatFs 的底层可以写一次命令,读写多个扇区。FatFs 的设计的读写的思想就很好,小块的数据,我就经过Buffer来存储,大块的数据,我就直接进行存取,那样速度,效率高了很多,看图:

FatFs文件系统的结构也很清晰,也是看图:

 

补充一点,FatFs的作者写了两个,一个是正宗的 FatFs,比较适合大的RAM的设备,另一个是FatFs/Tiny,比较适合小RAM的系统,比如单片机,FatFs/Tiny占用较小的RAM,代价是更慢的读写速度和更少的API函数。不过两个都支持FAT12,FAT16,FAT32文件系统。

下载下来的FatFs的FatFs有两个文件夹,一个是 doc ,FatFs的说明,包括特性,系统函数,以及可能的一些问题,另一个就是源代码文件夹src了,总共8个文件,diskio.c和diskio.h是硬件层,ff.c和 ff.h是FatFs的文件系统层和文件系统的API层,integer.h是文件系统所用到的数据类型的定义,tff.c和tff.h是Tiny的文件系统层和文件系统的API层,还有一个00readme.txt简要的介绍了FatFSHE FatFs/Tiny,包括他们所支持的API,怎么配置等等。

移植的问题,第一个是数据类型,在integer.h里面去定义好数据的类型。第二个,就是配置,打开ff.h(我用的FatFs,不是 Tiny),_MCU_ENDIAN,选择你的CPU是大端存储(big endding)还是小端存储(little endding),一般的都用的小端存储,1是小端,2是大端。这个相当重要,一会儿还要谈到这里。

其他的,按照自己的需要来配置了,说明文档够清楚了,我就不多说啥了。

第三件事情,就是写底层的驱动函数,包括:

* disk_initialize - Initialize disk drive
* disk_status - Get disk status
* disk_read - Read sector(s)
* disk_write - Write sector(s)
* disk_ioctl - Control device dependent features
* get_fattime - Get current time

所有的函数都牵涉到了选择第几个磁盘的问题,如果仅仅用一个,可以不必理会这个drv 参数。

disk_initialize ,如果不需要的话,直接返回0就行

disk_status ,这个嘛,先不管了,直接返回0就OK

disk_read - Read sector(s)
disk_write - Write sector(s)
读写扇区,注意参数哦!

disk_ioctl 需要回应CTRL_SYNC,GET_SECTOR_COUNT,GET_BLOCK_SIZE 三个命令,正确返回0即
RES_OK,不正确返回RES_ERROR。
所有的命令都从 ctrl 里面去读,返回值仅仅返回这次操作是否有效,而需要传递回去的数据在buff
里面,以下是我的:
CTRL_SYNC命令,直接返回0;
GET_SECTOR_COUNT,得到所有可用的扇区数目(逻辑寻址即LBA寻址方式)
GET_BLOCK_SIZE,得到每个扇区有多少个字节,比如 *((DWORD*)buff) = 512;
其他的命令,返回RES_PARERR

disk_ioctl 这个函数仅仅在格式化的时候被使用,在调试读写的时候,这个函数直接让他返回0就OK 了。

get_fattime - 得到系统的时间,格式请见文档。不用的话,返回0就行。

这样移植了,也基本上就成功了,但是在我的板子上面死活不行,每次一执行到几个宏定义比如
LD_WORD(ptr)        (WORD)(*(WORD*)(BYTE*)(ptr)) 就产生数据终止异常( DATA ABORT exception),但是网上的一个兄弟的(ouravr上的一个兄弟,用的SD卡,IAR编译器,平台是STM32,已经成功了,还公布了源码的,这里没有问题啊),没问题。分析下这个几个宏的意思:

LD_WORD(ptr)        (WORD)(*(WORD*)(BYTE*)(ptr)) 是在little endding里面定义的

LD_WORD(ptr) ,LD就是load,WORD在integer.h里面定义的是16位的无符号数,那这个需要完成的就是载入一个16位的数,或者说是2个字节,后面的 ptr是参数。(WORD)(*(WORD*)(BYTE*)(ptr)) ,先将这个ptr转换成一个指向BYTE类型数据的指针(BYTE *),在将这个指针转换成一个指向 16位无符号数的指针(WORD *),然后用一个 ” * “将这个数据取出来,转换成一个无符号的16位数据,这个仅仅从C语言的角度来看,实际上呢,这个完成的就是从ptr指针指向的位置,取出2个字节,作为一个16位的无符号数取出,而这2个字节是little endding,即小端模式,低字节是低8位,高字节是高8位。


既然是这样的,测试了下,定义了一个BYTE buf[512],定义一个WORD类型 zz,用一个指针pt,让pt指向buf[0],调用LD_WORD(ptr),zz=LD_WORD(pt);没问题,将pt指向 buf[1],呵呵,问题马上出来了,数据终止异常,然后测试了指针指向 buf[3],buf[5]等等奇数个,都是这样的问题,我就郁闷了啊,

TMD,编译器的问题!!!!

下面我加上的(282280072)

{

 

#define _MCU_ENDIAN   1 //下面分析的 只须把1——>2即可

1: Word access. Do not choose this unless following condition is met.

2: Byte-by-byte access. Always compatible with all platforms.

 

MCU_ENDIAN   这个名字很有歧义 表面是处理器的大端小段,其实不是 在FATFS的最新版本 007e里面已经改名字了 改为_WORD_ACCESS

 

#define _WORD_ACCESS 0 /* 0 or 1 */
/* The _WORD_ACCESS option defines which access method is used to the word
/ data on the FAT volume.
/
/   0: Byte-by-byte access. Always compatible with all platforms.
/   1: Word access. Do not choose this unless following condition is met.
/
/ When the byte order on the memory is big-endian or address miss-aligned
/ word access results incorrect behavior, the _WORD_ACCESS must be set to 0.
/ If it is not the case, the value can also be set to 1 to improve the
/ performance and code size. */


}

不过还好,找到问题了,就可以解决问题了,在 ff.h里面的宏定义里面把这即个东东给注释掉,然后在ff.c里面把这几个宏定义写成函数,这里贴一个出来:
WORD    LD_WORD(void *pt)
{
BYTE *PT = (BYTE*)pt;     //定义一个指针,将当前的指针指向的地址的值赋给PT
return (WORD)(PT[0]+PT[1]*256); //计算这个16位数,(低8位在前面,高8位在后面),并来个强制类

型转
//换,并返回
}
需要注意的是,LD_WORD返回的就必须是WORD。这样做了,编译器大部分的也可以编译通过,但是

ADS就是通不过,有3个地方,
finfo->fsize = LD_DWORD(&dir[DIR_FileSize]);    /* Size */
finfo->fdate = LD_WORD(&dir[DIR_WrtDate]);        /* Date */
finfo->ftime = LD_WORD(&dir[DIR_WrtTime]);        /* Time */
其中,dir的是这样定义的:const BYTE *dir,编译器报错是类型不匹配,因此,这里的几个LD_WORD和

LD_DWORD重写,定义成一致的类型即可:
WORD    LD_WORD_1(const BYTE *pt)
{
BYTE *PT = (BYTE*)pt;
return (WORD)(PT[0]+PT[1]*256);
}

DWORD    LD_DWORD_1(const BYTE *pt)
{
BYTE *PT = (BYTE*)pt;
return ((DWORD)PT[0]+(DWORD)(PT[1]*256)+(DWORD)(PT[2]*65536)+(DWORD)(PT[3]*16777216));


}

而后面改成:
finfo->fsize = LD_DWORD_1(&dir[DIR_FileSize]);    /* Size */
finfo->fdate = LD_WORD_1(&dir[DIR_WrtDate]);        /* Date */
finfo->ftime = LD_WORD_1(&dir[DIR_WrtTime]);        /* Time */

编译,一路OK,然后写一个文件,哇,哈哈哈哈!!!!终于出来了!!!!写文件没问题,读也没问

题!@~~~~~测试了常用的函数,都没有问题,包括格式化(f_mkfs,前提是你的disk_ioctl 没问题),

测试
了下速度,读12.5M的MP3,大约3秒,写这个12.5M的MP3大约6.5秒,勉强达到要求,再优化下驱动那边

就可以更快了!~~~~~~~

发个FatFs的官方网址 http://elm-chan.org/fsw/ff/00index_e.html

总结这次移植,差点失败就在于编译器的指针的转换问题,写出来,希望兄弟姐妹们在移植的时候不会

遇到这种问题。



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 4岁宝宝讲话结巴怎么办 网销客户不说话怎么办 两岁宝宝不说话怎么办? 4岁儿童说话结巴怎么办 3岁宝宝说话结巴怎么办 6岁儿童舌头短怎么办 5岁宝宝说话结巴怎么办 两岁宝宝说话晚怎么办 6岁儿童说话结巴怎么办 2岁宝宝呕吐拉稀怎么办 2岁宝宝突然呕吐怎么办 2岁宝宝呕吐发烧怎么办 2岁宝宝呕吐厉害怎么办 1岁宝宝半夜呕吐怎么办 2岁半宝宝呕吐怎么办 2岁宝宝半夜呕吐怎么办 两岁宝宝一直吐怎么办 原画师老了以后怎么办 孩子不想上学怎么办怎么去说服 嫉妒别人比我好怎么办 三岁宝宝爱打人怎么办 1岁宝宝喜欢打人怎么办 ps图层解锁不了怎么办 沈腾结婚马丽怎么办 延长甲没有纸托怎么办 高考第一志愿没录取怎么办 电子画颜料干了怎么办 数字画颜料干了怎么办 彩砂纸画不好了怎么办 宝宝吃了油画棒怎么办 2岁宝宝不爱刷牙怎么办 两岁宝宝不刷牙怎么办 1岁宝宝不爱刷牙怎么办 3岁宝宝不肯刷牙怎么办 20岁没学历迷茫怎么办 四岁了不长头发怎么办 17岁掉头发严重怎么办 头发很油,又少怎么办 25岁头发变稀怎么办 宝宝头发少又黄怎么办 头旋附近头发少怎么办