单片机实现对CF卡的读写
FAT是给每个文件分配磁盘物理空间的表格。FAT16簇数的上限是2 16,即65536个,每簇扇区数的上限是64个,因此其分区空间的上限为2G。FAT1位于逻辑1扇区。FAT簇映射中,0000表示空簇,FFF0~FFF6备用,FFF8~FFFF表示簇链结束,FFF7表示坏簇,其余值表示其后续簇的簇号。图3所示的文件起始簇号为2,结束簇号为4,共占用2、3、4三个簇。
簇是存储文件的最小单位,可以包含多个扇区。当文件本身或文件的最后一簇哪怕只有1个字节,也要占去1簇。这样,当这种文件很多时,空间的浪费是很可观的。
文件目录表FDT(File Directory Table)是操作系统寻找文件的入口,其内容是每一个文件的目录。FDT中的每一个目录项由32个字节组成。前8个字节是文件名,不足时用空格填满。紧跟着的3个字节是文件扩展名,接下来是10个字节的系统保留字。然后是文件产生的时刻和日期占8个字节,再后的2个字节是文件首簇号,最后4个字节是文件大小。FDT的起始扇区可由FAT的大小计算出,而FAT的大小可在DBR中读出。
javascript:window.open(this.src);" style="cursor:pointer;"/>
4 软件实现
按照FAT16方式存储文件,是一个通用的解决方案。因为这样可以得到现有的DOS和Windows系统的支持,但是代价是浪费一部分空间,也就是说存储效率下降了。为了改善这一情况,采用了改进的存储方法。就是先创建一个空文件,并根据需要为其分配一个大的存储空间,写入动作只是从尾部追加数据。这样就避免了很多小文件的产生,既可以充分利用存储空间,又可以使地址连续。
CF卡的读写是通过卡内的缓冲区进行的,不支持直接读写存储区域。缓冲区为一个FIFO结构,读写顺序进行,不支持随机存取,系统只能一次性地按顺序读完或写完所有一个或多个扇区。
设计时使用LBA方式访问CF卡比较方便,读写时只需要先在相应的寄存器写入LBA地址即可。要设定LBA方式,需访问驱动器/磁头寄存器。内存模式下部分寄存器译码如表3所列。
表3 内存模式下部分寄存器译码
REG | A10 | A9~A4 | A3~A0 | offset | OE=0 | WE=0 |
1 | 0 | X | 0000 | 0 | 偶字节读 | 侧字节写 |
1 | 0 | X | 0001 | 1 | 错误寄存器 | 特性寄存器 |
1 | 0 | X | 0010 | 2 | 扇区数 | 扇区数 |
1 | 0 | X | 0011 | 3 | 扇区号(LBA7~0) | 扇区号(LBA7~0) |
1 | 0 | X | 0100 | 4 | 低柱面号(LBA15~8) | 低柱面号(LBA15~8) |
1 | 0 | X | 0101 | 5 | 高柱面号(LBA23~16 | 高柱面号(LBA23~16) |
1 | 0 | X | 0110 | 6 | 驱动器/磁头(LBA27~24) | 驱动器/磁头(LBA27~24) |
1 | 0 | X | 0111 | 7 | 状态寄存器 | 命令寄存器 |
驱动器/磁头寄存器结构如下:
1 | LBA | 1 | DRV | HS3 | HS2 | HS1 | HS0 |
LBA—1为LBA方式,0为C/H/S(柱面/磁头/扇区)方式;DRV—选择驱动器0或驱动器1;HS3~HS0—LBA27~24,或为C/H/S方式的磁头号。
文件创建过程也就是针对FAT和FDT的读写过程。首先在FDT中申请表项,创建文件名称、属性、起始簇号、文件大小等,然后修改FAT,分配数据空间,备份FAT。文件存储就是要先从FDT和FAT中获得文件的起始簇号和簇号链,即LBA地址。然后,将此地址送给寄存器3、4、5、6(表3中的offset3、4、5、6),向扇区数寄存器填写读写数据所占的扇区个数,再向CF卡的命令寄存器写入操作的命令字,写操作30H,读操作20H。当写入命令或写入数据后要查询状态寄存器的状态,以判定CF卡是否准备就绪或写入成功。状态寄存器结构如下:
BUSY | RDY | DWF | DSC | DRQ | CORR | 0 | ERR |
各位的值为1时含义如下:
BUSY—CF卡记,此时不能接受其它命令;
RDY—卡可以接受命令;
DWF—写错误;
DSC—卡准备就绪;
DRQ—CF卡请求数据传送;
CORR—数据错误但被修正,不会终止多扇区读操作;
ERR—在上一命令以某种错误结束,可以在错误寄存器中查看错误类型。
下面以向CF卡写一个扇区数据为例,给出图4所示流程和C程序代码。
bit flag_1,flag_2;
void cfwr()
{
unsigned char status;
cfwr_comm(0xe0,0x00,0x00,0x6c);
//写参数命令,指向逻辑6c扇区
do{status=PBYTE[0x07]; //读状态寄存器
if((status & 0x01)==0x01)
flag_1=1; //若ERR=1,置出错标志,做相应处理
while(status!=0x58);
cfwr_dat(); //写入数据
do{status=PBYTE[0x07]; //读状态寄存器
if((status & 0x20)==0x20)
flag_2=1; //若DWF=1时,置出错标志,做相应处理
while(status!=0x50);
}
void cfwr_comm(unsigned char lba27,lba23,la15,lba7) //写参数命令函数
{PBYTE[0x02] 扇区数为1
PBYTE[0x03]=lba7;javascript:window.open(this.src);" style="cursor:pointer;"/>
PBYTE[0x04]=la15;
PBYTE[0x05]=lba23;
PBYTE[0x06]=lba27; //设定LBA方式
PBYTE[0x07]=0x30; //送写入命令30H
}
void cfwr_dat() //写数据函数
{unsigned int i,temp;
unsigned char xdata dat[512]; //dat[]存放一个扇区的数据
for (i=0;i<512;i++) //连续写512字节
{P1=P1 & 0xf8; //选中外部RAM
temp=dat;
P1++; //根据实际电路选择中CF卡
PBYTE[0x00]=temp;}
}
5 结论
笔者在湿度检测仪中,根据本文所介绍的方法,用CF卡向计算机转存数据,可以非常方便地对数据进行维护。