您的位置:首页 > 其它

FAT16文件系统学习笔记(2009/12/31)

2010-05-04 11:13 561 查看
SD+FAT16文件系统学习笔记
―――LSHICEMAN、091231

一、物理扇区0(MBR)

物理扇区0常称为引导扇区(MBR).读取出SD卡的物理扇区0,在物理扇区0的0x1C6~0x1C9这4个字节中,可以得到逻辑扇区0(主引导区)的物理扇区地址.可能是因为我不会用WinHex,无法打开物理扇区,只能选择打开逻辑扇区.所以只有从开发板上读出物理扇区0的信息,然后用UART输出.我的SD卡物理扇区0的部分内容如下:
0 1
2 3 4
5 6 7 8
9 A B
C D E F
1B0 0 0
0 0 0
0 0 0 0
0 0 0
0 0 0 2

1C0 c 0
6 3f ff b7 89 0 0
0 77 91 3a 0 0
0
1D0 0
0 0 0
0 0 0 0
0 0 0
0 0 0 0
0
1E0 0 0
0 0 0
0 0 0 0
0 0 0
0 0 0 0

1F0 0 0
0 0 0
0 0 0 0
0 0 0
0 0 55 aa

前446字节为一些引导信息,地址为
0x00~0x1BD。从0x1BE开始,每16字节就为一个分区的信息,我的SD卡只有一个分区,所以只有0X1BE~0X1CD有内容.最后两个字节0X55、0XAA为签名.从0X1C6~0X1C9可以知道逻辑扇区0的位置。我这里为89
00 00 00,即0X89,所以第137个物理扇区为逻辑扇区0.这16字节的分区信息的其它内容参考下表(表截图于jimsboy(海洋之星)的“SD卡中
FAT16 文件分析”一文中)。



二、主引导区逻辑扇区0(BPB--BIOS参数数据块)

知道了逻辑扇区0的物理扇区位置,接下来的文件系统操作都是基于这个逻辑扇区0,但SD卡的读、写扇区的操作都是从物理扇区地址计算的,比如我想读出逻辑扇区0的数据,我这里就是ReadSingelBlock(137,buffer).我的SD逻辑分区0的内容如下:



从这里,我们可以得到每扇区的字节数,每簇的扇区数,FAT表所占的扇区数,总扇区数(逻辑扇区),隐藏扇区数(逻辑扇区0前面的物理扇区数),详细内容参考下表(表截图于
jimsboy(海洋之星)的“SD卡中
FAT16 文件分析”一文中)。



对于上表,通过我的SD卡的逻辑扇区0中的数据可得出:每扇区的字节数为512,每簇的扇区数是0X40(64),既32KB,FAT表的SIZE为0XEB(235)个扇区,总逻辑扇区数为0X003A9177(3838327),隐藏扇区数为0X89(137).

三、FAT表



逻辑扇区0之后,跟着的就是两张一模一样的FAT表,从上可知FAT表的SIZE为235,所以第一张FAT表的扇区地址是1~235,第二张FAT表的扇区地址是236~470,只要加上隐藏的扇区数,就可以计算出物理扇区的地址,因为FAT文件系统的操作是基于物理扇区地址的。FAT表用两个字节表示一个簇,第0簇用第0、1字节表示,第1簇用第2、3字节表示,以此类推。这两个字节里的数值指出下一簇的序号,用查FAT表的方法,就可以知道存放着一个文件的所有簇,当然,一个文件的簇可能并不是连续的。结束簇的值是0XFFFF其它特殊值的含义如下表(表截图于jimsboy(海洋之星)的“SD卡中
FAT16 文件分析”一文中)。



结合上表,可以看出,第0、1簇已经为文件系统所用了,所以我们储存文件就是从第2簇开始的。综合上面分析过的,第2个簇的物理扇区地址=隐藏的扇区数+FAT保留的扇区数+FAT表的扇区数*2+根目录扇区数(FAT16的根目录扇区数固定为32),我的SD卡为137+1+235*2+32=640。

四、根目录
根目录里记录着在SD卡的根目录上的文件,包括文件夹,用32个字节来保存根目录下一个文件的信息。FAT16的根目录扇区数为32,因为一个文件信息占32字节,所以FAT16的根目录下的文件最大数为512个。这32个字节的内容如下表(表截图于
jimsboy(海洋之星)的“SD卡中
FAT16 文件分析”一文中)。



一般文件对照上表就可以查出相关信息,特殊的是文件夹。如果根目录下的文件信息显示这个文件是文件夹的话,进入相关簇,可以看到,这个文件夹的簇的格式和根目录也是一样的,也是用32个字节记录文件信息,如果这里面还是有文件夹,则以此类推下去。无论这个文件夹里面是否有文件,里面总会有两个32字节的文件信息。如下图是我的SD卡的SD卡的根目录下的一个文件夹,这个文件夹在第2簇,这个文件夹里面是空的,名为123。
可以看到第一个文件名为0X2E,0X2E在ASCII中表示”.”,表示这是一个目录,0X20表示空格。这个文件就表示是当前目录,在DOS中,也是用”.”表示当前目录的。可以再看一下这个文件的所在簇,就是第2簇,所以这个就表示是当前文件夹。接下来,第三、四行数据显示文件名是0X2E
0X20 0X20……,0X2E 0X2E就是“..”,这就是表示上一级目录,可以看一下这个文件的簇地址,是00,00就表示是根目录。



fat.h:
#ifndef _FAT_H_
#define _FAT_H_

/*-------------------定义返回代码----------------------------------*/
#define FAT_OK        0x00
#define AA55_ERROR    0x01
#define JMPBOOT_ERROR 0x02

/*--------------------定义数据结构体---------------------------------*/
typedef __packed struct
{
unsigned char  Activity;                //活动分区标记,如果为0x80,则为活动分区,0则不为
unsigned char  StartHead;               //分区起始磁头号
unsigned short  Start_SectorCylinder;   //起始扇区和柱面号
unsigned char   Type;                   //分区类型,0x0b=fat32,0x83=linux,0x06=fat16
unsigned char  EndHwad;                 //分区结束磁头号
unsigned short  End_SectorCylinder;     //结束扇区和柱面号
unsigned int   FirLogSec;             //分区的第一个扇区(逻辑扇区0)
unsigned int   Sectors;                 //分区的总扇区数
}DPT;

typedef __packed struct
{
unsigned char Info[446];       //引导信息
DPT  subarea0;                 //分区0信息
DPT  subarea1;                 //分区1信息
DPT  subarea2;                 //分区2信息
DPT  subarea3;                 //分区3信息
unsigned short aa55;           //签名:0xaa ,0x55
}MBR;

typedef __packed struct
{
unsigned char JmpBoot[3];        //这应该为0XEB 0X?? 0X90 或 0XE9 0X?? 0X??
//(低字节在前)
unsigned long OEMName;          //名字
unsigned short BytesPerSec;     //每扇区字节数
unsigned char SecPerClust;      //每簇扇区数
unsigned  short ResSectors;     //保留扇区数
unsigned char Fats;             //FAT表份数
unsigned short RootDirEnts;     //根目录项数(根目录存放文件数)
unsigned short ToSec16;         //总扇区数的低16位
unsigned char  Medium ;         //介质种类, 0xF8 表示固定存储介质,F0 表示移动存//储介质,还有0xF9,FA,FB,FC,FD,FE 和 FF 都是合//法的值。但它必须和 FAT表中的 FAT[0]一致。
unsigned short  FATSize;        //FAT表的扇区数
unsigned short  SecPerTrk;      //每磁道扇区数(不是硬盘,没意义)
unsigned short Heads;           // 磁头数,同上
unsigned int   HideSec;         // FAT表所在的分区前面隐藏的扇区数,等于ResSectors
unsigned int ToSec32;           //总扇区数的高32位
unsigned char DrvNum;           //一般硬盘为0x80 软盘为 0x00
unsigned char reserved;         //供NT用的,这里必须为 0
unsigned char BootSig;          // 扩展引导标记
unsigned int VolID;             //ID
unsigned char VolLab[11];       //卷标
unsigned char FilesysType[8];   //文件系统名字
unsigned char code[347];        //可执行代码, 如果是引导分区那么会有相应的数据
DPT  Subarea0;                  //分区0信息,和MBR里的分区信息一样,也可能为0
DPT   Subarea1;                 //分区1信息
DPT  Subarea2;                  //分区2信息
DPT  Subarea3;                  //分区3信息
unsigned short aa55;            //签名:0xaa ,0x55
}BPB;

/*-------------------函数声明----------------------------------*/
unsigned char FAT_Init(void);

#endif


fat.c:
#include "FAT.H"

/*----------------------定义全局变量-------------------------------*/
unsigned long Capability;           //容量
unsigned int BytesPerSec;	    //每扇区字节数
unsigned char SecPerClust;          //每簇扇区数
unsigned int FirLogSec;             //第一个逻辑扇区的物理号
unsigned int FirFATSec;             //第一个FAT表的第一个扇区地址(物理)
unsigned int FATSecs;               //一个FAT表占的扇区数
unsigned long FirDirClust = 2;      //根目录簇号(因为是FAT16,所以为2.暂时未支持FAT32)
unsigned long RootDirSec;	    //根目录所在扇区
unsigned long FirDataSec;	    //数据区的第一个扇区

/*-----------------------函数定义------------------------------*/

/****************************************************
*  函数名:  unsigned char FAT_Init(void)
*  功  能:  初始化FAT文件系统(不包括SD卡的初始化)
*  参  数:  无
*  返回值: 返回FAT_OK则初始化成功,其它的则失败
****************************************************/
unsigned char FAT_Init(void)
{
unsigned char buf[512];
MBR *MBR_Temp;
BPB *BPB_Temp;
//read_sigleblock(0,buf);         //读取物理扇区0的数据
MBR_Temp = (MBR *)buf;            //转换成指针
if(MBR_Temp->aa55!=0xaa55){        //判断签名是否正确
return AA55_ERROR;
}
FirLogSec = MBR_Temp->subarea0.FirLogSec; //得到逻辑扇区0的地址
//read_sigleblock(FirLogSec,buf);         //读取逻辑扇区0的数据
BPB_Temp = (BPB *)buf;                    //转换成指针
if( BPB_Temp->JmpBoot[0]!=0xeb || BPB_Temp->JmpBoot[2]!=0x90 || BPB_Temp->JmpBoot[0]!=0xe9 ){             //判断JMPBOOT是否正确
return JMPBOOT_ERROR;
}
if(BPB_Temp->aa55!=0xaa55){               //判断签名是否正确
return AA55_ERROR;
}
Capability = BPB_Temp->BytesPerSec * ( (BPB_Temp->ToSec32<<16) || BPB_Temp->ToSec16 );   //计算容量
BytesPerSec = BPB_Temp->BytesPerSec;      //得到每扇区字节数
SecPerClust = BPB_Temp->SecPerClust;      //得到每簇扇区数
FATSecs = BPB_Temp->FATSize;              //得到FAT表的大小(所占扇区数)
FirFATSec = FATSecs+1;                    //得到第一个FAT表的首扇区
RootDirSec = FirFATSec + (FATSecs * BPB_Temp->Fats);   //根目录所在扇区
FirDataSec = RootDirSec + (BPB_Temp->RootDirEnts*32)>>9;  //数据区的第一个扇区
return FAT_OK;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: