细说STM32单片机SPI-Flash芯片的FatFS移植
目录
1、SPI-Flash芯片硬件电路
2、CubeMX项目基础设置
(1)RCC、SYS、Code Generator、USART3、TIM6、GPIO、NVIC
(2)RTC
(3)SPI2
(4)FatFS模式
(5)FatFS参数设置概述
1) Version组
2) Function Parameters组
3) Locale and Namespace Parameters组
4) Physical Driver Parameters组
5) System Parameters组
3、Function Parameters组参数设置
(1)参数FS_READONLY
(2)参数S_MINIMIZE
(3)参数USE_STRFUNC
4、Locale and Namespace Parameters组参数设置
(1)参数USE_LFN
(2)参数STRF_ENCODE
(3)参数FS_RPATH
5、Physical Driver Parameters组参数设置
6、System Parameters组参数设置
(1)参数FS_NORTC
(2)参数FS_REENTRANT
7、项目软件设计
本文以开发板上的SPI-Flash芯片W25Q16为媒介详细介绍CubeMX中FatFS各个参数的意义和设置,分析生成的代码中FatFS各个文件的作用和关联,完成针对W25Q16的硬件访问层的移植。
继续使用旺宝红龙开发板STM32F407ZGT6 KITV1.0。相关原理图如下:
1、SPI-Flash芯片硬件电路
开发板上有一个SPI-Flash存储芯片W25Q16,它与STM32F407的SPI2接口连接,W25Q16总容量是2M字节,存储空间参数如下。
- 总共32个块(block),每个块64KB。
- 每个块又分为16个扇区,共512个扇区,每个扇区4KB。
- 每个扇区又分为16个页(page),共8192个页,每个页256B。
2、CubeMX项目基础设置
本文创建一个示例针对SPI-Flash存储芯片W25Q16进行FatFS移植,使用FatFS进行文件读写等操作。此外,使用RTC为FatFS提供时间戳数据。
(1)RCC、SYS、Code Generator、USART3、GPIO、NVIC
参数设置可以本文作者发布的其他文章文章。
(2)RTC
- 启用RTC的时钟源和日历,设置初始日期和时间,其他功能无须开启。在示例中读取RTC的当前日期和时间。
- 在RCC组件中启用LSE,在时钟树上,使用LSE作为RTC的时钟源。
(3)SPI2
- 设置HCLK为168MHz,PCLK1的频率为42MHz,SPI2是挂在APB1总线上的,这样是为方便计算SPI2的波特率。
- SPI2的模式设置为Full-Duplex Master,不使用硬件NSS信号,分频系数(Prescaler)设置为8,波特率为5.25Mbit/s。数据传输是MSB先行,CPOL和CPHA的组合是SPI时序模式3。
(4)FatFS模式
模式设置就是选择FatFS应用的存储介质,有以下4个选项。
- External SRAM,外部SRAM存储器。例如,开发板上有一个外部SRAM芯片IS62WV51216,可以将此芯片的一部分或全部存储区域用作文件系统,使用FatFS管理SRAM上的文件,实现内存上的高速文件读写。
- SD Card,SD卡。此项需要启用SDIO接口后才可以选择。
- USB Disk,U盘。需要将USB-OTG-FS或USB-OTG-HS组件的模式设置为Host-Only(仅作为主机),并且将Middleware分组中的USB_HOST组件的IP类型设置为MassStorage Host Class(大容量存储主机类)之后,此项才可以选择。
- User-defined,用户定义器件。除以上3项之外的其他存储介质,例如,开发板上连接在SPI2接口上的Flash存储芯片W25Q16。
使用前3种存储介质时,CubeMX生成的代码里有FatFS完整的移植程序,因此用户无须再编程实现硬件层的Disk IO函数。使用用户定义器件时,CubeMX生成的代码里有FatFS移植代码框架,用户需要自己编程实现器件的Disk IO函数。本文的示例使用的存储介质是连接在SPI2接口上的Flash存储芯片W25Q16属于用户定义器件。通过这个示例,详细地了解FatFS程序移植的原理。
(5)FatFS参数设置概述
设置FatFS参数。这些参数分为多个组,大多数与FatFS配置文件ffconf.h中的宏定义对应,用于设置FatFS的一些参数,以及进行功能裁剪。
1) Version组
只有一个参数,显示了FatFS的版本。图中显示的版本为R0.12c。
2) Function Parameters组
用于配置是否包含某些函数,就是FatFS的功能裁剪。
3) Locale and Namespace Parameters组
本地化和名称空间参数,例如,设置代码页、是否使用长文件名等。
4) Physical Driver Parameters组
物理驱动器参数,包括卷的个数、扇区大小等。
5) System Parameters组
系统参数,一些系统级的参数定义,如是否支持exFAT文件系统。在设置参数时,用户可以单击参数设置界面右上方的显示描述信息的小按钮,这样就可以让每个参数的描述信息显示出来,例如参数设置的数值范围等。
3、Function Parameters组参数设置
Function Parameters组参数,第一列是参数名称,对应于源文件中的宏,宏的名称就是参数名称前加“_”,例如,参数FS_READONLY对应的宏是_FS_READONLY,参数USE_FIND对应的宏是_USE_FIND。第二列是参数值,这些参数一般是逻辑值,Disabled表示设置为0,Enabled表示设置为1。
这些参数有的用于定义系统的特性,如FS_MINIMIZE定义系统最小化级别,会影响多个函数;有的用于裁剪某个功能,可同时影响多个函数,如USE_FIND影响函数f_findfirst()和f_findnext();有的参数只影响一个函数,如USE_CHMOD只控制是否使用函数f_chmod()。
参数 | 默认值 | 可设置内容和影响的函数 |
FS_READONLY | Disabled | 只能设置为Disabled,表示不使用只读功能 |
FS_MINIMIZE | Disabled | 最小化级别,可设置为0、1、2、3等4种级别 |
USE_STRFUNC | 2 | 是否使用字符串函数,可设置为0、1或2 |
USE_FIND | Disabled | 是否使用查找函数f_findfirst()和f_findnext() |
USE_MKFS | Enabled | 是否使用函数f_mkfs() |
USE_FASTSEEK | Enabled | 是否使用快速寻找功能,无具体影响函数 |
USE_EXPAND | Disabled | 是否使用函数f_expand() |
USE_CHMOD | Disabled | 是否使用函数f_chmod() |
USE_LABEL | Disabled | 是否使用获取和设置卷标签的函数f_getlable()和f_setlabel() |
USE_FORWARD | Disabled | 是否使用函数f_forward() |
(1)参数FS_READONLY
在CubeMX中,参数FS_READONLY只能设置为Disabled,表示不使用只读功能。若在代码中设置_FS_READONLY为1,表示系统为只读系统,将移除用于写操作的函数,包括f_write()、f_sync()、f_unlink()、f_mkdir()、f_chmod()、f_rename()和f_truncate(),并且函数f_getfree()将变得无用。
(2)参数S_MINIMIZE
参数S_MINIMIZE设置系统的最小化级别,有如下4种选项。
- Disabled:对应级别0,启用所有基本函数。
- Enabled with 6 functions removed:对应级别1,移除函数f_stat()、f_getfree()、f_unlink()、f_mkdir()、f_truncate()和f_rename()。
- Enabled with 9 functions removed:对应级别2,在级别1的基础上,再移除函数f_opendir()、f_readdir()和f_closedir()。
- Enabled with 10 functions removed:对应级别3,在级别2的基础上,再移除函数f_lseek()。
(3)参数USE_STRFUNC
参数USE_STRFUNC设置是否使用字符串相关函数,以及如何使用,有如下3种选项。
- Disabled:对应值0,不使用字符串相关的函数,如f_gets()、f_putc()、f_puts()等。
- Enabled without LF->CRLF conversion:对应值1,使用字符串相关函数,但不使用LF->CRLF转换。
- Enabled with LF->CRLF conversion:对应值2,使用字符串相关函数,并且使用LF->CRLF转换,也就是字符串中的'\n'会被转换为\r¹+'n'。
4、Locale and Namespace Parameters组参数设置
Locale and Namespace Parameters组参数用于设置本地语言代码页,是否使用长文件名等。
参数 | 默认值 | 可设置内容和功能描述 |
CODE_PAGE | Latin 1 | 设置目标系统上使用的OEM编码页,编码页如果选择不正常,可能 |
USE_LFN | Disabled | 是否使用长文件名(LFN) |
MAX_LFN | 255 | 设定值范围为12至255,是LFN的最大长度 |
LFN_UNICODE | ANSI/OEM | 是否将FatFS API中的字符编码切换为Unicode。当USE_LFN设置为 |
STRF_ENCODE | UTF-8 | 启用Unicode后,FatFSAPI中的字符编码都需要转换为Unicode。这 |
FS_RPATH | Disabled | 是否使用相对路径,以及使用相对路径时的特性 |
(1)参数USE_LFN
参数USE_LFN控制是否使用LFN(Long File Name,长文件名),有如下4种选项。
- 0=Disabled:不使用LFN,参数MAX_LFN无影响。
- 1=Enable LFN with static working buffer on the BSS:使用LFN,且使用BSS段的静态工作缓冲区。这种情况下,LFN工作缓冲区是BSS段上的静态变量,总是不可重入的,即不是线程安全的。
- 2=Enable LFN with dynamic working buffer on the STACK:使用LFN,且在栈空间为LFN分配动态工作缓冲区。
- 3=Enable LFN with dynamic working buffer on the HEAP:使用LFN,且在堆空间为LFN分配动态工作缓冲区。
要使用LFN功能,请务必将处理Unicode的函数ff_convert()和f_wtoupper()添加到项目中。LFN工作缓冲区占用(MAX_LFN+1)×2字节。当使用栈空间作为LFN工作缓冲区时,要注意栈溢出问题。使用堆空间作为LFN工作缓冲区时,需要将内存管理函数ff_memalloc()和ff_memfree()添加到项目中。
如果不使用LFN,即参数USE_LFN设置为Disabled时,不含后缀的文件名的长度不能超过8个ASCII字符,后缀为3个ASCII字符。如果在嵌入式设备上使用LFN,最好也不要在文件名中使用汉字,即LFN_UNICODE不要设置为Unicode,而是设置为ANSI/OEM。本示例暂时不使用LFN,所以将USE_LFN设置为Disabled。但是FatFS需要能支持中文,所以CODE_PAGE设置为Simplified Chinese(DBCS)。
(2)参数STRF_ENCODE
参数STRF_ENCODE用于设置字符串的编码。当LFN_UNICODE设置为0(ANSI/OEM)时,这个参数无效。当LFN_UNICODE设置为1(Unicode)时,FatFS API中的字符编码都需要转换为Unicode。这个参数用于选择字符串操作相关函数,如f_gets()、f_putc()、f_puts()、f_printf()等,在读写文件时使用的编码,有如下几种选项。
- 0=ANSI/OEM。
- 1=UTF-16LE。
- 2=UTF-16BE。
- 3=UTF-8。
其中,UTF-8是最常用的汉字编码,需要使用汉字时,就将此参数设置为UTF-8。
(3)参数FS_RPATH
参数FS_RPATH用于设置是否使用相对路径,以及使用相对路径时的特性,有如下3种选项。
- 0=Disabled,不使用相对路径,移除相关函数。
- 1=Enabled without f_getcwd,使用相对路径,可使用函数f_chdrive()和f_chdir()。
- 2=Enabled with f_getcwd,在选项1的基础上,增加可使用函数f_getcwd()。
读取目录的函数f_readdir()的返回结果与此选项有关。
5、Physical Driver Parameters组参数设置
Physical Driver Parameters组参数用于设置系统内卷的个数、扇区的大小、是否有多个分区等。
参数 | 默认值 | 可设置内容和功能描述 |
VOLUMES | 1 | 使用的逻辑驱动器的个数,设置范围为1~9 |
MAX_SS | 512 | 最大扇区大小(字节数),只能设置为512、1024、2048或4096,比如使用的Flash存储芯片W25Q128的扇区大小为4096字节,所 |
MIN_SS | 512 | 最小扇区大小(字节数),只能设置为512、1024、2048或4096 |
MULTI_PARTITION | Disabled | 设置为Disabled时,每个卷与相同编号的物理驱动器绑定,只会挂 |
USE_TRIM | Disabled | 是否使用ATA_TRIM特性。要想使用Trim特性,需要在disk_ioctl() |
FS_NOFSINFO | 0 | 参数取值为0、1、2或3,它设置了函数f_getfree()的运行特性 |
FatFS可以支持多个卷,这多个卷可以是多个不同的存储介质,例如,一个嵌入式设备上同时有Flash存储芯片和SD卡。存储介质的扇区大小是固定的,但不同存储介质的扇区大小不一样,例如,SD卡的扇区大小是512字节,而Flash存储芯片W25Q16的扇区大小是4096字节。
参数FS_NOFSINFO是两位二进制的数[bit1,bit0],组成的参数值是0、1、2或3,用于设置函数f_getfree()的运行特性。函数f_getfree()用于获取一个卷的剩余簇个数。如果bit0=0,在卷挂载后,首次执行函数f_getfree()时,会强制进行完整的FAT扫描,得到的剩余簇个数会记录到返回的FATFS对象的free_clst变量中,bit1根据bit0的设置控制最后分配的簇编号,也就是结构体FATFS中的成员变量last_clst。结构体FATFS中的成员变量free_clst和last_clst称为FSINFO,也就是剩余空间信息(free space information)。
- bit0=0:使用FSINFO中的剩余簇个数,即成员变量free_clst。
- bit0=1:不要相信FSINFO中的剩余簇个数,因为不会进行完全的FAT扫描。
- bit1=0:使用FSINFO中的最后分配簇编号,即成员变量last_clst。
- bit1=1:不要相信FSINFO中的最后分配簇编号。
参数FS_NOFSINFO影响f_getfree()的运行效果。在后面的示例中,我们会讲到如何用函数f_getfree()获取存储介质空间信息。
6、System Parameters组参数设置
System Parameters组参数用于设置FatFS的一些系统级别信息。
参数 | 默认值 | 可设置内容和功能描述 |
FS_TINY | Disabled | 微小缓冲区模式,如果设置为Enabled,每个文件对象(FIL)可减少 |
FS_EXFAT | Disabled | 是否支持exFAT文件系统,当_USE_LFN设置为0时,这个参数只 |
FS_NORTC | Dynamic | 如果系统有RTC提供实时的时间,就设置为Dynamic timestamp;如 |
FS_REENTRANT | Disabled | 设置FatFS的可重入性,在CubeMX里如果没有启用FreeRTOS,这 |
FS_TIMEOUT | 1000 | 超时设置,单位是节拍数。FS_REENTRANT设置为Disabled时,这 |
FS_LOCK | 2 | 如果要启用文件锁定功能,设定FS_LOCK的值大于或等于1,表示 |
(1)参数FS_NORTC
参数FS_NORTC用于设置是否有RTC为FatFS提供时间戳。如果系统有RTC提供实时的时间,就设置为Dynamic timestamp,在移植时,实现硬件层访问函数get_fattime(),读取RTC的当前时间作为文件的时间戳。如果系统没有RTC提供实时的时间,就设置为Fixedtimestamp,会出现NORTC_YEAR(年)、NORTC_MON(月)和NORTC_MDAY(日)这3个参数,用于设置一个固定的时间戳数据。
(2)参数FS_REENTRANT
参数FS_REENTRANT用于设置FatFS的可重入性。可重入性表示是否线程安全,在使用RTOS时,才有可重入性问题。所以,在CubeMX里如果没有启用FreeRTOS,这个参数只能设置为Disabled;如果启用了FreeRTOS,这个参数只能设置为Enabled。
如果FS_REENTRANT设置为Enabled,会出现参数SYNC_t和USE_MUTEX。其中,SYNC_t是用于同步的对象类型。当USE_MUTEX设置为Disabled时,SYNC_t固定为oSSemaphoreld_t,即使用信号量;当USE_MUTEX设置为Enabled时,SYNC_t固定为osMutexId_t,即使用互斥量。
如果设置为可重入的,还需要在FatFS中移植函数ff_req_grant()、ff_rel_grant()、ff_del_syncobj()和ff_cre_syncobj()。
7、项目中FatFS的文件组成
完成设置后,CubeMX会自动生成代码。在CubeIDE中打开项目,将KEY_LED驱动程序目录添加到项目搜索路径。在本示例中,用到W25Q16芯片,其驱动程序FLASH目录也添加到项目搜索路径。
项目中FatFS相关的文件是自动加入的。使用CubeMX生成的FatFS的文件组成(对比:全手工移植的FatFS文件组成,两者区别很大)。Middlewares目录下加入的FatFS的源代码文件,这些是不允许用户修改的FatFS源程序文件,各个文件的作用描述如下。
- 文件integer.h:包含FatFS中用到的各种基础数据类型的定义。
- 文件ff.h和ff.c:这是FatFS应用程序接口API函数所在的文件,是与具体硬件无关的软件模块。
- 文件diskio.h和diskio.c:这是存储介质Disk IO访问通用接口函数所在的文件。在CubeMX生成的项目代码中,与具体存储介质相关的Disk IO函数在另外的文件里实现,例如,本示例使用的存储介质是User-defined,会自动生成用户程序文件user_diskio.h和user_diskio.c。文件diskio.c中的函数只是调用文件user_diskio.c中定义的具体Disk IO函数。
- 文件ff_gen_drv.h和ff_gen_drv.c:实现驱动器列表管理功能的文件,这些功能包括链接一个驱动器,或解除一个驱动器的链接。FatFS初始化时,就调用其中的函数FATFS_LinkDriver()链接驱动器。
- option子目录下的文件syscall.c:包含在使用RTOS系统时需要实现的一些函数的示例代码,如果使用FreeRTOS,需要重新实现这些函数。
与具体存储介质相关,可以由用户修改的FatFS相关文件在目录\FATFS下。这些文件的功能描述如下。
- 文件fatfs.h和fatfs.c:是用户的FatFS初始化程序文件。其中有FatFS初始化函数MX_FATFS_Init()、几个全局变量的定义,以及需要重新实现的获取RTC时间作为文件系统时间戳的函数get_fattime()。
- 文件ffconf.h,FatFS的配置文件,包含很多的宏定义,与CubeMX里的FatFS设置对应。
- 文件user_diskio.h和user_diskio.c,是user-defined存储介质的Disk IO函数的程序文件,自动生成了各个函数的框架,只需针对SPI-Flash芯片编写具体的函数代码即可。
FatFS相关文件的层次和关系如下图所示:
- 文件fatfs.h中包含CubeMX生成的FatFS初始化函数MX_FATFS_Init(),在主程序中进行外设初始化时,会调用这个函数。
- 函数MX_FATFS_Init()会调用文件ff_gen_drv.h中的函数FATFS_LinkDriver(),将文件user_diskio.h中定义的驱动器对象USER_Driver链接到FatFS管理的驱动器列表里,相当于完成了驱动器的注册。
- 文件user_diskio.c中实现了针对W25Q16芯片的Disk IO访问函数。文件user_diskio.h中定义了驱动器对象USER_Driver,并且使用函数指针将diskio.h中的Disk IO通用函数指向文件user_diskio.c中实现的针对W25Q16芯片的Diok IO函数。
- 在文件user_diskio.c中实现W25Q16芯片的Disk IO访问函数时,需要用到W25Q16芯片的驱动程序文件w25flash.b/.c,而这个驱动程序使用SPI接口的HAL驱动程序实现对W25Q16芯片的访问。