SD卡FAT16文件系統(tǒng)的學習筆記
一、 讀文件的流程
本文引用地址:http://www.ex-cimer.com/article/201607/294155.htm讀物理扇區(qū)0,得到引導扇(邏輯扇區(qū)0)的偏移地址。
讀引導扇的內容,得到文件系統(tǒng)基本配置信息。
根據文件系統(tǒng)的基本配置信息計算FAT,FDT,數據簇的起始地址和大小。
根據要讀的文件名搜索FDT表,找到要讀文件的起始數據簇編號,大小。
根據文件的起始數據簇編號在FAT表中查找所有該文件占用的數據簇及數據簇訪問的先后關系。
讀取該文件的起始數據簇的內容,及下一數據簇內容(有需要時)。
二、 讀取物理扇區(qū)0
先讀取SD卡的第一個扇區(qū)(512字節(jié)),即扇區(qū)0,然后該扇區(qū)最后部分的數據如下圖所示
正常的話,該扇區(qū)最后兩個字節(jié)如上圖所示為55 AA,如果不是則證明是讀錯了,或者該SD沒有被格式化。
該扇區(qū)有兩個重要信息:
一、 在0x1ca開始的四個字節(jié)9f c9 03 00,即0x3c99f=248223,代表該SD卡友248223個扇區(qū),因為每個扇區(qū)有512字節(jié),所以該SD卡容量為248223*512/1024/1024=121.2MByte
二、在0x1c6開始的四個字節(jié)61 00 00 00,即0x61=97,它表示引導扇區(qū)在扇區(qū)97。那我們就接著讀扇區(qū)97,獲取SD卡里更詳細的信息,這個扇區(qū)0就可以不用管了。
三、 讀取引導扇區(qū)
以下是扇區(qū)97前64Byte的內容。因為97*512=0xc200,所以可以下圖左邊的偏移地址為c200
首先第0x3到0xA的內容為ASCII碼的“MSDOS5.0”,不是重要信息
第0xb開始的兩個字節(jié)00 02,即0x2000=512,代表每個扇區(qū)(sector)有512個字節(jié)(byte)
接下來的04代表每個簇(cluster)有4個扇區(qū)
接下來的04 00 即0x4代表有4-1個保留扇區(qū),即是第一個FAT表所在扇區(qū)為引導扇區(qū)(97)+4=扇區(qū)101
接下來的02 代表有兩個FAT表
接下來的00 02,即0x2000=512,代表FDT(目錄區(qū))有512登記項
第0x16開始的兩個字節(jié)f2 00,即0xf2=242,代表每個FAT表占242個扇區(qū)
第0x36到0x3d代表的就是“FAT16 ”的ASCII碼,說明這SD卡是FAT16的格式
四、 FAT16文件系統(tǒng)的結構
知道以上的信息之后我們就可以根據以上信息計算出FAT1,FAT2,FDT和數據簇的首地址和結束地址,但在計算之前,我有必要介紹一下整個FAT16文件系統(tǒng)的結構和各個區(qū)的含義與作用。
五、 獲取文件系統(tǒng)基本配置信息
現在既然已經大概了解了引導扇,FAT1,FAT2,FDT和數據簇的作用,接下來就說一下怎么計算它們的起始地址和結束地址。我們用扇區(qū)來作為地址單位。
我們首先定義兩個結構體
typedef struct
{
U16 BytesPerSector; //每個扇區(qū)多少字節(jié)
U8 SectorsPerCluster; //每個簇有多少個扇區(qū)
U16 ReserveSectors; //保留扇區(qū)數
U8 FatTableNums; //有多少個FAT表
U16 RootDirRegNums; //根目錄允許的登記項數目
U16 SectorsPerFat; //每個FAT表有多少個扇區(qū)
U32 SectorNums; //總的扇區(qū)數
U8 FileType[7]; //文件系統(tǒng)類型
}FAT_PARA;
typedef struct
{
U32 Logic; //引導扇(邏輯扇區(qū)0)對物理0扇區(qū)里的偏移地址
U32 FAT1;
U32 FAT2;
U32 FDT;
U32 Cluster; //數據簇的偏移地址
}FAT_OFFSET;
FAT_PARA SD_para; //聲明兩個結構體變量
FAT_OFFSET SD_offset;
由之前的知識可以知道,我們首先從物理扇區(qū)0知道SD_offset.Logic的地址,然后就可以找到引導扇.然后再在引導扇里找到SD_para里面所有變量的值。
U8 buffer[512];
Read_Single_Block(0, buffer);//把物理扇區(qū)0的512個字節(jié)的數據讀到buffer里
SD_offset.Logic = (* (U16 *) (buffer + 0x1c6)) + ((* (U16 *) (buffer + 0x1c8)) << 16); //得到引導扇的偏移地址
Read_Single_Block(SD_offset.Logic, buffer) ; //讀引導扇的數據
//獲取參數,以便計算各個區(qū)的偏移地址
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;
六、 計算各重要區(qū)域的大小與起始地址
//計算各個區(qū)的偏移地址
//FAT1地址=引導扇地址+保留扇區(qū)數,大小為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個扇區(qū),共有512個文件登記信息,所以每個文件登記信息的大小為32*512/512=32Byte
每個文件登記信息如下圖所示
對于我們來說,這個文件記錄信息最重要的就是最后六個字節(jié)
最后四個字節(jié)代表文件大小,由文件大小可以推算出該文件占用多少個數據簇
第0x1a到0x1b個字節(jié)道標文件開始的首簇號,知道文件的首簇號我們就可以查看FAT表的相應信息,就可得到該文件所占用的所有數據簇的簇號。
以下是FAT表的結構
上表中,06、07單元映射了磁盤3號簇區(qū)。有之前的介紹中可以知道,我這張SD卡1個簇包含4個扇區(qū)。也就是說在寫數據時,只有寫完了3號簇的4個扇后,將FAT表的06,07單元填寫04,00;才可繼續(xù)在04號簇上寫數據。如果數據寫完后還沒有寫滿3號簇,則在FAT表的06,07單元填寫FF,FF.
也就是說在FAT表中記錄著每個數據簇的狀態(tài),且每個數據簇的狀態(tài)占用兩個字節(jié)。如果這兩個字節(jié)等于0xffff,則代表該數據簇以被占用,且文件在該數據簇中結束。如果這兩個字節(jié)等于0x0001~0xfffe,則代表該數據簇已被占用,且該文件沒有結束,而該文件存放的下一數據簇的簇號就等于這兩個字節(jié)的大小。
八、 讀取一個文件
下面以我的SD卡為例子,向大家介紹讀寫SD的FAT文件系統(tǒng)的文件(最好先安裝一個叫做winhex的軟件)。
首先我的SD卡存放著這樣一個文件
要打開我這個名為lqz.txt的文件的,我們先查找FDT表中關于lqz.txt這個文件的登記信息。
因為之前已經知道了FDT的首地址是第585扇區(qū),我們來到585扇區(qū),開始搜索LQZ.TXT(必須先轉換成大寫字母)
最后在地址為0x4a310(也就是第0x4a310/512=593扇區(qū))的地方搜索到LQZ.TXT的文件登記信息,在最后四個字節(jié)得知該文件大小為0x00002c89=11401Byte,占用11401/512/4=6個數據簇,從倒數第5,6個字節(jié)可以知道文件的首簇號為0x2fe4,然后在FAT表根據文件的首簇號查找接下來文件占用的五個數據簇簇號,數據簇0x2fe4在FAT的登記位置=FAT地址+0xfe4*2=0xca00+0x2fe4*2=0x129c8,我們來到0x129c8這個地址
數據簇0x2fe4的信息就存放在0x129c8,和0x129c9這個字節(jié)里,從上圖可以看出這兩個字節(jié)等于0x46F4,也就是說LQZ.TXT存放的下一個數據簇的簇號為0x46F4,由于該文件占用6個數據簇,所以我們必須繼續(xù)查找剩下的4個數據簇的簇號。我們繼續(xù)查找簇號為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,也就是在物理扇區(qū)49649~49652這個四個扇區(qū)都是數據簇0x2fe4的內容。
評論