SD卡加载程序

来源:互联网 发布:redis数据库叫什么 编辑:程序博客网 时间:2024/04/29 13:37
      隔了好长时间没有写东西了,这段时间事做了不少,也比较顺利,因为不需要写驱动而只是纯粹的写程序了。这段时间主要做了三件事:一是写了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                 // 定义一结构体用于存储分区的重要信息
        {
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;
}
/*
   查找根目录下是否存在所需文件,有则返回文件起始簇号,否则返回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.
0 0
原创粉丝点击