1、一、通过格式化命令-看磁盘文件系统的建立过程1、添加 format 命令,单步调试所有的底层驱动函数都已经准备好。添加格式化命令 format 后,编译下载。Format 命令的执行主要是调用 f_mkfs()函数,下面进行单步调试。以下主要列出函数的主要执行步骤:res=f_mkfs( 0, 1, 4096 ); /1表示不需要引导扇区。4096是8个扇区。进入 f_mkfs()函数,这里只列出主要执行步骤:if (disk_ioctl(drv, GET_SECTOR_COUNT, 所以文件系统确定为 FAT32类型。n_fat = (n_clst * 4) + 8 + SS(fs) - 1
2、) / SS(fs); 等于0x3CE = 974,表示 FAT 要占据974个扇区。n_rsv = 33 - partition; 保留扇区32个。n_dir = 0;b_fat = b_part + n_rsv; /* FATs start sector 32扇区*/b_dir = b_fat + n_fat * N_FATS; /* Directory start sector 0x3EE =1006,由于 FAT 表个数设为1个,所以目录区=FAT 起始+FAT 占用扇区数*/b_data = b_dir + n_dir; /* Data start sector */以上三项确定 F
3、AT 区域、根目录区、数据区的起始扇区。disk_ioctl(drv, GET_BLOCK_SIZE, n_fat += (n - b_data) / N_FATS;这两句话对 fat 所占扇区数进行了修正,保证擦除时,以 32个扇区为一个单位。n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; =0x1E588。tbl = fs-win; /* Clear buffer */mem_set(tbl, 0, SS(fs); 清零文件系统缓冲区。mem_set(tbl, 0, SS(fs);ST_DWORD(tbl+BS
4、_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */ST_WORD(tbl+BPB_BytsPerSec, SS(fs); /* Sector size */tblBPB_SecPerClus = (BYTE)allocsize; /* Sectors per cluster */ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */上面的工作主要是填充 引导扇区缓冲区,也就是常说的 DBR 扇区缓冲,等所有的参数写好,就可以写回磁盘。ST_WORD(tbl+BS_55AA, 0xAA5
5、5); /* Signature */if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)return FR_DISK_ERR; /这就是在写有效引导标志 sec510=0x55, sec511=0xAA。if (fmt = FS_FAT32)disk_write(drv, tbl, b_part+6, 1); /FAT32在第六扇区有个备份引导扇区。for (m = 0; m win, 0, SS(fs);tbl = fs-win+MBR_Table; /分区表从0x1BE 开始。ST_DWORD(tbl, 0x00010180); /* Part
6、ition start in CHS */ Table0x1BE = 0x80,表明该分区是活动扇区。00表示开始柱面,01、01表示开始扇区、开始磁头。if (n_disk 2) | 63); /结束的扇区。 else ST_WORD( /tbl5 = 254; /结束的磁头。if (fmt != FS_FAT32) /* System ID */tbl4 = (n_part win, 0, 1) != RES_OK)return FR_DISK_ERR;partition = 0xF8; /MBR 标志。 else partition = 0xF0;3、观察在有 MBR 区域的情况下,如何
7、检查文件系统fmt = check_fs(fs, bsect = 0); /* 检查0扇区的时候,没有发现 FAT 文件系统扇区,但是有0x55 0xAA 标志,说明这是有效磁盘,但是返回1. */if (fmt = 1) /* 表明可能存在分区 */* Check a partition listed in top of the partition table */tbl = /* Partition table */if (tbl4) 实际这里应该是0x0c, 表示 FAT32系统。bsect = LD_DWORD( /* 这个是文件系统 引导扇区的号码。 */fmt = check_fs
8、(fs, bsect); /* 再到这个扇区检查是否存在 FAT 文件系统标志。 */ 执行过后,仍然能够建立完整的 文件系统信息 结构体,只是里面的 FAT 分配起始扇区、数据区起始扇区地址相对 没有 MBR 的时候改变了,其它都差不多。二、将 SD 卡格式化成具有两个分区的磁盘。1、目的(1)深入理解 MBR、DPT 等概念。(2)修改 ff.c 中的 f_mkfs 函数,得到一个新函数,f_format(u8 partition,u16 allocsize),前一个参数是指磁盘等分的个数,接受 1、2、3、4四个参数,默认为1,最大为4。后一个参数是指 每簇占用的字节数。(3)添加命令
9、fdisk,调用上述函数。执行完成后,用读卡器在 PC 上读取该 SD 卡,应该显示两个可移动磁盘。2、f_format()函数的编写首先新建一个文件 fext.c,该文件就实现一个 函数 f_format.c,首先将 f_mkfs()函数复制过来,在此基础上修改。编译后,首先解决警告和错误:包含头文件 ff.h 和 diskio.h。引用了 ff.c 中的静态函数 mem_set()和mem_clr(),复制过来。定义 Null 为0。将 FATFS * FatFs_Drives做外部声明。同时发现,不同 c 源文件中 #define 同样的宏相互之间是不影响的。说明预处理的时候是一个一个文
10、件处理的,不检查相互之间的关联。但同一个文件中,一个宏不能两次定义。#ifndef NULL#define NULL 0#endif /采取这种方式,主要是防止其他 包含的头文件也对 NULL 进行了定义。函数中主要修改的地方就是:n_part = n_part /drv; /进行 drv 等分。每个磁盘的扇区总数就是这么多。在 DPT 对应增加的分区部分,填好分区表16个字节。特别重要的是四个地方:0字节写为00或0x80,第四字节写入0xc0 表示 FAT32系统。 (第一次调试找不到新磁盘,就是由于这个字节默认为0) 。第8-11写入分区引导扇区的线性扇区地址。第12-15写入该磁盘分区
11、的大小。for ( i=“0“; icdir,如果为0,表明是在磁盘根目录,显示0:或者1:。如果该簇号等于根目录所在簇号,当前目录也是在跟目录。如果不是在根目录,那就要逐层往上搜索了。根据 f_opendir (DIR,.目录)可以回溯到上层目录,DIR 结构体得到了上层目录(X+1)的起始簇号,目录项指针指向第0项。用 f_readdir()逐步读出目录项属性,得到 FILINFO 结构体。如果其簇号 等于 文件系统的当前搜索簇号,则其名称就是所要得到的本层目录名。如果是根目录下则为 0:/X 目录名。当前搜索簇号设为(X+1)的起始簇号。再次调用 f_opendir( DIR,.目录),
12、此次得到(X+2)的起始簇号,判断是否根目录。查找当期搜索簇号在(X+2)目录层中对应的目录项,并获取 X+1层的目录名。如果是在根目录,则为0:/X+1目录名/X 目录名。循环直到到达根目录跳出搜索。目录名缓冲区的处理方法。定义总长度为100字节。Path99=0 ;初始化时先让指针指向 Path【99】 。得到一个目录,向前移动名称那么长,复制名称。再往前移动1,添加/标志。如果是根目录添加:,往前移动1,在添加 驱动号,然后可以显示整个字符串。(2)代码实现for ( ; ; )if ( CurClust = 0 | CurClust = CurFileSys-dirbase ) Pat
13、hPtr-;*PathPtr-=:; /*PathPtr = CurDrive+0; /如果当前簇号对应根目录。break; res = f_opendir ( /第一次执行时,DIR 结构体的开始簇号变为X+1层目录的簇号 /第二次时,变为 X+2层目录的簇号。if ( res!= FR_OK ) break;do res = f_readdir ( TempClust = FileAttrib.sclust;if ( TempClust = CurClust ) break; /如果该目录项的簇号等于当前搜索簇号,它的名称就是当前需要的目录名 while( res = FR_OK );if
14、 ( res!= FR_OK ) break;DirLen= Str_Length ( (const char*)FileAttrib.fname ); /这里要得到字符串的长度。PathPtr -= DirLen; /文件指针往后退目录名长度mem_cpy ( (void *)PathPtr, ( const void*)FileAttrib.fname, DirLen);PathPtr-;*PathPtr=/; /添加文件间隔/符号。CurClust = DirInf.sclust; /当前搜索簇号跟着上移。Uart_PutString( PathPtr); /这是显示的当前目录。Uart
15、_PutString( “rn“);编译,下载,显示两层目录是正确的。但是目录一旦到第三层,就进入死循环。还要接着调试。(3)调试经调试发现 res = f_opendir ( UCHAR c, f, r;ULONG val;char s16;int i, w, res, cc;va_start(arp, str); /arp 实际是一个通用类指针,指向 str 后的第一个参数。for (cc = res = 0; cc != EOF; res += cc) c = *str+;if (c = 0) break; /* End of string */if (c != %) /* Non es
16、cape cahracter */cc = f_putc(c, fil); /如果不是%,表示正常字符,写入文件if (cc != EOF) cc = 1; /cc 是本次写对应的字符数字。当然如果写入不成功,返回 EOF,则跳出循环continue; /res 是总共写入的数字。w = f = 0; /从此处开始表示遇到了%后的处理c = *str+; /取出下一个字符。if (c = 0) /* Flag: 0 padding */f = 1; c = *str+; /如果是字符 0,表示要控制宽度,flag 第0位标志置位。取出下一字符while (c = 0 /十六进制,3A 表示大写
17、 A,但实际的字符 A 是0x41,中间差7.s-i = c; /前一位存储该字符。val /= r; /val 变成123,也就是把尾端 一位去掉。 while (i /最多存储15位,要不然溢出。if (i /如果为负数,还要加上符号标志。w = sizeof(s) - 1 - w; /要求的宽度计算。8位宽,则填充从第7位开始。7-14 ,正好8位。while (i /如果0填充置位填充 0,否则填充空格。i 退到0,或者 i 推导W 都结束填充。cc = f_puts( va_end(arp);return (cc = EOF) ? cc : res;把代码看了一遍,基本还是能看懂。4
18、、移植。(1)添加命令 fstring,该命令一个参数,是用户字符串。以附加的写入到指定的文件 fstring.txt。(2)实习方法以写文件的方式,打开文件,然后指针移动到文件末尾。调用代码 f_printf( /res=f_lseek( f_printf (f_close (功能比较简单,测试通过。六、支持长文件名(一) 。1、代码页的功能(1)在原来的程序中,我把 #define _CODE_PAGE 936,代码页定义为936.这时候可以在磁盘上新建中文名称的文件夹和文件, (通过 fwrite 命令实现,只要数目小于4个就行,也可以用 fread 命令读出来) ,也可以显示中文文件名
19、 。 (通过命令 flist 来实现) 。(2)我把 #define _CODE_PAGE 1,代码页定义为1后,调用 flist 能正常显示中文名。但是调用fread 和 fwrite 命令不能读取和创建中文名文件了。进入代码分析,显示时关键在函数 get_fileinfo() ,它原封不动的将系统标准名 转换为一般字符串,对字符串不做任何处理,送到串口以后自然可以正常显示。但是读取文件和写入文件时,首先调用 f_open()函数 ,进入 follow_path()函数,然后调用create_name() ,当它遇到大于 0x80 的字符时,产生如下效果:if (c = 0x80) /* Extended char */#ifdef _EXCVTc = cvtc - 0x80; /* Convert extend char (SBCS) */#elseb |= 3; /* Eliminate NT flag if ext char is exist */