注: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));
- }
- //读取主引导记录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,所以这里读取的是物理扇区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.zip
图文PDF下载: FAT16模块详解.pdf
转自:http://blog.csdn.net/hexiaolong2009/article/details/17592583