日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FAT16图文详解

發布時間:2024/3/26 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FAT16图文详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

注:FAT16驅動代碼不是本人編寫的,是從網上下載的,本人只是對該代碼進行研讀學習,并做下筆記。該FAT16驅動應該是比較老的了,猜測應該在DOS時代比較流行,但放在今天,對于剛剛進階FAT16的小伙伴來說,還是很適合初學者學習的好資料!筆者也相信,只要小伙伴們靜下心來,慢慢讀懂該代碼,相信很快就能在腦海中形成一張FAT16的總覽圖了。
??????? 筆者對代碼進行了簡單測試,在STM32平臺上對2G SD卡進行了讀寫TXT操作,沒有問題。當然,這個代碼功能還是很簡單的,只有創建、讀、寫文件3個操作,而且寫操作不能修改文件的大小,即沒有追加功能,文件的大小是由CreateFile一開始創建好了的。

?

以下為源碼部分:

[cpp] view plaincopyprint?
  • //**************************************************************************************************** ??
  • //文件名:FAT16.c ??
  • //來源:網絡 ??
  • //注釋:hexiaolong2009(http://blog.csdn.net/hexiaolong2009) ??
  • //**************************************************************************************************** ??
  • #include?"stm32f10x.h" ??
  • #include?"fat16.h" ??
  • #include?"sd.h" ??
  • ??
  • //**************************************************************************************************** ??
  • //全局變量定義 ??
  • u16?BytesPerSector;??
  • u16?ResvdSectors;??
  • u16?RootDirCnt;??
  • u16?SectorsPerFAT;??
  • u16?DirStartSector;??
  • u16?DataStartSector;??
  • u16?DBRStartSector;??
  • u8?SectorsPerClus;??
  • u8?FATCount;??
  • u8?SectorBuf[512];??
  • ??
  • //讀取一個邏輯扇區 ??
  • static?u8?ReadBlock(u16?LBA)??
  • {??
  • ????return?SD_ReadSector(SectorBuf,?LBA?+?DBRStartSector,?1);??
  • }??
  • ??
  • //寫入一個邏輯扇區 ??
  • static?u8?WriteBlock(u16?LBA)??
  • {??
  • ????return?SD_WriteSector(SectorBuf,?LBA?+?DBRStartSector,?1);??
  • }??
  • ??
  • //將文件名格式化成標準的DOS?8.3格式的文件名 ??
  • static?void?NameFormat(const?char*?SrcName,?char*?DstName)??
  • {??
  • ????u8?i,?j;??
  • ??
  • ????//首先用空格初始化目標緩沖區 ??
  • ????for(i?=?0;?i?<?11;?i++)??
  • ????????*(DstName?+?i)?=?0x20;??
  • ??
  • ????//其次拷貝文件名 ??
  • ????for(i?=?0,?j?=?0;?i?<?8;?i++,?j++)??
  • ????{??
  • ????????if((*SrcName)?==?'.')???????
  • ????????{??
  • ????????????SrcName++;??
  • ????????????break;??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????*(DstName?+?j)?=?*SrcName++;??
  • ????????}??
  • ????}??
  • ??
  • ????//最后拷貝擴展名 ??
  • ????for(i?=?0,?j?=?8;?i?<?3;?i++,?j++)??
  • ????{??
  • ????????if((*SrcName)?==?0)???break;??
  • ????????else??????
  • ????????{??
  • ????????????*(DstName?+?j)?=?*SrcName++;??
  • ????????}??
  • ????}??
  • }??
  • ??
  • //比較兩個緩沖區的前size個字節是否完全相同 ??
  • static?u8?IsEqual(void*?Src1,?void*?Src2,?u32?size)??
  • {??
  • ????u8?*p1,?*p2;??
  • ??
  • ????p1?=?Src1;??
  • ????p2?=?Src2;??
  • ????for(;?size--;?)??
  • ????{??
  • ????????if((*p1++)?!=?(*p2++))??
  • ????????????return?0;??
  • ??
  • ????}??
  • ????return?1;??
  • }??
  • ??
  • //將簇號轉換為邏輯扇區號 ??
  • static?u16?Clus2Sector(u16?clus)??
  • {??
  • ????return?(DataStartSector?+?((clus?-?2)?*?SectorsPerClus));??
  • }??
  • ??
  • //讀取主引導記錄MBR ??
  • static?u8?ReadMBR(void)??
  • {??
  • ????tMBR?*pmbr?=?(tMBR?*)SectorBuf;??
  • ??
  • ????//因為此時的DBRStartSector還未被賦值,等于0,所以這里讀取的是物理扇區0 ??
  • ????if(0?==?ReadBlock(0))???return?0;??
  • ????if(0xAA55?!=?pmbr->Flag)?return?0;??
  • ????//通過磁盤分區表DPT字段來獲取系統引導扇區DBR的扇區偏移量 ??
  • ????DBRStartSector?=?(pmbr->DPT[0].LBAoffest[1]?<<?16)?+?pmbr->DPT[0].LBAoffest[0];???
  • ??
  • ????return?1;??
  • }??
  • ??
  • //讀取系統引導扇區DBR ??
  • static?u8?ReadDBR(void)??
  • {??
  • ????tDBR?*pdbr?=?(tDBR*)SectorBuf;??
  • ??
  • ????if(0?==?ReadBlock(0))?return?0;??
  • ????if(0xAA55?!=?pdbr->Flag)?return?0;??
  • ??
  • ????//通過系統引導扇區中的BPB字段,計算磁盤的相關參數 ??
  • ????BytesPerSector?=?(pdbr->BPB.BytesPerSector[1]?<<?8)?+?pdbr->BPB.BytesPerSector[0];??
  • ????SectorsPerClus?=?pdbr->BPB.SectorsPerClus;??
  • ????ResvdSectors?=?(pdbr->BPB.ResvdSectors[1]?<<?8)?+?pdbr->BPB.ResvdSectors[0];??
  • ????FATCount?=?pdbr->BPB.FATCount;??
  • ????RootDirCnt?=?(pdbr->BPB.DirCount[1]?<<?8)?+?pdbr->BPB.DirCount[0];??
  • ????SectorsPerFAT?=?(pdbr->BPB.SectorsPerFAT[1]?<<?8)?+?pdbr->BPB.SectorsPerFAT[0];??
  • ????DirStartSector?=?ResvdSectors?+?SectorsPerFAT?*?FATCount;??
  • ????DataStartSector?=?DirStartSector?+?32;??
  • ??
  • ????return?1;??
  • }??
  • ??
  • //讀取FAT表項的值 ??
  • static?u16?ReadFAT(u16?Index)??
  • {??
  • ????u16?*pItem?=?(u16*)&SectorBuf[0];??
  • ??
  • ????//因為1扇區?=?256個FAT表項,所以Index?>>?8表示從FAT開始的扇區偏移 ??
  • ????if(0?==?ReadBlock((Index?>>?8)?+?ResvdSectors))?return?0;??
  • ????//Index?%?256?表示扇區內的字偏移 ??
  • ????return?*(pItem?+?(Index?%?256));??
  • }??
  • ??
  • //寫入某一FAT表項的值 ??
  • static?u16?WriteFAT(u16?Index,?u16?val)??
  • {??
  • ????u16?*pItem?=?(u16*)&SectorBuf[0];??
  • ????//計算Index所在的邏輯扇區號 ??
  • ????u16?sector?=?(Index?>>?8)?+?ResvdSectors;??
  • ??
  • ????if(0?==?ReadBlock(sector))?return?0;??
  • ????//Index?%?256?表示扇區內的字偏移 ??
  • ????*(pItem?+?(Index?%?256))?=?val;??
  • ????if(0?==?WriteBlock(sector))?return?0;??
  • ????return?1;??
  • }??
  • ??
  • //將FAT1的某一扇區拷貝到FAT2所對應的扇區 ??
  • //sector表示從FAT1開始的扇區偏移 ??
  • static?u8?CopyFAT(u16?sector)??
  • {??
  • ????if(!ReadBlock(ResvdSectors?+?sector))?return?0;??
  • ????if(!WriteBlock(ResvdSectors?+?SectorsPerFAT?+?sector))?return?0;??
  • ????return?1;??
  • }??
  • ??
  • //FAT16初始化 ??
  • u8?FAT_Init(void)??
  • {??
  • ????//先讀取MBR,找到系統引導扇區的位置 ??
  • ????if(0?==?ReadMBR())?return?0;??
  • ????//再讀取系統引導扇區中的BPB,獲取磁盤的相關參數 ??
  • ????if(0?==?ReadDBR())?return?0;??
  • ??
  • ????return?1;?????
  • }??
  • ??
  • //查找根目錄下是否存在name所對應的文件,如果存在則將該文件信息存放到dir所指向的結構體中 ??
  • u8?GetFileDir(const?char*?name,?tDIR?*dir)??
  • {??
  • ????u8?i,?j;??
  • ????tDIR?*pDir;??
  • ????char?DOSname[11];??
  • ??
  • ????//第一步要將name格式化成標準8.3格式的文件名 ??
  • ????NameFormat(name,?DOSname);??
  • ??
  • ????//因為根目錄區總共占32個扇區 ??
  • ????for(j?=?0;?j?<?32;?j++)??
  • ????{??
  • ????????if(0?==?ReadBlock(DirStartSector?+?j))?return?0;??
  • ????????//而每個扇區又包含16個目錄項 ??
  • ????????for(i?=?0;?i?<?16;?i++)??
  • ????????{??
  • ????????????//每個目錄項又占32個字節,所以這里用i?<<?5表示目錄項在一個扇區中的字節偏移 ??
  • ????????????pDir?=?(tDIR?*)&SectorBuf[i?<<?5];??
  • ????????????//通過文件名來查找文件 ??
  • ????????????if(IsEqual(DOSname,?pDir->Name,?11))??
  • ????????????{??
  • ????????????????*dir?=?*pDir;??
  • ????????????????return?1;?????
  • ????????????}??
  • ????????}??
  • ????}??
  • ????return?0;??
  • }??
  • ??
  • //將文件信息寫入Index所指定的目錄項中 ??
  • static?u8?WriteDir(u16?Index,?tDIR?*dir)??
  • {??
  • ????tDIR?*pDir;??
  • ????//計算Index所在的邏輯扇區偏移,Index?/?16表示從目錄區開始的扇區偏移量 ??
  • ????u16?sector?=?Index?/?16?+?DirStartSector;??
  • ??
  • ????if(!ReadBlock(sector))?return?0;??
  • ????pDir?=?(tDIR*)&SectorBuf[0];??
  • ????//Index?%?16表示1個扇區內的目錄項偏移 ??
  • ????*(pDir?+?(Index?%?16))?=?*dir;??
  • ????if(!WriteBlock(sector))?return?0;??
  • ??
  • ????return?1;??
  • }??
  • ??
  • //從根目錄區中獲取一個空的目錄項 ??
  • static?u16?GetEmptyDir(void)??
  • {??
  • ????u8?j,?i;??
  • ????u16?index?=?0;??
  • ??
  • ????//因為根目錄區總共占32個扇區 ??
  • ????for(i?=?0;?i?<?32;?i++)??
  • ????{??
  • ????????if(!ReadBlock(DirStartSector?+?i))?return?0xffff;??
  • ????????//而每個扇區又包含16個目錄項 ??
  • ????????for(j?=?0;?j?<?16;?j++)??
  • ????????{??
  • ????????????//每個目錄項又占32個字節,所以這里用j?*?32表示目錄項在一個扇區中的字節偏移 ??
  • ????????????if(0?==?SectorBuf[j?*?32])??
  • ????????????????return?index;??
  • ????????????index++;??????
  • ????????}??
  • ????}??
  • ??
  • ????return?0xffff;??
  • }??
  • ??
  • //獲取一個空的FAT表項,即一個空簇的簇號 ??
  • static?u16?GetEmptyFAT(void)??
  • {??
  • ????u16?i,?j;??
  • ????u16?*pItem;??
  • ??
  • ????//遍歷FAT表所占的每個扇區 ??
  • ????for(i?=?0;?i?<?SectorsPerFAT;?i++)??
  • ????{??
  • ????????if(0?==?ReadBlock(i?+?ResvdSectors))?return?0;??
  • ????????pItem?=?(u16*)&SectorBuf[0];??
  • ????????//遍歷扇區內的每個FAT表項 ??
  • ????????for(j?=?0;?j?<?256;?j++)??
  • ????????{??
  • ????????????if(*(pItem?+?j)?==?0)?return?((i?<<?8)?+?j);??
  • ????????}??
  • ????}??
  • ????return?0;??
  • }??
  • ??
  • //新建一個文件 ??
  • //注意:文件的大小已固定為size字節,即使新建的文件沒有寫任何內容,文件的大小始終為size大小; ??
  • //??????即使對該文件寫入了超過size大小的內容,文件的大小依然不改變 ??
  • //該函數有待進一步優化,對于大文件的創建,該函數速度非常緩慢 ??
  • u8?CreateFile(const?char*?name,?u32?size)??
  • {??
  • ????tDIR?dir?=?{0};?//一定要初始化為0,否則在WINDOWS系統下無法識別文件 ??
  • ????u16?ClusID;??
  • ????u16?i;??
  • ????u16?FATSector;??
  • ????//計算一簇所占的字節數 ??
  • ????u32?BytesPerClus?=?BytesPerSector?*?SectorsPerClus;??
  • ??
  • ????//文件已存在,則返回 ??
  • ????if(GetFileDir(name,?&dir))?return?0;??
  • ??
  • ????//首先從根目錄區獲取一個空的目錄項 ??
  • ????i?=?GetEmptyDir();??
  • ????if(i?==?0xffff)?return?0;??
  • ??
  • ????//從FAT表中獲取一個空的FAT表項 ??
  • ????ClusID?=?GetEmptyFAT();??
  • ????//立即將該空的FAT表項填充為0xFFFF,以免后面再次獲取空的FAT表項時錯誤的分配到同一表項 ??
  • ????if(0?==?WriteFAT(ClusID,?0xFFFF))?return?0;??
  • ??
  • ????//然后給該目錄項填充文件信息 ??
  • ????NameFormat(name,?dir.Name);??
  • ????dir.Attri?=?0;??
  • ????dir.FirstClus?=?ClusID;??
  • ????dir.Length[0]?=?size;??
  • ????dir.Length[1]?=?size?>>?16;??
  • ????//將目錄信息回寫到目錄表中 ??
  • ????if(0?==?WriteDir(i,?&dir))?return?0;??
  • ??
  • ????//計算分配到的FAT表項在FAT表中的扇區偏移 ??
  • ????FATSector?=?ClusID?/?256;??
  • ????for(/*文件所占簇個數*/i?=?size?/?BytesPerClus;?i?!=?0;?i--)??
  • ????{??
  • ????????u16?NextClus;??
  • ??
  • ????????//獲取下一個空簇的簇號 ??
  • ????????NextClus?=?GetEmptyFAT();??
  • ????????if(!NextClus)?return?0;??
  • ????????//此部分有待優化 ??
  • ????????if(0?==?WriteFAT(ClusID,?NextClus))?return?0;??
  • ????????if(0?==?WriteFAT(NextClus,?0xFFFF))?return?0;??
  • ????????//當下一FAT表項所在位置不在當前FAT扇區時,立即將當前FAT1扇區的內容拷貝到對應的FAT2中 ??
  • ????????if(FATSector?!=?(NextClus?/?256))??
  • ????????{??
  • ????????????CopyFAT(FATSector);??
  • ????????????//并將下一FAT表項所在扇區偏移賦值給FATSector ??
  • ????????????FATSector?=?NextClus?/?256;??
  • ????????}??
  • ????????ClusID?=?NextClus;??
  • ????}??
  • ????//將最后一扇區拷貝到FAT2中的相應位置 ??
  • ????CopyFAT(FATSector);??
  • ??
  • ????return?1;??
  • }?????????????????
  • ??
  • //讀取文件 ??
  • u8?ReadFile(const?char*?name,?u32?offest,?void*?dst,?u32?len)??
  • {??
  • ????tDIR?dir;??
  • ????u16?ClusID;??
  • ????u16?StartSector;??
  • ????u32?FileSize;??
  • ????u32?PtrByteOffest,?PtrSectorOffest,?PtrClusOffest;??
  • ????u16?i;??
  • ????u8?*pDst?=?(u8*)dst;??
  • ??
  • ????//首先找到文件對應的目錄項 ??
  • ????if(!GetFileDir(name,?&dir))?return?0;??
  • ??
  • ????FileSize?=?(dir.Length[1]?<<?16)?+?dir.Length[0];??
  • ????//文件指針超出文件尾則返回 ??
  • ????if(offest?>?FileSize)?return?0;??
  • ????//len大于文件長度的情況 ??
  • ????if((offest?+?len)?>?FileSize)??
  • ????????len?=?FileSize?-?offest;??
  • ??
  • ????ClusID?=?dir.FirstClus;??
  • ????//文件指針相對于文件頭的扇區偏移 ??
  • ????PtrSectorOffest?=?offest?/?BytesPerSector;??
  • ????//文件指針相對于當前扇區的字節偏移 ??
  • ????PtrByteOffest?=?offest?%?BytesPerSector;??
  • ????//文件指針相對于文件頭的簇偏移 ??
  • ????PtrClusOffest?=?PtrSectorOffest?/?SectorsPerClus;??
  • ??
  • ????//找到文件指針所在簇號 ??
  • ????for(i?=?0;?i?<?PtrClusOffest;?i++)??
  • ????????ClusID?=?ReadFAT(ClusID);??
  • ????//文件指針相對于系統分區的扇區偏移 ??
  • ????StartSector?=?Clus2Sector(ClusID)?+?PtrSectorOffest;??
  • ??
  • ????while(1)??
  • ????{??
  • ????????//2.從指針所在的扇區開始,遍歷文件的每個扇區 ??
  • ????????for(;?PtrSectorOffest?<?SectorsPerClus;?PtrSectorOffest++)??
  • ????????{??
  • ????????????if(!ReadBlock(StartSector++))?return?0;??
  • ??
  • ????????????//1.從指針所在扇區的字節偏移開始,遍歷扇區的每個字節 ??
  • ????????????for(;?PtrByteOffest?<?BytesPerSector;?PtrByteOffest++)??
  • ????????????{??
  • ????????????????*pDst++?=?SectorBuf[PtrByteOffest];??
  • ????????????????len--;??
  • ????????????????if(0?==?len)?return?1;??
  • ????????????}??
  • ????????????PtrByteOffest?=?0;??
  • ????????}??
  • ????????//讀取下一簇號 ??
  • ????????ClusID?=?ReadFAT(ClusID);??
  • ????????//獲取下一簇所在的扇區號 ??
  • ????????StartSector?=?Clus2Sector(ClusID);??
  • ????????PtrSectorOffest?=?0;??
  • ????}??
  • }??
  • ??
  • //寫文件 ??
  • u8?WriteFile(const?char*?name,?u32?offest,?void*?src,?u32?len)??
  • {??
  • ????tDIR?dir;??
  • ????u16?ClusID;??
  • ????u16?StartSector;??
  • ????u32?FileSize;??
  • ????u32?PtrByteOffest,?PtrSectorOffest,?PtrClusOffest;??
  • ????u16?i;??
  • ????u8?*pSrc?=?(u8*)src;??
  • ??
  • ????if(!GetFileDir(name,?&dir))?return?0;??
  • ??
  • ????FileSize?=?(dir.Length[1]?<<?16)?+?dir.Length[0];??
  • ????//文件指針超出文件尾則返回 ??
  • ????if(offest?>?FileSize)?return?0;??
  • ????//len大于文件長度的情況 ??
  • ????if((offest?+?len)?>?FileSize)??
  • ????????len?=?FileSize?-?offest;??
  • ??
  • ????ClusID?=?dir.FirstClus;??
  • ????//文件指針相對于文件頭的扇區偏移 ??
  • ????PtrSectorOffest?=?offest?/?BytesPerSector;??
  • ????//文件指針相對于當前扇區的字節偏移 ??
  • ????PtrByteOffest?=?offest?%?BytesPerSector;??
  • ????//文件指針相對于文件頭的簇偏移 ??
  • ????PtrClusOffest?=?PtrSectorOffest?/?SectorsPerClus;??
  • ??
  • ????//找到文件指針所在簇號 ??
  • ????for(i?=?0;?i?<?PtrClusOffest;?i++)??
  • ????????ClusID?=?ReadFAT(ClusID);??
  • ????//文件指針相對于系統分區的扇區偏移 ??
  • ????StartSector?=?Clus2Sector(ClusID)?+?PtrSectorOffest;??
  • ??
  • ????while(1)??
  • ????{??
  • ????????//2.從指針所在的扇區開始,遍歷文件的每個扇區 ??
  • ????????for(;?PtrSectorOffest?<?SectorsPerClus;?PtrSectorOffest++)??
  • ????????{??
  • ????????????if(!ReadBlock(StartSector))?return?0;??
  • ??
  • ????????????//1.從指針所在扇區的字節偏移開始,遍歷扇區的每個字節 ??
  • ????????????for(;?PtrByteOffest?<?BytesPerSector;?PtrByteOffest++)??
  • ????????????{??
  • ????????????????SectorBuf[PtrByteOffest]?=?*pSrc++;??
  • ????????????????len--;??
  • ????????????????if(0?==?len)??
  • ????????????????{??
  • ????????????????????if(!WriteBlock(StartSector))?return?0;??
  • ????????????????????else?return?1;??
  • ????????????????}??
  • ????????????}??
  • ????????????if(!WriteBlock(StartSector++))?return?0;??
  • ????????????PtrByteOffest?=?0;??
  • ????????}??
  • ????????//讀取下一簇號 ??
  • ????????ClusID?=?ReadFAT(ClusID);??
  • ????????//獲取下一簇所在的扇區號 ??
  • ????????StartSector?=?Clus2Sector(ClusID);??
  • ????????PtrSectorOffest?=?0;??
  • ????}??
  • }??
  • //**************************************************************************************************** //文件名:FAT16.c //來源:網絡 //注釋:hexiaolong2009(http://blog.csdn.net/hexiaolong2009) //**************************************************************************************************** #include "stm32f10x.h" #include "fat16.h" #include "sd.h"//**************************************************************************************************** //全局變量定義 u16 BytesPerSector; u16 ResvdSectors; u16 RootDirCnt; u16 SectorsPerFAT; u16 DirStartSector; u16 DataStartSector; u16 DBRStartSector; u8 SectorsPerClus; u8 FATCount; u8 SectorBuf[512];//讀取一個邏輯扇區 static u8 ReadBlock(u16 LBA) {return SD_ReadSector(SectorBuf, LBA + DBRStartSector, 1); }//寫入一個邏輯扇區 static u8 WriteBlock(u16 LBA) {return SD_WriteSector(SectorBuf, LBA + DBRStartSector, 1); }//將文件名格式化成標準的DOS 8.3格式的文件名 static void NameFormat(const char* SrcName, char* DstName) {u8 i, j;//首先用空格初始化目標緩沖區for(i = 0; i < 11; i++)*(DstName + i) = 0x20;//其次拷貝文件名for(i = 0, j = 0; i < 8; i++, j++){if((*SrcName) == '.') {SrcName++;break;}else{*(DstName + j) = *SrcName++;}}//最后拷貝擴展名for(i = 0, j = 8; i < 3; i++, j++){if((*SrcName) == 0) break;else {*(DstName + j) = *SrcName++;}} }//比較兩個緩沖區的前size個字節是否完全相同 static u8 IsEqual(void* Src1, void* Src2, u32 size) {u8 *p1, *p2;p1 = Src1;p2 = Src2;for(; size--; ){if((*p1++) != (*p2++))return 0;}return 1; }//將簇號轉換為邏輯扇區號 static u16 Clus2Sector(u16 clus) {return (DataStartSector + ((clus - 2) * SectorsPerClus)); }//讀取主引導記錄MBR static u8 ReadMBR(void) {tMBR *pmbr = (tMBR *)SectorBuf;//因為此時的DBRStartSector還未被賦值,等于0,所以這里讀取的是物理扇區0if(0 == ReadBlock(0)) return 0;if(0xAA55 != pmbr->Flag) return 0;//通過磁盤分區表DPT字段來獲取系統引導扇區DBR的扇區偏移量DBRStartSector = (pmbr->DPT[0].LBAoffest[1] << 16) + pmbr->DPT[0].LBAoffest[0]; return 1; }//讀取系統引導扇區DBR static u8 ReadDBR(void) {tDBR *pdbr = (tDBR*)SectorBuf;if(0 == ReadBlock(0)) return 0;if(0xAA55 != pdbr->Flag) return 0;//通過系統引導扇區中的BPB字段,計算磁盤的相關參數BytesPerSector = (pdbr->BPB.BytesPerSector[1] << 8) + pdbr->BPB.BytesPerSector[0];SectorsPerClus = pdbr->BPB.SectorsPerClus;ResvdSectors = (pdbr->BPB.ResvdSectors[1] << 8) + pdbr->BPB.ResvdSectors[0];FATCount = pdbr->BPB.FATCount;RootDirCnt = (pdbr->BPB.DirCount[1] << 8) + pdbr->BPB.DirCount[0];SectorsPerFAT = (pdbr->BPB.SectorsPerFAT[1] << 8) + pdbr->BPB.SectorsPerFAT[0];DirStartSector = ResvdSectors + SectorsPerFAT * FATCount;DataStartSector = DirStartSector + 32;return 1; }//讀取FAT表項的值 static u16 ReadFAT(u16 Index) {u16 *pItem = (u16*)&SectorBuf[0];//因為1扇區 = 256個FAT表項,所以Index >> 8表示從FAT開始的扇區偏移if(0 == ReadBlock((Index >> 8) + ResvdSectors)) return 0;//Index % 256 表示扇區內的字偏移return *(pItem + (Index % 256)); }//寫入某一FAT表項的值 static u16 WriteFAT(u16 Index, u16 val) {u16 *pItem = (u16*)&SectorBuf[0];//計算Index所在的邏輯扇區號u16 sector = (Index >> 8) + ResvdSectors;if(0 == ReadBlock(sector)) return 0;//Index % 256 表示扇區內的字偏移*(pItem + (Index % 256)) = val;if(0 == WriteBlock(sector)) return 0;return 1; }//將FAT1的某一扇區拷貝到FAT2所對應的扇區 //sector表示從FAT1開始的扇區偏移 static u8 CopyFAT(u16 sector) {if(!ReadBlock(ResvdSectors + sector)) return 0;if(!WriteBlock(ResvdSectors + SectorsPerFAT + sector)) return 0;return 1; }//FAT16初始化 u8 FAT_Init(void) {//先讀取MBR,找到系統引導扇區的位置if(0 == ReadMBR()) return 0;//再讀取系統引導扇區中的BPB,獲取磁盤的相關參數if(0 == ReadDBR()) return 0;return 1; }//查找根目錄下是否存在name所對應的文件,如果存在則將該文件信息存放到dir所指向的結構體中 u8 GetFileDir(const char* name, tDIR *dir) {u8 i, j;tDIR *pDir;char DOSname[11];//第一步要將name格式化成標準8.3格式的文件名NameFormat(name, DOSname);//因為根目錄區總共占32個扇區for(j = 0; j < 32; j++){if(0 == ReadBlock(DirStartSector + j)) return 0;//而每個扇區又包含16個目錄項for(i = 0; i < 16; i++){//每個目錄項又占32個字節,所以這里用i << 5表示目錄項在一個扇區中的字節偏移pDir = (tDIR *)&SectorBuf[i << 5];//通過文件名來查找文件if(IsEqual(DOSname, pDir->Name, 11)){*dir = *pDir;return 1; }}}return 0; }//將文件信息寫入Index所指定的目錄項中 static u8 WriteDir(u16 Index, tDIR *dir) {tDIR *pDir;//計算Index所在的邏輯扇區偏移,Index / 16表示從目錄區開始的扇區偏移量u16 sector = Index / 16 + DirStartSector;if(!ReadBlock(sector)) return 0;pDir = (tDIR*)&SectorBuf[0];//Index % 16表示1個扇區內的目錄項偏移*(pDir + (Index % 16)) = *dir;if(!WriteBlock(sector)) return 0;return 1; }//從根目錄區中獲取一個空的目錄項 static u16 GetEmptyDir(void) {u8 j, i;u16 index = 0;//因為根目錄區總共占32個扇區for(i = 0; i < 32; i++){if(!ReadBlock(DirStartSector + i)) return 0xffff;//而每個扇區又包含16個目錄項for(j = 0; j < 16; j++){//每個目錄項又占32個字節,所以這里用j * 32表示目錄項在一個扇區中的字節偏移if(0 == SectorBuf[j * 32])return index;index++; }}return 0xffff; }//獲取一個空的FAT表項,即一個空簇的簇號 static u16 GetEmptyFAT(void) {u16 i, j;u16 *pItem;//遍歷FAT表所占的每個扇區for(i = 0; i < SectorsPerFAT; i++){if(0 == ReadBlock(i + ResvdSectors)) return 0;pItem = (u16*)&SectorBuf[0];//遍歷扇區內的每個FAT表項for(j = 0; j < 256; j++){if(*(pItem + j) == 0) return ((i << 8) + j);}}return 0; }//新建一個文件 //注意:文件的大小已固定為size字節,即使新建的文件沒有寫任何內容,文件的大小始終為size大小; // 即使對該文件寫入了超過size大小的內容,文件的大小依然不改變 //該函數有待進一步優化,對于大文件的創建,該函數速度非常緩慢 u8 CreateFile(const char* name, u32 size) {tDIR dir = {0}; //一定要初始化為0,否則在WINDOWS系統下無法識別文件u16 ClusID;u16 i;u16 FATSector;//計算一簇所占的字節數u32 BytesPerClus = BytesPerSector * SectorsPerClus;//文件已存在,則返回if(GetFileDir(name, &dir)) return 0;//首先從根目錄區獲取一個空的目錄項i = GetEmptyDir();if(i == 0xffff) return 0;//從FAT表中獲取一個空的FAT表項ClusID = GetEmptyFAT();//立即將該空的FAT表項填充為0xFFFF,以免后面再次獲取空的FAT表項時錯誤的分配到同一表項if(0 == WriteFAT(ClusID, 0xFFFF)) return 0;//然后給該目錄項填充文件信息NameFormat(name, dir.Name);dir.Attri = 0;dir.FirstClus = ClusID;dir.Length[0] = size;dir.Length[1] = size >> 16;//將目錄信息回寫到目錄表中if(0 == WriteDir(i, &dir)) return 0;//計算分配到的FAT表項在FAT表中的扇區偏移FATSector = ClusID / 256;for(/*文件所占簇個數*/i = size / BytesPerClus; i != 0; i--){u16 NextClus;//獲取下一個空簇的簇號NextClus = GetEmptyFAT();if(!NextClus) return 0;//此部分有待優化if(0 == WriteFAT(ClusID, NextClus)) return 0;if(0 == WriteFAT(NextClus, 0xFFFF)) return 0;//當下一FAT表項所在位置不在當前FAT扇區時,立即將當前FAT1扇區的內容拷貝到對應的FAT2中if(FATSector != (NextClus / 256)){CopyFAT(FATSector);//并將下一FAT表項所在扇區偏移賦值給FATSectorFATSector = NextClus / 256;}ClusID = NextClus;}//將最后一扇區拷貝到FAT2中的相應位置CopyFAT(FATSector);return 1; } //讀取文件 u8 ReadFile(const char* name, u32 offest, void* dst, u32 len) {tDIR dir;u16 ClusID;u16 StartSector;u32 FileSize;u32 PtrByteOffest, PtrSectorOffest, PtrClusOffest;u16 i;u8 *pDst = (u8*)dst;//首先找到文件對應的目錄項if(!GetFileDir(name, &dir)) return 0;FileSize = (dir.Length[1] << 16) + dir.Length[0];//文件指針超出文件尾則返回if(offest > FileSize) return 0;//len大于文件長度的情況if((offest + len) > FileSize)len = FileSize - offest;ClusID = dir.FirstClus;//文件指針相對于文件頭的扇區偏移PtrSectorOffest = offest / BytesPerSector;//文件指針相對于當前扇區的字節偏移PtrByteOffest = offest % BytesPerSector;//文件指針相對于文件頭的簇偏移PtrClusOffest = PtrSectorOffest / SectorsPerClus;//找到文件指針所在簇號for(i = 0; i < PtrClusOffest; i++)ClusID = ReadFAT(ClusID);//文件指針相對于系統分區的扇區偏移StartSector = Clus2Sector(ClusID) + PtrSectorOffest;while(1){//2.從指針所在的扇區開始,遍歷文件的每個扇區for(; PtrSectorOffest < SectorsPerClus; PtrSectorOffest++){if(!ReadBlock(StartSector++)) return 0;//1.從指針所在扇區的字節偏移開始,遍歷扇區的每個字節for(; PtrByteOffest < BytesPerSector; PtrByteOffest++){*pDst++ = SectorBuf[PtrByteOffest];len--;if(0 == len) return 1;}PtrByteOffest = 0;}//讀取下一簇號ClusID = ReadFAT(ClusID);//獲取下一簇所在的扇區號StartSector = Clus2Sector(ClusID);PtrSectorOffest = 0;} }//寫文件 u8 WriteFile(const char* name, u32 offest, void* src, u32 len) {tDIR dir;u16 ClusID;u16 StartSector;u32 FileSize;u32 PtrByteOffest, PtrSectorOffest, PtrClusOffest;u16 i;u8 *pSrc = (u8*)src;if(!GetFileDir(name, &dir)) return 0;FileSize = (dir.Length[1] << 16) + dir.Length[0];//文件指針超出文件尾則返回if(offest > FileSize) return 0;//len大于文件長度的情況if((offest + len) > FileSize)len = FileSize - offest;ClusID = dir.FirstClus;//文件指針相對于文件頭的扇區偏移PtrSectorOffest = offest / BytesPerSector;//文件指針相對于當前扇區的字節偏移PtrByteOffest = offest % BytesPerSector;//文件指針相對于文件頭的簇偏移PtrClusOffest = PtrSectorOffest / SectorsPerClus;//找到文件指針所在簇號for(i = 0; i < PtrClusOffest; i++)ClusID = ReadFAT(ClusID);//文件指針相對于系統分區的扇區偏移StartSector = Clus2Sector(ClusID) + PtrSectorOffest;while(1){//2.從指針所在的扇區開始,遍歷文件的每個扇區for(; PtrSectorOffest < SectorsPerClus; PtrSectorOffest++){if(!ReadBlock(StartSector)) return 0;//1.從指針所在扇區的字節偏移開始,遍歷扇區的每個字節for(; PtrByteOffest < BytesPerSector; PtrByteOffest++){SectorBuf[PtrByteOffest] = *pSrc++;len--;if(0 == len){if(!WriteBlock(StartSector)) return 0;else return 1;}}if(!WriteBlock(StartSector++)) return 0;PtrByteOffest = 0;}//讀取下一簇號ClusID = ReadFAT(ClusID);//獲取下一簇所在的扇區號StartSector = Clus2Sector(ClusID);PtrSectorOffest = 0;} }


    ?


    ?

    ?

    ?

    ?

    ?

    ?

    源碼下載:??FAT16.zip?
    圖文PDF下載:??FAT16模塊詳解.pdf?


    轉自:http://blog.csdn.net/hexiaolong2009/article/details/17592583

    總結

    以上是生活随笔為你收集整理的FAT16图文详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。