一、 读文件的流程
读物理扇区0,得到引导扇(逻辑扇区0)的偏移地址。
读引导扇的内容,得到文件体系根本装备信息。
依据文件体系的根本装备信息核算FAT,FDT,数据簇的开端地址和巨细。
依据要读的文件名查找FDT表,找到要读文件的开端数据簇编号,巨细。
依据文件的开端数据簇编号在FAT表中查找一切该文件占用的数据簇及数据簇拜访的先后联系。
读取该文件的开端数据簇的内容,及下一数据簇内容(有需求时)。
二、 读取物理扇区0
先读取SD卡的第一个扇区(512字节),即扇区0,然后该扇区最终部分的数据如下图所示

正常的话,该扇区最终两个字节如上图所示为55 AA,假如不是则证明是读错了,或许该SD没有被格局化。
该扇区有两个重要信息:
一、 在0x1ca开端的四个字节9f c9 03 00,即0x3c99f=248223,代表该SD卡友248223个扇区,因为每个扇区有512字节,所以该SD卡容量为248223*512/1024/1024=121.2MByte
二、在0x1c6开端的四个字节61 00 00 00,即0x61=97,它表明引导扇区在扇区97。那咱们就接着读扇区97,获取SD卡里更具体的信息,这个扇区0就能够不必管了。
三、 读取引导扇区
以下是扇区97前64Byte的内容。因为97*512=0xc200,所以能够下图左面的偏移地址为c200

首要第0x3到0xA的内容为ASCII码的“MSDOS5.0”,不是重要信息
第0xb开端的两个字节00 02,即0x2000=512,代表每个扇区(sector)有512个字节(byte)
接下来的04代表每个簇(cluster)有4个扇区
接下来的04 00 即0x4代表有4-1个保存扇区,便是第一个FAT表地点扇区为引导扇区(97)+4=扇区101
接下来的02 代表有两个FAT表
接下来的00 02,即0x2000=512,代表FDT(目录区)有512挂号项
第0x16开端的两个字节f2 00,即0xf2=242,代表每个FAT表占242个扇区
第0x36到0x3d代表的便是“FAT16 ”的ASCII码,阐明这SD卡是FAT16的格局
四、 FAT16文件体系的结构
知道以上的信息之后咱们就能够依据以上信息核算出FAT1,FAT2,FDT和数据簇的首地址和完毕地址,但在核算之前,我有必要介绍一下整个FAT16文件体系的结构和各个区的意义与效果。

五、 获取文件体系根本装备信息
现在已然现已大约了解了引导扇,FAT1,FAT2,FDT和数据簇的效果,接下来就说一下怎样核算它们的开端地址和完毕地址。咱们用扇区来作为地址单位。
咱们首要界说两个结构体
typedef struct
{
U16 BytesPerSector; //每个扇区多少字节
U8 SectorsPerCluster; //每个簇有多少个扇区
U16 ReserveSectors; //保存扇区数
U8 FatTableNums; //有多少个FAT表
U16 RootDirRegNums; //根目录答应的挂号项数目
U16 SectorsPerFat; //每个FAT表有多少个扇区
U32 SectorNums; //总的扇区数
U8 FileType[7]; //文件体系类型
}FAT_PARA;
typedef struct
{
U32 Logic; //引导扇(逻辑扇区0)对物理0扇区里的偏移地址
U32 FAT1;
U32 FAT2;
U32 FDT;
U32 Cluster; //数据簇的偏移地址
}FAT_OFFSET;
FAT_PARA SD_para; //声明两个结构体变量
FAT_OFFSET SD_offset;
由之前的常识能够知道,咱们首要从物理扇区0知道SD_offset.Logic的地址,然后就能够找到引导扇.然后再在引导扇里找到SD_para里边一切变量的值。
U8 buffer[512];
Read_Single_Block(0, buffer);//把物理扇区0的512个字节的数据读到buffer里
SD_offset.Logic = (* (U16 *) (buffer + 0x1c6)) + ((* (U16 *) (buffer + 0x1c8)) << 16); //得到引导扇的偏移地址
Read_Single_Block(SD_offset.Logic, buffer) ; //读引导扇的数据
//获取参数,以便核算各个区的偏移地址
SD_para.BytesPerSector = (* (U8 *) (buffer + 0xb)) + ((* (U8 *) (buffer + 0xc)) << 8);
SD_para.SectorsPerCluster = * (buffer + 0x0d);
SD_para.ReserveSectors = * (U16 *) (buffer + 0x0e);
SD_para.FatTableNums = * (buffer + 0x10);
SD_para.RootDirRegNums = (* (U8 *) (buffer + 0x11)) + ((* (U8 *) (buffer + 0x12)) << 8);
SD_para.SectorsPerFat = * (U16 *) (buffer + 0x16);
SD_para.SectorNums = * (U32 *) (buffer + 0x20);
for(i = 0; i < 6; i++)
SD_para.FileType[i] = *(buffer+0x36+i);
SD_para.FileType[6] = 0;
六、 核算各重要区域的巨细与开端地址
//核算各个区的偏移地址
//FAT1地址=引导扇地址+保存扇区数,巨细为SD_para.SectorsPerFat
SD_offset.FAT1 = SD_offset.Logic + SD_para.ReserveSectors;
//假如存在两个FAT表,一般不是1便是2
if (SD_para.FatTableNums == 2)
//FAT2地址=FAT1地址+ SD_para.SectorsPerFat
SD_offset.FAT2 = SD_offset.FAT1 + SD_para.SectorsPerFat;
else SD_offset.FAT2 = 0;
//FDT地址=FAT1+FAT表数*FAT表巨细
SD_offset.FDT=SD_offset.FAT1+SD_para.FatTableNums*SD_para.SectorsPerFat;
//因为数据簇2紧跟在FDT后,所以数据簇0易求得
SD_offset.Cluster = SD_offset.FDT + 32 – 2 * SD_para.SectorsPerCluster;
七、 FDT与FAT表的简略介绍
读取文件之前要先具体了解一下FDT,和FAT表的内容
一个FDT表占32个扇区,共有512个文件挂号信息,所以每个文件挂号信息的巨细为32*512/512=32Byte
每个文件挂号信息如下图所示

关于咱们来说,这个文件记载信息最重要的便是最终六个字节
最终四个字节代表文件巨细,由文件巨细能够推算出该文件占用多少个数据簇
第0x1a到0x1b个字节道标文件开端的首簇号,知道文件的首簇号咱们就能够检查FAT表的相应信息,就可得到该文件所占用的一切数据簇的簇号。
以下是FAT表的结构

上表中,06、07单元映射了磁盘3号簇区。有之前的介绍中能够知道,我这张SD卡1个簇包括4个扇区。也便是说在写数据时,只要写完了3号簇的4个扇后,将FAT表的06,07单元填写04,00;才可持续在04号簇上写数据。假如数据写完后还没有写满3号簇,则在FAT表的06,07单元填写FF,FF.
也便是说在FAT表中记载着每个数据簇的状况,且每个数据簇的状况占用两个字节。假如这两个字节等于0xffff,则代表该数据簇以被占用,且文件在该数据簇中完毕。假如这两个字节等于0x0001~0xfffe,则代表该数据簇已被占用,且该文件没有完毕,而该文件寄存的下一数据簇的簇号就等于这两个字节的巨细。
八、 读取一个文件
下面以我的SD卡为比如,向咱们介绍读写SD的FAT文件体系的文件(最好先装置一个叫做winhex的软件)。
首要我的SD卡寄存着这样一个文件

要翻开我这个名为lqz.txt的文件的,咱们先查找FDT表中关于lqz.txt这个文件的挂号信息。
因为之前现已知道了FDT的首地址是第585扇区,咱们来到585扇区,开端查找LQZ.TXT(有必要先转换成大写字母)
最终在地址为0x4a310(也便是第0x4a310/512=593扇区)的当地查找到LQZ.TXT的文件挂号信息,在最终四个字节得知该文件巨细为0x00002c89=11401Byte,占用11401/512/4=6个数据簇,从倒数第5,6个字节能够知道文件的首簇号为0x2fe4,然后在FAT表依据文件的首簇号查找接下来文件占用的五个数据簇簇号,数据簇0x2fe4在FAT的挂号方位=FAT地址+0xfe4*2=0xca00+0x2fe4*2=0x129c8,咱们来到0x129c8这个地址
数据簇0x2fe4的信息就寄存在0x129c8,和0x129c9这个字节里,从上图能够看出这两个字节等于0x46F4,也便是说LQZ.TXT寄存的下一个数据簇的簇号为0x46F4,因为该文件占用6个数据簇,所以咱们有必要持续查找剩下的4个数据簇的簇号。咱们持续查找簇号为0x46f4的数据簇在FAT表的信息,地址为0xca00+0x46f4*2=0x157e8
由上图能够下一数据簇的簇号为0x46f5。然后依照上诉办法查找剩下三个簇号分别为0x46f6,0x46f7,0x46f8,最终在0x46f8对应的当地寄存着0xffff,代表文件到此完毕。
通过上诉过程咱们知道我lqz.txt文件顺次寄存在0x2fe4,0x46f4,0x46f5,0x46f6,0x46f7,0x46f8这6个数据簇,接下来咱们就读取这6个数据簇的内容即可。
比如说:数据簇0x2fe4的地址=数据簇0地址+0x2fe4*4*512=0x4c200+0x2fe4*4*512=0x183e200,0x183e200/512=49649,也便是在物理扇区49649~49652这个四个扇区都是数据簇0x2fe4的内容。