本帖最后由 hexiaolong2009 于 2013-12-25 21:42 编辑
注:FAT16驱动代码不是本人编写的,是从网上下载的,本人只是对该代码进行研读学习,并做下笔记。该FAT16驱动应该是比较老的了,猜测应该在DOS时代比较流行,但放在今天,对于刚刚进阶FAT16的小伙伴来说,还是很适合初学者学习的好资料!笔者也相信,只要小伙伴们静下心来,慢慢读懂该代码,相信很快就能在脑海中形成一张FAT16的总览图了。
笔者对代码进行了简单测试,在STM32平台上对2G SD卡进行了读写TXT操作,没有问题。当然,这个代码功能还是很简单的,只有创建、读、写文件3个操作,
而且写操作不能修改文件的大小,即没有追加功能,文件的大小是由CreateFile一开始创建好了的。
以下为源码部分:
- #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;
- }
复制代码
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 上传
点击文件名下载附件
厉害啊,我咋花画不出这么美观的图呢
一周热门 更多>