FATFS移植 - 基于N32G4FR在SD卡(sd nand)上移植FATFS文件系统
文章目录
FATFS移植 - 基于N32G4FR在SD卡(sd nand)上移植FATFS文件系统
1. 前言
2. FATFS文件系统介绍
2.1 文件系统的概念和作用
2.2 FATFS的特点和优势
3. 相关源码获取
3.1 FatFs文件源码下载
3.2 国民技术N32软件开发套件获取
4. 文件架构说明
4.1 FatFs文件架构说明
4.1.1 本地离线文档访问方式
4.1.2 源码文件介绍
4.2 国民技术N32文件架构说明
5. FatFs移植
5.1 SD卡/SD nand读写实现
5.2 FatFs移植
5.2.1 添加源文件
5.2.2 添加设备号
5.2.3 修改 `disk_status()` 函数
5.2.4 修改 `disk_initialize()` 函数
5.2.5 修改 `disk_read()` 函数
5.2.6 修改 `disk_write()` 函数
5.2.7 修改 `disk_ioctl()` 函数
5.2.8 修改 `ffconf.h` 取消 `get_fattime` 实现
5.2.9 接口测试
5.2.10 调用通用文件操作接口进行读写测试
5.2.10.1 `f_mount()` 函数
5.2.10.2 `f_mkfs()` 函数
5.2.10.3 读写测试
6. 结束语
————————————————
1. 前言
FATFS(File Allocation Table File System)是一个轻量级的文件系统,被广泛应用于嵌入式系统和小型存储设备中。它由Chan提供,并在嵌入式系统中得到了广泛的应用和支持。
FatFS官网地址为:FatFs - Generic FAT Filesystem Module。
本文主要分享关于 FatFS 文件系统在SD卡/SD nand上的移植使用,本文所采用的硬件环境如下:
控制器:国民技术 N32G4FR,Cortex-M4内核控制器
SD nand:创世 CSNP4GCR01-AMW,4Gb SD nand
2. FATFS文件系统介绍
2.1 文件系统的概念和作用
在计算机系统中,文件系统是用于组织和管理存储介质上的文件和目录的一种结构。文件系统提供了对文件的读取、写入、删除、重命名等操作,以及对目录的创建、删除、遍历等操作。它使得用户和应用程序能够方便地访问和管理存储设备上的数据。
2.2 FATFS的特点和优势
FATFS是一个开源的文件系统,具有以下特点和优势:
轻量级和高效性:FATFS是一个轻量级的文件系统,适用于资源受限的嵌入式系统和小型存储设备。它的代码量相对较小,占用的存储空间较少,并且具有较高的运行效率。
跨平台兼容性:FATFS可以在多个操作系统和平台上运行,包括嵌入式系统、Windows、Linux等。这使得开发人员可以方便地将FATFS应用于不同的硬件平台和操作系统环境中。
易于集成和使用:FATFS的源代码结构清晰,具有简单的API接口,易于集成到嵌入式系统的应用程序中。开发人员可以通过简单的函数调用来实现文件的读写、目录的创建和遍历等操作。
支持多种存储介质:FATFS支持多种存储介质,包括SD卡、SPI Flash、硬盘等。它可以根据不同的存储介质进行配置和适配,并提供统一的文件系统接口。
支持多种文件操作:FATFS支持常见的文件操作,如文件的打开、关闭、读取、写入、定位等。它还支持目录的创建、删除、重命名和遍历,以及文件和目录的属性管理。
支持长文件名和短文件名:FATFS同时支持长文件名(Long File Name,LFN)和短文件名(Short File Name,SFN)。这使得文件系统更加灵活和兼容,能够处理各种不同的文件命名规则。
支持文件系统的格式化和检查:FATFS提供了格式化和检查文件系统的功能。开发人员可以通过相应的API函数来格式化存储介质并创建文件系统,也可以进行文件系统的检查和修复。这使得文件系统能够保持良好的状态,提高数据的可靠性和完整性。
支持文件的读写缓存:FATFS允许开发人员配置读写缓存,以提高文件的读写性能。通过使用读写缓存,可以减少对存储介质的频繁访问,提高文件的读写速度。
具有错误处理和容错机制:FATFS提供了错误处理和容错机制,能够检测和处理各种错误情况。例如,当存储介质出现错误或不可用时,FATFS能够进行相应的错误处理和恢复,保证文件系统的稳定性和可靠性。
3. 相关源码获取
3.1 FatFs文件源码下载
移植FatFs,第一步当然就是获取对应的移植文件了, FatFs的源文件在其官网上即可直接下载,地址:FatFs - Generic FAT Filesystem Module
3.2 国民技术N32软件开发套件获取
国民技术作为一款国产的IC,对标stm32,性能上也还是很不错了,不过其开发资料下载会有点麻烦,不过有幸你看到这里,就不会感觉很麻烦了,其开发资料下载方法如下:
1.打开电脑的文件管理器,输入以下内容:ftp://download.nationstech.com ,之后回车访问
2.此远程文件夹就是国民技术所有IC的全部资料了,之后便可根据自己手上所使用的IC,下载对应的资料,此处我们使用的是N32G4FR系列芯片,进入1-Microcontrollers
目录,选择对应的压缩包
,单击鼠标右键
,选择 复制到文件夹
进行下载
4. 文件架构说明
4.1 FatFs文件架构说明
documents:主要存放关于FatFs有关离线本地文档,文档内容和官网一致,等同于一个本地的副本,可以很好解决官网访问速度过慢的问题
source:里面存放了FatFs有关的源码
4.1.1 本地离线文档访问方式
使用本地浏览器打开 documents 目录下的 00index_e.html 文件,即可访问一个和官网完全一样的网页文档!且文档内的超链接也可直接点击跳转!
此文档非常重要,在FatFs文件使用中,很多API的接口,我们都需要参考此文档才行!
4.1.2 源码文件介绍
进入source 目录我们即可看到 fatfs 所有源码文件,7个文件,非常的精简,其各自功能如下:
综上,其实完成FatFs文件的移植,主要是修改 diskio.c 和 ffconf.h 这两个文件就可以了,如此简单!
在 diskio.c 文件内实现物理存储器的读写实体访问,在ffconf.h 内实现FatFs文件系统子功能的开关,之后就可以使用FatFs文件系统了!
4.2 国民技术N32文件架构说明
N32 资料压缩包下载后,内容如下,里面包含了对应系列芯片的所有资料,手册、应用笔记、软硬件资料等等。
其中我们关注最核心的几个文件,路径如下:
芯片包路径如下:N32G4FR_V3.0.0\6-软件开发套件(Software Development Kit)
例程Demo路径如下:N32G4FR_V3.0.0\6-软件开发套件(Software Development Kit)\Nationstech.N32G4FR_Library.2.1.0\projects\n32g4fr_EVAL\examples
5. FatFs移植
5.1 SD卡/SD nand读写实现
SD卡和SD nand只是封装上存在差异,软件上没有区别,因此后续不作区分
我们需要在SD nand上移植FatFs文件系统,肯定首先需要实现对SD nand的基本读写访问,之后再在读写访问的基础上给它穿上文件系统这层外套了,这就像一件一件穿衣服一样。
关于SD nand的读写操作,我们可以直接打开N32对应配套的SDIO例程,例程已完成了对SD卡的读写测试,我们直接拿过来测一下,可以跑起来说明硬件上就没有什么问题,关于例程的优化可以后续再慢慢进行。
例程路径:N32G4FR_V3.0.0\6-软件开发套件(Software Development Kit)\Nationstech.N32G4FR_Library.2.1.0\projects\n32g4fr_EVAL\examples\SDIO
对应例程硬件配置如下:
1、SystemClock:144MHz
2、DMA通道:DMA2_CH4
3、SDIO 配置:
D0 --> PC8 50MHz,AF_PP
D1 --> PC9 50MHz,AF_PP
D2 --> PC10 50MHz,AF_PP
D3 --> PC11 50MHz,AF_PP
CLK --> PC12 50MHz,AF_PP
CMD --> PD2 50MHz,AF_PP
分频系数:178 (SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + 分频系数))
上升沿有效
禁用旁路
禁用时钟保持
总线位宽4bit
4、USART1配置:
TX --> PA9 50MHz,AF_PP
波特率:115200
数据位:8bit
停止位:1bit
无校验
5、测试步骤与现象
a,测试前请先安装好TF卡
b,编译下载代码复位运行
c,从串口看打印信息,验证结果
例程正确运行后,串口打印结果如下:
备注:
如果例程运行不起来,硬件也确认没有问题,尝试修改下SD卡识别过程中的速度,具体内容如下:
sdio_sdcard.c文件 710 行,SDIO_InitStructure.ClkDiv = 178; 修改为 SDIO_InitStructure.ClkDiv = 179;
初始化SD nand
Status = SD_Init(0, 3, 4);
if (Status != SD_OK)
{
printf("SD Card initialization failed!\r\n");
return testResult;
}
写SD nand,同时注意写区分多块写和单块写操作
#ifdef MUL_BLOCK_RW
Status = SD_WriteMultiBlocks(Buf_TX, 0x01, BLOCK_SIZE, 4);
#else
Status = SD_WriteBlock(Buf_TX, 0x00, BLOCK_SIZE);
#endif
Status = SD_WaitWriteOperation();
while (SD_GetStatus() != SD_TRANSFER_OK);
if (Status != SD_OK)
{
printf("SD Card write block failed!\r\n");
return testResult;
}
读SD nand,注意读也区分多块读和单块读
#ifdef MUL_BLOCK_RW
Status = SD_ReadMultiBlocks(Buf_RX, 0x00, BLOCK_SIZE, 4);
#else
Status = SD_ReadBlock(Buf_RX, 0x00, BLOCK_SIZE);
#endif
Status = SD_WaitReadOperation();
while (SD_GetStatus() != SD_TRANSFER_OK);
if (Status != SD_OK)
{
printf("SD Card read block failed!\r\n");
return testResult;
}
5.2 FatFs移植
5.2.1 添加源文件
4.打开diskio.c
文件,采用SD nand读写及初始化接口完成对应 diskio.c
文件内函数的实现,对应FatFs文档内的 Media Access Interface
,实现下述接口即可使用FatFs。
5.2.2 添加设备号
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "./sdio_sdcard/sdio_sdcard.h"
/* Definitions of physical drive number for each drive */
// #define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
// #define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
// #define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
#define DEV_SD 0
FatFs通过物理设备号区别实际的设备,如果我们的系统上有两个硬件存储设备,且均需移植FatFs,则应再次定义,且设备号应不一样。
同时 ffconf.h 文件内的 FF_VOLUMES 配置项,用于设置FatFs实际支持的物理设备数量上限,如果有超过一个以上的物理设备使用FatFs,需要修改此参数
#define FF_VOLUMES 1
/* Number of volumes (logical drives) to be used. (1-10) */
5.2.3 修改 disk_status()
函数
DSTATUS disk_status (
BYTE pdrv /* [IN] Physical drive number */
);
传入参数:
pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。
返回值:
DSTATUS:返回驱动器的状态,FatFs仅关心 STA_NOINIT 和 STA_PROTECT
STA_NOINIT: 表示驱动器尚未初始化
STA_NODISK:表示驱动器中没有介质
STA_PROTECT:表示介质被写保护,如果 STA_NODISK 状态位为1,则此位无效
注意,每个状态占据一个bit位,返回值为三个数据位的或。
/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
根据SD nand操作接口,修改实现如下:
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
int ret = 0, result = 0;
if (pdrv == DEV_SD) {
ret = SD_GetStatus();
if(ret == SD_TRANSFER_ERROR)
result |= STA_NODISK;
else
result = 0;
}
return result;
}
5.2.4 修改 disk_initialize()
函数
DSTATUS disk_initialize (
BYTE pdrv /* [IN] Physical drive number */
);
pdrv
:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。
DSTATUS
:该函数返回当前驱动器状态标志作为结果。参考disk_status()
函数
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
int ret;
ret = SD_Init(0, 3, 4);
if (ret != SD_OK)
return STA_NOINIT;
else
return 0;
}
5.2.5 修改 disk_read()
函数
DRESULT disk_read (
BYTE pdrv, /* [IN] Physical drive number */
BYTE* buff, /* [OUT] Pointer to the read data buffer */
LBA_t sector, /* [IN] Start sector number */
UINT count /* [IN] Number of sectros to read */
);
传入参数:
pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。
buff:指向存储读取数据的字节数组的指针。
sector:起始扇区号,注意此处数据类型LBA_t是DWORD或QWORD的别名,具体取决于配置选项。
count:读取的扇区数。
返回值:
DRESULT
RES_OK : 成功
RES_ERROR:错误
RES_PARERR:无效参数
RES_NOTRDY:设备尚未初始化
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
根据SD nand操作接口,修改实现如下:
/*-----------------------------------------------------------------------*/
/* 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 */
)
{
int ret;
if (count == 1) {
ret = SD_ReadBlock(buff, sector * 512, 512);
} else {
ret = SD_ReadMultiBlocks(buff, sector * 512, 512, count);
}
if (ret != SD_OK) goto error;
ret = SD_WaitReadOperation();
if (ret != SD_OK) goto error;
while (SD_GetStatus() != SD_TRANSFER_OK);
if (ret != SD_OK) goto error;
return RES_OK;
error:
return RES_ERROR;
}
关于 disk_read 有以下注意事项:
对通用存储设备(如存储卡、硬盘和光盘)的读/写操作是以称为扇区的数据字节块单位进行的,FatFs 支持 512 到 4096 字节范围内的扇区大小。当 FatFs 配置为固定扇区大小(FF_MIN_SS == FF_MAX_SS,这是大多数情况)时,通用读/写功能必须仅在此扇区大小下工作。当 FatFs 配置为可变扇区大小(FF_MIN_SS < FF_MAX_SS )时,在disk_initialize函数成功后使用disk_ioctl函数查询介质的扇区大小。
此外,关于 buff 传递的内存地址,它不一定是内存对齐的,这个需要大家在使用的时候注意。
5.2.6 修改 disk_write()
函数
DRESULT disk_write (
BYTE pdrv, /* [IN] Physical drive number */
const BYTE* buff, /* [IN] Pointer to the data to be written */
LBA_t sector, /* [IN] Sector number to write from */
UINT count /* [IN] Number of sectors to write */
);
pdrv
:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。buff
:指向存储被写入数据的字节数组的指针。sector
:起始扇区号,注意此处数据类型LBA_t
是DWORD
或QWORD
的别名,具体取决于配置选项。count
:读取的扇区数。
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
根据SD nand操作接口,修改实现如下:
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
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 */
)
{
int ret;
if (count == 1) {
ret = SD_WriteBlock((uint8_t *)buff, sector * 512, 512);
} else {
ret = SD_WriteMultiBlocks((uint8_t *)buff, sector * 512, 512, count);
}
if (ret != SD_OK) goto error;
ret = SD_WaitWriteOperation();
if (ret != SD_OK) goto error;
while (SD_GetStatus() != SD_TRANSFER_OK);
if (ret != SD_OK) goto error;
return RES_OK;
error:
return RES_ERROR;
}
#endif
关于 disk_write() 有以下注意事项:
buff 参数和disk_read一样,不一定是字节对齐的,需要注意
多个扇区的写请求通常不建议拆解成单个的扇区写操作,这回降低传输速率,但是我们在使用的时候也需要注意对应的SD卡/SD nand所支持的最大连续写入扇区数限制!
调用disk_write()函数不要求写操作完成后才返回,可以在写过程或数据丢入缓存后返回,但是需要注意返回后,buff指针所指向的内容将变成非法的,不能再使用。写完成操作完成,可在调用 disk_ioctl() 函数CTRL_SYNC 命令时完成。通过延迟写入的实现,可增大文件系统的吞吐量(加快读写速度的点子!!!)
5.2.7 修改 disk_ioctl()
函数
DRESULT disk_ioctl (
BYTE pdrv, /* [IN] Drive number */
BYTE cmd, /* [IN] Control command code */
void* buff /* [I/O] Parameter and data buffer */
);
传入参数:
pdrv:用于标识目标设备的物理驱动器号,在单驱动系统中始终为零。
cmd:命令代码。
buff:指针参数,具体取决于 cmd 参数。
返回值:
DRESULT
RES_OK : 成功
RES_ERROR:错误
RES_PARERR:无效参数
RES_NOTRDY:设备尚未初始化
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
以下是可选命令,大家亦可自行查看官方文档,此处不再实现。
根据SD nand操作接口,修改实现如下:
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
switch (cmd) {
case CTRL_SYNC:
break;
case GET_SECTOR_COUNT:
*(LBA_t *)buff = (SDCardInfo.CardCapacity / SDCardInfo.CardBlockSize);
break;
case GET_SECTOR_SIZE:
*(WORD *)buff = SDCardInfo.CardBlockSize;
break;
case GET_BLOCK_SIZE:
*(DWORD *)buff = 1;
break;
case CTRL_TRIM:
break;
}
return RES_OK;
}
5.2.8 修改 ffconf.h
取消 get_fattime
实现
#define FF_FS_NORTC 1
#define FF_NORTC_MON 5
#define FF_NORTC_MDAY 14
#define FF_NORTC_YEAR 2023
/* 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). */
5.2.9 接口测试
在使用使用通用文件操作接口(指 f_open、f_read、f_write、f_close)访问SD nand之前,我们可以先测试下我们刚刚实现的 diskio.c 内的接口是否可用。因为这是通用文件操作底层所依赖的接口,如果这些接口出问题,通用文件操作接口肯定是跑不起来的!
修改 main 函数如下:
#include "diskio.h"
int main(void)
{
int res;
bsp_uart_init();
printf("fatfs test!\r\n");
Memset(Buf_RX, 0x00, Buf_Len);
Fill_Buffer(Buf_TX, Buf_Len, 0x00);
res = disk_initialize(0);
printf("disk inital res:%d\r\n", res);
SD_Info(&SDCardInfo);
res = disk_read(0, Buf_RX, 1, 4);
printf("disk read res:%d\r\n", res);
dataShow(Buf_RX, Buf_Len);
res = disk_write(0, Buf_TX, 1, 4);
printf("disk write res:%d\r\n", res);
dataShow(Buf_TX, Buf_Len);
res = disk_read(0, Buf_RX, 1, 4);
printf("disk read2 res:%d\r\n", res);
dataShow(Buf_RX, Buf_Len);
while(1);
}
5.2.10 调用通用文件操作接口进行读写测试
测试通过,接下来,我们便可调用通用的文件操作接口,通过FatFs完成对SD nand的读写访问了。
在进行读写访问之前,我们首先需要格式化SD nand,这是因为新的SD nand上是没有数据的,也就没有文件系统,因此我们需要先对齐进行格式化。此操作与U盘插入电脑上点击格式化操作类似。
// 初始化文件系统
res = f_mount(&fs, "0:", 1);
if (res != FR_OK) {
printf("fatfs mount error! ret = %d\r\n", res);
}
if(res == FR_NO_FILESYSTEM) { //FR_NO_FILESYSTEM值为13,表示没有有效的设备
// 格式化SD卡
res = f_mkfs("0:", 0, buffer, sizeof(buffer));
if (res != FR_OK) {
printf("fatfs mkfs error! ret:%d\r\n", res);
}
res = f_mount(NULL, "0:", 1);
printf("fatfs unmount ret:%d!\r\n", res);
res = f_mount(&fs, "0:", 1);
if (res != FR_OK) {
printf("2: fatfs mount error! ret = %d\r\n", res);
}
}
5.2.10.1 f_mount()
函数
FRESULT f_mount (
FATFS* fs, /* [IN] Filesystem object */
const TCHAR* path, /* [IN] Logical drive number */
BYTE opt /* [IN] Initialization option */
);
fs
:指向一个文件系统对象指针,当传入为NULL
时,表示卸载文件系统path
:一个非空字符串,内容为特定的逻辑物理设备号。opt
:可选项,0:不立即挂载(第一次访问时挂载);1:立即挂载
FR_OK
FR_INVALID_DRIVE
FR_DISK_ERR
FR_NOT_READY
FR_NOT_ENABLED
FR_NO_FILESYSTEM
使用 f_mount
即可实现挂载和卸载,当然卸载也可以使用f_unmount
函数,原型如下:
FRESULT f_unmount (
const TCHAR* path /* [IN] Logical drive number */
);
上述代码,首先调用f_mount
挂载设备,当检测到设备内没有文件系统时调用f_mkfs
格式化设备。
5.2.10.2 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 */
);
参数:
path:一个非空字符串,内容为特定的逻辑物理设备号。
opt:指定格式选项结构MKFS_PARM持有格式选项。如果给出空指针,它会以默认值为函数提供每个选项。该结构有五个成员,顺序如下:
BYTE fmt:指定 FAT 类型标志FM_FAT、FM_FAT32、FM_EXFAT和这三者的按位或FM_ANY的组合。未启用 exFAT 时忽略FM_EXFAT 。这些标志指定要创建的 FAT 卷类型。如果指定了两种或多种类型,将根据卷大小和au_size选择其中一种。标志FM_SFD指定以 SFD 格式在驱动器上创建卷。默认值为FM_ANY。
BYTE n_fat:指定 FAT/FAT32 卷上的 FAT 副本数。此成员的有效值为 1 或 2。默认值 (0) 和任何无效值都为 1。如果 FAT 类型为 exFAT,则此成员无效。
UINT n_align:以扇区为单位指定卷数据区(文件分配池,通常是闪存介质的擦除块边界)的对齐方式。该成员的有效值介于 1 和 32768 之间(包括 2 的幂)。如果给出零(默认值)或任何无效值,该函数将使用 disk_ioctl 函数从较低层获取块大小。
DWORD au_size:以字节为单位指定分配单元(簇)的大小。对于 FAT/FAT32 卷,有效值为扇区大小和 128 * 扇区大小(包括在内)之间的 2 的幂,或者对于 exFAT 卷,最大为 16 MB。如果给出零(默认值)或任何无效值,则该函数使用默认分配单元大小,具体取决于卷大小。
UINT n_root:指定 FAT 卷上的根目录条目数。此成员的有效值最大为 32768 并与扇区大小/32 对齐。默认值 (0) 和任何无效值都为 512。如果 FAT 类型为 FAT32 或 exFAT,则此成员无效。
work:格式化过程中的工作缓冲区指针
len:缓冲区大小,单位字节。大小最小为FF_MAX_SS,大量的工作缓冲区减少了向驱动器写入事务的数量,因此格式化过程将很快完成。
FR_OK
FR_DISK_ERR
FR_NOT_READY
FR_WRITE_PROTECTED
FR_INVALID_DRIVE
FR_MKFS_ABORTED
FR_INVALID_PARAMETER
FR_NOT_ENOUGH_CORE
5.2.10.3 读写测试
#include "ff.h"
#include <string.h>
BYTE buffer[FF_MAX_SS];
/**
* @brief Main program
*/
int main(void)
{
FATFS fs;
FIL file;
FRESULT res;
bsp_uart_init();
printf("fatfs test!\r\n");
// 初始化文件系统
res = f_mount(&fs, "0:", 1);
if (res != FR_OK) {
printf("fatfs mount error! ret = %d\r\n", res);
}
if(res == FR_NO_FILESYSTEM) { //FR_NO_FILESYSTEM值为13,表示没有有效的设备
// 格式化SD卡
res = f_mkfs("0:", 0, buffer, sizeof(buffer));
if (res != FR_OK) {
printf("fatfs mkfs error! ret:%d\r\n", res);
}
res = f_mount(NULL, "0:", 1);
printf("fatfs unmount ret:%d!\r\n", res);
res = f_mount(&fs, "0:", 1);
if (res != FR_OK) {
printf("2: fatfs mount error! ret = %d\r\n", res);
}
}
// 创建一个新文件
res = f_open(&file, "test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if (res != FR_OK) {
printf("fatfs open error!\r\n");
}
// 写入数据到文件
const char* data = "Hello, FatFs!";
UINT bytes_written;
res = f_write(&file, data, strlen(data), &bytes_written);
if (res != FR_OK) {
printf("fatfs write error!\r\n");
}
// 关闭文件
res = f_close(&file);
if (res != FR_OK) {
printf("fatfs close error!\r\n");
}
// 打开文件并读取数据
res = f_open(&file, "test.txt", FA_READ);
if (res != FR_OK) {
printf("2: fatfs open error!\r\n");
}
// 读取文件数据
char read_data[50];
UINT bytes_read;
res = f_read(&file, read_data, sizeof(read_data), &bytes_read);
if (res != FR_OK) {
printf("2: fatfs read error!\r\n");
}
// 关闭文件
res = f_close(&file);
if (res != FR_OK) {
printf("2: fatfs close error!\r\n");
}
// 打印读取的数据
read_data[bytes_read] = '\0'; // 添加字符串结束符
printf("Read data: %s\n", read_data);
while (1) {
}
}
测试结果如下:
6. 结束语
至此,FatFs已经移植完成,需要注意的是,在此次移植过程中,以分享移植流程、思路为主,对于部分函数接口的实现比较粗糙,在实际项目中需要结合本文中对相应接口的描述,对其进行优化以确保产品的稳定性,不过相信那些都不是什么难题。
————————————————
【本文转载自CSDN,作者: 爱出名的狗腿子】
亲爱的卡友们,欢迎光临雷龙官网,如果看完文章之后还是有疑惑或不懂的地方,请联系我们,自己去理解或猜答案是件很累的事,请把最麻烦的事情交给我们来处理,术业有专攻,闻道有先后,深圳市雷龙发展专注存储行业13年,专业提供小容量存储解决方案。