fatfs源码阅读
使用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源码阅读的更多相关文章
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 【原】FMDB源码阅读(一)
[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...
- 【原】AFNetworking源码阅读(六)
[原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...
- 【原】AFNetworking源码阅读(五)
[原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...
- 【原】AFNetworking源码阅读(四)
[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...
- 【原】AFNetworking源码阅读(三)
[原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...
- 【原】AFNetworking源码阅读(二)
[原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...
- 【原】AFNetworking源码阅读(一)
[原]AFNetworking源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 AFNetworking版本:3.0.4 由于我平常并没有经常使用AFNetw ...
随机推荐
- Python动态构造类:借助强悍的内建 type()
内建的 type() 函数带三个参数时, 将作为强悍的动态类构造器. 如下: type(name, bases, dict) 返回一个新的type对象. 基本上是 class 语句的动态形式. 参数: ...
- sriov-网络问题Debug记录
我司容器云平台使用了sriov的底层网络模型,这个网络驱动的好处是配置少,转发效率高,但是缺点也很明显,出了问题比较难Debug. 现就工作中出现的问题记录如下: 容器删除后,或者docker进程异常 ...
- 【GStreamer开发】GStreamer基础教程04——时间管理
目标 本教程主要讲述一些和时间相关的内容.主要包括: 1. 如何问pipeline查询到流的总时间和当前播放的时间 2. 如何在流内部实现跳转功能 介绍 GstQuery是向一个element或者pa ...
- Swoole练习 TCP
TCP <?php $serv = new swoole_server("127.0.0.1", 9501); //监听连接进入事件 $serv->on('connec ...
- solr搜索结果转实体类对象的两种方法
问题:就是把从solr搜索出来的结果转成我们想要的实体类对象,很常用的情景. 1.使用@Field注解 @Field这个注解放到实体类的属性[字段]中,例如下面 public class User{ ...
- mybatis xml动态语句写法
mapper.java: /** * @Description: 根据摄像机Id查询出入记录 * @Param: * name 姓名 * monitorId 布控ID * starttime 开始时间 ...
- dotnet core JWT Demo
JWT介绍 JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案.JWT的官网地址:https://jwt.io/. 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT令牌在 ...
- CentOS7服务器查看相关配置命令
CPU个数:(base) [jiangshan@localhost ~]$ grep 'physical id' /proc/cpuinfo | sort -u | wc -l2CPU核数:(base ...
- 长乐培训Day8
T1 远征 题目 [题目描述] 寒枫将军将要带领他的部队去圣雪山消灭那里的冰龙.部队分成了若干个小队,属于同一个小队的人兵种相同. 寒枫将军有着杰出的指挥能力,在战斗的时候,寒枫将军能够让所有相同兵种 ...
- 剑指offer41:所有和为S的连续正数序列,例如,有多少种连续的正数序列的和为100
1 题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没多久 ...