FAT16图文详解

来源:互联网 发布:sql 试图的意义 编辑:程序博客网 时间:2024/05/18 19:22

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

 

以下为源码部分:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //****************************************************************************************************  
  2. //文件名:FAT16.c   
  3. //来源:网络   
  4. //注释:hexiaolong2009(http://blog.csdn.net/hexiaolong2009)  
  5. //****************************************************************************************************  
  6. #include "stm32f10x.h"   
  7. #include "fat16.h"   
  8. #include "sd.h"   
  9.   
  10. //****************************************************************************************************  
  11. //全局变量定义   
  12. u16 BytesPerSector;  
  13. u16 ResvdSectors;  
  14. u16 RootDirCnt;  
  15. u16 SectorsPerFAT;  
  16. u16 DirStartSector;  
  17. u16 DataStartSector;  
  18. u16 DBRStartSector;  
  19. u8 SectorsPerClus;  
  20. u8 FATCount;  
  21. u8 SectorBuf[512];  
  22.   
  23. //读取一个逻辑扇区   
  24. static u8 ReadBlock(u16 LBA)  
  25. {  
  26.     return SD_ReadSector(SectorBuf, LBA + DBRStartSector, 1);  
  27. }  
  28.   
  29. //写入一个逻辑扇区   
  30. static u8 WriteBlock(u16 LBA)  
  31. {  
  32.     return SD_WriteSector(SectorBuf, LBA + DBRStartSector, 1);  
  33. }  
  34.   
  35. //将文件名格式化成标准的DOS 8.3格式的文件名   
  36. static void NameFormat(const char* SrcName, char* DstName)  
  37. {  
  38.     u8 i, j;  
  39.   
  40.     //首先用空格初始化目标缓冲区   
  41.     for(i = 0; i < 11; i++)  
  42.         *(DstName + i) = 0x20;  
  43.   
  44.     //其次拷贝文件名   
  45.     for(i = 0, j = 0; i < 8; i++, j++)  
  46.     {  
  47.         if((*SrcName) == '.')       
  48.         {  
  49.             SrcName++;  
  50.             break;  
  51.         }  
  52.         else  
  53.         {  
  54.             *(DstName + j) = *SrcName++;  
  55.         }  
  56.     }  
  57.   
  58.     //最后拷贝扩展名   
  59.     for(i = 0, j = 8; i < 3; i++, j++)  
  60.     {  
  61.         if((*SrcName) == 0)   break;  
  62.         else      
  63.         {  
  64.             *(DstName + j) = *SrcName++;  
  65.         }  
  66.     }  
  67. }  
  68.   
  69. //比较两个缓冲区的前size个字节是否完全相同   
  70. static u8 IsEqual(void* Src1, void* Src2, u32 size)  
  71. {  
  72.     u8 *p1, *p2;  
  73.   
  74.     p1 = Src1;  
  75.     p2 = Src2;  
  76.     for(; size--; )  
  77.     {  
  78.         if((*p1++) != (*p2++))  
  79.             return 0;  
  80.   
  81.     }  
  82.     return 1;  
  83. }  
  84.   
  85. //将簇号转换为逻辑扇区号   
  86. static u16 Clus2Sector(u16 clus)  
  87. {  
  88.     return (DataStartSector + ((clus - 2) * SectorsPerClus));  
  89. }  
  90.   
  91. //读取主引导记录MBR   
  92. static u8 ReadMBR(void)  
  93. {  
  94.     tMBR *pmbr = (tMBR *)SectorBuf;  
  95.   
  96.     //因为此时的DBRStartSector还未被赋值,等于0,所以这里读取的是物理扇区0   
  97.     if(0 == ReadBlock(0))   return 0;  
  98.     if(0xAA55 != pmbr->Flag) return 0;  
  99.     //通过磁盘分区表DPT字段来获取系统引导扇区DBR的扇区偏移量   
  100.     DBRStartSector = (pmbr->DPT[0].LBAoffest[1] << 16) + pmbr->DPT[0].LBAoffest[0];   
  101.   
  102.     return 1;  
  103. }  
  104.   
  105. //读取系统引导扇区DBR   
  106. static u8 ReadDBR(void)  
  107. {  
  108.     tDBR *pdbr = (tDBR*)SectorBuf;  
  109.   
  110.     if(0 == ReadBlock(0)) return 0;  
  111.     if(0xAA55 != pdbr->Flag) return 0;  
  112.   
  113.     //通过系统引导扇区中的BPB字段,计算磁盘的相关参数   
  114.     BytesPerSector = (pdbr->BPB.BytesPerSector[1] << 8) + pdbr->BPB.BytesPerSector[0];  
  115.     SectorsPerClus = pdbr->BPB.SectorsPerClus;  
  116.     ResvdSectors = (pdbr->BPB.ResvdSectors[1] << 8) + pdbr->BPB.ResvdSectors[0];  
  117.     FATCount = pdbr->BPB.FATCount;  
  118.     RootDirCnt = (pdbr->BPB.DirCount[1] << 8) + pdbr->BPB.DirCount[0];  
  119.     SectorsPerFAT = (pdbr->BPB.SectorsPerFAT[1] << 8) + pdbr->BPB.SectorsPerFAT[0];  
  120.     DirStartSector = ResvdSectors + SectorsPerFAT * FATCount;  
  121.     DataStartSector = DirStartSector + 32;  
  122.   
  123.     return 1;  
  124. }  
  125.   
  126. //读取FAT表项的值   
  127. static u16 ReadFAT(u16 Index)  
  128. {  
  129.     u16 *pItem = (u16*)&SectorBuf[0];  
  130.   
  131.     //因为1扇区 = 256个FAT表项,所以Index >> 8表示从FAT开始的扇区偏移  
  132.     if(0 == ReadBlock((Index >> 8) + ResvdSectors)) return 0;  
  133.     //Index % 256 表示扇区内的字偏移   
  134.     return *(pItem + (Index % 256));  
  135. }  
  136.   
  137. //写入某一FAT表项的值   
  138. static u16 WriteFAT(u16 Index, u16 val)  
  139. {  
  140.     u16 *pItem = (u16*)&SectorBuf[0];  
  141.     //计算Index所在的逻辑扇区号   
  142.     u16 sector = (Index >> 8) + ResvdSectors;  
  143.   
  144.     if(0 == ReadBlock(sector)) return 0;  
  145.     //Index % 256 表示扇区内的字偏移   
  146.     *(pItem + (Index % 256)) = val;  
  147.     if(0 == WriteBlock(sector)) return 0;  
  148.     return 1;  
  149. }  
  150.   
  151. //将FAT1的某一扇区拷贝到FAT2所对应的扇区   
  152. //sector表示从FAT1开始的扇区偏移   
  153. static u8 CopyFAT(u16 sector)  
  154. {  
  155.     if(!ReadBlock(ResvdSectors + sector)) return 0;  
  156.     if(!WriteBlock(ResvdSectors + SectorsPerFAT + sector)) return 0;  
  157.     return 1;  
  158. }  
  159.   
  160. //FAT16初始化   
  161. u8 FAT_Init(void)  
  162. {  
  163.     //先读取MBR,找到系统引导扇区的位置   
  164.     if(0 == ReadMBR()) return 0;  
  165.     //再读取系统引导扇区中的BPB,获取磁盘的相关参数   
  166.     if(0 == ReadDBR()) return 0;  
  167.   
  168.     return 1;     
  169. }  
  170.   
  171. //查找根目录下是否存在name所对应的文件,如果存在则将该文件信息存放到dir所指向的结构体中  
  172. u8 GetFileDir(const char* name, tDIR *dir)  
  173. {  
  174.     u8 i, j;  
  175.     tDIR *pDir;  
  176.     char DOSname[11];  
  177.   
  178.     //第一步要将name格式化成标准8.3格式的文件名   
  179.     NameFormat(name, DOSname);  
  180.   
  181.     //因为根目录区总共占32个扇区   
  182.     for(j = 0; j < 32; j++)  
  183.     {  
  184.         if(0 == ReadBlock(DirStartSector + j)) return 0;  
  185.         //而每个扇区又包含16个目录项   
  186.         for(i = 0; i < 16; i++)  
  187.         {  
  188.             //每个目录项又占32个字节,所以这里用i << 5表示目录项在一个扇区中的字节偏移   
  189.             pDir = (tDIR *)&SectorBuf[i << 5];  
  190.             //通过文件名来查找文件   
  191.             if(IsEqual(DOSname, pDir->Name, 11))  
  192.             {  
  193.                 *dir = *pDir;  
  194.                 return 1;     
  195.             }  
  196.         }  
  197.     }  
  198.     return 0;  
  199. }  
  200.   
  201. //将文件信息写入Index所指定的目录项中   
  202. static u8 WriteDir(u16 Index, tDIR *dir)  
  203. {  
  204.     tDIR *pDir;  
  205.     //计算Index所在的逻辑扇区偏移,Index / 16表示从目录区开始的扇区偏移量  
  206.     u16 sector = Index / 16 + DirStartSector;  
  207.   
  208.     if(!ReadBlock(sector)) return 0;  
  209.     pDir = (tDIR*)&SectorBuf[0];  
  210.     //Index % 16表示1个扇区内的目录项偏移   
  211.     *(pDir + (Index % 16)) = *dir;  
  212.     if(!WriteBlock(sector)) return 0;  
  213.   
  214.     return 1;  
  215. }  
  216.   
  217. //从根目录区中获取一个空的目录项   
  218. static u16 GetEmptyDir(void)  
  219. {  
  220.     u8 j, i;  
  221.     u16 index = 0;  
  222.   
  223.     //因为根目录区总共占32个扇区   
  224.     for(i = 0; i < 32; i++)  
  225.     {  
  226.         if(!ReadBlock(DirStartSector + i)) return 0xffff;  
  227.         //而每个扇区又包含16个目录项   
  228.         for(j = 0; j < 16; j++)  
  229.         {  
  230.             //每个目录项又占32个字节,所以这里用j * 32表示目录项在一个扇区中的字节偏移   
  231.             if(0 == SectorBuf[j * 32])  
  232.                 return index;  
  233.             index++;      
  234.         }  
  235.     }  
  236.   
  237.     return 0xffff;  
  238. }  
  239.   
  240. //获取一个空的FAT表项,即一个空簇的簇号   
  241. static u16 GetEmptyFAT(void)  
  242. {  
  243.     u16 i, j;  
  244.     u16 *pItem;  
  245.   
  246.     //遍历FAT表所占的每个扇区   
  247.     for(i = 0; i < SectorsPerFAT; i++)  
  248.     {  
  249.         if(0 == ReadBlock(i + ResvdSectors)) return 0;  
  250.         pItem = (u16*)&SectorBuf[0];  
  251.         //遍历扇区内的每个FAT表项   
  252.         for(j = 0; j < 256; j++)  
  253.         {  
  254.             if(*(pItem + j) == 0) return ((i << 8) + j);  
  255.         }  
  256.     }  
  257.     return 0;  
  258. }  
  259.   
  260. //新建一个文件   
  261. //注意:文件的大小已固定为size字节,即使新建的文件没有写任何内容,文件的大小始终为size大小;  
  262. //      即使对该文件写入了超过size大小的内容,文件的大小依然不改变   
  263. //该函数有待进一步优化,对于大文件的创建,该函数速度非常缓慢   
  264. u8 CreateFile(const char* name, u32 size)  
  265. {  
  266.     tDIR dir = {0}; //一定要初始化为0,否则在WINDOWS系统下无法识别文件   
  267.     u16 ClusID;  
  268.     u16 i;  
  269.     u16 FATSector;  
  270.     //计算一簇所占的字节数   
  271.     u32 BytesPerClus = BytesPerSector * SectorsPerClus;  
  272.   
  273.     //文件已存在,则返回   
  274.     if(GetFileDir(name, &dir)) return 0;  
  275.   
  276.     //首先从根目录区获取一个空的目录项   
  277.     i = GetEmptyDir();  
  278.     if(i == 0xffff) return 0;  
  279.   
  280.     //从FAT表中获取一个空的FAT表项   
  281.     ClusID = GetEmptyFAT();  
  282.     //立即将该空的FAT表项填充为0xFFFF,以免后面再次获取空的FAT表项时错误的分配到同一表项   
  283.     if(0 == WriteFAT(ClusID, 0xFFFF)) return 0;  
  284.   
  285.     //然后给该目录项填充文件信息   
  286.     NameFormat(name, dir.Name);  
  287.     dir.Attri = 0;  
  288.     dir.FirstClus = ClusID;  
  289.     dir.Length[0] = size;  
  290.     dir.Length[1] = size >> 16;  
  291.     //将目录信息回写到目录表中   
  292.     if(0 == WriteDir(i, &dir)) return 0;  
  293.   
  294.     //计算分配到的FAT表项在FAT表中的扇区偏移   
  295.     FATSector = ClusID / 256;  
  296.     for(/*文件所占簇个数*/i = size / BytesPerClus; i != 0; i--)  
  297.     {  
  298.         u16 NextClus;  
  299.   
  300.         //获取下一个空簇的簇号   
  301.         NextClus = GetEmptyFAT();  
  302.         if(!NextClus) return 0;  
  303.         //此部分有待优化   
  304.         if(0 == WriteFAT(ClusID, NextClus)) return 0;  
  305.         if(0 == WriteFAT(NextClus, 0xFFFF)) return 0;  
  306.         //当下一FAT表项所在位置不在当前FAT扇区时,立即将当前FAT1扇区的内容拷贝到对应的FAT2中  
  307.         if(FATSector != (NextClus / 256))  
  308.         {  
  309.             CopyFAT(FATSector);  
  310.             //并将下一FAT表项所在扇区偏移赋值给FATSector   
  311.             FATSector = NextClus / 256;  
  312.         }  
  313.         ClusID = NextClus;  
  314.     }  
  315.     //将最后一扇区拷贝到FAT2中的相应位置   
  316.     CopyFAT(FATSector);  
  317.   
  318.     return 1;  
  319. }                 
  320.   
  321. //读取文件   
  322. u8 ReadFile(const char* name, u32 offest, void* dst, u32 len)  
  323. {  
  324.     tDIR dir;  
  325.     u16 ClusID;  
  326.     u16 StartSector;  
  327.     u32 FileSize;  
  328.     u32 PtrByteOffest, PtrSectorOffest, PtrClusOffest;  
  329.     u16 i;  
  330.     u8 *pDst = (u8*)dst;  
  331.   
  332.     //首先找到文件对应的目录项   
  333.     if(!GetFileDir(name, &dir)) return 0;  
  334.   
  335.     FileSize = (dir.Length[1] << 16) + dir.Length[0];  
  336.     //文件指针超出文件尾则返回   
  337.     if(offest > FileSize) return 0;  
  338.     //len大于文件长度的情况   
  339.     if((offest + len) > FileSize)  
  340.         len = FileSize - offest;  
  341.   
  342.     ClusID = dir.FirstClus;  
  343.     //文件指针相对于文件头的扇区偏移   
  344.     PtrSectorOffest = offest / BytesPerSector;  
  345.     //文件指针相对于当前扇区的字节偏移   
  346.     PtrByteOffest = offest % BytesPerSector;  
  347.     //文件指针相对于文件头的簇偏移   
  348.     PtrClusOffest = PtrSectorOffest / SectorsPerClus;  
  349.   
  350.     //找到文件指针所在簇号   
  351.     for(i = 0; i < PtrClusOffest; i++)  
  352.         ClusID = ReadFAT(ClusID);  
  353.     //文件指针相对于系统分区的扇区偏移   
  354.     StartSector = Clus2Sector(ClusID) + PtrSectorOffest;  
  355.   
  356.     while(1)  
  357.     {  
  358.         //2.从指针所在的扇区开始,遍历文件的每个扇区   
  359.         for(; PtrSectorOffest < SectorsPerClus; PtrSectorOffest++)  
  360.         {  
  361.             if(!ReadBlock(StartSector++)) return 0;  
  362.   
  363.             //1.从指针所在扇区的字节偏移开始,遍历扇区的每个字节   
  364.             for(; PtrByteOffest < BytesPerSector; PtrByteOffest++)  
  365.             {  
  366.                 *pDst++ = SectorBuf[PtrByteOffest];  
  367.                 len--;  
  368.                 if(0 == len) return 1;  
  369.             }  
  370.             PtrByteOffest = 0;  
  371.         }  
  372.         //读取下一簇号   
  373.         ClusID = ReadFAT(ClusID);  
  374.         //获取下一簇所在的扇区号   
  375.         StartSector = Clus2Sector(ClusID);  
  376.         PtrSectorOffest = 0;  
  377.     }  
  378. }  
  379.   
  380. //写文件   
  381. u8 WriteFile(const char* name, u32 offest, void* src, u32 len)  
  382. {  
  383.     tDIR dir;  
  384.     u16 ClusID;  
  385.     u16 StartSector;  
  386.     u32 FileSize;  
  387.     u32 PtrByteOffest, PtrSectorOffest, PtrClusOffest;  
  388.     u16 i;  
  389.     u8 *pSrc = (u8*)src;  
  390.   
  391.     if(!GetFileDir(name, &dir)) return 0;  
  392.   
  393.     FileSize = (dir.Length[1] << 16) + dir.Length[0];  
  394.     //文件指针超出文件尾则返回   
  395.     if(offest > FileSize) return 0;  
  396.     //len大于文件长度的情况   
  397.     if((offest + len) > FileSize)  
  398.         len = FileSize - offest;  
  399.   
  400.     ClusID = dir.FirstClus;  
  401.     //文件指针相对于文件头的扇区偏移   
  402.     PtrSectorOffest = offest / BytesPerSector;  
  403.     //文件指针相对于当前扇区的字节偏移   
  404.     PtrByteOffest = offest % BytesPerSector;  
  405.     //文件指针相对于文件头的簇偏移   
  406.     PtrClusOffest = PtrSectorOffest / SectorsPerClus;  
  407.   
  408.     //找到文件指针所在簇号   
  409.     for(i = 0; i < PtrClusOffest; i++)  
  410.         ClusID = ReadFAT(ClusID);  
  411.     //文件指针相对于系统分区的扇区偏移   
  412.     StartSector = Clus2Sector(ClusID) + PtrSectorOffest;  
  413.   
  414.     while(1)  
  415.     {  
  416.         //2.从指针所在的扇区开始,遍历文件的每个扇区   
  417.         for(; PtrSectorOffest < SectorsPerClus; PtrSectorOffest++)  
  418.         {  
  419.             if(!ReadBlock(StartSector)) return 0;  
  420.   
  421.             //1.从指针所在扇区的字节偏移开始,遍历扇区的每个字节   
  422.             for(; PtrByteOffest < BytesPerSector; PtrByteOffest++)  
  423.             {  
  424.                 SectorBuf[PtrByteOffest] = *pSrc++;  
  425.                 len--;  
  426.                 if(0 == len)  
  427.                 {  
  428.                     if(!WriteBlock(StartSector)) return 0;  
  429.                     else return 1;  
  430.                 }  
  431.             }  
  432.             if(!WriteBlock(StartSector++)) return 0;  
  433.             PtrByteOffest = 0;  
  434.         }  
  435.         //读取下一簇号   
  436.         ClusID = ReadFAT(ClusID);  
  437.         //获取下一簇所在的扇区号   
  438.         StartSector = Clus2Sector(ClusID);  
  439.         PtrSectorOffest = 0;  
  440.     }  
  441. }  


 


 

 

 

 

 

 

源码下载:  FAT16.zip 
图文PDF下载:  FAT16模块详解.pdf 


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

0 0