|
![](static/image/common/ico_lz.png)
楼主 |
发表于 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口的模式是输入,输出还是高阻之类的,但是这些配置一般只出现在初始化阶段,基本上是可以忽略那点效率的 |
|