FatFs源码剖析

来源:互联网 发布:mac桌面图标怎么隐藏 编辑:程序博客网 时间:2024/04/30 05:00

一、介绍:

本文以网上开源文件系统FatFs 0.01为研究对象,剖析FatFs文件系统的核心操作。FatFs目前最新版本已更新到0.10a版本,而我之所以选择0.01版本,是因为这是最早的发布版本,与最新的版本相比,去掉了很多高级应用,且代码量相对较小,宏开关也少了许多,易于阅读和理解,用来研究它的雏形再合适不过了,所以笔者选择0.01版本进行剖析。当大家了解了0.01的核心思想后,再回去看最新的版本,也就容易理解多了。

FatFs历史版本下载:http://elm-chan.org/fsw/ff/00index_e.html    在官网的最下面,能找到所有版本的下载链接。

 

二、重点:

1. FatFs中有两个重要的缓冲区:win[]和buffer。win[]在FATFS结构体中,buffer在FIL结构体中。

    win[]:系统缓冲区。当操作MBR,DBR,FAT表,根目录区时,使用该缓冲区;

    buffer:文件缓冲区。当对文件的内容进行操作时,如读、写、修改时,才使用该缓冲区。

   

2. 在对文件的f_read和f_write过程中(不考虑f_lseek的情况),只有需要读写的最后一个扇区(内容小于512字节)才会被暂存到buffer中,而前面的扇区内容是直接通过磁盘驱动disk_read/disk_write在用户缓冲区和物理磁盘之间进行交互的。

 

注意:FatFs 0.01与最新的版本还是不少差异的,如在0.01中,buffer只是个指针,需要用户自己定义文件缓冲区,并将buffer指向该缓冲区。而最新版本中,FIL已经有自己的buf[_MAX_SS]。在0.01中没有f_mount()函数,所以需要手动将全局指针FatFs指向自己定义的变量fs。具体的操作流程可参考官网上的示例代码。

 

三、关键函数总结:

1. f_open

 f_open()

{

    1. 初始化SD卡,初始化FATFS对象;

    2. 查找文件的目录项;

    3. 填充文件结构体FIL。

}

 

2. f_read

f_read()

{

    1. 从物理磁盘直接读取所需扇区到用户缓冲区,这个过程中未使用buffer缓冲区;

    2. 如果最后一扇区要读取的字节数少于512,则先将最后一扇区读到buffer中,然后再从buffer拷贝需要的字节到用户缓冲区中。

}

 

 

 

3. f_write

f_write()

{

    1. 从用户缓冲区直接写入到物理磁盘,这个过程中未使用buffer缓冲区;

    2. 如果要写入的最后一扇区内容少于512字节,则先从物理磁盘读取最后一扇区的内容到buffer中,然后修改buffer中的相应内容,并设置回写标记,这样下次调用f_close/f_write时,就会将buffer回写到物理磁盘中。

}

 

 

 

4. f_close

关键是调用了f_sync函数。

 

5. f_sync

f_sync()

{

    1. 将buffer回写到物理磁盘中;

    2. 读取文件对应的目录项到win[]中,更新参数,并回写。

}

 

 

 

 

5. move_window

move_window专用于操作win[]系统缓冲区。

 

move_window(B);

调用前:

                     

 

执行中:

 

 

调用后:

                      

 

 

四、源码注释

本人在不破坏源码逻辑的前提下,对FatFs 0.01源代码进行了中文注释,个别函数重新修改了排版布局,以方便阅读。结合以上示意图即伪代码,相信大家会很快理解FatFs 0.01的核心思想及架构。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <string.h>  
  2. #include "ff.h"         /* FatFs declarations */  
  3. #include "diskio.h"     /* Include file for user provided functions */  
  4.   
  5.   
  6. FATFS *FatFs;           /* Pointer to the file system object */  
  7.   
  8.   
  9. /*------------------------------------------------------------------------- 
  10.   Module Private Functions 
  11. -------------------------------------------------------------------------*/  
  12.   
  13. /*----------------------*/  
  14. /* Change Window Offset */  
  15.   
  16. //读取新的扇区内容到临时缓冲区win[],  
  17. //如果sector为0,则只需要把win[]的内容写入对应的物理扇区即可  
  18. static  
  19. BOOL move_window (  
  20.     DWORD sector        /* Sector number to make apperance in the FatFs->win */  
  21. )                       /* Move to zero only writes back dirty window */  
  22. {  
  23.     DWORD wsect;        //wsect用于检索FAT备份表的相应扇区  
  24.     FATFS *fs = FatFs;  
  25.   
  26.   
  27.     wsect = FatFs->winsect;  
  28.     /*首先检查目标扇区号是否与win[]对应的扇区号相同,如果相同则不进行任何操作。*/  
  29.     if (wsect != sector) {  /* Changed current window */  
  30. #ifndef _FS_READONLY  
  31.         BYTE n;  
  32.   
  33.         //首先检测win[]的内容是否做过修改,如果修改过,则需要先将其写入SD卡中。  
  34.         if (FatFs->dirtyflag) {  /* Write back dirty window if needed */  
  35.             if (disk_write(FatFs->win, wsect, 1) != RES_OK) return FALSE;  
  36.   
  37.             //清除修改标记  
  38.             FatFs->dirtyflag = 0;  
  39.               
  40.             /*如果当前操作的是FAT表,那么需要将修改后的FAT表拷贝到对应的FAT备份中*/  
  41.             if (wsect < (FatFs->fatbase + FatFs->sects_fat)) { /* In FAT area */  
  42.                 for (n = FatFs->n_fats; n >= 2; n--) {    /* Refrect the change to all FAT copies */  
  43.                     wsect += FatFs->sects_fat;  
  44.                     if (disk_write(FatFs->win, wsect, 1) != RES_OK) break;  
  45.                 }  
  46.             }  
  47.         }  
  48. #endif  
  49.   
  50.         //然后再读取新的扇区内容到win[]中  
  51.         if (sector) {  
  52.             if (disk_read(FatFs->win, sector, 1) != RES_OK) return FALSE;  
  53.             FatFs->winsect = sector; //更新当前缓冲区的扇区号  
  54.         }  
  55.     }  
  56.     return TRUE;  
  57. }  
  58.   
  59.   
  60.   
  61.   
  62.   
  63.   
  64.   
  65. /*--------------------------------------------------------------------------*/  
  66. /* Public Funciotns                                                         */  
  67. /*--------------------------------------------------------------------------*/  
  68.   
  69.   
  70. /*----------------------------------------------------------*/  
  71. /* Load File System Information and Initialize FatFs Module */  
  72. //本函数做三件事:  
  73. // 1.初始化SD卡  
  74. // 2.检查文件系统类型,FAT16还是FAT32  
  75. // 3.填充FatFs对象,即记录物理磁盘的相关参数  
  76. FRESULT f_mountdrv ()  
  77. {  
  78.     BYTE fat;  
  79.     DWORD sect, fatend;  
  80.     FATFS *fs = FatFs;  
  81.   
  82.   
  83.     if (!fs) return FR_NOT_ENABLED;  
  84.   
  85.     //首先对文件系统对象清空  
  86.     /* Initialize file system object */  
  87.     memset(fs, 0, sizeof(FATFS));  
  88.   
  89.     //然后初始化SD卡  
  90.     /* Initialize disk drive */  
  91.     if (disk_initialize() & STA_NOINIT) return FR_NOT_READY;  
  92.   
  93.     //接着收搜索DBR系统引导记录,先检查第0扇区是否就是DBR(无MBR的SD卡),如果是则检查文件系统的类型;  
  94.     //如果不是则说明第0扇区是MBR,则根据MBR中的信息定位到DBR所在扇区,并检查该文件系统的类型  
  95.     /* Search FAT partition */  
  96.     fat = check_fs(sect = 0);       /* Check sector 0 as an SFD format */  
  97.     if (!fat) {                     /* Not a FAT boot record, it will be an FDISK format */  
  98.         /* Check a pri-partition listed in top of the partition table */  
  99.         if (fs->win[0x1C2]) {                    /* Is the partition existing? */  
  100.             sect = LD_DWORD(&(fs->win[0x1C6]));  /* Partition offset in LBA */  
  101.             fat = check_fs(sect);               /* Check the partition */  
  102.         }  
  103.     }  
  104.     if (!fat) return FR_NO_FILESYSTEM;  /* No FAT patition */  
  105.   
  106.     //初始化文件系统对象,根据DBR参数信息对Fs成员赋值  
  107.     /* Initialize file system object */  
  108.   
  109.     //文件系统类型:FAT16/FAT32  
  110.     fs->fs_type = fat;                               /* FAT type */  
  111.   
  112.     //单个FAT表所占的扇区数  
  113.     fs->sects_fat =                              /* Sectors per FAT */  
  114.         (fat == FS_FAT32) ? LD_DWORD(&(fs->win[0x24])) : LD_WORD(&(fs->win[0x16]));  
  115.   
  116.     //单个簇所占的扇区数  
  117.     fs->sects_clust = fs->win[0x0D];              /* Sectors per cluster */  
  118.   
  119.     //FAT表总数  
  120.     fs->n_fats = fs->win[0x10];                       /* Number of FAT copies */  
  121.   
  122.     //FAT表起始扇区(物理扇区)  
  123.     fs->fatbase = sect + LD_WORD(&(fs->win[0x0E]));   /* FAT start sector (physical) */  
  124.   
  125.     //根目录项数  
  126.     fs->n_rootdir = LD_WORD(&(fs->win[0x11]));        /* Nmuber of root directory entries */  
  127.   
  128.     //计算根目录起始扇区、数据起始扇区(物理扇区地址)  
  129.     fatend = fs->sects_fat * fs->n_fats + fs->fatbase;  
  130.     if (fat == FS_FAT32) {  
  131.         fs->dirbase = LD_DWORD(&(fs->win[0x2C])); /* Directory start cluster */  
  132.         fs->database = fatend;                       /* Data start sector (physical) */  
  133.     } else {  
  134.         fs->dirbase = fatend;                        /* Directory start sector (physical) */  
  135.         fs->database = fs->n_rootdir / 16 + fatend;   /* Data start sector (physical) */  
  136.     }  
  137.   
  138.     //最大簇号  
  139.     fs->max_clust =                              /* Maximum cluster number */  
  140.         (LD_DWORD(&(fs->win[0x20])) - fs->database + sect) / fs->sects_clust + 2;  
  141.   
  142.     return FR_OK;  
  143. }  
  144.   
  145.   
  146.   
  147.   
  148.   
  149. /*-----------------------*/  
  150. /* Open or Create a File */  
  151.   
  152. FRESULT f_open (  
  153.     FIL *fp,            /* Pointer to the buffer of new file object to create */  
  154.     const char *path,   /* Pointer to the file path */  
  155.     BYTE mode           /* Access mode and file open mode flags */  
  156. )  
  157. {  
  158.     FRESULT res;  
  159.     BYTE *dir;  
  160.     DIR dirscan;  
  161.     char fn[8+3+1];         //8.3 DOS文件名  
  162.     FATFS *fs = FatFs;  
  163.   
  164.     /*首先初始化SD卡,检测文件系统类型,初始化FATFS对象*/  
  165.     if ((res = check_mounted()) != FR_OK) return res;  
  166.       
  167. #ifndef _FS_READONLY  
  168.   
  169.     //如果磁盘设置为写保护,则返回错误码:FR_WRITE_PROTECTED  
  170.     if ((mode & (FA_WRITE|FA_CREATE_ALWAYS)) && (disk_status() & STA_PROTECT))  
  171.         return FR_WRITE_PROTECTED;  
  172. #endif  
  173.   
  174.     //根据用户提供的文件路径path,将文件名对应的目录项及其整个扇区读取到win[]中,  
  175.     //并填充目录项dirscan、标准格式的文件名fn,以及目录项在win[]中的字节偏移量dir  
  176.     res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */  
  177.   
  178. #ifndef _FS_READONLY  
  179.     /* Create or Open a File */  
  180.     if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS)) {  
  181.         DWORD dw;  
  182.   
  183.         //如果文件不存在,则强制新建文件  
  184.         if (res != FR_OK) {     /* No file, create new */  
  185.             mode |= FA_CREATE_ALWAYS;  
  186.             if (res != FR_NO_FILE) return res;  
  187.             dir = reserve_direntry(&dirscan);   /* Reserve a directory entry */  
  188.             if (dir == NULL) return FR_DENIED;  
  189.             memcpy(dir, fn, 8+3);       /* Initialize the new entry */  
  190.             *(dir+12) = fn[11];  
  191.             memset(dir+13, 0, 32-13);  
  192.         }   
  193.         else {              /* File already exists */  
  194.             if ((dir == NULL) || (*(dir+11) & (AM_RDO|AM_DIR))) /* Could not overwrite (R/O or DIR) */  
  195.                 return FR_DENIED;  
  196.               
  197.             //如果文件存在,但又以FA_CREATE_ALWAYS方式打开文件,则重写文件  
  198.             if (mode & FA_CREATE_ALWAYS) {  /* Resize it to zero */  
  199.                 dw = fs->winsect;            /* Remove the cluster chain */  
  200.                 if (!remove_chain(((DWORD)LD_WORD(dir+20) << 16) | LD_WORD(dir+26))  
  201.                     || !move_window(dw) )  
  202.                     return FR_RW_ERROR;  
  203.                 ST_WORD(dir+20, 0); ST_WORD(dir+26, 0); /* cluster = 0 */  
  204.                 ST_DWORD(dir+28, 0);                    /* size = 0 */  
  205.             }  
  206.         }  
  207.   
  208.         //如果是强制新建文件操作,则还需更新时间和日期  
  209.         if (mode & FA_CREATE_ALWAYS) {  
  210.             *(dir+11) = AM_ARC;  
  211.             dw = get_fattime();  
  212.             ST_DWORD(dir+14, dw);   /* Created time */  
  213.             ST_DWORD(dir+22, dw);   /* Updated time */  
  214.             fs->dirtyflag = 1;  
  215.         }  
  216.     }  
  217.     /* Open a File */  
  218.     else {  
  219. #endif  
  220.         if (res != FR_OK) return res;       /* Trace failed */  
  221.   
  222.         //如果打开的是一个目录文件,则返回错误码:FR_NO_FILE  
  223.         if ((dir == NULL) || (*(dir+11) & AM_DIR))  /* It is a directory */  
  224.             return FR_NO_FILE;  
  225.           
  226. #ifndef _FS_READONLY  
  227.   
  228.         //如果以FA_WRITE方式打开Read-Only属性的文件,则返回错误码:FR_DENIED  
  229.         if ((mode & FA_WRITE) && (*(dir+11) & AM_RDO)) /* R/O violation */  
  230.             return FR_DENIED;  
  231.     }  
  232. #endif  
  233.   
  234.     //填充FIL文件结构体参数  
  235. #ifdef _FS_READONLY  
  236.     fp->flag = mode & (FA_UNBUFFERED|FA_READ);  
  237. #else  
  238.     fp->flag = mode & (FA_UNBUFFERED|FA_WRITE|FA_READ);  
  239.     fp->dir_sect = fs->winsect;               /* Pointer to the directory entry */  
  240.     fp->dir_ptr = dir;  
  241. #endif  
  242.     fp->org_clust =  ((DWORD)LD_WORD(dir+20) << 16) | LD_WORD(dir+26); /* File start cluster */  
  243.     fp->fsize = LD_DWORD(dir+28);        /* File size */  
  244.     fp->fptr = 0;                        /* File ptr */  
  245.   
  246.     //这一步很重要,它将直接导致f_read和f_write操作中的逻辑顺序  
  247.     fp->sect_clust = 1;                  /* Sector counter */  
  248.       
  249.     fs->files++;  
  250.     return FR_OK;  
  251. }  
  252.   
  253.   
  254.   
  255. /*-----------*/  
  256. /* Read File */  
  257. //文件读操作  
  258. FRESULT f_read (  
  259.     FIL *fp,        /* Pointer to the file object */  
  260.     BYTE *buff,     /* Pointer to data buffer */  
  261.     WORD btr,       /* Number of bytes to read */  
  262.     WORD *br        /* Pointer to number of bytes read */  
  263. )  
  264. {  
  265.     DWORD clust, sect, ln;  
  266.     WORD rcnt;      /*已经读取的字节数*/  
  267.     BYTE cc;        /*实际单次要读取的连续最大扇区数*/  
  268.     FATFS *fs = FatFs;  
  269.   
  270.   
  271.     *br = 0;  
  272.       
  273.     //错误处理  
  274.     if (!fs) return FR_NOT_ENABLED;  
  275.     if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY;   /* Check disk ready */  
  276.     if (fp->flag & FA__ERROR) return FR_RW_ERROR;    /* Check error flag */  
  277.     if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */  
  278.       
  279.     //检查要读取的字节数是否超出了剩余的文件长度,如果超出了,则只读取剩余字节长度  
  280.     ln = fp->fsize - fp->fptr;  
  281.     if (btr > ln) btr = ln;                          /* Truncate read count by number of bytes left */  
  282.   
  283.     //该循环以簇为单位,每循环一次就读完一个簇的内容  
  284.     /* Repeat until all data transferred */  
  285.     for ( ;  btr; buff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt)   
  286.     {  
  287.               
  288.         //当文件指针与扇区边界对齐时(如:刚打开文件时),执行以下操作;  
  289.         //否则(如:调用了f_lseek函数)只将当前buffer中需要的内容直接copy到目标缓冲区,然后进入下一次循环。  
  290.         if ((fp->fptr & 511) == 0)                   /* On the sector boundary */  
  291.         {                 
  292.             //如果还没有读完当前簇的所有扇区,则将下一个扇区号赋给变量sect。  
  293.             //注意,当第一次读取文件时,由于f_open函数中,已经将sect_clust赋值为1,  
  294.             //所以应该执行else语句  
  295.             if (--(fp->sect_clust))              /* Decrement sector counter */  
  296.             {     
  297.                 //一般走到这里就意味着只剩下最后一个扇区需要读取,且要读取的内容小于512字节  
  298.                 sect = fp->curr_sect + 1;            /* Next sector */  
  299.             }   
  300.             //如果已经读完当前簇的所有扇区,则计算下一个簇的起始位置,  
  301.             //并更新FIL中的当前簇号curr_clust和当前簇剩余扇区数sect_clust  
  302.             else                                    /* Next cluster */  
  303.             {     
  304.                 //如果当前文件指针在起始簇内,则将clust设置为起始簇号;  
  305.                 //否则,将clust设置为下一簇号  
  306.                 clust = (fp->fptr == 0) ? fp->org_clust : get_cluster(fp->curr_clust);  
  307.                 if ((clust < 2) || (clust >= fs->max_clust)) goto fr_error;  
  308.                   
  309.                 fp->curr_clust = clust;              /* Current cluster */  
  310.                 sect = clust2sect(clust);           /* Current sector */  
  311.                 fp->sect_clust = fs->sects_clust; /* Re-initialize the sector counter */  
  312.             }  
  313.               
  314. #ifndef _FS_READONLY  
  315.   
  316.             //如果buffer中的内容被修改过,则需要先将buffer中的内容回写到物理磁盘中  
  317.             if (fp->flag & FA__DIRTY)                /* Flush file I/O buffer if needed */  
  318.             {                 
  319.                 if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) goto fr_error;  
  320.                 fp->flag &= ~FA__DIRTY;  
  321.             }  
  322. #endif  
  323.   
  324.             //更新当前扇区号  
  325.             fp->curr_sect = sect;                    /* Update current sector */  
  326.   
  327.             //计算需要读取部分的剩余扇区数cc(最后一个扇区内容若不满512字节则不计入cc中)  
  328.             cc = btr / 512;                           
  329.   
  330.             //只要当前扇区不是最后一个扇区,则执行连续的读操作,然后直接进入下一次循环  
  331.             /* When left bytes >= 512 */  
  332.             /* Read maximum contiguous sectors */  
  333.             if (cc)                                   
  334.             {     
  335.                 //如果cc小于当前簇的剩余扇区数sect_clust,则连续读取cc个扇区;  
  336.                 //如果cc大于当前簇的剩余扇区数sect_clust,则只读取当前簇的剩余扇区  
  337.                 if (cc > fp->sect_clust) cc = fp->sect_clust;     
  338.   
  339.                 //执行实际的磁盘读操作,读取当前簇的剩余扇区内容到目标缓冲区中  
  340.                 //注意,是直接读到目标接收缓冲区的,而不是到buffer中  
  341.                 if (disk_read(buff, sect, cc) != RES_OK) goto fr_error;  
  342.   
  343.                 //更新当前簇的剩余扇区数  
  344.                 //该语句实际为:sect_clust = sect_clust - (cc - 1) = sect_clust - cc + 1;  
  345.                 //之所以+1是因为当cc == sect_clust时,sect_clust = sect_clust - sect_clust + 1 = 1;  
  346.                 //所以当下一次循环执行到 if (--(fp->sect_clust)) 时直接进入else语句。  
  347.                 fp->sect_clust -= cc - 1;  
  348.                   
  349.                 //更新当前扇区号,扇区号是基于0索引的,所以这里要-1  
  350.                 fp->curr_sect += cc - 1;  
  351.   
  352.                 //更新已读的字节数  
  353.                 rcnt = cc * 512;   
  354.   
  355.                 //直接进入下一次循环  
  356.                 continue;  
  357.             }  
  358.   
  359.             if (fp->flag & FA_UNBUFFERED)            /* Reject unaligned access when unbuffered mode */  
  360.                 return FR_ALIGN_ERROR;  
  361.   
  362.             //对于文件的最后一个扇区,则先将内容读取到buffer中,然后将需要的内容拷贝到目标缓冲区中,并退出循环  
  363.             if (disk_read(fp->buffer, sect, 1) != RES_OK)    /* Load the sector into file I/O buffer */  
  364.                 goto fr_error;  
  365.               
  366.         }//end if ((fp->fptr & 511) == 0)  
  367.   
  368.         //计算从buffer中实际要读取的字节数rcnt  
  369.         rcnt = 512 - (fp->fptr & 511);                 
  370.         if (rcnt > btr) rcnt = btr;  
  371.   
  372.         //最后将buffer中的指定数据拷贝到目标缓冲区中  
  373.         memcpy(buff, &fp->buffer[fp->fptr & 511], rcnt);    /* Copy fractional bytes from file I/O buffer */  
  374.   
  375.         //一般走到这里就说明已经读完了最后一扇区,下一步将退出循环;  
  376.         //如果还没有读完最后一扇,则有可能是在调用f_lseek后第一次调用f_read,那么将进入下一次循环  
  377.     }//end for(...)  
  378.   
  379.     return FR_OK;  
  380.   
  381. fr_error:   /* Abort this file due to an unrecoverable error */  
  382.     fp->flag |= FA__ERROR;  
  383.     return FR_RW_ERROR;  
  384. }  
  385.   
  386.   
  387. FRESULT f_write (  
  388.     FIL *fp,            /* Pointer to the file object */  
  389.     const BYTE *buff,   /* Pointer to the data to be written */  
  390.     WORD btw,           /* Number of bytes to write */  
  391.     WORD *bw            /* Pointer to number of bytes written */  
  392. )  
  393. {  
  394.     DWORD clust, sect;  
  395.     WORD wcnt;          /*已经写入的字节数*/  
  396.     BYTE cc;            /*实际单次要写入的连续最大扇区数*/  
  397.     FATFS *fs = FatFs;  
  398.   
  399.   
  400.     *bw = 0;  
  401.   
  402.     //错误处理  
  403.     if (!fs) return FR_NOT_ENABLED;  
  404.     if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY;  
  405.     if (fp->flag & FA__ERROR) return FR_RW_ERROR;    /* Check error flag */  
  406.     if (!(fp->flag & FA_WRITE)) return FR_DENIED;    /* Check access mode */  
  407.       
  408.     //保证写入后整个文件大小不得超过4GB  
  409.     if (fp->fsize + btw < fp->fsize) btw = 0;      /* File size cannot reach 4GB */  
  410.   
  411.     //该循环以簇为单位,每循环一次就写完一个簇的内容  
  412.     /* Repeat until all data transferred */  
  413.     for ( ;  btw; buff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt)   
  414.     {  
  415.         //当文件指针与扇区边界对齐时(如:刚打开文件时),执行以下操作;  
  416.         //否则(如:调用了f_lseek函数)只将当前扇区所需要的内容copy到buffer中,然后进入下一次循环。  
  417.         if ((fp->fptr & 511) == 0)                  /* On the sector boundary */  
  418.         {                 
  419.             //如果还没有写完当前簇的所有扇区,则将下一个扇区号赋给变量sect。  
  420.             //注意,当第一次写文件时,由于f_open函数中,已经将sect_clust赋值为1,  
  421.             //所以应该执行else语句  
  422.             if (--(fp->sect_clust))                 /* Decrement sector counter */  
  423.             {                 
  424.                 //一般走到这里就意味着只剩下最后一个扇区需要写入,且要写入的内容小于512字节  
  425.                 sect = fp->curr_sect + 1;            /* Next sector */  
  426.             }   
  427.             //如果已经写完当前簇的所有扇区,则计算下一个簇的起始位置,  
  428.             //并更新FIL中的当前簇号curr_clust和当前簇剩余扇区数sect_clust  
  429.             else                                    /* Next cluster */  
  430.             {     
  431.                 //如果当前文件指针在起始簇内,则将当前簇设置为起始簇;  
  432.                 if (fp->fptr == 0)                  /* Top of the file */  
  433.                 {                 
  434.                     clust = fp->org_clust;  
  435.                       
  436.                     //如果文件不存在,则先创建一个新的簇,并将该簇号设置为当前簇号  
  437.                     if (clust == 0)                 /* No cluster is created */  
  438.                         fp->org_clust = clust = create_chain(0); /* Create a new cluster chain */  
  439.                 }   
  440.                 //如果当前文件指针不在起始簇内,则将下一簇号设置为当前簇号  
  441.                 else                                /* Middle or end of file */  
  442.                 {                             
  443.                     clust = create_chain(fp->curr_clust);            /* Trace or streach cluster chain */  
  444.                 }  
  445.                 if ((clust < 2) || (clust >= fs->max_clust)) break;  
  446.                   
  447.                 fp->curr_clust = clust;              /* Current cluster */  
  448.                 sect = clust2sect(clust);           /* Current sector */  
  449.                 fp->sect_clust = fs->sects_clust; /* Re-initialize the sector counter */  
  450.             }  
  451.               
  452.             //如果buffer中的内容被修改过,则需要先将buffer中的内容回写到物理磁盘中  
  453.             if (fp->flag & FA__DIRTY)               /* Flush file I/O buffer if needed */  
  454.             {                 
  455.                 if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) goto fw_error;  
  456.                 fp->flag &= ~FA__DIRTY;  
  457.             }  
  458.               
  459.             //更新当前扇区号  
  460.             fp->curr_sect = sect;                    /* Update current sector */  
  461.   
  462.             //计算需要写入部分的剩余扇区数cc(最后一个扇区内容若不满512字节则不计入cc中)  
  463.             cc = btw / 512;                           
  464.   
  465.             //只要当前扇区不是最后一个扇区,则执行连续的写操作,然后直接进入下一次循环  
  466.             /* When left bytes >= 512 */  
  467.             /* Write maximum contiguous sectors */  
  468.             if (cc)                                   
  469.             {                                
  470.                 //如果cc小于当前簇的剩余扇区数sect_clust,则连续写入cc个扇区;  
  471.                 //如果cc大于当前簇的剩余扇区数sect_clust,则只将当前簇的剩余扇区写入物理磁盘中  
  472.                 if (cc > fp->sect_clust) cc = fp->sect_clust;  
  473.                   
  474.                 //执行实际的磁盘写操作,将源缓冲区中的内容写入到当前簇的剩余扇区中  
  475.                 //注意,是直接从源缓冲区写到物理磁盘中的,没有经过buffer  
  476.                 if (disk_write(buff, sect, cc) != RES_OK) goto fw_error;  
  477.                   
  478.                 //更新当前簇的剩余扇区数  
  479.                 //该语句实际为:sect_clust = sect_clust - (cc - 1) = sect_clust - cc + 1;  
  480.                 //之所以+1是因为当cc == sect_clust时,sect_clust = sect_clust - sect_clust + 1 = 1;  
  481.                 //所以当下一次循环执行到 if (--(fp->sect_clust)) 时直接进入else语句。  
  482.                 fp->sect_clust -= cc - 1;  
  483.                   
  484.                 //更新当前扇区号,扇区号是基于0索引的,所以这里要-1  
  485.                 fp->curr_sect += cc - 1;  
  486.                   
  487.                 //更新已写的字节数  
  488.                 wcnt = cc * 512;   
  489.   
  490.                 //直接进入下一次循环  
  491.                 continue;  
  492.             }  
  493.               
  494.             if (fp->flag & FA_UNBUFFERED)            /* Reject unalighend access when unbuffered mode */  
  495.                 return FR_ALIGN_ERROR;  
  496.               
  497.             //如果已经写到最后一个扇区了,则先将SD卡中所对应扇区的原始内容读取到buffer中,然后修改buffer中的内容,再从buffer回写到物理磁盘中  
  498.             if ((fp->fptr < fp->fsize) && (disk_read(fp->buffer, sect, 1) != RES_OK)) /* Fill sector buffer with file data if needed */  
  499.                     goto fw_error;  
  500.               
  501.         }//end if ((fp->fptr & 511) == 0)   
  502.           
  503.         //计算实际要写入的字节数wcnt  
  504.         wcnt = 512 - (fp->fptr & 511);               /* Copy fractional bytes to file I/O buffer */  
  505.         if (wcnt > btw) wcnt = btw;  
  506.           
  507.         //将源缓冲区中的数据拷贝到buffer中的指定位置  
  508.         memcpy(&fp->buffer[fp->fptr & 511], buff, wcnt);  
  509.   
  510.         //设置buffer回写标记,当调用f_close或执行下一次循环操作时,执行buffer回写操作  
  511.         fp->flag |= FA__DIRTY;  
  512.           
  513.         //一般走到这里就说明已经写完了最后一扇区,下一步将退出循环;  
  514.         //如果还没有写完最后一扇,则有可能是在调用f_lseek后第一次调用f_write,那么将进入下一次循环  
  515.     }//end for(; btw; buff += wcnt,...)  
  516.   
  517.     //如果对原始文件增加了新的内容,则需要更新文件的大小,并设置文件大小更新标记FA__WRITTEN  
  518.     if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;  /* Update file size if needed */  
  519.     fp->flag |= FA__WRITTEN;                     /* Set file changed flag */  
  520.       
  521.     return FR_OK;  
  522.   
  523. fw_error:   /* Abort this file due to an unrecoverable error */  
  524.     fp->flag |= FA__ERROR;  
  525.     return FR_RW_ERROR;  
  526. }  
  527.   
  528.   
  529. FRESULT f_sync (  
  530.     FIL *fp     /* Pointer to the file object */  
  531. )  
  532. {  
  533.     BYTE *ptr;  
  534.     FATFS *fs = FatFs;  
  535.   
  536.   
  537.     if (!fs) return FR_NOT_ENABLED;  
  538.     if ((disk_status() & STA_NOINIT) || !fs->fs_type)  
  539.         return FR_INCORRECT_DISK_CHANGE;  
  540.   
  541.     //检查文件是否增加了新的内容,如果是则需要修改目录项  
  542.     /* Has the file been written? */  
  543.     if (fp->flag & FA__WRITTEN) {  
  544.   
  545.         //如果buffer的内容被修改了,则需要先将buffer的内容回写到物理磁盘中去  
  546.         /* Write back data buffer if needed */  
  547.         if (fp->flag & FA__DIRTY) {  
  548.             if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) return FR_RW_ERROR;  
  549.             fp->flag &= ~FA__DIRTY;  
  550.         }  
  551.   
  552.     /*************由于更改了文件长度,所以需要修改文件对应的目录项************/  
  553.   
  554.         //首先读取文件对应的根目录扇区到win[]  
  555.         /* Update the directory entry */  
  556.         if (!move_window(fp->dir_sect)) return FR_RW_ERROR;  
  557.   
  558.         //然后修改根目录项  
  559.         ptr = fp->dir_ptr;  
  560.         *(ptr+11) |= AM_ARC;                    /* Set archive bit */  
  561.         ST_DWORD(ptr+28, fp->fsize);         /* Update file size */  
  562.         ST_WORD(ptr+26, fp->org_clust);          /* Update start cluster */  
  563.         ST_WORD(ptr+20, fp->org_clust >> 16);  
  564.         ST_DWORD(ptr+22, get_fattime());        /* Updated time */  
  565.   
  566.         //设置win[]回写标志  
  567.         fs->dirtyflag = 1;  
  568.   
  569.         //清除文件大小更改标记  
  570.         fp->flag &= ~FA__WRITTEN;  
  571.     }  
  572.   
  573.     //将win[]中的根目录信息回写到对应的物理扇区  
  574.     if (!move_window(0)) return FR_RW_ERROR;  
  575.   
  576.     return FR_OK;  
  577. }  
0 0