瑞萨RA系列FSP库开发实战指南之基于FLASH的FatFs文件系统移植实验
24.4
FatFs文件系统移植实验
24.4.1
硬件设计及FSP
FatFs属于软件组件,不需要附带其他硬件电路。我们使用串行Flash芯片作为物理存储设备,其硬件电路在上一章已经做了分析,这里就直接使用。
24.4.2
FatFs移植步骤概述
基本步骤:
实现底层驱动接口
修改配置文件
移植FatFs之前我们先通过FatFs的程序结构图了解FatFs在程序中的关系网络,见图FatFs程序结构图。

用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到f_mount()、f_open()、f_write()、f_read()就可以实现文件的读写操作。
FatFs组件是FatFs的主体,文件都在源码src文件夹中,其中ff.c、ff.h、ffsystem.c以及ffunicode.c4个文件我们不需要改动,只需要修改ffconf.h和diskio.c/.h3个文件。
底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。我们使用串行Flash芯片作为物理设备,在上一章节已经编写好了串行Flash芯片的驱动程序,这里我们就直接使用。
24.4.2.1
实现底层驱动接口
FatFs文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已。表FatFs移植需要用户支持函数为FatFs移植时用户必须支持的函数。
通过表FatFs移植需要用户支持函数我们可以清晰知道很多函数是在一定条件下才需要添加的,只有前三个函数是必须添加的。我们完全可以根据实际需求选择所需用到的函数。
前三个函数是实现读文件最基本需求。接下来三个函数是实现创建文件、修改文件需要的。为实现格式化功能,需要在disk_ioctl添加两个获取物理设备信息选项。我们一般只要实现前面六个函数就可以了,已经足够满足大部分功能。
为支持简体中文长文件名称需要添加ff_uni2oem、ff_oem2uni和ff_wtoupper 函数,实际这三个已经在ffunicode.c文件中实现,我们只要直接把ffunicode.c文件添加到工程中就可以。
底层设备驱动函数是存放在diskio.c文件,我们的目的就是把diskio.c中的函数接口与串行Flash芯片驱动连接起来。总共有五个函数,分别为设备状态获取(disk_status)、设备初始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)。
接下来,我们对每个函数结合串行Flash芯片驱动做详细讲解。
24.4.2.2
修改配置文件
ffconf.h文件是FatFs的配置文件。
下面是ffconf.h文件中,需要修改的部分,只把需要修改的部分放出来:
列表1:
代码清单24‑1 FatFs的配置文件:ffconf.h
左右滑动查看完整内容
/********************/ /* 下面是经过修改部分 */ /********************/ #define FF_USE_MKFS 1 //此选项切换是否启用 f_mkfs() 函数,用于格式化 Flash、SD卡等 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ #define FF_CODE_PAGE 936 //此选项指定使用的OEM代码页 /* This option specifies the OEM code page to be used on the target system. / Incorrect code page setting can cause a file open failure. / / 437 - U.S. / 720 - Arabic / 737 - Greek / 771 - KBL / 775 - Baltic / 850 - Latin 1 / 852 - Latin 2 / 855 - Cyrillic / 857 - Turkish / 860 - Portuguese / 861 - Icelandic / 862 - Hebrew / 863 - Canadian French / 864 - Arabic / 865 - Nordic / 866 - Russian / 869 - Greek 2 / 932 - Japanese (DBCS) / 936 - Simplified Chinese (DBCS) / 949 - Korean (DBCS) / 950 - Traditional Chinese (DBCS) / 0 - Include all code pages above and configured by f_setcp() */ #define FF_USE_LFN 2 //此选项切换对长文件名的支持 #define FF_MAX_LFN 255 //设置长文件名的最长长度 /* The FF_USE_LFN switches the support for LFN (long file name). / / 0: Disable LFN. FF_MAX_LFN has no effect. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. #define FF_LFN_UNICODE 2 //此选项设置是否启用 Unicode 字符编码 /* This option switches the character encoding on the API when LFN is enabled. / / 0: ANSI/OEM in current CP (TCHAR = char) / 1: Unicode in UTF-16 (TCHAR = WCHAR) / 2: Unicode in UTF-8 (TCHAR = char) / 3: Unicode in UTF-32 (TCHAR = DWORD) / / Also behavior of string I/O functions will be affected by this option. / When LFN is not enabled, this option has no effect. */ #define FF_VOLUMES 2 //要使用的卷(逻辑驱动器)的数量。范围(1-10) /* Number of volumes (logical drives) to be used. (1-10) */ #define FF_MIN_SS 512 #define FF_MAX_SS 4096 //这组选项配置支持的扇区大小范围 /* This set of options configures the range of sector size to be supported. (512, / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and / harddisk, but a larger value may be required for on-board flash memory and some / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured / for variable sector size mode and disk_ioctl() function needs to implement / GET_SECTOR_SIZE command. */ /* 若 FF_MIN_SS != FF_MAX_SS。则需要在 disk_ioctl() 中指定所需操作的设备的扇区大小 * 在 case GET_SECTOR_SIZE 项中指定 */ #define FF_FS_NORTC 1 //设置为1关闭时间戳 启用时间戳功能需要RTC #define FF_NORTC_MON 1 #define FF_NORTC_MDAY 1 #define FF_NORTC_YEAR 2022 /* The option FF_FS_NORTC switches timestamp feature. If the system does not have / an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the / timestamp feature. Every object modified by FatFs will have a fixed timestamp / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be / added to the project to read current time form real-time clock. FF_NORTC_MON, / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
24.4.3.路径名格式说明
在 FatFs 的 API 函数中,经常能看到 “const TCHAR* path” 这样的函数参数,比如:
FRESULT f_mount (
FATFS* fs, /* 指向要注册的文件系统对象的指针(NULL:卸载) */
const TCHAR* path, /* 要装载/卸载的逻辑驱动器号 */
BYTE opt /* 装载选项:0=不装载(延迟装载),1=立即装载 */
)
FRESULT f_open (
FIL* fp, /* 指向空白文件对象的指针 */
const TCHAR* path, /* 指向文件名的指针 */
BYTE mode /* 访问模式和打开模式标志 */
)
这就要求我们要知道 path 这个参数的可输入值是什么,也就是我们要了解FatFs的文件命名规范。
24.4.3.1.文件命名格式
FatFs模块上的路径名格式与DOS/Windows的文件名规范类似,如下所示:
[drive#:][/]"directory 目录"/"file 文件"
FatFs 模块支持长文件名(LFN) 和 8.3 格式的文件名 (SFN)。长文件名(LFN) 可在 FF_USE_LFN >= 1 时使用。 子目录用 \ 或 / 分隔,方式与DOS/Windows API相同。 重复的分隔符和终止分隔符将被忽略,例如:“//animal///cat/”。 唯一不同的是,FatFs中指定逻辑驱动器(FAT卷)的标题驱动器前缀是数字(0-9)+冒号,而在DOS/Windows中是字母(A-Z)+冒号。 逻辑驱动器编号是指定要访问的卷的标识符。 如果省略驱动器前缀,则假定逻辑驱动器编号为默认驱动器。
注解
LFN 和 SFN
8.3 命名规则,又称短文件名(Short File Name,SFN)是一种限制文件名长度的方法,这在DOS和Windows 95及Windows NT 3.51以前的Microsoft Windows版本中,在FAT文件系统中的常用方法。
长文件名,(Long file name,LFN)也指长文件名支持。在旧版本的DOS操作系统下,因为文件名称有8.3格式的限制,凡文件主档名超过8字节或扩展名超过3字节的文件名,都被称为“长文件名”,在Windows下正常的文件名置换于DOS(或“命令提示字符”)环境下则可能无法完整显示,如“Program files”资料夹可能会显示成其对应的8.3文件名“PROGRA~1”。
24.4.3.2.FF_FS_RPATH
在默认配置 (FF_FS_RPATH==0) 中, 是没有当前目录的概念的。 卷上的每个对象始终以从根目录开始的完整路径名指定。 不可以使用点目录名称(“.”,“..”)。标题分隔符被忽略, 它可以存在或省略。默认驱动器固定为驱动器0。
启用相对路径功能时(FF_FS_RPATH>=1),如果存在分隔符, 则从根目录跟随指定的路径。如果没有分隔符,则从默认驱动器的当前目录开始。 路径名也允许使用点目录名。 当前目录由f_chdir函数设置,默认驱动器为f_chdrive函数设置的当前驱动器。

此外,驱动器前缀可以采用预定义的任意字符串。 当选项FF_STR_VOLUME_ID == 1 时,也可以将任意字符串卷 ID 用作驱动器前缀。 例如 “flash:file1.txt”、“ram:temp.dat” 或 “sd:” 。 如果 srting 与任何卷 ID 都不匹配,则该函数将失败并返回 FR_INVALID_DRIVE。
当FF_STR_VOLUME_ID == 2 时,可以使用 Unix 样式的驱动器前缀。 例如 “/flash/file1.txt”、“/ram/temp.dat” 或 “/sd” 。
FF_STR_VOLUME_ID 和 FF_VOLUME_STRS 宏
#define FF_STR_VOLUME_ID 0 //FF_STR_VOLUME_ID开关支持任意字符串形式的卷ID。
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
FF_STR_VOLUME_ID: 此选项切换对字符串卷 ID 的支持。
FF_STR_VOLUME_ID 为不同数值时,对应的可选项
| 数值 | 描述 | 示例 |
|---|---|---|
| 0 | 只能使用数字 ID 中的 DOS/Windows 样式驱动器前缀。 | 1:/filename |
| 1 | 还可以使用字符串 ID 中的 DOS/Windows 样式驱动器前缀。 | flash:/filename |
| 2 | 也可以使用字符串ID中的Unix样式驱动器前缀。 | /flash/filename |
FF_VOLUME_STRS: 此选项定义每个逻辑驱动器的卷 ID 字符串。项目数量不得少于FF_VOLUMES。 卷 ID 字符串的有效字符是 A-Z、a-z 和 0-9,不区分大小写。 如果FF_STR_VOLUME_ID == 0,则此选项不起作用。 如果 FF_STR_VOLUME_ID >= 1 并且未定义此选项, 则需要定义用户定义的卷字符串表,如下所示。不应动态修改该表。
用户定义卷ID字符串
/* 0: ~ 3: 的用户定义卷ID字符串: */
const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb"};
这里我们并没有用到任意字符串形式的卷ID,就没有开启 FF_STR_VOLUME_ID 宏。
24.4.4.接口函数
主要的接口文件都在 diskio.c 文件中了,我们只需要根据函数所提供的输入参数和返回参数, 来实现函数的功能,提供给FatFs调用就好了。
FatFs文件系统与存储设备的连接函数在diskio.c文件中,主要有5个函数需要我们编写的。
宏定义和存储设备状态获取函数
代码清单: 24_2 disk_ioctl 函数
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case DEV_FLASH :
QSPI_Flash_WaitForWriteEnd(); //等待Flash芯片内部操作完成
stat = RES_OK;
return stat;
}
return STA_NOINIT;
}
存储设备初始化函数
代码清单: 24_3 disk_ioctl 函数
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case DEV_FLASH :
QSPI_Flash_Init();
stat = RES_OK;
return stat;
}
return STA_NOINIT;
}
存储设备数据读取函数
代码清单: 24_4 disk_ioctl 函数
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
DRESULT res;
switch (pdrv) {
case DEV_FLASH :
// translate the arguments here
QSPI_Flash_BufferRead(buff, sector<<12, count<<12); //1 sector == 4096 bytes
res = RES_OK;
return res;
}
return RES_PARERR;
}
存储设备数据写入函数
代码清单: 24_5 disk_ioctl 函数
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
DRESULT res;
uint32_t write_addr;
switch (pdrv) {
case DEV_FLASH :
write_addr = sector << 12;
QSPI_Flash_SectorErase(write_addr);
QSPI_Flash_BufferWrite(buff, write_addr, count<<12);
res = RES_OK;
return res;
}
return RES_PARERR;
}
IO控制函数
代码清单: 24_6 disk_ioctl 函数
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
switch (pdrv) {
case DEV_FLASH :
switch (cmd) {
case GET_SECTOR_COUNT: /* 扇区数量:1024*4096/1024/1024 = 4(MB) */
*(DWORD *)buff = 1024;
break;
case GET_SECTOR_SIZE: /* 扇区大小 */
*(WORD *)buff = 4096;
break;
case GET_BLOCK_SIZE: /* 同时擦除扇区个数 */
*(DWORD *)buff = 1;
break;
}
res = RES_OK;
return res;
}
return RES_PARERR;
}
get_fattime函数
代码清单: 24_7 disk_ioctl 函数
DWORD get_fattime(void) {
/* 返回当前时间戳 */
return (DWORD)(2022 - 80) << 25 | /* Year */
(DWORD)(1 + 1) << 21 | /* Month */
(DWORD)1 << 16 | /* Mday */
(DWORD)1 << 11 | /* Hour */
(DWORD)1 << 5 | /* Min */
(DWORD)1 >> 1; /* Sec */
}
由于之前在配置文件关闭了 RTC 和时间戳功能,因此其实不需要实现这个返回当前时间戳函数。
由 FF_FS_NORTC 决定是否启用,FF_FS_NORTC设置为1以禁用时间戳功能。
24.4.5.FatFs 基本 API 函数说明
更多关于 FatFs 接口函数的说明请看官方的说明:http://elm-chan.org/fsw/ff/00index_e.html
24.4.5.1.f_mount
f_mount 函数原型如下:
FRESULT f_mount ( FATFS* fs, /* [IN] Filesystem object */ const TCHAR* path, /* [IN] Logical drive number */ BYTE opt /* [IN] Initialization option */ );
FatFs 需要每个逻辑驱动器(FAT 卷)的工作区域(文件系统对象)。 在执行任何文件/目录操作之前,需要使用逻辑驱动器的f_mount函数注册文件系统对象。 完成此过程后,文件/目录 API 函数已准备好工作。 某些卷管理功能(f_mkfs、f_fdisk和f_setcp)不需要文件系统对象。
f_mount函数将文件系统对象注册/注销到 FatFs 模块,如下所示:
确定由 path 指定的逻辑驱动器。
清除并注销卷的已注册工作区(如果存在)。
如果 fs 不为 NULL,则清除新工作区并将其注册到卷中。
如果指定了强制装入,则对卷执行卷装入过程。
opt:安装选项。0:现在不挂载(要在第一次访问卷时挂载),1:强制挂载卷以检查它是否准备好工作。
24.4.5.2.f_mkfs
f_mkfs函数原型如下:
FRESULT f_mkfs ( const TCHAR* path, /* [IN] Logical drive number */ const MKFS_PARM* opt,/* [IN] Format options */ void* work, /* [-] Working buffer */ UINT len /* [IN] Size of working buffer */ );
簇(cluster): 由于扇区的空间比较小且数目众多,在寻址时比较困难, 所以操作系统就将多个的扇区组合在一起,形成一个更大的单位, 再对这个单位进行整体的操作。也可以理解为文件的磁盘空间分配单位。 当簇的大小为 32768 字节时,大小为 100 字节的文件将占用 32768 字节的磁盘空间。 随着簇大小的增加,磁盘使用的空间效率会变得很低,但与此同时,读/写效率也会提高。 因此,簇的大小是空间效率和读/写效率之间的权衡。
opt:若为NULL,则启用默认参数
f_mkfs opt 默认参数
static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */
由work来指向用于格式化过程的工作缓冲区的指针。
由len来确定工作缓冲区的大小(以字节为单位)。至少需要FF_MAX_SS。
提示
当 FF_FS_READONLY == 0 且 FF_USE_MKFS == 1 时可用。
24.4.5.3.f_setlabel
f_setlabel 函数原型如下:
FRESULT f_setlabel ( const TCHAR* label /* [IN] Volume label to be set */ );
当字符串具有驱动器前缀时,卷标将设置为驱动器前缀指定的卷。 Unix 样式的卷 ID 不能用于指定卷。 如果未指定驱动器号,则卷标将设置为默认驱动器。 如果给定卷标的长度为零,则将删除卷上的卷标。卷标的格式如下所示:
在 FAT 卷上转换 OEM 代码页时最多 11 个字节。 在 exFAT 卷中最多 11 个字符。 FAT 卷允许的字符数为:SFN 允许的字符不包括点。低写字符向上转换。 exFAT 卷允许的字符为:LFN 允许的字符包括点。将保留小写字符。 空格可以嵌入卷标中的任何位置。尾随空格在 FAT 音量处被截断。
提示
当FF_FS_READONLY == 0 且FF_USE_LABEL == 1 时可用。
24.4.5.4.f_open
f_open 函数原型如下:
FRESULT f_open ( FIL* fp, /* [OUT] Pointer to the file object structure */ const TCHAR* path, /* [IN] File name */ BYTE mode /* [IN] Mode flags */ );
打开一个文件并创建一个文件对象。 完成对文件的访问后应该使用f_close函数将其关闭。 如果在关机、移出介质或重新装入之前对文件进行了任何更改且未调用f_close函数将其关闭,则文件可能会 collapsed (损坏)。
可选mode选项:
提示
当FF_FS_READONLY == 1 时,只有FA_READ和FA_OPEN_EXISTING可用于模式标志。
24.4.5.5.f_close
f_close 函数原型如下:
FRESULT f_close ( FIL* fp /* [IN] Pointer to the file object */ );
关闭打开的文件对象。如果文件已更改,则文件的缓存信息将写回卷。 函数成功后,文件对象不再有效,可以丢弃。
请注意,如果文件对象处于只读模式且未启用FF_FS_LOCK, 则也可以在不执行f_close函数的情况下丢弃该文件对象。 但是,不建议这样做。
24.4.5.6.f_write
f_write 函数原型如下:
FRESULT f_write ( FIL* fp, /* [IN] Pointer to the file object structure */ const void* buff, /* [IN] Pointer to the data to be written */ UINT btw, /* [IN] Number of bytes to write */ UINT* bw /* [OUT] Pointer to the variable to return number of bytes written */ );
该函数开始在读/写指针指向的文件偏移处将数据写入文件。 读/写指针随着写入的字节数而前进。 功能成功后,应检查 *bw 以检测磁盘已满。 如果 *bw < btw,则表示卷在写入操作期间已满。 该函数在卷已满或接近满时可能需要一段时间。
提示
FF_FS_READONLY == 0 时可用。
24.4.5.7.f_read
f_read 函数原型如下:
FRESULT f_read ( FIL* fp, /* [IN] File object */ void* buff, /* [OUT] Buffer to store read data */ UINT btr, /* [IN] Number of bytes to read */ UINT* br /* [OUT] Number of bytes read */ );
该函数开始在读/写指针指向的文件偏移处从文件中读取数据。 读/写指针随着读取的字节数而前进。 函数成功后,应检查 *br 以检测文件的末尾。 如果 *br < btr,则表示在读取操作期间读/写指针命中文件。
24.4.6.软件设计
与FatFs文件系统使用相关的变量定义如下:
代码清单: 24_8 hal_entry.c:全局变量
FATFS fs; /* FatFs文件系统对象 */
FIL fnew; /* 文件对象 */
UINT fnum; /* 文件成功读写数量 */
FRESULT res_flash; /* 文件操作结果 */
BYTE fileReadBuffer[1024]; /* 读缓冲区 */
BYTE fileWriteBuffer[] = /* 写缓冲区 */
"感谢您选用野火启明瑞萨RA开发板 [FatFs读写测试文件.txt]";
BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */
FATFS是在ff.h文件定义的一个结构体类型,针对的对象是物理设备,包含了物理设备的物理编号、扇区大小等等信息, 一般我们都需要为每个物理设备定义一个FATFS变量。
FIL也是在ff.h文件定义的一个结构体类型,针对的对象是文件系统内具体的文件,包含了文件很多基本属性,比如文件大小、 路径、当前读写地址等等。如果需要在同一时间打开多个文件进行读写,才需要定义多个FIL变量,不然一般定义一个FIL变量即可。
FRESULT是也在ff.h文件定义的一个枚举类型,作为FatFs函数的返回值类型,主要管理FatFs运行中出现的错误。 总共有19种错误类型,包括物理设备读写错误、找不到文件、没有挂载工作空间等等错误。这在实际编程中非常重要, 当有错误出现是我们要停止文件读写,通过返回值我们可以快速定位到错误发生的可能地点。如果运行没有错误才返回FR_OK。
fnum是个32位无符号整形变量,用来记录实际读取或者写入数据的数组。
fileReadBuffer和fileWriteBuffer分别对应读取和写入数据缓存区,都是8位无符号整形数组。
hal_entry 入口函数
代码清单: 24_9 hal_entry 入口函数
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "debug_uart/bsp_debug_uart.h"
//FatFs
#include "FatFs/ff15/ff.h"
FATFS fs; /* FatFs文件系统对象 */
FIL fnew; /* 文件对象 */
UINT fnum; /* 文件成功读写数量 */
FRESULT res_flash; /* 文件操作结果 */
BYTE fileReadBuffer[1024]; /* 读缓冲区 */
BYTE fileWriteBuffer[] = /* 写缓冲区 */
"感谢您选用野火启明瑞萨RA开发板 [FatFs读写测试文件.txt]";
BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */
void hal_entry(void)
{
/* TODO: add your own code here */
LED_Init(); // LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
printf("这是一个串行FLASH的FatFs使用演示例程\r\n");
printf("打开串口助手查看打印的信息\r\n\r\n");
/* 尝试挂载外部FLASH FAT文件系统 */
res_flash = f_mount(&fs, "0:", 1);
if (res_flash == FR_NO_FILESYSTEM)
{
printf(">>> FLASH还没有文件系统,即将进行格式化...\r\n");
/* 格式化 */
res_flash = f_mkfs("0:", NULL, work, sizeof(work));
if (res_flash == FR_OK)
{
printf(">>> FLASH已成功格式化文件系统。\r\n");
/* 格式化后,先取消挂载 */
res_flash = f_mount(NULL,"0:",1);
/* 重新挂载 */
res_flash = f_mount(&fs,"0:",1);
}
else
{
printf(">>> 格式化失败!!\r\n");
while (1);
}
}
else if (res_flash == FR_OK)
{
printf(">>> 文件系统挂载成功,可以进行读写测试。\r\n");
}
else
{
printf("!!外部Flash挂载文件系统失败。(%d)\r\n", res_flash);
printf("!!可能原因:Flash初始化不成功。\r\n");
while (1);
}
/*----------------------- 文件系统测试:写测试 -----------------------------*/
printf("\r\n****** 即将进行文件写入测试 ******\r\n");
/* 打开文件,如果文件不存在则创建它 */
res_flash = f_open(&fnew, "0:FatFs读写测试文件.txt", FA_CREATE_ALWAYS | FA_WRITE);
if ( res_flash == FR_OK )
{
printf(">>> 打开/创建“FatFs读写测试文件.txt”文件成功,向文件写入数据。\r\n");
/* 将指定存储区内容写入到文件内 */
res_flash = f_write(&fnew, fileWriteBuffer, sizeof(fileWriteBuffer), &fnum);
if (res_flash==FR_OK) {
printf(">>> 文件写入成功,写入字节数据:%d\r\n", fnum);
printf(">>> 向文件写入的数据为:%s\r\n", fileWriteBuffer);
} else {
printf("!!文件写入失败:(%d)\n",res_flash);
}
/* 不再读写,关闭文件 */
f_close(&fnew);
}
else
{
printf("!!打开/创建文件失败。\r\n");
}
/*----------------------- 文件系统测试:读测试 -----------------------------*/
printf("****** 即将进行文件读取测试 ******\r\n");
/* 打开文件,该文件前面已创建 */
res_flash = f_open(&fnew, "0:FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ);
if (res_flash == FR_OK)
{
printf(">>> 打开文件成功。\r\n");
res_flash = f_read(&fnew, fileReadBuffer, sizeof(fileReadBuffer), &fnum);
if (res_flash==FR_OK) {
printf(">>> 文件读取成功,读到字节数据:%d\r\n", fnum);
printf(">>> 读取得的文件数据为:%s\r\n", fileReadBuffer);
} else {
printf("!!文件读取失败:(%d)\n",res_flash);
}
/* 不再读写,关闭文件 */
f_close(&fnew);
}
else
{
printf("!!打开文件失败。\r\n");
}
/* 不再使用文件系统,取消挂载文件系统 */
f_mount(NULL,"0:",1);
printf("****** 测试结束 ******\r\n");
while(1);
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
程序的开头首先初始化调试串口,用来打印程序运行的一些调试信息。 此处的代码是以启明6M5板子的为例,另外两块板子的串口初始化函数名可能不同,读者需要注意。
FatFs 使用的第一步工作就是使用 f_mount 函数挂载文件系统。 f_mount函数有三个形参,第一个参数是指向FATFS变量指针,如果赋值为NULL可以取消物理设备挂载。 第二个参数为逻辑设备编号,使用设备根路径表示,与物理设备编号挂钩, 在 diskio.h 中我们定义外部FLASH存储器的物理编号为0,所以这里使用“0:”。 第三个参数可选0或1,1表示立即挂载,0表示不立即挂载,延迟挂载。 f_mount函数会返回一个 FRESULT 类型的值,指示挂载结果的情况。
如果 f_mount 函数返回值为 FR_NO_FILESYSTEM,说明外部FLASH存储器还没有FAT文件系统。 我们就必须对外部FLASH存储器进行格式化处理,将里面的存储空间以FAT文件系统的格式进行格式化。 使用 f_mkfs 函数可以实现格式化操作。 f_mkfs 函数有三个形参,第一个参数为逻辑设备编号; 第二个参数为格式化选项,选择格式化的类型,比如:FAT16/FAT32/exFAT等,NULL表示使用默认选项进行格式化; 第三个参数指定工作缓存区;第四个参数提供工作缓存区的大小。 格式化成功后需要先取消挂载原来设备,再重新挂载设备。
在设备正常挂载后,就可以进行文件读写操作了。使用文件之前,必须使用f_open函数打开文件,不再使用文件时必须使用f_close函数关闭文件, 这个跟电脑端操作文件步骤类似。f_open函数有三个形参,第一个参数为文件对象指针。第二参数为目标文件,包含绝对路径的文件名称和后缀名。 第三个参数为访问文件模式选择,可以是打开已经存在的文件模式、读模式、写模式、新建模式、总是新建模式等的或运行结果。比如对于写测试, 使用FA_CREATE_ALWAYS和FA_WRITE组合模式,就是总是新建文件并进行写模式。
f_close函数用于不再对文件进行读写操作关闭文件,f_close函数只要一个形参,为文件对象指针。f_close函数运行可以确保缓冲区完全写入到文件内。
成功打开文件之后就可以使用f_write函数和f_read函数对文件进行写操作和读操作。这两个函数用到的参数是一致的,只不过一个是数据写入,一个是数据读取。 f_write函数第一个形参为文件对象指针,使用与f_open函数一致即可。第二个参数为待写入数据的首地址,对于f_read函数就是用来存放读出数据的首地址。 第三个参数为写入数据的字节数,对于f_read函数就是欲读取数据的字节数。第四个参数为32位无符号整形指针,这里使用fnum变量地址赋值给它, 在运行读写操作函数后,fnum变量指示成功读取或者写入的字节个数。之后 我们通过相应函数的返回值赋值到res_flash,后进行判断当前程序是否运行成功我们点亮绿灯和蓝灯,如果其中有一个失败我们点亮红灯。
最后,不再使用文件系统时,使用f_mount函数取消挂载。
24.4.7.实验验证
用USB线连接开发板“USB TO UART”接口跟电脑,在电脑端打开串口调试助手, 把编译好的程序下载到开发板。在串口调试助手可看到FatFs测试的调试信息。

本文 zblog模板 原创,转载保留链接!网址:https://www.wbaas.cn/fengrong/1478.html
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
