使用fatfs文件的第一步,就是调用F_mount函数注册一个工作空间。

F_mount函数的原型如下:

第一个参数根据网上大神的答复,是外设类型,如果是sd卡就是0,flash等等其他的外设就是其他得数,据说有定义,不过我没找到。

第二个参数FATFS指针就是工作空间的指针,个人感觉有点lwip网卡数据结构的感觉。

FATFS数据结构及解释如下,个人感觉了解FATFS这个工作空间数据结构是什么东西就行:

typedef struct {    BYTE    fs_type;      /* FAT sub-type (0:Not mounted) */    BYTE    drv;          /* Physical drive number */    BYTE    csize;        /*每簇里有多少个扇区 (一般一簇是4KB 一扇区是512B)*/    BYTE    n_fats;       /* FAT表的数目  一般是两个 */    BYTE    wflag;        /* win[ ] dirty flag */    BYTE    fsi_flag;     /* fsinfo 区域脏数据标志 */    WORD    id;           /* File system mount ID */    WORD    n_rootdir;    /* Number of root directory entries (FAT12/16) */#if _MAX_SS != 512    WORD    ssize;        /* 扇区大小  对应着sd卡中的block  一般是512B*/

#endif

#if _FS_REENTRANT    _SYNC_t sobj;         /* Identifier of sync object */#endif

#if !_FS_READONLY    DWORD   last_clust;   /* Last allocated cluster */    DWORD   free_clust;   /* 空闲簇的数目*/    DWORD   fsi_sector;   /* fsinfo sector (FAT32) */

#endif#if _FS_RPATH    DWORD   cdir;         /* Current directory cluster (0:root) */#endif    DWORD   n_fatent;     /* Number of FAT entries (== Number of clusters + 2) */    DWORD   fsize;        /*FAT表对应着多少个扇区*/    DWORD   fatbase;      /*FAT表的起始扇区 */    DWORD   dirbase;      /* 根目录的起始扇区*/    DWORD   database;     /* 数据区的起始扇区 */    DWORD   winsect;      /* Current sector appearing in the win[ ] */    BYTE    win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data on tiny cfg) */} FATFS;

注册的FATFS并不在f_mount函数中完成对其属性的初始化,而是第一次调用f_open发现还没有初始化的时候在完成初始化。f_mount函数中做得事情很简单,如下:

FRESULT f_mount (

BYTE vol, /* Logical drive number to be mounted/unmounted */

FATFS *fs /* Pointer to new file system object (NULL for unmount)*/

)

{

FATFS *rfs;

if (vol >= _VOLUMES) /* Check if the drive number is valid */

return FR_INVALID_DRIVE;

rfs = FatFs[vol]; /* Get current fs object */

if (rfs) {

rfs->fs_type = 0; /* Clear old fs object */

}

if (fs) {

fs->fs_type = 0; /* Clear new fs object */

}

FatFs[vol] = fs; /* Register new fs object */

return FR_OK;

}

我删去了一些没用的条件编译,发现最后的代码是上面的代码,其实就是将fatfs文件系统已经定义好的FatFs[vol]指针数组的0号指向我们传进来的工作空间。

------------------------------------------------------------------------------------------------------------

这就是一个文件对象的属性:

typedef struct {    FATFS*  fs;           /* 指向属于自己的文件系统 */    WORD    id;           /* Owner file system mount ID */    BYTE    flag;         /*文件状态标识*/    BYTE    pad1;    DWORD   fptr;         /* 文件读写指针 (Byte offset origin from top of the file) */    DWORD   fsize;        /*文件大小*/    DWORD   sclust;       /*文档开始扇区 */    DWORD   clust;        /* 当前扇区 */    DWORD   dsect;        /* Current data sector */

#if !_FS_READONLY    DWORD   dir_sect;     /* Sector containing the directory entry */    BYTE*   dir_ptr;      /* Ponter to the directory entry in the window */

#endif

#if !_FS_TINY    BYTE    buf[_MAX_SS]; /* Data read/write buffer */

#endif} FIL;

上面的是一个文件对象的属性,f_open函数就是用来填充它,而f_close函数则是主要用来将其fs属性置为空,以此让此文件对象失效。

F_open:

打开一个文件时,有五个选项:

FA_READ:以读方式打开文件

FA_WRITE:以写方式打开文件,和上一个组合就变成了以读写的方式打开文件

FA_OPEN_EXISTING:打开的文件必须已经存在,如不存在函数失败

FA_OPEN_ALWAYS:如果不存在这个文件,则新建,如果存在这个文件,则在后面添加。用这种方法打开文件要之后调用 f_lseek方法。

FA_CREATE_NEW:创建一个新文件,如果已经存在,则函数失败。

FA_CREATE_ALWAYS:创建一个新文件,如果文件存在,则覆盖。

在f_open函数中,调用了下面这个函数:

这个函数会进行检查,如果文件系统对象还不是有效的还不是有效的,则会初始化它。

F_close:

用来将文件对象的fs属性置为空,以此让此文件对象失效。其中调用f_sync函数,来将缓冲区的数据写入sd卡。

----------------------------------------------------------------------------------------------------------

比较正常的两个函数F_READ和F_WRITE。

F_READ:

第一个参数:文件对象

第二个参数:读取所用的的缓冲区

第三个参数:想要读多少字节的数据

第四个参数:这个参数实际的功能是作为一个返回值,告诉使用者函数完成后实际上读了多少数值,那么如果函数完成后*byteread<bytetoread,那就说明这个文件本身没有bytetoread那么大。

F_WRITE:

这个函数只有在_FS_READONLY == 0时才可以用。

第一个参数:文件对象

第二个参数:针对写入所用的的缓冲区

第三个参数:想要写入少字节的数据

第四个参数:这个参数也是函数的一个返回值,当函数成功执行之后,如果第四个指针所指向的值小于第三个传进去的参数,那就说明,磁盘满了。

详细步骤如下:

准备开始写,先查看一下当前的文件的字节数是不是512的整数倍,如果是的话再查看flag标志中,通过查看可以得出缓冲区是不是又一个完整扇区的数据,如果有的话先将缓冲区中一个扇区的数据写入sd卡,写入512*最大整数倍的数据。剩下的存入缓冲区中。

准备开始写,先查看一下当前的文件的字节数是不是512的整数倍,如果不是,说明缓冲区中还有部分数据,那么就用要写入的数据中的一部分填满缓冲区,更新flag标志,然后回到上一段文字。

所谓的缓冲区就是文件对象的buf[]属性。

--------------------------------------------------------------------------------------------------------------

f_read和f_write函数都只能从文件开头开始读写,那么这个时候就需要一个函数来移动当前指针,配合f_read和f_write函数,这个函数就是f_lseek。

函数原型如下:

第一个参数:文件对象的指针

第二个参数:从文件开始处向后移动多少

Offset是指相对于文件起始处的字节数。

在写模式写了一个超过文件大小的offset,文件将被拓展,并且拓展区域的数据是未定义的。这可以用来迅速的创建一个大文件。

F_lseek函数成功后,为确保指针已经以已经成功移动,必须检查文件对象中的fptr的值,如果其不为所期望的值则说明:1.文件以只读打开,指针只能移动到文件结束处。2.磁盘满了。

示例:

------------------------------------------------------------------------------------------------------------

F_truncate:

缩短文件大小,将文件尾缩短到当前指针处,如果当前指针已经在文件末尾,则不会发生任何改变。

f_sync:

介绍这个函数之前先详细的说一下fatfs的f_write的流程,此流程为读文件系统代码得出:

准备开始写,先查看一下当前的文件的字节数是不是512的整数倍,如果是的话再查看flag标志中,通过查看可以得出缓冲区是不是又一个完整扇区的数据,如果有的话先将缓冲区中一个扇区的数据写入sd卡,写入512*最大整数倍的数据。剩下的存入缓冲区中。

准备开始写,先查看一下当前的文件的字节数是不是512的整数倍,如果不是,说明缓冲区中还有部分数据,那么就用要写入的数据中的一部分填满缓冲区,更新flag标志,然后回到上一段文字。

那么也就说我们不断操作的过程中,其实有可能有一部分文本信息并未真正的写入sd卡中,而是存储在文件的buf数组也就是缓冲区里。那么f_sync的作用就是把缓冲区的数据强制写入sd卡中,这样的话,如果写入文件的持续时间长,周期性的的f_sync一次,可以尽量的保证断电等意外突发情况数据损失减小。

对,你没猜错,f_close函数中会调用这个函数以保证关闭文件之后所有数据均以写入sd卡。所以这个函数利f_close唯一的区别就在于函数调用之后文件不失效。

因为sd卡每次最少写入512字节,所以这次写入不足512字节,那么就用无用的数据填补够512字节,而这部分数据其实还留在缓冲区,下次再写的时候直接用新的数据将这次不足的还有一部分无用信息的数据覆盖掉。(本段为猜测)

F_opendir:

可以打开一个目录,也可以创建一个目录。该目录对象可以不经过任意函数直接丢弃。

f_readdir:

读取一个目录中的项目(这个项目可能是个文件,也可能是个目录),将返回的这一个项目的信息存在FILINFO中,一次返回一个,不停的重复调用这个函数,就能得到这个目录下所有项目的信息了。当读完之后,再次调用这个函数,FILINFO中的f_name属性为空,可以此来判断是不是读取完了所有的该目录下的项目。

注:fatfs支持长文件名,需_USE_LFN宏为真,FILINFO数据结构中有相应的属性处理长文件名。FILINFO数据结构如下:

使用示例:

f_getfree:

这个函数使用来得到空闲的簇的的数目并把文件系统的信息通过第三个参数返回。通过文件系统数据结构的属性同样能计算出空闲的簇数。

f_stat:

获取一个文件或者目录的相关信息。返回到FILINFO结构体中。

f_mkdir:

就是新建一个目录。

用法如下:

f_unlink:

删除一个文件或者目录,但要注意以下几点:

1.文件或者目录不能使只读的。

2.目录的话一定要是空的并且不能是当前目录。

3.文件的话不能是打开的。

f_chmod:

第一个参数:文件名

第二个参数:待被设置的属性标志

第三个参数:要被修改的属性标志

首先文件的属性有以下几种:

那么这个函数的第二个参数和第三个参数是什么意思呢?第二个参数是指函数要把文件要设置成那种属性,第三个参数是指函数要修改那些属性,也就是说出现在第三个参数而没有出现在第二个参数就是要清除的标志,比如:

就是代表设置只读标志,清除读档标志。

f_utime:

修改一个目录的时间戳,把想要修改成的数据通过第二个参数传入。

使用例子:

f_rename:

可以用来重命名一个文件,也可用来移动一个文件,如下:

注意,移动文件不能跨设备移动,比如sd卡的文件只能在sd卡内移动,也不能移动一个已经打开的文件。

f_chdir:

修改某个设备的当前目录。当设备初始打开的时候,当前目录自动设置为根目录。用例如下:

f_chdrive:

修改当前设备。初始的当前设备为0号设备。

f_getcwd:

恢复当前设备上的当前目录。

f_forward:(看的很粗)

从文件中读取数据并直接将数据转发到输出流,而不使用数据缓冲区,适用于小存储系统。

f_mkfs:

新建一个文件系统,其实感觉主要作用是格式化。

f_fdisk:(没好好看)

分区,最多可以分四个,创建分区表到设备的MBR。

以下函数大概介绍一下:

这四个输入输出函数,前三个封装的是f_read和f_write,最后一个封装的是f_putc和f_puts。

f_tell:

得到当前的读写指针,其实是一个宏,如下:

f_eof:

检查当前的读写指针是否打到了文件结尾。

f_size:

又是一个宏,返回的是文件大小。

f_error:

测试文件是否出错。同样是个宏。

fatfs源码阅读的更多相关文章

  1. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  2. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  3. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

  4. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  5. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  6. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  7. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  8. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

  9. 【原】AFNetworking源码阅读(一)

    [原]AFNetworking源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 AFNetworking版本:3.0.4 由于我平常并没有经常使用AFNetw ...

随机推荐

  1. 一台服务器部署多台tomcat

    如题,多个项目部署在一台服务器.减少容错性,觉得分开部署,这样一个tomcat挂了不会影响另一个项目.看配置和应用大小决定数量,一般四五个没问题,也有单台服务器部署8个tomcat稳定运行的. 下面记 ...

  2. netty5自定义私有协议实例

    一般业务需求都会自行定义私有协议来满足自己的业务场景,私有协议也可以解决粘包和拆包问题,比如客户端发送数据时携带数据包长度,服务端接收数据后解析消息体,获取数据包长度值,据此继续获取数据包内容.我们来 ...

  3. linux非root用户安装5.7.27版本mysql

    先下安装包,到mysql官网https://dev.mysql.com/downloads/mysql/选好安装包版本.操作系统类型(默认是最新版本,点击右边链接Looking for previou ...

  4. 【Leetcode_easy】925. Long Pressed Name

    problem 925. Long Pressed Name solution1: class Solution { public: bool isLongPressedName(string nam ...

  5. orleans 的一种模式

    为了避免过热的grain,按时间%cpu数,分派任务到grain中,然后有限制的去访问原来过热的grain.eg:tokengrain,1个半小时后,更新所有的grain.

  6. 智能指针.Qt测试

    1.Qt598x64vs2017(或 Qt598x86vs2015[配置使用vs2017]).Win10x64 2.测试代码: 2.1.MainWindow.h class MainWindow : ...

  7. linux系统界面转换

    普通使用的切换: 命令行->图形 startx 或者 ctrl+alt+F7切换到图形界面,虚拟机里面使用Alt+F7返回到图形界面 图形->命令行 Ctrl+Alt+F1--F6 如果想 ...

  8. 如何将生产环境的服务Docker镜像拉取到本地进行调试

    背景 很多时候我们在将开发环境的代码推送到GitLab上面以后,我们在测试的时候发现了问题后无法通过现有的日志输出级别来定位问题,比如我们需要看EFCore生成的SQL语句,在生产环境我们是不可能输出 ...

  9. memset 导致的一个段错误

    原型: void *memset(void *s, int c, size_t n); 解释: memset :是 逐字节 拷贝,即n是指整个变量所占字节,在用于数组时一定要注意n不一定是 数组元素. ...

  10. scrapy框架3——请求传参

    当使用scrapy爬取的数据不在同一张页面中(一次请求与数据后解析无法获得想要的全部数据),需要请求传参,在第一次解析时实例化item,将item传递,再次将请求到的数据解析后,封装在item中. 关 ...