FAT16图文详解

来源:互联网 发布:mac怎么安装exe软件 编辑:程序博客网 时间:2024/05/05 04:41

注:FAT16驱动代码不是本人编写的,是从网上下载的,本人只是对该代码进行研读学习,并做下笔记。该FAT16驱动应该是比较老的了,猜测应该在DOS时代比较流行,但放在今天,对于刚刚进阶FAT16的小伙伴来说,还是很适合初学者学习的好资料!笔者也相信,只要小伙伴们静下心来,慢慢读懂该代码,相信很快就能在脑海中形成一张FAT16的总览图了。
        笔者对代码进行了简单测试,在STM32平台上对2G SD卡进行了读写TXT操作,没有问题。当然,这个代码功能还是很简单的,只有创建、读、写文件3个操作,而且写操作不能修改文件的大小,即没有追加功能,文件的大小是由CreateFile一开始创建好了的。

 

以下为源码部分:

//****************************************************************************************************//文件名: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));}//读取主引导记录MBRstatic 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;}//读取系统引导扇区DBRstatic 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 

1 0
原创粉丝点击