FATFS 简介
FATFS 是一个完全免费开源的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。支持 FATl2、FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写。
FATFS 模块的层次结构如图 所示:
最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如 f_open,f_read,f_write 和 f_close 等,就可以像在 PC 上读/写文件那样简单。中间层 FATFS 模块,实现了 FAT 文件读/写协议。FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口(diskI/O)和供给文件创建修改时间的实时时钟。
下载最新版本的 FATFS软件包,解压后可以得到两个文件夹:doc 和 src。doc 里面主要是对 FATFS 的介绍,而 src 里面才是我们需要的源码。其中,与平台无关的是:
ffconf.h FATFS 模块配置文件
ff.h FATFS 和应用模块公用的包含文件
ff.c FATFS 模块
diskio.h FATFS 和 disk I/O 模块公用的包含文件
interger.h 数据类型定义
option 可选的外部功能(比如支持中文等)
与平台相关的代码(需要用户提供)是:
diskio.c FATFS 和 disk I/O 模块接口层文件
FATFS 模块在移植的时候,我们一般只需要修改 2 个文件,即 ffconf.h 和 diskio.c。FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们可以通过配置里面的一些选项,来满足自己的需求。
本次暂时不讲怎么去移植fatfs,会根据部分源代码分析上一讲FAT文件系统中的MBR内容,MBR在FATFS是怎么实现的,怎么创建DPT,当我们使用f_mount,f_mkfs对MBR又做了什么操作。
我们在使用fatfs进行应用软件编程时,我们一般会按照如下流程来进行。
int main(void)
{
......
res=f_mount(fs[0],"0:",1); //挂载SD卡
f_mount(fs[1],"1:",1); //挂载FLASH.
//FLASH磁盘,FAT文件系统错误,重新格式化FLASH
if(res==FR_NO_FILESYSTEM)
{
res=f_mkfs("1:",1,4096);//格式化FLASH,1,盘符;1,不需要引导区,8个扇区为1个簇
if(res==0)
{
f_setlabel((const TCHAR *)"1:XXXX"); //设置Flash磁盘的名字为:XXXX
}else
delay_ms(1000);
}
......
mf_mkdir("0:/cvt");
f_open(file, "0:/cvt/1.txt",FA_WRITE | FA_CREATE_ALWAYS);
res=f_write(file,dat,len,&bw);
f_close(file);
}
当我们使用f_mount进行挂在时,会出现挂在不成的现象。根据其返回值就能找到其失败的原因,有硬件驱动跟软件两种错误。当返回值时没有文件系统时,我们可以在编程时创建文件系统。
FR_INVALID_DRIVE: 传入参数不对,磁盘无效。
FR_WRITE_PROTECTED: 或者驱动层SD卡读写状态,驱动未使能写。
FR_NOT_READY: 底层驱动SD卡初始化失败。
FR_DISK_ERR: 读取SD卡的MBR信息
FR_NO_FILESYSTEM 没有文件系统
下面我们看f_mount中是怎么检验mbr信息的,f_mkfs是怎么创建文件系统的。
1.获取磁盘驱动编号,强制挂载磁盘。
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt)
{
......
/* 获取磁盘驱动编号 */
vol = get_ldnumber(&rp);
/* 挂在磁盘 */
res = mount_volume(&path, &fs, 0);
......
}
2.获取磁盘状态,初始化磁盘。
static FRESULT mount_volume (const TCHAR** path, FATFS** rfs, BYTE mode)
{
/* 获取磁盘驱动器编号 */
vol = get_ldnumber(path);
/* 根据磁盘状态返回 */
mode &= (BYTE)~FA_READ;
if (fs->fs_type != 0) {
stat = disk_status(fs->pdrv);
if (!(stat & STA_NOINIT)) {
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) {
return FR_WRITE_PROTECTED;
}
return FR_OK;
}
}
/* 初始化磁盘驱动器 */
fs->fs_type = 0;
fs->pdrv = LD2PD(vol);
stat = disk_initialize(fs->pdrv);
if (stat & STA_NOINIT) {
return FR_NOT_READY;
}
/* 读取主引导记录信息,校验文件系统 */
fmt = find_volume(fs, LD2PT(vol));
if (fmt == 4) return FR_DISK_ERR;
if (fmt >= 2) return FR_NO_FILESYSTEM;
......
}
3.检验MBR文件信息
static UINT find_volume (FATFS* fs, UINT part)
{
/* 读取MBR信息并校验 */
fmt = check_fs(fs, 0);
if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt;
/* 在MBR信息中获取DPT,并校验DPT文件系统信息 */
for (i = 0; i < 4; i++) {
mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba);
}
i = part ? part - 1 : 0;
do {
fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3;
} while (part == 0 && fmt >= 2 && ++i < 4);
return fmt;
}
4.对MBR进行校验。MBR的引导程序占了其中的前 446 个字节(偏移 0H~偏移 1BDH),随后的 64 个字节(偏移 1BEH~偏移 1FDH)为DPT(DiskPartitionTable,硬盘分区表),最后的两个字节“55 AA”(偏移 1FEH~偏移1FFH)是分区有效结束标志。
/* check fs on the physical drive in MBR
* fs: Filesystem object
* sect: Sector to load and check if it is an FAT-VBR or not
* return 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
static UINT check_fs (FATFS* fs, LBA_t sect)
{
WORD w, sign;
BYTE b;
fs->wflag = 0; fs->winsect = (LBA_t)0 - 1;
/* 读取第1个扇区信息数据,即MBR数据 */
if (move_window(fs, sect) != FR_OK) return 4;
/* 拿到MBR结束标志 */
sign = ld_word(fs->win + BS_55AA);
#if FF_FS_EXFAT
/* It is an exFAT VBR */
if (sign == 0xAA55 && !memcmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1;
#endif
b = fs->win[BS_JmpBoot];
if (b == 0xEB || b == 0xE9 || b == 0xE8) {
/* 跳转指令,结束标志,且文件系统信息比对正确,返回 */
if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) {
return 0; /* It is an FAT32 VBR */
}
/* 使用早期MS-DOS缺少BS_55AA,因此需要识别FAT VBR来判断FAT文件系统 */
w = ld_word(fs->win + BPB_BytsPerSec);
b = fs->win[BPB_SecPerClus];
if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS
&& b != 0 && (b & (b - 1)) == 0
&& ld_word(fs->win + BPB_RsvdSecCnt) != 0
&& (UINT)fs->win[BPB_NumFATs] - 1 <= 1
&& ld_word(fs->win + BPB_RootEntCnt) != 0
&& (ld_word(fs->win + BPB_TotSec16) >= 128 || ld_dword(fs->win + BPB_TotSec32) >= 0x10000)
&& ld_word(fs->win + BPB_FATSz16) != 0) {
return 0;
}
}
return sign == 0xAA55 ? 2 : 3;
}
5.创建文件系统中的DBR信息及MBR数据
FRESULT f_mkfs (const TCHAR* path,const MKFS_PARM* opt,void* work,UINT len)
{
/* 获取磁盘驱动编号 */
vol = get_ldnumber(&path);
/* 初始化磁盘,获取磁盘总扇区个数,扇区大小 */
ds = disk_initialize(pdrv);
if (ds & STA_NOINIT) return FR_NOT_READY;
if (ds & STA_PROTECT) return FR_WRITE_PROTECTED;
sz_blk = opt->align;
if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK) sz_blk = 1;
if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1;
if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
/* 文件系统及DBR初始化*/
......
/* Create FAT VBR */
memset(buf, 0, ss);
/* 跳转指令, 文件系统标志和版本号,这里为MSDOS5.0 */
memcpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);
/* 每扇区字节数,0x0200=512 */
st_word(buf + BPB_BytsPerSec, ss);
/* 每簇扇区数,0x08 */
buf[BPB_SecPerClus] = (BYTE)pau;
/* 保留扇区数,0x0C22=3106 */
st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv);
/* FAT表个数,0x02 */
buf[BPB_NumFATs] = (BYTE)n_fat;
/* FAT32必须等于0,FAT12/FAT16为根目录中目录的个数 */
st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root));
/* FAT32必须等于0,FAT12/FAT16为扇区总数 */
if (sz_vol < 0x10000) {
st_word(buf + BPB_TotSec16, (WORD)sz_vol);
} else {
st_dword(buf + BPB_TotSec32, (DWORD)sz_vol);
}
/* 哪种存储介质,0xF8标准值,可移动存储介质 */
buf[BPB_Media] = 0xF8;
/* 每磁道扇区数,只对于“特殊形状”(由磁头和柱面分割为若干磁道)的存储介质有效,0x003F=63 */
st_word(buf + BPB_SecPerTrk, 63);
/* 磁头数,只对特殊的介质才有效,0x00FF=255 */
st_word(buf + BPB_NumHeads, 255);
/* EBR分区之前所隐藏的扇区数,0x0004A800=305152又出现了呢,与MBR中地址0x1C6开始的4个字节数值相等 */
st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /
if (fsty == FS_FAT32) {
st_dword(buf + BS_VolID32, vsn);
st_dword(buf + BPB_FATSz32, sz_fat);
st_dword(buf + BPB_RootClus32, 2);
st_word(buf + BPB_FSInfo32, 1);
st_word(buf + BPB_BkBootSec32, 6);
buf[BS_DrvNum32] = 0x80;
buf[BS_BootSig32] = 0x29;
memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19);
}
/* 签名标志“55 AA */
st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */
/* 将数据信息写入到DBR中*/
if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */
......
/* 创建MBR及DPT*/
fr = create_partition(pdrv, lba, sys, buf);
......
}
6.创建MBR及DPT,64 个字节(偏移 1BEH~偏移 1FDH)为DPT(DiskPartitionTable,硬盘分区表),最后的两个字节“55 AA”
/* Create partitions on the physical drive in format of MBR or GPT
* drv: Physical drive number
* plst[]: Partition list
* sys: System ID (for only MBR, temp setting)
* buf: Working buffer for a sector */
static FRESULT create_partition (BYTE drv, const LBA_t plst[], BYTE sys, BYTE* buf)
{
UINT i, cy;
LBA_t sz_drv;
DWORD sz_drv32, nxt_alloc32, sz_part32;
BYTE *pte;
BYTE hd, n_hd, sc, n_sc;
/* 获取物理磁盘扇区总数 */
if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR;
/* 在MBR中创建磁盘DPT信息 */
sz_drv32 = (DWORD)sz_drv;
/* 每柱面扇区数 */
n_sc = N_SEC_TRACK;
/* 根据总扇区大小,计算出磁头个数 */
for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ;
if (n_hd == 0) n_hd = 255;
/* 清空MBR buf信息 */
memset(buf, 0, FF_MAX_SS);
/* 偏移到DPT处, MBR_Table = 446 */
pte = buf + MBR_Table;
for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) {
/* 获取该分区总扇区数 */
sz_part32 = (DWORD)plst[i];
/* 计算该分区有效扇区总数, 如果传入参数是百分比大小,则根据磁盘百分比来计算 */
if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32;
if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32;
/* 如分区大小为零,则退出分区创建 */
if (sz_part32 == 0) break;
/* 记录从磁盘开始到该分区的开始的偏移扇区数(相对扇区数)*/
st_dword(pte + PTE_StLba, nxt_alloc32);
/* 记录该分区的总扇区数 */
st_dword(pte + PTE_SizLba, sz_part32);
/* 记录系统ID */
pte[PTE_System] = sys;
/* 填充开始结束CHS*/
cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* 开始柱面 */
hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* 开始磁头 */
sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* 开始扇区 */
pte[PTE_StHead] = hd;
pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
pte[PTE_StCyl] = (BYTE)cy;
cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* 结束柱面 */
hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* 结束磁头 */
sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* 结束扇区 */
pte[PTE_EdHead] = hd;
pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
pte[PTE_EdCyl] = (BYTE)cy;
/* 偏移到下一个分区表项地址,记录信息 */
pte += SZ_PTE;
}
/* 填充MBR结束标志 */
st_word(buf + BS_55AA, 0xAA55); /* MBR signature */
/* 将数据写入到磁盘的第1个扇区0开始 */
if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */
return FR_OK;
}
文章评论