搜索
bottom↓
回复: 44

贡献我的FAT32文件系统代码以及相关资料,里面还有我的代码库,大家可以拾掇一下

[复制链接]

出0入0汤圆

发表于 2012-2-2 14:04:10 | 显示全部楼层 |阅读模式
经过一个多星期的努力,把以前写的文件系统的功能拓展了一下,也只是完成了核心的任意读写的功能

现在把代码献给大家,希望大家喜欢

介绍介绍功能:
fread和fwrite是核心的功能(个人这个认为的)另外还有fseek,可以读写任意目录下的文件(创建和删除文件的功能我还没写)的任意字节

其实网上很多代码都有这些功能,那么这个代码有哪些不同?(感觉我好像在推销产品 = = )(让菜鸟自夸一下,勿喷哈~)

1. 每个文件内部花了16个字节去维护了一个簇链,如果有一个好几M的文件在物理分布上是连续的,那么,如果你是一直顺序读这个文件,会很流畅,不需要

去读FAT表,也就是速度会快一点,(而且fread本来是基于缓存的,但是我留出了一种更高效的用法,详细内容见代码里面的注释)

2. 个人认为内存开销还是比较小的,可以很容易移植到另的平台上(废话文件系统都应该这样。。。)

3. 我认为最大的优点就是,能支持同时多文件操作,等下会演示,当然,在51单片机上这样做效率会偏低,不过总会有应用场合的,比如说,你在处理一个文件的同时,在要利用字库文件等各种辅助的系统文件。刚才说到效率偏低,因为是利用了以时间换空间的原理,我也留出了修改的接口,在比较大的RAM上想提高效率,或者你只想处理一个文件(这种情况下没什么必要改,因为处理方式本身对效率影响不大,我认为从算法分析的角度上讲,影响速度的主要是硬件驱动的问题),都可以对代码进行一些修改

也有最明显的缺点
    因为我是菜鸟级的,所以文件系统模块的代码特别长,超过2K行了。。。我先在这里说明了,所以看到代码以后就不要喷我了。。。

下面演示我测试的内容:
MUC用的是STC12C5A60S2,存储器是SD卡
因为还没有增加创建文件的功能,所以我先在电脑上新建了两个文件,hello.txt,new.txt
然后在程序里同时打开文件,写入数据,最后同时读取写入的内容:
然后这个是串口读到的信息:

DATE :Feb 02 2012 TIME :13:32:43

SD init success!

fat_fs init success!

I'm going to open file 1.

file1 open succeed.

I'm going to open file 2.

file2 open succeed.

fwrite write 13 bytes to file1
fwrite write 19 bytes to file1file2 is closed.

file open succeed.

fread read 13 bytes to file1

fread read 19 bytes to file2

now the contents of buffer :hello~ourdev~my name is Pony279~

最后把卡放到PC上看看:



为了证明这个是多文件操作,我就贴上测试的代码
void main()
{
        u16 xdata temp;

        Debug_Init();

        //初始化
        //初始化动态内存
        init_mempool(mem_pool, MEM_POOL_SIZE);
        SPI_Init();
        while(SD_Init())
        {
                DB_Delay_ms(1000);
        };

        DB_SendString("SD init success!\n");
       
        if(FileSysInit())
        {
                DB_SendString("\nfat_fs init failed!\n");
                while(1);
        }
        else
        {
                DB_SendString("\nfat_fs init success!\n");
        }

        DB_SendString("\nI'm going to open file 1.\n");
        p_file1 = fopen(name1, FILE_READ | FILE_WRITE);
        if(p_file1 == NULL)
        {
                DB_SendString("\nopen file error!\n");
                while(1);        //错误,不要进行下去了
        }
        else
        {
                DB_SendString("\nfile1 open succeed.\n");
        }

        DB_SendString("\nI'm going to open file 2.\n");
        p_file2 = fopen(name2, FILE_READ | FILE_WRITE);
        if(p_file2 == NULL)
        {
                DB_SendString("\nopen file error!\n");
                while(1);        //错误,不要进行下去了
        }
        else
        {
                DB_SendString("\nfile2 open succeed.\n");
        }

        temp = fwrite(p_file1, str1, StringLength(str1));
        DB_SendString("\nfwrite write ");
        DB_SendDec(temp);
        DB_SendString(" bytes to file1");

        temp = fwrite(p_file2, str2, StringLength(str2));
        DB_SendString("\nfwrite write ");
        DB_SendDec(temp);
        DB_SendString(" bytes to file1");

        if(fclose(p_file2) == 0)
        {
                DB_SendString("file2 is closed.\n");
                p_file2 = NULL;
        }
        else
        {
                DB_SendString("fclose error!\n");
                while(1);
        }

        p_file2 = fopen(name2, FILE_READ | FILE_WRITE);
        if(p_file2 == NULL)
        {
                DB_SendString("\nopen file error!\n");
                while(1);        //错误,不要进行下去了
        }
        else
        {
                DB_SendString("\nfile open succeed.\n");
        }

        fseek(p_file1, 0, SEEK_SET);        //重新定位到文件开始
        temp = fread(p_file1, buffer, StringLength(str1) + 5);
        DB_SendString("\nfread read ");
        DB_SendDec(temp);
        DB_SendString(" bytes to file1\n");

        temp = fread(p_file2, buffer + temp, StringLength(str2) + 5);
        DB_SendString("\nfread read ");
        DB_SendDec(temp);
        DB_SendString(" bytes to file2\n");

        DB_SendString("\nnow the contents of buffer :");
        DB_SendString(buffer);

        //不再使用文件后一定要关闭文件特别是对于写入过的文件
        fclose(p_file1);
        fclose(p_file2);

        while(1);
}



最后上传工程代码:
(呃。。。这个工程是专门用来开发各种驱动的,所以有我的现在用的代码库在里面,很多东西,所以编译的时候要参考这个帖子的四楼:
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5090488&bbs_page_no=1&search_mode=3&search_text=Pony279&bbs_id=9999

声明:文件系统部分的代码欢迎用于各种用途,但使用时请注明出处。其它模块的代码,只供学习交流,禁止用于商业(因为有些代码不是我写的。。。)
点击此处下载 ourdev_715583BLXRME.zip(文件大小:240K) (原文件名:Test-20120202.zip)
代码里面都有注释希望大家喜欢

补充内容 (2012-3-25 15:14):
文件写功能的应用,请看这里
http://www.ourdev.cn/thread-5451505-1-1.html
需要注意的时,那个帖子的代码和这里的代码有些不同,和原来的不兼容了。

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2012-2-2 14:11:08 | 显示全部楼层
我写的早期的文件系统的代码可以在这个帖子里找到
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5095479&bbs_id=9999

这里还有一个相关的帖子
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5380108&bbs_page_no=1&search_mode=3&search_text=Pony279&bbs_id=9999

现在还有一些功能不完善的,网友们有时间也可以帮我完善完善的哈~

出0入0汤圆

发表于 2012-2-2 14:30:15 | 显示全部楼层
标记   近期正准备学习文件系统

出0入0汤圆

 楼主| 发表于 2012-2-2 14:38:15 | 显示全部楼层
回复【2楼】xinbihui
-----------------------------------------------------------------------

呵呵,忘了上传我看过的一些资料了:
点击此处下载 ourdev_715594IM0UZ2.pdf(文件大小:2.44M) (原文件名:FAT32文件系统详解.pdf)
点击此处下载 ourdev_715595TOUWR3.pdf(文件大小:714K) (原文件名:FAT文件系统 (中文版).pdf)
点击此处下载 ourdev_715596TADJCJ.pdf(文件大小:1.10M) (原文件名:FAT文件系统原理.pdf)

还有WINHEX软件绿色版,直接解压使用就行了
点击此处下载 ourdev_715599HFCK3H.zip(文件大小:1.33M) (原文件名:WinHex.zip)

出0入0汤圆

发表于 2012-2-2 14:43:24 | 显示全部楼层
偷着标记一下

出0入9汤圆

发表于 2012-2-2 14:59:56 | 显示全部楼层
顶一下下

出0入0汤圆

 楼主| 发表于 2012-2-2 15:09:41 | 显示全部楼层
代码里面的很多东西都是有来历的。。。
比如说文件系统是从MP3那里开始的http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5095479&bbs_id=9999

还有一开始的Debug_Init函数 里面的一些功能和这个帖子有关
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5287022&bbs_page_no=1&search_mode=3&search_text=Pony279&bbs_id=9999

另外还有很多,都是接触到的时候就积累进来的,大家可以拾掇一下


如果觉得模块太多编译起来速度慢,可以在项目面板里对着那个组右键,options for group "..."
然后取消这个就行了

出0入0汤圆

发表于 2012-2-2 15:40:54 | 显示全部楼层
感谢楼主无私奉献!

出0入0汤圆

发表于 2012-2-2 16:24:56 | 显示全部楼层
mark

出0入0汤圆

发表于 2012-2-2 16:27:09 | 显示全部楼层
下了 谢谢

出0入0汤圆

 楼主| 发表于 2012-2-3 16:32:09 | 显示全部楼层
咦。。。貌似帖子要沉了。。。

不就少个创建文件的功能么。。。我写还不行么

打开文件的时候只要加个 FILE_CREATE 模式,在找不到文件的时候,就会自动创建,但是文件名需要符合短文件名的标准,
要想实现长文件名,自己改代码去
p_file2 = fopen(name2, FILE_READ | FILE_WRITE  | FILE_CREATE);

测试代码跟原来一样,只是这次不需要先在PC上创建文件了

点击此处下载 ourdev_715827OI0YOM.zip(文件大小:243K)

出0入0汤圆

 楼主| 发表于 2012-2-3 16:36:06 | 显示全部楼层
创建目录的功能也好实现,懒得写了

删除文件的功能也懒得写,暂时还没发现这个需求

出0入0汤圆

发表于 2012-2-3 16:41:08 | 显示全部楼层
mark@

出0入0汤圆

 楼主| 发表于 2012-2-3 17:24:46 | 显示全部楼层
简单介绍下代码的结构好了


先讲从外部看,文件系统模块的功能

文件系统本身已经算是抽象层的了,但是也需要硬件(存储器,我这里是SD卡)的支持,所以需要提供两个驱动函数,一个写扇区一个读扇区,根据你自己的硬件的不同,可以通过宏来配置,在我的头文件里是这样定义的
#define        ReadSector(sector_address, buffer) SD_ReadSingleBlock(sector_address, buffer)
#define WriteSector(sector_address, buffer) SD_WriteSingleBlock(sector_address, buffer)
在文件系统内部,只使用这两个宏,这样就实现了硬件无关性

然后介绍介绍给外部提供有哪些函数,这些函数在定义那里都有详细说明功能,我这里只写名字,相信学过C的都看得懂:
File* fopen(const u8* name_x,u8 mode);
u16 fread(File* p_file,u8* buffer,u16 size);
u16 fwrite(File* p_file,u8* buffer,u16 size);
u8 fseek(File* p_file, signed long int offset, u8 fromwhere);
u8 fclose(File* p_file);

u8 ChangeDir(u8* path, u8 len, File* p_dir);  //改变当前目录的,第三个参数保留作拓展用的,一般为空就可以了

这三个函数是扫描当前目录下有哪些文件,然后得到这些文件的文件名的,参数也是保留作拓展用的
u8 MoveToFirstFile(File* p_dir);
u8 MoveToNextFile(File* p_dir);
u8 GetShortFileNameX(File* p_dir, u8* buf);

-------------------------------------------------------------------------------------------------------------------------------------

然后简单讲下文件系统的实现

在FAT文件系统中,文件的存储是一种链表结构,这种结构记录在FAT表中,在初始化的时候通过读第0扇区可以得到一些基本的信息(如果第0扇区是MBR,那么还要去找DBR的位置)

然后遵循这种结构,边看资料边写代码,读写的功能都不难实现(想是这样想的,其实写起代码来来是比较麻烦的),

还有一点就是我在调试的时候通过串口信息发现,很多时候一个文件的存放在物理上是连续的,所以我就为每个被打开的文件加上16个字节去记录文件的分布情况,如果文件是完全连续的,那么读文件的时候就会非常流畅
(注:其实这里说的被打开只是从逻辑上看好像是这样,因为得到一个文件的信息和操作一个文件需要开辟一些内存空间嘛,在不使用这个文件的时候又要回收这些空间嘛,回收的过程就是关闭文件啦)


然后就是多文件操作的实现,我一开始以为不可能的,但是后来理了下思路后发现,是可以的。
我只有一个512 BYTE的缓冲区,但是在每次读写文件前,都可以记录下当前在使用缓冲区的文件,并记录一些文件是否有被写入的信息,然后到另一个文件要使用缓冲区时,就根据之前记录的信息(比如说上一个文件如果有写入内容,那么就需要先把去更新缓冲区的内容写到相应的扇区)

这样,从逻辑上看,每个被打开的文件都独占有一个512字节的缓冲区,文件内部只需要记录缓冲区对应的扇区地址,和文件是否被写入新数据的信息就可以了

相信大家很容易看出,我这样处理会带来效率问题,但是,多文件操作我也只能这样实现了,如果只是操作一个文件,那么中间的几句判断对总体效率的影响是很小的。
而且我也留出了让用户去修改的接口,可以改变我的多文件实现机制(就是文件创建的时候怎样分配缓存,文件操作前怎样处理缓存),但是一般不需要去改。

--------------------------------------------------------------------------------------------------------------------

再说说文件系统依赖的一些其它的模块


debug.h 调试的时候辅助用的,如果需要这个模块,那么还需要串口模块,如果不需要,在包含之前通过一个宏定义#define NODEBUG就可以解决,相应的代码就全部变成空语句了

utilities.h 一些比较简单又常用的函数,比如说StringCopy之类的,因为不想依赖没有源码的库,所以一些的函数就自己实现了

memory.c  这块代码是从KEIL的安装目录下的malloc.c free.c 和 init_mempool.c 三个文件整合起来的,代码没有改动,使用也很
          简单,只需要定义一个比较大的数组,传给init_mempool去初始化,这样你就有动态内存了。每打开一个文件,都会在动态
          内存里申请一些空间,如果你只想操作一个文件,那个比较大的数数组(叫内存池)的大小定义到60就可以了

-------------------------------------------------------------------------------------------------------------------

然后介绍整个代码库的一些东西


先说硬件无关的:
type.h  一些类型定义,没什么好说的

macro_functions.h
         这里面有一些提高代码阅读性的宏,比如说BIT(x)  SetBit() 通过使用这些宏,可以在写代码的时候和那些烦人的与或非说88了

utilities 一些简单的常用的函数,也没什么好说的

debug.h  这个不算和硬件无关,但是推荐那些经常为调试程序焦头烂额的菜鸟们看看,因为这种调试方式很好,调试文件系统的时候很多地方都是靠这种方式去定位错误的。对于没有仿真器的我来说,这种调试方式就是法宝哇~(其实也就是简单的串口信息,只是过是消除了你写太多零散的调试语句后不知道哪些该删哪些不该删的顾忌。。。一个宏定义_解决)


另外就是一些和硬件有关的了,有些和传统51单片机兼容,有些不兼容,因为我用的片子是STC12C5A60S2,定时器中断部分应该是兼容的,GPIO应该也是。

核心思想就是,我老是记不住那些寄存器,所以我就把那些烦人的东西全部用宏来封装了,比如说,我要重新设置定时器0的初值,就去头文件找到Timer0_AssignCounter,复制过来,写个参数就行了,(其实如果代码编辑器强大的话完全可以直接写关键字,从提示里就可以找到),不损失效率,又提高了阅读性,何乐而不为呢?
也有损失效率的,比如说GPIO_Config和相关宏GPIO_IO,GPIO_I等等...,是配置IO口的模式是输入,输出还是高阻之类的,但是这些配置一般只出现在初始化阶段,基本上是可以忽略那点效率的

出0入0汤圆

发表于 2012-2-3 19:07:40 | 显示全部楼层
mark

出0入17汤圆

发表于 2012-2-3 20:30:52 | 显示全部楼层
支持楼主一下~~~~~~

出0入0汤圆

发表于 2012-2-5 00:15:43 | 显示全部楼层
好贴子,顶

出0入0汤圆

发表于 2012-2-5 02:08:44 | 显示全部楼层
标记

出0入0汤圆

发表于 2012-2-5 06:30:36 | 显示全部楼层
不错

出0入0汤圆

发表于 2012-2-5 09:36:55 | 显示全部楼层
强烈mark

出0入0汤圆

发表于 2012-2-5 12:11:15 | 显示全部楼层
哇,自己写的文件系统啊

出0入0汤圆

发表于 2012-2-5 17:13:06 | 显示全部楼层
下了 谢谢
你的意思就是說連續打開 n 個檔案都使用512byte。
希望有完整的 FAT32 建檔及刪檔。

出0入0汤圆

 楼主| 发表于 2012-2-5 17:49:47 | 显示全部楼层
回复【21楼】nono2000
-----------------------------------------------------------------------

“你的意思就是說連續打開 n 個檔案都使用512byte。”

嗯,在模块内部是这样子的,在模块外部是想做到PC上的C语言可以怎样,在51上也可以

不过比较花时间,我的水平也比较有限

“希望有完整的 FAT32 建檔及刪檔。 ”

建档现在是可以的(在10楼),删档的功能还没做,这段时间在学习STM32,打算移植到STM32上再继续发展,毕竟在51上用文件系统的

需求也不多。网上好像有外国人写的FATFS,功能挺全的,打算研究研究,取长补短

出0入0汤圆

发表于 2012-2-5 18:15:38 | 显示全部楼层
回复【22楼】Pony279  霍斯
....................................
这段时间在学习stm32,打算移植到stm32上再继续发展,毕竟在51上用文件系统的
需求也不多。网上好像有外国人写的fatfs,功能挺全的,打算研究研究,取长补短
-----------------------------------------------------------------------

stm32 使用sdcard.c很多bug,而且多次版本都沒改善 。
雖然 4bit DMA 速度快,但是穩定性差,連續開關檔 100次,可能有1次失敗,錯誤的原因都是STM提供的LIB。
希望有非stm官方的SD_Card.c,可能商品化問題多。

出0入0汤圆

发表于 2012-2-9 20:13:28 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2012-3-5 23:23:50 | 显示全部楼层
文件系统在测试的时候发现了小BUG,
已经修复,最新完整的测试代码可以看这个帖子:
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=5451505&bbs_page_no=1&bbs_id=1006

因为发现以前的代码有些地方不合理,所以对其它一些地方都做了一些改动,
所以代码模块可能不能和以前我发上来的代码兼容了
那个帖子只是测试VS1003录音功能的代码,以前有的一些模块有些在那里就没了。。。
不好意思。。。我也没时间一下子把全部代码都改一遍。。。

出0入30汤圆

发表于 2012-3-6 10:00:32 | 显示全部楼层
MARK.

出0入296汤圆

发表于 2012-3-6 10:13:18 | 显示全部楼层
这样的分享行为,非常赞!顶一下!

出0入0汤圆

 楼主| 发表于 2012-3-6 12:27:19 | 显示全部楼层
回复【27楼】Gorgon_Meducer 傻孩子
-----------------------------------------------------------------------

谢谢支持!

出0入0汤圆

发表于 2012-3-26 18:35:12 | 显示全部楼层
太谢谢楼主了,辛苦了

出0入0汤圆

发表于 2012-4-16 23:05:18 | 显示全部楼层
请问楼主,你的系统能不能再根目录下新建文件夹啊,我找了很多资料,新建文件基本上实现了,但是新建文件夹后,子文件夹的扇区中前64个字节是关于父目录和该目录本身的信息,有没有这方面的资料?

出0入0汤圆

 楼主| 发表于 2012-4-16 23:34:57 | 显示全部楼层
本帖最后由 Pony279 于 2012-4-16 23:40 编辑
xuancqu 发表于 2012-4-16 23:05
请问楼主,你的系统能不能再根目录下新建文件夹啊,我找了很多资料,新建文件基本上实现了,但是新建文件夹 ...


没有做这个功能,不过不难实现的,参考FAT文件系统原理的资料,基本上都是有介绍这个的,其实和新建文件差不了多少的,
前64个字节本质上也就是前2个目录项,软件里调整一下就行,多实验几次再用winhex对比就知道了。

建文件功能都实现了你还问这个问题。。。让我有点诧异

出0入0汤圆

发表于 2012-4-17 11:07:03 | 显示全部楼层
Pony279 发表于 2012-4-16 23:34
没有做这个功能,不过不难实现的,参考FAT文件系统原理的资料,基本上都是有介绍这个的,其实和新建文件 ...

我对比了一下,还是没有搞清楚子目录里前64字节是怎么定义的...而且确实资料不好找

出0入0汤圆

发表于 2012-4-25 22:54:10 | 显示全部楼层
mark.

出0入0汤圆

发表于 2012-4-26 00:24:49 | 显示全部楼层
不错啊,赞一个

出0入0汤圆

发表于 2012-4-26 05:50:17 来自手机 | 显示全部楼层
标记,学习了

出0入0汤圆

发表于 2012-5-1 16:57:28 | 显示全部楼层
你好,我想用60s2实现播放wav,用它自带的PWM播放!不知道要怎么改?

出0入0汤圆

发表于 2012-5-1 17:17:50 | 显示全部楼层
标记,再看

出0入0汤圆

发表于 2013-10-19 21:33:05 | 显示全部楼层
顶  牛逼  还有一个问题 就是哪些老帖子看不见了

出0入0汤圆

发表于 2013-10-20 00:27:58 来自手机 | 显示全部楼层
必须顶一个啊

出0入0汤圆

发表于 2013-10-20 07:43:52 来自手机 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2013-10-21 19:53:00 | 显示全部楼层
Pony279 发表于 2012-2-2 14:38
回复【2楼】xinbihui
-----------------------------------------------------------------------

非常好的资料

出0入0汤圆

发表于 2013-10-22 08:14:22 | 显示全部楼层
这是你自己的fs么

最近正好在看ZNFAT   

出0入0汤圆

发表于 2013-10-22 08:47:06 | 显示全部楼层
学习,谢谢!!!

出0入0汤圆

发表于 2013-10-22 09:07:26 | 显示全部楼层
感谢LZ分享,之前看过一下ZNFAT,现在又搞别的东西了
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 16:16

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表