隔了好长时间没有写东西了,这段时间事做了不少,也比较顺利,因为不需要写驱动而只是纯粹的写程序了。这段时间主要做了三件事:一是写了FAT32文件系统的读程序,实现了从SD卡加载程序(BIN文件形式的程序);二是认识了一下Windows的ICO图标文件,实现了对图标文件的读取和显示;三是加载了汉字字库(GB2312 16*16点阵汉字),实现了对TXT文件的读取显示。下面先说第一件:
前面已经大概讲过了FAT32文件系统,但是没有写出程序来,这次写出来了,而且可以正确读取文件了。前面已经讲过SD卡的驱动,这里的FAT32文件系统也是基于读SD卡中的FAT32文件。 SD卡格式化后一般只有一个分区,整个卡的第一个扇区(512字节)称为MBR主引导扇区,其中包括64字节的硬盘分区表,偏移地址为: 0x1BE ~ 0x1FD,每十六个字节代表一个分区,一般SD卡只有一个区,所以一般只有前十六个字节有内容,其余为0。每条分区记录从第8个偏移字节开始的下面4个字节,代表了该分区开始处相对于SD卡开头的偏移量,单位为扇区,由这个参数可以计算出分区开始的地址。
每个分区的第一个扇区记录了该分区的信息,如保留扇区的数量(用于计算FAT表的起始位置),每扇区的字节数(一般为512字节),每簇的扇区数,FAT表的数量和大小,根目录所在簇号等重要信息。
前面日志讲过,每个簇在FAT表中都有四字节与之对应,用于文件存储下一簇的簇号,存储结构为一链式结构。如果文件存储在根目录下,在根目录的数据区里有一条32字节的记录,记录了文件的名称,存储的起始簇号等重要信息,有了起始簇号就可以读第一簇的内容,读完后再读这一簇号在FAT表中的内容,若表项中的内容显示还有下一簇存储了内容,则可以继续读下一簇,直到FAT表项中的内容显示文件到此簇结束了。
下面给出FAT32文件系统初始化的程序,供有兴趣的朋友参考(程序不是很完善,比较粗糙):
struct VOL // 定义一结构体用于存储分区的重要信息
/*
前面已经大概讲过了FAT32文件系统,但是没有写出程序来,这次写出来了,而且可以正确读取文件了。前面已经讲过SD卡的驱动,这里的FAT32文件系统也是基于读SD卡中的FAT32文件。 SD卡格式化后一般只有一个分区,整个卡的第一个扇区(512字节)称为MBR主引导扇区,其中包括64字节的硬盘分区表,偏移地址为: 0x1BE ~ 0x1FD,每十六个字节代表一个分区,一般SD卡只有一个区,所以一般只有前十六个字节有内容,其余为0。每条分区记录从第8个偏移字节开始的下面4个字节,代表了该分区开始处相对于SD卡开头的偏移量,单位为扇区,由这个参数可以计算出分区开始的地址。
每个分区的第一个扇区记录了该分区的信息,如保留扇区的数量(用于计算FAT表的起始位置),每扇区的字节数(一般为512字节),每簇的扇区数,FAT表的数量和大小,根目录所在簇号等重要信息。
前面日志讲过,每个簇在FAT表中都有四字节与之对应,用于文件存储下一簇的簇号,存储结构为一链式结构。如果文件存储在根目录下,在根目录的数据区里有一条32字节的记录,记录了文件的名称,存储的起始簇号等重要信息,有了起始簇号就可以读第一簇的内容,读完后再读这一簇号在FAT表中的内容,若表项中的内容显示还有下一簇存储了内容,则可以继续读下一簇,直到FAT表项中的内容显示文件到此簇结束了。
下面给出FAT32文件系统初始化的程序,供有兴趣的朋友参考(程序不是很完善,比较粗糙):
struct VOL // 定义一结构体用于存储分区的重要信息
{
unsigned int Relative_sectors; //分区相对于硬盘开始的偏移扇区数
unsigned int FAT_startSec; //FAT表起始扇区号
unsigned int Root_startSec; //根目录开始扇区号
unsigned int Total_sectors; //分区总扇区数
unsigned int BytsPerSec; //每扇区的字节数
unsigned int SecPerClus; //每簇的扇区数
unsigned int RsvdSecCnt; //此分区保留扇区数目,包括启动扇区
unsigned int NumFATs; //此分区FAT表数目
unsigned int Media; //介质类型
unsigned int ToSec32; //此分区总扇区数
unsigned int FATSz32; //一个FAT表所占的扇区数
unsigned int Flags; //
unsigned int RootClus; //根目录所在第一个簇的簇号,通常该数值为2
unsigned int BytePerClus; //每簇字节数
};
struct VOL v0;
void fat32_init(void)
{
unsigned int data_buf[128]; //用于缓存一个扇区内容,SD卡一扇区一扇区的读
sd_read_single(0,data_buf); //读取SD卡第0扇区内容,MBR
v0.Relative_sectors=(data_buf[114]<<16)|(data_buf[113]>>16); //读取第一分区开始扇区号
sd_read_single(v0.Relative_sectors,data_buf); //读取分区引导扇区内容,BPB
v0.BytsPerSec=0x200; //默认为512字节,这里设置只支持512字节
v0.SecPerClus=(data_buf[3]>>8)&0xff; //每簇扇区数
v0.RsvdSecCnt=(data_buf[3]>>16)&0x0000ffff; //保留扇区数
v0.NumFATs=data_buf[4]&0xff; //FAT表数量
v0.FATSz32=data_buf[9]; //FAT 表大小扇区数
v0.RootClus=data_buf[11]; //根目录所在簇号
v0.FAT_startSec=v0.Relative_sectors+v0.RsvdSecCnt; //FAT表起始地址(扇区号)
v0.BytePerClus=512*v0.SecPerClus; //每簇字节数
}
/*
读取某一簇号在FAT表中的内容,FAT表项中内容只用了28位,高四位忽略,
0x0FFFFFFF标记文件在本簇结束,0x0FFFFFF7标记为坏簇,文件没结束则返回有效簇号,
结束簇号或坏簇则返回0。
*/
unsigned int fat32_read_fat(unsigned int clus_num)
{
unsigned int data_buf[128];
sd_read_single(v0.FAT_startSec+clus_num/128,data_buf); //读簇号所在FAT表扇区内容
if((data_buf[clus_num%128]>1)&&(data_buf[clus_num%128]<0x0FFFFFF7))
return data_buf[clus_num%128];
else
return 0;
}
/*
读取根目录和数据区某一簇内容,并返回当前簇号在FAT表中对应项中的内容,
若文件不在此簇结束则返回下一存储簇号,用于读取下一簇内容,否则返回0。
注:读完一簇后,目的指针要在函数外加128*v0.SecPerClus.
*/
unsigned int fat32_read_sinclus(unsigned int clus_num,unsigned int *file_data)
{
unsigned int start_sec,file_startSec,i;
start_sec=v0.FAT_startSec+v0.NumFATs*v0.FATSz32; //此地址为数据目录区始地址,不一定就是根目录地址
file_startSec=start_sec+(clus_num-2)*v0.SecPerClus; //指定簇起始地址
for(i=0;i<v0.SecPerClus;i++)
{
sd_read_single(file_startSec,file_data);
file_startSec++;
}
i=fat32_read_fat(clus_num);
if((i>1)&&(i<0x0FFFFFF7))
return i;
else
return 0;
}
/*
加载某一根目录下的文件到内存起始地址
*/
需要从SD卡拷贝BIN文件到内存时,可在BOOTLOAD中利用SD卡,及FAT32初始化程序,再调用文件查找函数和加载函数,最后写入汇编加载PC值为0x50000000,则实现了程序的加载和启动。
/*
查找根目录下是否存在所需文件,有则返回文件起始簇号,否则返回0
文件名暂只支持8字符英文和数字,只识别前三个字符。
FAT32文件名若字母全为小写或大写,只有短文件名,若全是小写文件名项0x0C偏移内容为0x80
全为大写该值为0,有大小写则有长文件名。若含有汉字则有长文件名,短文件名内汉字用GB编码,
字母用ASCII编码,长文件名一律用uicode编码(16位)。短文件名中字符全为大写字符的ASCII码。
*/
unsigned int fat32_find_file(char *file_name)
{
unsigned int data_buf[128*v0.SecPerClus],*p,*q,i;
unsigned int next_fat=v0.RootClus;
q=(unsigned int *)file_name; //此处有错误不能将字符型直接接为整型,因为它们的对齐要求不同。只有都为4字节对齐时才正确
do
{
next_fat=fat32_read_sinclus(next_fat,data_buf);
p=data_buf;
for(i=0;i<16*v0.SecPerClus;i++)
{
if(((*q)&0x00FFFFFF)==((*p)&0x00FFFFFF))
return ((*(p+5))<<16)|((*(p+6))>>16);
else
p+=8;
}
}while(next_fat);
return 0;
}
/*
加载某一根目录下的文件到内存起始地址
*/
void load_zk(char *zk) //zk为文件名字符串指针
{
unsigned int next_fat;
unsigned int *zk_p;
zk_p=(unsigned int*)0x50000000; //如将文件拷到0x50000000起始的内存
next_fat=
fat32_find_file
(zk); //查找字库并读取第一簇内容,返回下一簇号
while(next_fat!=0)
{
next_fat=fat32_read_sinclus(next_fat,zk_p);
zk_p+=v0.BytePerClus/4;
} //加载完所有数据
}
需要从SD卡拷贝BIN文件到内存时,可在BOOTLOAD中利用SD卡,及FAT32初始化程序,再调用文件查找函数和加载函数,最后写入汇编加载PC值为0x50000000,则实现了程序的加载和启动。
注:关于FAT32文件系统简介可参考我的QQ日志,270428231.