原创 智林STM32开发板上的FatFS移植过程分析(六)

2010-3-18 21:18 7176 7 9 分类: MCU/ 嵌入式

 


<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 


八、读文件执行流程分析


l        输入读命令fread 0:/111/aaa.txt


在我的SD卡,根目录下有两个可见文件aaa.txtccc.txt,两个目录111222。目录111下面有三个文件aaa.txtbbb.txtccc.txt。上面这个命令的作用是读取111目录下aaa.txt的内容,这个文件的大小是0x0564,里面都是12345这些数字。


在我的程序的开头,首先执行了这么一个函数:


FATFS FileSys; 


res=f_mount(0,&FileSys); //这个函数的作用是将ff.c内定义的FATFS *FatFs[_DRIVES]这个文件系统结构指针指向用户定义的文件系统结构。初始化程序填充这个结构,而其它函数可以使用里面的参数。


程序对输入命令进行解析,得到argc=2, argv[0]=”fread”, argv[1]=”0:/111/aaa.txt”。系统调用UartCmdFRead函数。


 


l       文件执行到f_open(&FileInf,argv[1],FA_READ | FA_OPEN_ALWAYS);


FileInf是前面定义的一个文件信息结构体,将指针传递给这个函数,是程序获得argv[1]文件的信息后,填充该结构体的。


单步执行进去,首先定义了DIR dj,这是目录信息结构体,然后NAMEBUF(sfn, lfn);作用是为文件名准备空间。Sfn所指向的空间最后存放标准短文件名,比如aaa.txt,存进去应该是aaa     txt


u       然后执行res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));这个函数的主要作用是初始化磁盘、检查是否存在文件系统等,这里这个path= argv[1]= ”0:/111/aaa.txt”


进入到这个函数,首先const XCHAR *p = *path;这个path= &argv[1],而p= argv[1],实际指向0:/中的0然后执行


vol = p[0] - '0';  


if (vol <= 9 && p[1] == ':') { 


             p += 2; *path = p;      } 执行完这句以后*path=p指向111前面的/


*rfs = fs = FatFs[vol];执行完这句,目录结构的指针指向用户定义的文件系统结构体


stat = disk_initialize(fs->drive);   这句对磁盘进行初始化,成功则磁盘可用。


 


u       fmt = check_fs(fs, bsect = 0);这句是检查是否存在文件系统。进入这个函数,


       if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK)       /*读取引导扇区 */


return 3; 这里读入缓冲区是文件系统结构体中自带的缓冲区,其地址为0x20001FB0


       if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)       /* 检查引导标志 */


              return 2;


       if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)


              return 0;  //三个字符表式FAT


l         回到chk_mounted函数,


       fsize *= fs->n_fats;             


       fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt);


       fs->csize = fs->win[BPB_SecPerClus];                  


       fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);       /* Nmub


这些实际上是利用引导扇区里的信息对文件系统结构体进行填充,这里再熟悉一下这个结构。


typedef struct _FATFS_ {


       BYTE      fs_type;   /* FAT类型, FAT32的数值为3*/


       BYTE      csize;             /* 每簇多少扇区 一般为8*/


       BYTE      n_fats;           /* FAT表的数目,一般为2 */


       BYTE      wflag;            /* 扇区缓冲内容是否修改过,为1时要写回磁盘) */


       BYTE      fsi_flag;   /* FAT32特有,当空闲簇数目和当前可分配簇改变时,信息要写入FSI扇区,一般都是1号扇区 */


       WORD    n_rootdir ;      /* FAT32没有用到,为0 */


       DWORD last_clust;       /* 可分配簇簇号*/


       DWORD free_clust;      /* 空闲簇的数量 */


       DWORD fsi_sector;      /* fsinfo扇区,为1 */


 


       DWORD sects_fat;       /* 每个分配表所占用的扇区,我的SD卡上为0x3cc */


       DWORD max_clust;      /* 最大簇号,可用簇=最大簇号-2,这个一般是根据磁盘最大扇区数-保留-FAT*2后再除以每簇扇区数得到的,我的SD卡上是0x1E58B,这个数据也是区分FAT16FAT32的依据。 */


       DWORD fatbase;   /* FAT表开始扇区,保留扇区后,一般为32 */


       DWORD dirbase;   /* 根目录开始扇区,根目录一般占用2号簇*/


       DWORD database; /* 数据区开始簇号,就是根目录开始扇区,2号簇,紧跟在FAT表后,故其数值为32+2*0x3cc=0x7B8=1976号扇区  */


       DWORD winsect;  /* 当前缓冲区内容对应的扇区号 */


       BYTE      win[512];/* D目录和文件系统工作所用扇区缓冲区。 */


} FATFS;


l         回到f_open函数


如果chk_mounted返回FR_OK则磁盘上文件系统可用。且文件系统的关键信息都已经写入结构体。


INITBUF(dj, sfn, lfn);这是对目录结构体的一次操作,有必要先了解一下:


typedef struct _DIR_ {


       FATFS*   fs;                 /* 指向文件系统 */


       WORD    index;             /* 当前的目录项索引号,一项占32个字节 */


       DWORD sclust;            /* 当前目录的开始簇号 */


       DWORD clust;      /* 当前扇区对应的当前簇,因为一个目录可能占据多簇 */


       DWORD sect;              /* 当前扇区号 */


       BYTE*    dir;  /* 指向文件系统缓冲扇区win[] 里短文件名对应的目录项*/


       BYTE*    fn;                 /* 指向标准短文件名数组 */


} DIR;


INITBUF(dj, sfn, lfn)执行完后,fn指向标准短文件名数组


res = follow_path(&dj, path);这个函数的作用是从文件路径中一层一层取出目录名和文件名。然后将最后一级目录扇区所在簇、扇区号、最后对应文件在目录扇区中的位置填入目录机构体。下面跟踪进去看一下。


 


u      进入函数follow_path(&dj, path)


现在这个path=”/111/aaa.txt”path指向/


       if (*path == '/' || *path == '\\') /* Strip heading separator if exist */


             path++; 执行完这句后,path=”111/aaa.txt”,前面的/已经跳过


进入获取目录、文件名的循环:


for (;;) { res = create_name(dj, &path);    这个函数的作用从path中抽出第一层的名称111,调整path=/aaa.txt”,处理111变为标准短文件名。


u      进入函数create_name(dj, &path)


       sfn = dj->fn;


       mem_set(sfn, ' ', 11);  将目录结构体指向的短文件名数组填充空格


       si = i = b = 0; ni = 8;


       p = *path;   现在p指向”111/aaa.txt”


       for (;;) {


              c = p[si++];


              if (c <= ' ' || c == '/' || c == '\\') break;  这句的作用现在可以看明白了,遇到111后面的/,循环退出,本层的。


sfn[i++] = c; 取出路径名中的文件名存入目录的标准短文件名数组}


        *path = &p[si];  现在*path直接指向aaa.txt”,前面的/都已经去掉了。


c = (c <= ' ') ? NS_LAST : 0; 获取名字是否已经结束。


返回到函数follow_path后,马上又进入新的函数dir_find(dj),这个函数的作用是找到目录结构体dj->fn对应的dj->dir,使它直接指向目录项,并获取该目录项的起始簇号等信息。


 


u      进入函数dir_find(dj)


马上又进入函数res = dir_seek(dj, 0);这是从索引0开始。第二个参数是目录项索引号。


       dj->index = idx;


       clst = dj->sclust;


       if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */  clst=0说明是第一层次的目录。


clst = dj->fs->dirbase;第一层次目录,从2号簇也就是根目录区簇开始。


       ic = SS(dj->fs) / 32 * dj->fs->csize;     /* 计算出一簇的最大索引号127<128 */


dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);将簇号和目录项索引号转换为对应扇区号,


dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;此时目录项所指只是指定索引号目录项对应的扇区缓冲中的地址。此时缓冲区还没有读入根目录扇区,所以此时指针指向的内容无效。


u      进入函数dir_find(dj)


       do {


              res = move_window(dj->fs, dj->sect); 执行完这句,文件系统缓冲区已经读入了根目录区第一扇区的内容。


              if (res != FR_OK) break;


              dir = dj->dir;                              


              c = dir[DIR_Name];  取得第一个字符,如果为0,则找不到文件


              if (c == 0) { res = FR_NO_FILE; break; }   /* Reached to end of table */


if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11))


                     break; 如果经比较就是该目录项,则indexdir指针保留。跳出循环表明已经找到目录或文件。


res = dir_next(dj, FALSE);           /* 进入函数dir_next(dj, FALSE) */


       } while (res == FR_OK);


u      进入函数dir_next(dj, FALSE)


i = dj->index + 1; 用于查找下一项。


       dj->index = i;


       dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;


可以说这个函数主要是处理查找目录项递增时,如果扇区数和簇号发生变化时的处理。


u      进入函数dir_find(dj)


开始循环,又执行res = move_window(dj->fs, dj->sect);此时缓冲区扇区与所要读取扇区内容一致,所以这个函数直接返回。


当查找到索引11的时候,目录项被找到了,于是


if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11))


                     break;从这里跳出,回到上一层函数create_name(dj, &path),此时目录结构体中的sclustclustsectdir三个存储了足够的读取该目录的信息。


l         回到函数follow_path(&dj, path)


res = create_name(dj, &path);      /* Get a segment */


if (res != FR_OK) break;


res = dir_find(dj);                


last = *(dj->fn+NS) & NS_LAST; 从这句开始进行,在create_name(dj, &path)执行时已经设置了路径是否结束的标志位,这里进行读取。


if (last) break;        /* 如果有结束标志,说明最后的名字是个文件,dir已经指向该文件的目录项,所有的文件信息都可以由此而得,直接跳出循环。. */


dir = dj->dir;          /* 如果没有结束,说明这可定是个目录 */


if (!(dir[DIR_Attr] & AM_DIR)) { /* 如果其性质不是目录,错误跳出循环 */


       res = FR_NO_PATH; break;                }


dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);


              }


l        循环执行,又回到上面,进入函数create_name.


res = create_name(dj, &path);


这次执行时path=aaa.txt”,转换成标准短文件名存入dj->fn所指向的缓冲区。


       *path = &p[si];                                         /* Rerurn pointer to the next segment */


       c = (c <= ' ') ? NS_LAST : 0;              /* Set last segment flag if end of path */


在拷贝完成后path=null,并在dj->fn所指向的缓冲区设置了结束标志。


       if ((b & 0x03) == 0x01) c |= NS_EXT;       /* 扩展名都是小写*/


       if ((b & 0x<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />0C) == 0x04) c |= NS_BODY;    /*文件名都是小写 */


       sfn[NS] = c; c包括结束和文件名的大小写信息。存入目录结构体信息区。


l         循环执行,又回到上面,进入函数dir_find(dj).


res = dir_find(dj);   这次开始执行时sclust指向6号簇,这是111目录所在的簇。


res = dir_seek(dj, 0);仍然是从索引0开始搜寻目录项。这个函数执行完成后clust也指向6号簇,sector等于此簇对应的第一个扇区,dir指向扇区缓冲对应索引位置。


这下在索引2位置就找到了该文件aaa.txt”,索引很快返回


l         回到函数follow_path(&dj, path)


res = create_name(dj, &path);      /* Get a segment */


if (res != FR_OK) break;


res = dir_find(dj);                        /* Find it */


last = *(dj->fn+NS) & NS_LAST; 这个标志位被设置。表明路径已经搜索结束


if (last) break;                      /*跳出循环. */


此时只要有目录结构体里的sectdir两个参数就可以读取该文件的数据。


l         终于又回到f_open函数


       接下来的工作就是填充文件结构体:


fp->dir_sect = dj.fs->winsect;             /* 指向文件目录所在扇区2008 */


       fp->dir_ptr = dj.dir;  指向文件所在目录扇区缓冲对应的文件目录项。


       fp->flag = mode;           比如说0x11代表总是打开和读取模式


       fp->org_clust =             文件的起始簇,通过文件目录项获得。


((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);


       fp->fsize = LD_DWORD(dir+DIR_FileSize);      /*文件的大小 */


       fp->fptr = 0;          文件指针清0,指向文件的开始。


 fp->csect = 255;           /* 文件当前扇区 */


       fp->dsect = 0;   文件的数据扇区


 


 


 


 

文章评论2条评论)

登录后参与讨论

用户377235 2015-7-19 14:58

好乱

用户567466 2015-3-11 22:52

很好

用户377235 2014-2-28 20:42

我也没有解决哎

用户443625 2013-8-21 10:28

你好,为什么我这么设置之后还不能解决问题?
相关推荐阅读
nthq2004 2010-05-08 20:04
USB自定义设备驱动02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  本来还想编写应用程序测试一下自定...
nthq2004 2010-05-07 21:35
USB自定义设备驱动01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、USB设备驱动入门1、学习目...
nthq2004 2010-05-04 21:01
智林开发板上实现自定义的USB HID设备
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、自定义HID设备的相关概念1...
nthq2004 2010-05-01 21:58
U盘例程在智林开发板上的移植
 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 一、移植前的准备工作1、有哪些操...
nthq2004 2010-04-30 19:19
U盘实现流程跟踪分析02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   二、追踪USB大容量设备的实现...
nthq2004 2010-04-27 21:51
U盘实现流程跟踪分析01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   一、追踪USB大容量设备的实现...
我要评论
2
7
关闭 站长推荐上一条 /2 下一条