FAT16图文解析

2020-01-01 17:54发布

本帖最后由 hexiaolong2009 于 2013-12-25 21:42 编辑

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

以下为源码部分:
  1. #include "stm32f10x.h"
  2. #include "fat16.h"
  3. #include "sd.h"

  4. //****************************************************************************************************
  5. //全局变量定义
  6. u16 BytesPerSector;
  7. u16 ResvdSectors;
  8. u16 RootDirCnt;
  9. u16 SectorsPerFAT;
  10. u16 DirStartSector;
  11. u16 DataStartSector;
  12. u16 DBRStartSector;
  13. u8 SectorsPerClus;
  14. u8 FATCount;
  15. u8 SectorBuf[512];

  16. //读取一个逻辑扇区
  17. static u8 ReadBlock(u16 LBA)
  18. {
  19.         return SD_ReadSector(SectorBuf, LBA + DBRStartSector, 1);
  20. }

  21. //写入一个逻辑扇区
  22. static u8 WriteBlock(u16 LBA)
  23. {
  24.         return SD_WriteSector(SectorBuf, LBA + DBRStartSector, 1);
  25. }

  26. //将文件名格式化成标准的DOS 8.3格式的文件名
  27. static void NameFormat(const char* SrcName, char* DstName)
  28. {
  29.         u8 i, j;

  30.         //首先用空格初始化目标缓冲区
  31.         for(i = 0; i < 11; i++)
  32.                 *(DstName + i) = 0x20;

  33.         //其次拷贝文件名
  34.         for(i = 0, j = 0; i < 8; i++, j++)
  35.         {
  36.                 if((*SrcName) == '.')         
  37.                 {
  38.                         SrcName++;
  39.                         break;
  40.                 }
  41.                 else
  42.                 {
  43.                         *(DstName + j) = *SrcName++;
  44.                 }
  45.         }

  46.         //最后拷贝扩展名
  47.         for(i = 0, j = 8; i < 3; i++, j++)
  48.         {
  49.                 if((*SrcName) == 0)          break;
  50.                 else        
  51.                 {
  52.                         *(DstName + j) = *SrcName++;
  53.                 }
  54.         }
  55. }

  56. //比较两个缓冲区的前size个字节是否完全相同
  57. static u8 IsEqual(void* Src1, void* Src2, u32 size)
  58. {
  59.         u8 *p1, *p2;

  60.         p1 = Src1;
  61.         p2 = Src2;
  62.         for(; size--; )
  63.         {
  64.                 if((*p1++) != (*p2++))
  65.                         return 0;

  66.         }
  67.         return 1;
  68. }

  69. //将簇号转换为逻辑扇区号
  70. static u16 Clus2Sector(u16 clus)
  71. {
  72.         return (DataStartSector + ((clus - 2) * SectorsPerClus));
  73. }

  74. //读取主引导记录MBR
  75. static u8 ReadMBR(void)
  76. {
  77.         tMBR *pmbr = (tMBR *)SectorBuf;

  78.         //因为此时的DBRStartSector还未被赋值,等于0,所以这里读取的是物理扇区0
  79.         if(0 == ReadBlock(0))        return 0;
  80.         if(0xAA55 != pmbr->Flag)        return 0;
  81.         //通过磁盘分区表DPT字段来获取系统引导扇区DBR的扇区偏移量
  82.         DBRStartSector = (pmbr->DPT[0].LBAoffest[1] << 16) + pmbr->DPT[0].LBAoffest[0];        

  83.         return 1;
  84. }

  85. //读取系统引导扇区DBR
  86. static u8 ReadDBR(void)
  87. {
  88.         tDBR *pdbr = (tDBR*)SectorBuf;

  89.         if(0 == ReadBlock(0)) return 0;
  90.         if(0xAA55 != pdbr->Flag)        return 0;

  91.         //通过系统引导扇区中的BPB字段,计算磁盘的相关参数
  92.         BytesPerSector = (pdbr->BPB.BytesPerSector[1] << 8) + pdbr->BPB.BytesPerSector[0];
  93.         SectorsPerClus = pdbr->BPB.SectorsPerClus;
  94.         ResvdSectors = (pdbr->BPB.ResvdSectors[1] << 8) + pdbr->BPB.ResvdSectors[0];
  95.         FATCount = pdbr->BPB.FATCount;
  96.         RootDirCnt = (pdbr->BPB.DirCount[1] << 8) + pdbr->BPB.DirCount[0];
  97.         SectorsPerFAT = (pdbr->BPB.SectorsPerFAT[1] << 8) + pdbr->BPB.SectorsPerFAT[0];
  98.         DirStartSector = ResvdSectors + SectorsPerFAT * FATCount;
  99.         DataStartSector = DirStartSector + 32;

  100.         return 1;
  101. }

  102. //读取FAT表项的值
  103. static u16 ReadFAT(u16 Index)
  104. {
  105.         u16 *pItem = (u16*)&SectorBuf[0];

  106.         //因为1扇区 = 256个FAT表项,所以Index >> 8表示从FAT开始的扇区偏移
  107.         if(0 == ReadBlock((Index >> 8) + ResvdSectors)) return 0;
  108.         //Index % 256 表示扇区内的字偏移
  109.         return *(pItem + (Index % 256));
  110. }

  111. //写入某一FAT表项的值
  112. static u16 WriteFAT(u16 Index, u16 val)
  113. {
  114.         u16 *pItem = (u16*)&SectorBuf[0];
  115.         //计算Index所在的逻辑扇区号
  116.         u16 sector = (Index >> 8) + ResvdSectors;

  117.         if(0 == ReadBlock(sector)) return 0;
  118.         //Index % 256 表示扇区内的字偏移
  119.         *(pItem + (Index % 256)) = val;
  120.         if(0 == WriteBlock(sector)) return 0;
  121.         return 1;
  122. }

  123. //将FAT1的某一扇区拷贝到FAT2所对应的扇区
  124. //sector表示从FAT1开始的扇区偏移
  125. static u8 CopyFAT(u16 sector)
  126. {
  127.         if(!ReadBlock(ResvdSectors + sector)) return 0;
  128.         if(!WriteBlock(ResvdSectors + SectorsPerFAT + sector)) return 0;
  129.         return 1;
  130. }

  131. //FAT16初始化
  132. u8 FAT_Init(void)
  133. {
  134.         //先读取MBR,找到系统引导扇区的位置
  135.         if(0 == ReadMBR()) return 0;
  136.         //再读取系统引导扇区中的BPB,获取磁盘的相关参数
  137.         if(0 == ReadDBR()) return 0;

  138.         return 1;        
  139. }

  140. //查找根目录下是否存在name所对应的文件,如果存在则将该文件信息存放到dir所指向的结构体中
  141. u8 GetFileDir(const char* name, tDIR *dir)
  142. {
  143.         u8 i, j;
  144.         tDIR *pDir;
  145.         char DOSname[11];

  146.         //第一步要将name格式化成标准8.3格式的文件名
  147.         NameFormat(name, DOSname);

  148.         //因为根目录区总共占32个扇区
  149.         for(j = 0; j < 32; j++)
  150.         {
  151.                 if(0 == ReadBlock(DirStartSector + j)) return 0;
  152.                 //而每个扇区又包含16个目录项
  153.                 for(i = 0; i < 16; i++)
  154.                 {
  155.                         //每个目录项又占32个字节,所以这里用i << 5表示目录项在一个扇区中的字节偏移
  156.                         pDir = (tDIR *)&SectorBuf[i << 5];
  157.                         //通过文件名来查找文件
  158.                         if(IsEqual(DOSname, pDir->Name, 11))
  159.                         {
  160.                                 *dir = *pDir;
  161.                                 return 1;        
  162.                         }
  163.                 }
  164.         }
  165.         return 0;
  166. }

  167. //将文件信息写入Index所指定的目录项中
  168. static u8 WriteDir(u16 Index, tDIR *dir)
  169. {
  170.         tDIR *pDir;
  171.         //计算Index所在的逻辑扇区偏移,Index / 16表示从目录区开始的扇区偏移量
  172.         u16 sector = Index / 16 + DirStartSector;

  173.         if(!ReadBlock(sector)) return 0;
  174.         pDir = (tDIR*)&SectorBuf[0];
  175.         //Index % 16表示1个扇区内的目录项偏移
  176.         *(pDir + (Index % 16)) = *dir;
  177.         if(!WriteBlock(sector)) return 0;

  178.         return 1;
  179. }

  180. //从根目录区中获取一个空的目录项
  181. static u16 GetEmptyDir(void)
  182. {
  183.         u8 j, i;
  184.         u16 index = 0;

  185.         //因为根目录区总共占32个扇区
  186.         for(i = 0; i < 32; i++)
  187.         {
  188.                 if(!ReadBlock(DirStartSector + i)) return 0xffff;
  189.                 //而每个扇区又包含16个目录项
  190.                 for(j = 0; j < 16; j++)
  191.                 {
  192.                         //每个目录项又占32个字节,所以这里用j * 32表示目录项在一个扇区中的字节偏移
  193.                         if(0 == SectorBuf[j * 32])
  194.                                 return index;
  195.                         index++;        
  196.                 }
  197.         }

  198.         return 0xffff;
  199. }

  200. //获取一个空的FAT表项,即一个空簇的簇号
  201. static u16 GetEmptyFAT(void)
  202. {
  203.         u16 i, j;
  204.         u16 *pItem;

  205.         //遍历FAT表所占的每个扇区
  206.         for(i = 0; i < SectorsPerFAT; i++)
  207.         {
  208.                 if(0 == ReadBlock(i + ResvdSectors)) return 0;
  209.                 pItem = (u16*)&SectorBuf[0];
  210.                 //遍历扇区内的每个FAT表项
  211.                 for(j = 0; j < 256; j++)
  212.                 {
  213.                         if(*(pItem + j) == 0) return ((i << 8) + j);
  214.                 }
  215.         }
  216.         return 0;
  217. }
复制代码
FAT总览图:
写.png (126.79 KB, 下载次数: 1) 下载附件 2013-12-25 21:27 上传


源码下载: FAT16.zip (4.17 KB, 下载次数: 392) 2013-12-25 21:41 上传 点击文件名下载附件

图文PDF下载: FAT16模块详解.pdf (84.63 KB, 下载次数: 600) 2013-12-25 21:42 上传 点击文件名下载附件
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
29条回答
dushanwu522
1楼-- · 2020-01-01 18:21
真全,楼主辛苦了…………
pcwhy
2楼-- · 2020-01-01 19:00
为何不直接移植使用FatFS?
jack_yu
3楼-- · 2020-01-01 19:11
好东东收藏了。谢谢楼主!
laujc
4楼-- · 2020-01-01 19:39
 精彩回答 2  元偷偷看……
DevLabs
5楼-- · 2020-01-01 23:39
标记一下, 图做的很不错.
hexiaolong2009
6楼-- · 2020-01-02 00:55
pcwhy 发表于 2013-12-25 22:49
为何不直接移植使用FatFS?

只为学习。。。。。。

一周热门 更多>