当前位置:   article > 正文

STM32实战总结:HAL之FatFS_hal fatfs

hal fatfs

FAT

是文件配置表(英语:File Allocation Table,首字母缩略字:FAT),是一种由微软发明并拥有部分专利的文件系统,供MS-DOS使用,也是所有非NT核心(windowsNT操作系统)的微软窗口使用的文件系统。

FAT文件系统考虑当时电脑性能有限,所以未被复杂化,因此几乎所有个人电脑的操作系统都支持。这特性使它成为理想的软盘和存储卡文件系统,也适合用作不同操作系统中的数据交流。现在,一般所讲的FAT专指FAT32

但FAT有一个严重的缺点:当文件删除后写入新数据,FAT不会将文件整理成完整片段再写入,长期使用后会使文件数据变得逐渐分散,而减慢了读写速度。碎片整理是一种解决方法,但必须经常重组来保持FAT文件系统的效率。

exFAT

Extended File Allocation Table File System,扩展FAT,即扩展文件分配表。是Microsoft在Windows Embeded 5.0以上中引入的一种适合于闪存的文件系统,为了解决FAT32等不支持4G及其更大的文件而推出。

NTFS

New Technology File System,是windowsNT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式,提供长文件名、数据保护和恢复,能通过目录和文件许可实现安全性,并支持跨越分区。

NTFS文件系统最早出现于1993年的windowsNT操作系统中,它的出现大幅度地提高了微软原来的FAT文件系统的性能。

NTFS是一个日志文件系统,这意味着除了向磁盘中写入信息,该文件系统还会为所发生的所有改变保留一份日志。这一功能让NTFS文件系统在发生错误的时候(比如系统崩溃或电源供应中断)更容易恢复,也让这一系统更加强壮。在这些情况下,NTFS能够很快恢复正常,而且不会丢失任何数据。

后多用FAT来泛指文件系统。

到底什么是文件系统?

负责管理和存储文件信息的软件结构,在磁盘上组织文件的方法。其本质上也是一套程序,只不过是别人已经写好的一套用来进行文件的增删改查及管理等操作的代码框架。 

想想电脑上对文件进行的一系列操作就能明白了。

就好比一个巨大的图书馆无人管理,杂乱无章的存放着各种书籍,难以查找所需的文档。这样直接存储数据的方式对于小容量的EEPROM还可以接受,但对于SPI FLASH芯片或者SD卡之类的大容量设备,我们需要一种高效的方式来管理它的存储内容。

这些管理方式即为文件系统,它是为了存储和管理数据,而在存储介质建立的一种组织结构,这些结构包括操作系统引导区,目录和文件。常见的windows下的文件系统格式包括FAT32、NTFS、exFAT.在使用文件系统之前,要先对存储介质进行格式化。格式化先擦除原来内容,在存储介质上新建立一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址、剩余空间。

使用文件系统时,数据都以文件的形式存储。写入新文件时先在目录中创建一个文件索引,它指示了文件存放的物理地址,再把数据存储到该地址中。当需要读取数据时,可以从目录中找到该文件的索引,进而在相应的地址中读取出数据。具体还涉及到逻辑地址、簇大小、不连续存储等一系列辅助结构或处理过程。

文件系统的存在使我们在存储数据时,不再是简单的向某物理地址直接读写,而是要遵循它的读写格式。如经过逻辑转换,一个完整的文件可能被分开成多段存储到不连续的物理地址,使用目录或者链表的方式来获知下一段的位置。

FatFs

FatFs是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051、PIC、AVR、ARM、Z80、RX等等,不需要做任何修改。

免费开源,专门为小型嵌入式系统设计,完全用C语言编写。支持FAT12、AT16与FAT32,支持多种存储媒介,有独立的缓冲区,可对多个文件进行读写。

极为重要的是,它是一款可裁剪的文件系统。

FATFS的特点:
Windows兼容的FAT文件系统,(支持FAT12,FAT16,FAT32)
与平台无关,移植简单
代码量少,效率高
多种配置选项,可以裁剪
支持多卷(物理驱动器或分区,最多10个卷)
多个ANSI/OEM代码页包括DBCS
支持长文件名、ANSI/OEM或Unicode
支持RTOS
支持多种扇区大小
只读,最小化的API和I/O缓冲区等


由于它以上的特点,使得FATFS在嵌入式系统中被广泛应用

FATFS层次结构:

✔底层接口,包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟,需要我们根据平台和存储介质编写移植代码。
✔中间层FATFS模块,实现了FAT文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
✔最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write,f_close等,就可以像在PC上读写文件那样简单。

FATFS的整个系统可以在FATFS的官网下载:官网地址

官网是个外网,很难打开。

同时在官网还可以查看每个函数的说明,同时大部分的函数都带有示例,是不错的学习资源。

进入官网后往下翻到Resources栏下载最新版本,本文撰写时最新版本是R0.14a

在这里插入图片描述

注意即使最新版本也是在不断更新打Patch的

在这里插入图片描述

官网还给出了不同平台的示例工程,其中包含STM32的

在这里插入图片描述

Application Interface 栏列出了常用的接口函数,点击可查看函数应用介绍及示例

在这里插入图片描述

下载后,可以查看其目录结构

diskio.c和diskio.h是硬件层,需要根据存储介质来修改。

ff.c和ff.h是FATFS的文件系统层和文件系统的API层

移植步骤: 

1、数据类型:在integer.h里面定义好数据的类型,这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。

 2、配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要。

 3、函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6个。

相关配置宏:
_FS_TINY                   mini版本的FATFS

_FS_READONLY        设置只读,可以减少所占的空间

_FS_MINIMIZE           削减函数

_USE_STRFUNC        字符及字符串操作函数

_USE_MKFS               是否启用格式化

_USE_FASTSEEK      使能快速定位

_USE_LABEL              是否支持磁盘盘符的设置和读取

_CODE_PAGE            设置语言936——中文GBK编码

_USE_LFN                  是否支持长文件名,值不同存储的位置不同

_MAX_LFN                  文件名的最大长度

_VOLUMES                 支持的逻辑设备数目

_MAX_SS                    扇区缓冲最大值,一般为512

FATFS程序结构图

移植FATFS文件系统之前,我们先通过FATFS的程序结构图了解FATFS在程序中的关系网络,见图FATFS程序结构图。

  

用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到f_mount()、f_open()、f_write()、f_read()就可以实现文件的读写操作。

FATFS组件是FATFS的主体,文件都在源码src中,其中ff.c和ff.h、integer.h以及diskio.h四个文件我们不需要改动,只需要修改ffconf.h和diskio.c两个文件。

底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。我们使用SPI FLASH芯片作为物理设备来作为示例,编写对应的驱动程序,这里我们可以直接使用。

FATFS底层设备驱动函数
FATFS文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已。表FATFS移植需要用户支持函数为FATFS移植时用户必须支持的函数。通过表FATFS移植需要用户支持函数我们可以清晰知道很多函数是在一定条件下才需要添加的,只有前三个函数是必须添加的。我们完全可以根据实际需求选择实现用到的函数。

前三个函数是实现读文件的最基本需求。接下来三个函数是实现创建文件、修改文件需要的。为实现格式化功能,需要在disk_ioctl添加两个获取物理设备信息选项。我们一般只需要实现前面6个函数就可以了,已经满足大部分功能。

  
底层设备驱动函数是diskio.c文件,我们的目的就是把diskio.c中的函数接口与SPI Flash芯片驱动连接起来。总共有5个函数,分别为:

设备状态获取(disk_status)、设备初始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)。

MX配置

HAL已经集成了FATFS,所以无需从官网下载后再移植。

用户自定义则默认操作的是内置Flash。

更详细的参数设置:

功能配置
FS_MINIMIZE 这个设置是选择是否裁剪掉一些函数,disabled,不剪裁,全功能
USE_STRFUNC 这个用来设置是否支持字符串类操作,我们选择使用。带LF->CRLF转换
USE_FIND 使能或禁用在指定目录内搜索指定文件函数:f_findfirst和f_findnext
USE_MKFS 使能或禁用f_mkfs函数
USE_FASTSEEK 使能或禁用快速搜索功能,使能后,可以加快f_lseek、f_read和f_write函数执行。
USE_EXPAND 使能或禁用f_expand函数,该函数可以为文件分配连续数据区域。
USE_CHMOD 使能或禁用元数据控制函数:f_chmod和f_utime。
USE_LABEL 使能或禁用卷标签API函数:f_getlabel和f_setlabel。
USE_FORWARD 使能或禁用f_forward函数。

命名空间和本地环境配置
CODE_PAGE 选择支持中文
规定目标系统使用的OEM代码。如果该代码设置的不正确,可能会引起文件打开失败。如果没有根本没有使用扩展字符,则使用任何代码都没区别。
USE_LFN
使能或禁用长文件名(LFN)。
0,当然就是不支持长文件名了,节省空间.
1,支持长文件名,但不支持重入.
2,支持长文件名,工作区存放在STACK,也就是死数组里面.
3,支持长文件名,工作区采用动态内存管理,存放在HEAP,也就是必须实现动态内存管理.
MAX_LFN
定义长文件名工作缓冲区大小,可以为12~255字节。当禁用长文件名时,此选项无效。
LFN_UNICODE
使能或禁用Unicode。如果要使用Unicode(UTF16)字符串路径名,需要使能LFN和设置本选项为1。此选项还影响字符串I/O功能函数。如果禁用长文件名,此选项必须为0。
STRF_ENCODE
通过设置_LFN_UNICODE为1使能Unicode API函数时,这个选项定义通过字符串I/O函数读写的文件字符编码。字符串I/O函数有f_gets、f_putc、f_puts和f_printf。当_LFN_UNICODE等于0时,此选项不起作用。
FS_RPATH
配置相对路径函数。

卷/驱动器配置
VOLUMES
配置可用卷的数目,可设置为1~10。
MIN_SS、MAX_SS
定义扇区大小,有效值为512、1024、2048、4096,需要根据硬件配置来定义。_MIN_SS定义最小扇区大小,_MAX_SS定义最大扇区大小。都设置为512可以兼容所有SD卡和硬盘,但是在某些片上Flash和其它存储设备可能需要更大值。当_MAX_SS > _MIN_SS,FatFs被配置为扇区大小可变的并且必须在函数disk_ioctl中实现GET_SECTOR_SIZE命令。
MULTI_PARTITION
使能或禁止多分区函数。默认禁止,此时每个逻辑驱动器数目一定与物理驱动器数目相同,并且物理驱动器仅能安装FAT卷。如果使能,物理设备上可以有多个逻辑扇区,每个逻辑驱动器一定要预先定义在分区解析表VolToPart[]中。同时,f_disk函数有效。
USE_TRIM
使能或禁用ATA-TRIM函数。TRIM指令被文件系统用来通知设备哪些逻辑地址不再被占用,可以被设备回收为空闲空间。对于FatFs来说,使用函数f_unlink移除一个文件时,只是将对应的FAT区域设置为空,文件实际上在扇区中。如果想在移除文件时强制擦除扇区,只需将_USE_TRIM设置为1。如果使能TRIM函数,必须在函数disk_ioctl中实现CTRL_TRIM命令。
FS_NOFSINFO
使能或禁用空闲簇计数和最后分配的簇计数。

系统配置
FS_TINY
配置FatFs为正常模式或者微型(TINY)模式。配置为微型模式后,对内存需求变小,文件对象数据结构FIL会减少_MAX_SS字节。程序复用FATFS数据结构中的缓冲区代替FIL数据结构中去除掉的缓冲区。
FS_EXFAT
使能或禁用exFAT文件系统。要使能exFAT文件系统,必须使能长文件名功能并且配置_LFN_UNICODE = 1。如果要使用全功能版的exFAT,推荐_MAX_LFN = 255。注意,使能exFAT意味着不再兼容C89,因为要用到64位整形数。
FS_NORTC
使能或禁用时间戳函数。使能时间戳函数需要硬件RTC,并且需要提供底层函数get_fattime。如果系统没有硬件RTC或者不需要时间戳功能,设置_FS_NORTC为1禁用时间戳函数。此时,如果FatFs修改任何文件,使用固定的时间戳,固定时间戳由宏_NORTC_MON、_NORTC_MDAY和_NORTC_YEAR定义。如果系统没有RTC,这些宏用来定义固定时间戳。只读或者_FS_NORTC=0时,这些宏无意义。
比如:
#define _NORTC_MON 1
#define _NORTC_MDAY 1
#define _NORTC_YEAR 2016
FS_REENTRANT
使能或禁用FatFs模块的可重入特性。注意,访问不同卷上的文件/目录总是可重入的,无论是否使能本参数,卷控制函数f_mount、f_mkfs和f_fdisk总是不可重入的。要使能可重入特性,用户必须提供同步处理,要向工程中添加ff_req_grant、ff_rel_grant、ff_del_syncobj和ff_cre_syncobj函数。可以在文件option/syscall.c中找到示例。
FS_TIMEOUT
设置超时时间,单位为系统时钟滴答周期,当宏_FS_REENTRANT=0时,本设置无效。
FS_LOCK
使能或禁用文件锁功能。控制重复打开文件和非法打开文件对象。注意:文件锁功能不具有可重入性。只读模式下,这个宏必须为0。

修改红框中两项,其他默认:

会生成FatFs相关文件夹

其中,这个驱动文件是需要移植的。

 

关键代码

下面我们结合SPI Flash芯片驱动函数做详细讲解。

宏定义

  1. /*为每个设备定义一个物理编号*/
  2. #define ATA 0 //预留SD卡用
  3. #define SPI_FLASH 1 //外部SPI FLASH

这个两个宏定义在FATFS文件系统中非常重要,FATFS是支持多物理设备的,必须为每个物理设备定义一个不同的编号。

SD卡是预留接口,是和SDIO有关的内容。

设备状态获取

  1. DSTATUS disk_status(
  2. BYTE prdv /*物理编号*/
  3. )
  4. {
  5. DSTATUS status = STA_NOINIT;
  6. switch(pdrv){
  7. case ATA : /*SD CARD*/
  8. break;
  9. case SPI_FLASH :
  10. /*spi flash 状态检测:读取SPI_FLASH设备ID*/
  11. if(sFLASH_ID == SPI_FLASH_ReadID())
  12. /*设备ID读取结果正常*/
  13. {
  14. status &= ~STA_NOINIT;
  15. }
  16. else
  17. {
  18. /*设备ID读取结果失误*/
  19. status = STA_NOINIT;
  20. }
  21. break;
  22. default:
  23. status = STA_NOINIT;
  24. }
  25. return status;
  26. }

disk_status函数只有一个参数pdrv,表示物理编号。一般我们都是使用switch函数实现对prdv的分支判断。对于SD卡只是预留接口,留空即可。对于SPI FLASH芯片,我们直接调用在SPI_FLASH_ReadID()获取设备ID,然后判断是否正确,如果正确,函数返回正常标准;如果错误,函数返回异常标志。SPI_FLASHReadID()是定义在之前的SPI FLASH驱动文件中的函数。


设备初始化

  1. DSTATUS disk_initialize(
  2. BYTE prdv /*物理编号*/
  3. )
  4. {
  5. uint16_t i;
  6. DSTATUS stauts = STA_NOINIT;
  7. switch(pdrv){
  8. case ATA :
  9. /*sd card*/
  10. break;
  11. case SPI_FLASH :
  12. SPI_FLASH_Init();
  13. /*延时一小段时间*/
  14. i= 500
  15. while(i--);
  16. /*唤醒SPI FLASH*/
  17. SPI_FLASH_WAKEUP();
  18. /*获取SPI FLASH芯片状态*/
  19. status = disk_status(SPI_FLASH);
  20. break;
  21. default :
  22. status = STA_NOINIT;
  23. }
  24. return status;
  25. }

disk_initialize函数也是有一个参数prdv,用来指定设备物理编号。对于SPI FLASH芯片我们调用SPI_FLASH_Init()函数实现对SPI FLASH芯片引脚GPIO初始化配置以及SPI通信参数配置。SPI_FLASH_WAKEUP()函数唤醒SPI FLASH芯片,当芯片处于睡眠模式时需要唤醒芯片才可以进行读写。

最后调用disk_status函数获取SPI FLASH芯片状态,并返回状态值。


读取扇区

  1. DRESULT disk_read(
  2. BYTE pdrv, //设备物理编号
  3. BYTE *buff, //数据缓存区
  4. DWORD sector, //扇区首地址
  5. UINT count //扇区个数
  6. )
  7. {
  8. DRESULT status = RES_PARERR;
  9. switch(pdrv)
  10. {
  11. case ATA :
  12. break;
  13. case SPI_FLASH :
  14. /*扇区偏移2MB,外部FLASH文件系统空间放在SPI FLASH后面的6MB空间*/
  15. sector += 512
  16. SPI_FLASH_BufferRead(buff,sector<<12,count<<12);
  17. stauts = RES_OK;
  18. break;
  19. default :
  20. status = RES_PARERR;
  21. }
  22. return status;
  23. }

disk_read函数有4个形参,pdrv为设备物理编号,buff是一个BYTE型指针变量,buff指向用来存放读取到数据的存储首地址。sector是一个DWORD类型变量,指定要读取数据的扇区首地址,count是一个UINT类型变量,指定扇区数量。

扇区写入

  1. DRESULT disk_write(
  2. BYTE prdv, //设备物理编号
  3. const BYTE *buff, //欲写入数据的缓存区
  4. DWORD sector, //扇区首地址
  5. UINT count //扇区个数
  6. )
  7. {
  8. uint32_t write_addr;
  9. DRESULT status = RES_PARERR;
  10. if(!count){
  11. return RES_PARERR;
  12. }
  13. switch(prdv){
  14. case ATA :
  15. break;
  16. case SPI_FALSH :
  17. /*扇区偏移2MB,外部FLASH文件系统空间放在SPI FLASH 后面的6MB空间*/
  18. sector += 512
  19. write_addr = sector<<12;
  20. SPI_FLASH_SectorErase(write_addr);
  21. SPI_FALSH_BufferWrite((u8 *)buff,write_addr,count<<12);
  22. status = RES_OK;
  23. break;
  24. default:
  25. status = RES_PARERR;
  26. }
  27. return status;
  28. }

disk_write函数有四个形参,pdrv为设备物理编号,buff指向待写入扇区数据的首地址。sector指定要写入数据的扇区首地址,count指定扇区数量。对于SPI FLASH芯片,在写入数据之前,需要先擦除,所以用到扇区擦除函数(SPI_FLASH_SectorErase).然后就是在调用数据写入函数(SPI_FLASH_BufferWrite)把数据写入到指定位置。

FATFS给用户提供了大量的API函数,可以满足我们对文件的各种操作。

f_mount                              注册/注销一个工作区域

f_open                                       打开/创建一个文件

f_close                                        关闭一个文件

f_read                                        读文件

f_write                                         写文件

f_lseek                                        移动文件读写指针

f_sync                                冲洗缓冲数据Flush Cached Data

f_forward                        直接转移文件数据到一个数据流

f_stat                                获取文件状态

f_opendir                打开一个目录

f_closedir                关闭一个已经打开的目录

诸如其他的还有很多的相关的文件操作,具体的和C语言的文件操作大同小异,可以去FATFS文件系统官网看看详细的文件操作函数。这里笔者不再赘述。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/464034
推荐阅读
相关标签
  

闽ICP备14008679号