FatFs 之一 R0.13c版源码目录文件、函数、全配置项详解及移植说明

FatFs 是用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。FatFs 模块的编写符合 ANSI C(C89),并与磁盘 I/O 层完全分离,因此它独立于硬件平台。 它可以集成到资源有限的小型微控制器中,例如 8051,PIC,AVR,ARM,Z80,RX 等。此外,还提供用于微型微控制器的 Petit FatFs 模块。
  看本文时需要有点 FAT 文件系统的基础,可以参考FatFs 之三 FAT文件系统基础、FAT 数据格式、引导、编码

变更记录

  具体参见源码文件中的 /source/00history.txt 即可!也可以去官网查看。从中我们可以看到修复的各问题,尤其是源码文件的变动。例如:在 R0.13c 中,原来独立文件 integer.h 中的内容被直接包含在了 ff.h 中,原来的 integer.h 被删除!对比如下图所示:
在这里插入图片描述

源码目录文件

  目前,最新版本为 R0.13c。相比于之前的版本,源码有了一定的变化(参见上图)。FatFs 的源码包中,文件非常简单。其源码目录结构如下所示(对于简单的文件以注释的形式给出,核心源码下文会详细说明):

FatFs R0.13c
│  LICENSE.txt					// 版权说明
├─documents						// 配套的说明文档
└─source
        00history.txt			// 更新历史记录
        00readme.txt			// 对于以下每个文件的功能简介
        diskio.c				// FATFS 与硬件的接口实现文件模板
        diskio.h				// FATFS 与硬件的接口实现文件模板
        ff.c					// FATFS 核心源代码
        ff.h					// FATFS 核心源代码
        ffconf.h				// FATFS 的配置文件
        ffsystem.c				// 定义了:可选的与操作系统对接的各接口相关实现示例。
        ffunicode.c				// 提供了 Unicode 相关的转换函数

以下主要介绍 source 目录下的源代码部分!

ffconf.h

  该文件是 FatFs 的配置文件。用户需要根据自己的需求,来修改该文件中的各配置项。在 FatFs 默认的配置文件中,很多我们常用的函数都是被禁用的,必须修改下面的配置项来启用。这也就意味着,直接使用默认的配置文件一般是无法满足我们的需求的!接下来详细介绍一下每个配置项的具体含义及使用时需要注意的问题。以下为 FatFs 源码中默认的配置项说明。

FatFs Functional Configurations

FFCONF_DEF

定义了 FatFs 的版本号,与实际功能无关。 主要是 FatFs 自身用来防止出错。具体在 ff.h 中,会检查该文件中的改宏值是否与 ff.h 中版本一致。在 ff.h 中,有如下语句:

#if FF_DEFINED != FFCONF_DEF
#error Wrong configuration file (ffconf.h).
#endif

Function Configurations 功能配置

FF_FS_READONLY

定义 FatFs 是否工作在只读模式。

  • 0:读/写。默认值。
  • 1:只读。只读模式下,写相关的函数 f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 以及其他和写操作相关的函数都将被移除。

FF_FS_MINIMIZE

用来极限精简 FatFs,此选项定义最小化级别,以删除一些基本 API 函数,如下所示

  • 0:所有基本的 API 函数都可用。默认值。
  • 1:f_stat,f_getfree,f_unlink,f_mkdir,f_chmod,f_utime,f_truncate和f_rename函数被删除。
  • 2:除了移除 1 中的函数,还将移除 f_opendir, f_readdir and f_closedir
  • 3:除了移除 2 中的函数,还将移除 f_lseek

FF_USE_STRFUNC

定义字符操作的相关函数 f_gets(), f_putc(), f_puts() 及 f_printf() 是否有效。

  • 0:禁用所有的字符串相关操作函数。默认值。
  • 1:启用,但是没有 LF-CRLF 转换。即:不会忽略回车符 \r
  • 2:启用,且带有 LF-CRLF转换。即:忽略回车符 \r

FF_USE_FIND

定义目录读取的相关函数(f_findfirst(), f_findnext())是否有效

  • 0: 禁用。默认值
  • 1: 启用
  • 2: 启用,且会检查是否匹配 altname[]。在 1 情况下,匹配条件只有文件名。此情况下,如果 altname[]匹配也认为是匹配的。FF_FS_MINIMIZE 必须为 0 或 1

FF_USE_MKFS

定义函数 f_mkfs() 是否有效

  • 0: 禁用。默认值
  • 1: 启用

FF_USE_FASTSEEK

定义快速 seek 模式是否有效。

  • 0: 禁用。默认值
  • 1: 启用。会额外记录很多信息,以供在 f_lseek 中使用

FF_USE_EXPAND

定义函数 f_expand() 是否有效

  • 0: 禁用。默认值
  • 1: 启用

FF_USE_CHMOD

定义属性操作函数 f_chmod() 和 f_utime() 是否有效

  • 0: 禁用。默认值
  • 1: 启用。此外必须定义 FF_FS_READONLY 为 0。即:不能开启只读模式

FF_USE_LABEL

定义卷标函数 f_getlabel() 和 f_setlabel() 是否有效

  • 0: 禁用。默认值
  • 1: 启用

FF_USE_FORWARD

定义函数 f_forward() 是否有效

  • 0: 禁用。默认值
  • 1: 启用

Locale and Namespace Configurations 本地化和命名空间设置

FF_CODE_PAGE

指定要在目标系统上使用的 OEM 代码页,错误的编码页设置将导致读写文件失败。支持的编码页如下:

取值编码页
0包括以下的所有代码页并由 f_setcp() 配置
437U.S.
720Arabic
737Greek
771KBL
775Baltic
850Latin 1
852Latin 2
855Cyrillic
857Turkish
860Portuguese
861Icelandic
862Hebrew
863Canadian French
864Arabic
865Nordic
866Russian
869Greek 2
932Japanese (DBCS)
936Simplified Chinese (DBCS)
949Korean (DBCS)
950Traditional Chinese (DBCS)

如果路径名未使用任何非ASCII字符,则任何代码页设置之间都没有区别,将它设置为 437 即可。

关于编码页,可以参考一下博文 FatFs 之 路径规则、字符编码、编码页、卷管理详解

FF_USE_LFN

  此选项可切换对长文件名(LFN)的支持。 启用 LFN 时,需要将 Unicode 支持模块 ffunicode.c 添加到项目中。 当使用堆栈作为工作缓冲区时,请注意堆栈溢出。 当使用堆内存作为工作缓冲区时,需要将 ffsystem.c 添加到项目中,并实现其中的内存管理函数 ff_memalloc 和 ff_memfree。

  • 0: 不启用。默认值。FF_MAX_LFN 无效
  • 1: 启用。且 LFN 在代码段 BSS 上具有静态工作缓冲区。 始终不是线程安全的。
  • 2: 启用。且在 STACK 上具有动态工作缓冲区的 LFN。需要注意栈溢出的问题。
  • 3: 启用。且在 HEAP 上具有动态工作缓冲区的 LFN。此时,必须要启用 ffsystem.c 中的动态内存申请函数 ff_memalloc() 和 ff_memfree()

注意:长文件名与上面的编码页有关系!有些编码页就是 Unicode 字符集,也因此需要长文件名支持!

关于长文件名,可以参考一下博文 FatFs 之 路径规则、字符编码、编码页、卷管理详解

FF_MAX_LFN

  启用 LFN ,会增加 (FF_MAX_LFN + 1) * 2 字节的固定缓冲区空间。具体使用方式见 ff.c 文件的开头部分的宏定义即可。如果是 exFAT 文件系统,则还需要再占用 (FF_MAX_LFN + 44) / 15 * 32 字节的缓冲区空间。且 exFAT 必须启用长文件名支持
  FF_MAX_LFN 以 UTF-16 代码单位定义工作缓冲区的大小,它可以在 12 到 255 的范围内。建议将 255 设置为完全支持LFN规范。长文件名是微软的专利,使用中与上面的编码有关系。

FF_LFN_UNICODE

  此选项可在 API 上切换文件名的字符编码。选择 Unicode 时,FF_CODE_PAGE 实际上没有任何意义,除了与遗留系统的兼容性,例如 MS-DOS 和任何不支持 LFN 的系统。 FatFs 支持代码点达到 U + 10FFFF。

  • 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)

此外,字符串输入/输出函数的行为也会受到此选项的影响。如果关闭了长文件名支持,则该项无效!

FF_LFN_BUF 和 FF_SFN_BUF

  这组选项在 FILINFO 结构中定义了文件名成员 fname[] 和 altname[] 的大小,FILINFO 结构用于读取目录项。这些值应该足够读取文件名。读取文件名的最大可能长度取决于API上的字符编码,如下所示:

EncodingLFN lengthSFN length
ANSI/OEM at SBCS255 items12 items
ANSI/OEM at DBCS510 items12 items
Unicode in UTF-16/32255 items12 items
Unicode in UTF-8765 items34 items

如果名称成员的大小不足以用于LFN,则该项目将被视为没有LFN。 如果未启用LFN,则这些选项无效。

FF_STRF_ENCODE

  当 API 上的字符编码为 Unicode(FF_LFN_UNICODE> = 1)时,字符串 I/O 函数,f_gets,f_putc,f_puts和 f_printf 将转换其中的字符编码。 此选项定义了要通过这些函数读取/写入的文件的字符编码。 当LFN 未启用或 FF_LFN_UNICODE 为 0 时,字符串函数在没有任何编码转换的情况下工作,此选项无效。

取值文件字符编码
0ANSI/OEM in current code page
1Unicode in UTF-16LE
2Unicode in UTF-16BE
3Unicode in UTF-8

FF_FS_RPATH

定义是否支持相对路径

  • 0: 禁用。且相对路径相关的函数也被移除
  • 1: 支持。f_chdir() 和 f_chdrive() 有效
  • 2: 支持。除了提供 1 中的函数,还提供 f_getcwd()

Volume/Drive Configurations 驱动器/卷配置

FF_VOLUMES

此选项配置要使用的卷数(逻辑驱动器最多10个),最小值 1。

FF_STR_VOLUME_ID

  此选项定义对字符串卷ID的支持。当为驱动器前缀启用任意卷ID字符串时,可以使用 FF_VOLUME_STRS 预定义的字符串或用户定义的字符串作为路径名称中的驱动器前缀。无论该选项是什么,数字驱动器号总是有效的,而且该选项还可以启用驱动器前缀的任何一种格式。

取值描述示例
0仅 DOS/Windows 风格的数字ID前缀可用0:/filename
1DOS/Windows 风格的字符串ID前缀也可用flash:/filename
2Unix 风格的字符串ID前缀也可用/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且未定义此选项,则需要定义用户定义的卷字符串表,如下所示。 该表不应动态修改。

/* User defined volume ID strings for 0:    1:      2:    3:   ... */
const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sdc","usb"};

FF_MULTI_PARTITION

  禁用(0)或启用(1)。 此选项可切换多分区功能。 默认情况下(0),每个逻辑驱动器号绑定到相同的物理驱动器号,并且仅安装物理驱动器中的卷。 启用后,每个逻辑驱动器都绑定到用​​户定义的分区解析表 VolToPart [] 中列出的物理驱动器上的分区。 此外,还将提供 f_fdisk功能。

FF_MIN_SS, FF_MAX_SS

  这组选项定义了低级磁盘 I/O 接口,disk_read 和 disk_write函数使用的扇区大小范围。 有效值为 512、1024、2048 和 4096。FF_MIN_SS定义最小扇区大小,FF_MAX_SS 定义最大扇区大小。 始终为存储卡和硬盘设置 512。 但是,板载闪存和某些类型的光学介质可能需要更大的值。 当FF_MAX_SS > FF_MIN_SS 时,启用对可变扇区大小的支持,并且需要对 disk_ioctl 函数实现 GET_SECTOR_SIZE 命令。

FF_USE_TRIM

定义是否支持 ATA-TRIM

  • 0: 禁止
  • 1: 支持。此时,disk_ioctl() 函数需要实现 CTRL_TRIM 命令。

FF_FS_NOFSINFO

  取值 0 到 3。如果您需要知道FAT32 卷上的正确可用空间,请设置此选项的第0位,并且在卷 mount 后第一时间执行 f_getfree函数,将强制进行完整的FAT扫描。 位1 控制最后分配的簇编号的使用。

取值描述
bit0=0Use free cluster count in the FSINFO if available.
bit0=1Do not trust free cluster count in the FSINFO.
bit1=0Use last allocated cluster number in the FSINFO to find a free cluster if available.
bit1=1Do not trust last allocated cluster number in the FSINFO.

System Configurations (嵌入式)操作系统相关的配置

FF_FS_TINY

  取值为 正常(0)或微小(1)。 在微小的配置中,文件对象 FIL 的大小减少了 FF_MAX_SS 字节。 不是从文件对象中消除私有数据缓冲区,而是将文件系统对象 FATFS 中的公共扇区缓冲区用于文件数据传输。

FF_FS_EXFAT

  此选项定义对 exFAT 文件系统的支持,Enabled(1)或 Disabled(0)。开启之后,将同时支持 exFAT、FAT/FAT32。要启用 exFAT,还必须启用LFN 并为全功能 exFAT 功能配置 FF_LFN_UNICODE> = 1 和 FF_MAX_LFN == 255。 请注意,由于需要64位整数类型,启用 exFAT会丢弃ANSI C(C89)兼容性。

FF_FS_NORTC

  此选项控制时间戳功能,0:使用RTC;1:不使用 RTC。 如果系统没有任何 RTC 功能或不需要有效时间戳,请将 FF_FS_NORTC 设置为1 以禁用时间戳功能。此时,FatFs 修改的每个对象都有一个由 FF_NORTC_MON,FF_NORTC_MDAY 和 FF_NORTC_YEAR 定义的固定时间戳。 要使用时间戳功能,请设置 FF_FS_NORTC == 0 并将 get_fattime 函数添加到项目中以从 RTC 获取当前时间。 此选项对只读配置无效。

FF_NORTC_MON, FF_NORTC_MDAY, FF_NORTC_YEAR

这组选项定义了在无 RTC 系统中使用的时间。 此选项在只读配置或 FF_FS_NORTC == 0 时无效。

FF_FS_LOCK

  选项切换文件锁定功能,以控制打开重复文件和打开对象的非法操作。 请注意,文件锁定功能独立于重入。 只读配置时,此选项必须为 0。

取值描述
0禁用文件锁定功能。 为避免文件操作错误导致文件崩溃,应用程序需要避免非法打开,删除和重命名为打开的对象。
>0启用文件锁定功能。 该值定义在文件锁定控制下可以同时打开多少文件/子目录。 使用FR_LOCKED将拒绝对打开对象的非法操作。

FF_FS_REENTRANT

  此选项可切换 FatFs 模块本身的重入(线程安全),取值为 禁用(0)或启用(1)。 请注意,对不同卷的文件/目录访问始终是可重入的,无论此选项如何,它都可以同时工作。但是,卷管理函数 f_mount,f_mkfs 和 f_fdisk 始终不可重入。
  只有文件/目录访问同一个卷,换句话说,独占使用每个文件系统对象,才能受此功能的控制。 要启用此功能,用户需要将 ffsystem.c 添加到自己的项目中,同时实现其中的同步处理程序 ff_req_grant,ff_rel_grant,ff_del_syncobj和ff_cre_syncobj。

FF_FS_TIMEOUT

  当等待时间太长时,使用 FF_FS_TIMEOUT中止文件功能的时间滴答数。 当 FF_FS_REENTRANT == 0 时,此选项无效。

FF_SYNC_t

  此选项定义 OS 相关的同步对象类型。 例如 HANDLE,ID,OS_EVENT *,SemaphoreHandle_t等。用于OS 定义的头文件需要包含在 ff.c范围内的某处。 当 FF_FS_REENTRANT == 0 时,此选项无效。

ff.c/h

  这两个文件是 FatFs 的核心源码文件。所有的 FatFs 的操作函数均位于这个了文件中。我们在使用时,使用的 API 也均是出自于这两个文件。
   其中各函数比较多,后续单独来介绍!

ffsystem.c

  当使用了操作系统时,该文件必须要由用户来实现!源码包中的 ffsystem.c 给出了需要实现的各函数接口!用户需要根据自己使用的操作系统,修改其中的各接口的实现。注意:即使不使用系统,如果我们启用了长文件名,且长文件名的配置为 3,则也必须要实现以下两个动态内存申请函数! 下面我们看看该文件中的各个函数接口

动态内存申请

  该文件中的第一部分函数就是动态内存申请接口。当然,其受制于在ffconf.h中的配置项FF_USE_LFN的限制。也就说,如果不使用长文件名,则 FatFs 不需要使用动态内存申请。 标准接口和标准C语言类似,就是名字稍有变化,有以下两个:

  1. void* ff_memalloc (UINT msize); 申请动态内存
    1. msize: 要申请的内存的大小
    2. 返回值:成功,返回指向申请的内存的指针;失败返回空指针
  2. void ff_memfree (void* mblock); 释放之前申请的动态内存
    1. mblock: 之前申请的动态内存的指针
    2. 返回值: 无

  为什么要自定义这两个函数?在使用了嵌入式操作系统之后,嵌入式操作系统一般都会提供对内存的管理功能。其一般也会重新定义动态内存的相关函数,而不是直接使用标准 C 语言定义的 malloc 和 free 函数。

可重入性

  该文件中的第二部分函数是与系统紧密相关的相关的函数接口。这主要用来处理在多任务操作系统中,多线程操作相关的问题。在单线程进程(单任务)中,只存在一个控制流。因此,这些进程所执行的代码无需重入或着说是线程安全的。但是在多线程(多任务)程序中,相同的功能和资源可以通过多个控制流并发访问。
  FatFs 使用了同步对象(Synchronization Object)这个称呼,在实际系统中,通常为 信号量 或者 互斥量。当然,也不局限于这两种。
  以下为 FatFs 默认提供的示例代码。其中给出了常用的系统下的基本操作。如果使用其中的一种,则直接修改注释即可,否则,需要根据自己使用的系统来修改。

创建同步对象

该函数在 f_mount() 函数中被调用,用来为指定的卷创建一个同步对象。

int ff_cre_syncobj (	/* 1:成功, 0:失败 */
	BYTE vol,			/* 对应卷(逻辑驱动器号) */
	FF_SYNC_t* sobj		/* 返回创建的同步信号量的指针 */
)
{
	/* Win32 */
	*sobj = CreateMutex(NULL, FALSE, NULL);
	return (int)(*sobj != INVALID_HANDLE_VALUE);

	/* uITRON */
//	T_CSEM csem = {TA_TPRI,1,1};
//	*sobj = acre_sem(&csem);
//	return (int)(*sobj > 0);

	/* uC/OS-II */
//	OS_ERR err;
//	*sobj = OSMutexCreate(0, &err);
//	return (int)(err == OS_NO_ERR);

	/* FreeRTOS */
//	*sobj = xSemaphoreCreateMutex();
//	return (int)(*sobj != NULL);

	/* CMSIS-RTOS */
//	*sobj = osMutexCreate(&Mutex[vol]);
//	return (int)(*sobj != NULL);
}

删除同步对象

这个函数在f mount()函数中调用,以删除使用ff_cre_syncobj ()函数创建的同步对象。

int ff_del_syncobj (	/* 返回值:1:成功, 0:删除失败 */
	FF_SYNC_t sobj		/* 要删除的同步对象 */
)
{
	/* Win32 */
	return (int)CloseHandle(sobj);

	/* uITRON */
//	return (int)(del_sem(sobj) == E_OK);

	/* uC/OS-II */
//	OS_ERR err;
//	OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
//	return (int)(err == OS_NO_ERR);

	/* FreeRTOS */
//  vSemaphoreDelete(sobj);
//	return 1;

	/* CMSIS-RTOS */
//	return (int)(osMutexDelete(sobj) == osOK);
}

锁定卷

此函数在输入文件函数时调用,以锁定卷。

int ff_req_grant (	/* 返回值:1:获取锁定许可, 0:失败 */
	FF_SYNC_t sobj	/* Sync object to wait */
)
{
	/* Win32 */
	return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);

	/* uITRON */
//	return (int)(wai_sem(sobj) == E_OK);

	/* uC/OS-II */
//	OS_ERR err;
//	OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
//	return (int)(err == OS_NO_ERR);

	/* FreeRTOS */
//	return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);

	/* CMSIS-RTOS */
//	return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
}

释放卷

此函数在离开文件函数以解锁卷时调用。

void ff_rel_grant (
	FF_SYNC_t sobj	/* Sync object to be signaled */
)
{
	/* Win32 */
	ReleaseMutex(sobj);

	/* uITRON */
//	sig_sem(sobj);

	/* uC/OS-II */
//	OSMutexPost(sobj);

	/* FreeRTOS */
//	xSemaphoreGive(sobj);

	/* CMSIS-RTOS */
//	osMutexRelease(sobj);
}

ffunicode.c

  该文件很大,用来处理 Unicode 编码相关的功能。如果在以上配置中,启用了相关功能,则需要将该文件添加到自己的项目中。用户无需修改其中的任何内容。所有函数均由 FatFs 调用!
   FatFs 建议,如果系统中存在 Unicode 相关的处理函数,则应该以系统自带的为准,尽量减少对该文件的使用即可!

diskio.c/h

  由于 FatFs 模块是独立于平台和存储介质的文件系统层,因此它与物理设备(例如存储卡,硬盘和任何类型的存储设备)完全分离。 低级设备控制模块不是 FatFs 模块的一部分,需要由使用者提供。
  diskio.c/h 这两个文件用于实现 FatFs 与硬件的交互。其中定义了 FatFs 与硬件交互使用的接口。FatFs 中的这两个文件仅仅是个模板,用户需要根据需要来实现其中的各接口,并不能直接使用!其中:

  • diskio.h: 其中声明了各接口的形式以及一些 FatFs 使用的宏,用户不得更改!ff.c 直接包含该头文件,并使用该文件中定义的一些内容!
  • diskio.c: 其中存放了需要用户实现的各接口!用户需要根据自己的硬件平台(RAM、MMC、USB等)以及 ffcong.h中的配置(例如,配置项 FF_USE_TRIM定义为 1,则需要在 disk_ioctl() 函数中实现对 CTRL_TRIM 命令的处理)

  一般的使用了 FatFs 的第三方库,都会提供该文件的实现!我们可以参考。例如,在 STM32 的USB 驱动库中,有个名为 usbh_msc_fatfs.c 的文件,它其实就是 diskio.c 改了个名字而已。里面的函数就是 diskio.h 所声明的那些函数!也因此在使用 STM32 的USB 驱动库 + FatFs 时,我们不再需要 diskio.c了,但是 diskio.h 仍然是必须的!
下面我们来看看具体的各函数

DSTATUS disk_status (BYTE pdrv);

FatFs 调用此函数,查询当前驱动器状态

  • 参数:
    • pdrv:用于标识目标设备的物理驱动器号。 单驱动系统始终为零。
  • 返回值:当前驱动器状态以下面描述的状态标志的组合返回(状态宏值是按位定义并使用的,具体见 diskio.h)。 FatFs 仅使用了 STA_NOINIT 和 STA_PROTECT 这两个。
    • STA_NOINIT:表示设备尚未初始化且未准备好工作。 此标志在系统重置,介质删除或 disk_initialize 功能失败时设置。 在disk_initialize函数成功后清除它。 必须捕获异步发生的任何介质更改并将其反映到状态标志,否则自动安装功能将无法正常工作。 如果系统不支持介质更改检测,则应用程序需要在每次更改介质后使用 f_mount 函数显式重新装入卷。
    • STA_NODISK:表示驱动器中没有介质。 在固定磁盘驱动器上清除这项始终被清除。 请注意,FatFs 不会使用此标志。
    • STA_PROTECT:表示介质受写保护。 在没有写保护功能的驱动器上始终清除此值。 如果设置了STA_NODISK,则此项无效。

DSTATUS disk_initialize (BYTE pdrv);

FatFs 调用此函数,以初始化存储设备

  • 参数:
    • pdrv:用于标识目标设备的物理驱动器号。 单驱动系统始终为零。
  • 返回值:与 disk_status 的返回值相同!

此函数初始化存储设备并使其准备好进行通用读/写。 当函数成功时,返回值中的STA_NOINIT标志被清除。此功能需要受FatFs模块的控制。 应用程序不得调用此函数,否则可能会破坏卷上的FAT结构。 要重新初始化文件系统,请改用f_mount函数。

DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);

FatFs 调用此函数,以从存储设备的扇区读取数据

  • 参数:
    • pdrv:用于标识目标设备的物理驱动器号。
    • buff:指向存储读取数据的字节数组的第一项的指针。 读取数据的大小将是扇区大小*计数字节。
    • sector:32 位 LBA(Logical Block Address,逻辑区块地址) 的开始扇区号。
    • count:要读取的扇区数。
  • 返回值:
    • RES_OK:成功
    • RES_ERROR:在读取操作期间发生了不可恢复的硬错误。
    • RES_PARERR:无效的参数
    • RES_NOTRDY:设备尚未初始化。

  对通用存储设备(例如存储卡,hadddisk和光盘)的读/写操作是以称为扇区的数据字节块为单位完成的。 FatFs支持512到4096字节范围内的扇区大小。 当FatF配置为固定扇区大小(FF_MIN_SS == FF_MAX_SS,这是大多数情况)时,读/写功能必须以该扇区大小工作。 当FatFs配置为可变扇区大小(FF_MIN_SS <FF_MAX_SS)时,在disk_initialize函数成功后立即使用disk_ioctl函数查询media的扇区大小。
  buff 指定的内存地址并不总是与字边界对齐,因为参数定义为BYTE* 类型。 未对齐的读/写请求可以在直接传输时发生。 如果总线体系结构(尤其是DMA控制器)不允许未对齐的内存访问,则应在此函数中处理该问题。 下面介绍了一些解决方法以避免此问题。

  • 在此函数中使用某些方法将字转换为字节来进行传输。
  • 在 f_read() 调用中,避免包含整个扇区的长读取请求。 - 任何直接传输都不会发生。
  • 在 f_read(fp, dat, btw, bw) 调用中,确保 (((UINT)dat & 3) == (f_tell(fp) & 3)) 为真。 - 保证了 buff 的字对齐。

此外,DMA 可能无法访问内存区域。如果内存区域位于通常用于堆栈的紧密耦合的内存中,情况就是这样。使用双缓冲传输,或者避免将任何文件 I/O 缓冲区、FATFS 和 FIL 结构定义为堆栈中的局部变量。
通常,多扇区读请求不能被分割成对存储设备的单扇区事务,否则读吞吐量会变得更差。

DRESULT disk_write (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);

FatFs 调用此函数,将数据写入存储设备的扇区

  • 参数:
    • pdrv:用于标识目标设备的物理驱动器号。
    • buff:指向要写入的字节数组的第一项的指针。 要写入的数据大小是扇区大小*计数字节。
    • sector:32 位 LBA(Logical Block Address,逻辑区块地址) 的开始扇区号。
    • count:要写的扇区数。
  • 返回值:
    • RES_OK:成功
    • RES_ERROR:在写入操作期间发生了不可恢复的硬错误。
    • RES_WRPRT:介质是写保护的
    • RES_PARERR:无效的参数
    • RES_NOTRDY:设备尚未初始化。

  指定的内存地址并不总是与字边界对齐,因为参数定义为 BYTE*。 有关更多信息,请参阅 disk_read 函数的说明。
  通常,多扇区写请求(计数> 1)不得拆分为存储设备的单扇区事务,否则文件写吞吐量将大幅降低。
  FatFs期望磁盘控制层的写入功能延迟。 该函数返回后,写操作可能并没有完成,因为媒体介质可能正在执行写操作或者 仅仅是将数据放到了媒体介质的缓冲区中。但是从此函数返回后,buff 中的数据将无效。 写完成请求由 disk_ioctl 函数的 CTRL_SYNC 命令完成。 因此,如果实现延迟写入功能,则将改善文件系统的写入吞吐量。

DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

FatFs 调用此函数,来控制设备特定的功能和除通用读/写之外的其他功能。

  • 参数:
    • pdrv:用于标识目标设备的物理驱动器号。
    • cmd:命令代码,在 diskio.h 中有定义。
    • buff:指向参数的指针,是否有意义取决于命令代码。 如果命令代码不需要传递该参数,则忽略即可。
  • 返回值:
    • RES_OK (0):成功
    • RES_ERROR:出错
    • RES_PARERR:命令代码或参数无效。
    • RES_NOTRDY:设备尚未初始化。

FatFs模块仅需要五个与设备无关的命令,如下所述:

命令描述
CTRL_SYNC用来确保设备已完成了挂起的写入过程。 如果磁盘 I/O 模块或存储设备具有回写高速缓存,则必须立即将标记为脏的高速缓存数据写回介质。 如果在 disk_write 函数内完成对介质的每个写操作,此命令中无需任何处理。
GET_SECTOR_COUNT将驱动器上的可用扇区数返回到 buff 指向的 DWORD 类型的变量中。 f_mkfs 和 f_fdisk 函数使用此命令来确定要创建的卷/分区大小。 在配置了 FF_USE_MKFS == 1 时,必须要实现对该名利的处理。
GET_SECTOR_SIZE将设备的扇区大小返回到 buff 指向的 WORD 类型的变量中。 此命令的有效返回值为 512,1024,2048和4096. 仅当FF_MAX_SS > FF_MIN_SS 时才需要此命令。 当 FF_MAX_SS == FF_MIN_SS 时,从不使用此命令,并且设备必须以该扇区大小工作。
GET_BLOCK_SIZE将以扇区为单位将闪存介质的擦除块大小返回到 buff 指向的 DWORD 类型的变量中。 允许值为 1 到 32768的 2 次方。如果擦除块大小未知或非闪存介质,则返回1。 该命令仅由 f_mkfs 函数使用,它尝试对齐擦除块边界上的数据区域。 在FF_USE_MKFS == 1 时 必须实现对改命令的处理
CTRL_TRIM通知设备不再需要扇区块上的数据,并且可以擦除它。 扇区块由 buff 指向的DWORD数组 {,} 指定。 这是一个与 ATA 设备修剪完全相同的命令。 如果不支持此功能或闪存设备不支持此命令,则无需执行此操作。 FatFs 不检查结果代码,即使扇区块没有被很好地擦除,文件功能也不会受到影响。 该命令在删除簇和 在 f_mkfs 函数中被调用。 在 FF_USE_TRIM == 1 时必须实现对改命令的处理

FatFs从不使用任何依赖于设备的命令或用户定义的命令。下表显示了一个可能对某些应用程序有用的非标准命令示例。

命令描述
CTRL_FORMAT在媒体上创建物理格式。 如果 buff 不为 null,则它是指向进度通知的回调函数的指针。
CTRL_POWER_IDLE将设备置于空闲状态。 如果设备通过通用读/写功能进入活动状态,则可能不会设置当前状态标志中的STA_NOINIT。
CTRL_POWER_OFF将设备置为关闭状态。 如果需要,关闭设备电源并取消初始化设备接口。 必须设置当前状态标志中的STA_NOINIT。 设备通过disk_initialize功能进入活动状态。
CTRL_LOCK锁定介质弹出机制。
CTRL_UNLOCK解锁媒体弹出机制。
CTRL_EJECT弹出介质盒。 功能成功后,将设置状态标志中的STA_NOINIT和STA_NODISK。
CTRL_GET_SMART阅读SMART信息。
MMC_GET_TYPE获取卡类型。 类型标志bit0:MMCv3,bit1:SDv1,bit2:SDv2 +和bit3:LBA存储在buff指向的BYTE变量中。 (MMC / SDC特定命令)
MMC_GET_CSD将CSD寄存器读入buff指向的16字节缓冲区。 (MMC / SDC特定命令)
MMC_GET_CID将CID寄存器读入buff指向的16字节缓冲区。 (MMC / SDC特定命令)
MMC_GET_OCR将OCR寄存器读入buff指向的4字节缓冲区。 (MMC / SDC特定命令)
MMC_GET_SDSTAT将SDSTATUS寄存器读入buff指向的64字节缓冲区。 (SDC特定命令)
ATA_GET_REV将修订字符串转换为buff指向的16字节缓冲区。 (ATA / CFC特定命令)
ATA_GET_MODEL将模型字符串放入buff指向的40字节缓冲区中。 (ATA / CFC特定命令)
ATA_GET_SN将序列号字符串放入buff指向的20字节缓冲区中。 (ATA / CFC特定命令)
ISDIO_READ读取由buff指向的命令结构指定的iSDIO寄存器块。 (FlashAir特定命令)
ISDIO_WRITE将数据块写入由buff指向的命令结构指定的iSDIO寄存器。 (FlashAir特定命令)
ISDIO_MRITE更改由buff指向的命令结构指定的iSDIO寄存器中的位。 (FlashAir特定命令)

移植说明

第一步:源码文件整理

  根据上面的介绍,其中,ff.cff.hffconf.hdiskio.cdiskio.h 这六个文件是最基本的,必须要包含在我们的项目中!除此之外,根据自己的配置:

  • 如果需要 FatFs 支持可重入, 则 必须要包含 ffsystem.c, 且必须实现 ffsystem.c 中的 ff_req_grant(), ff_rel_grant(), ff_del_syncobj() 和 ff_cre_syncobj() 这四个函数!
  • 如果需要 FatFs 支持长文件名,则必须要包含 ffunicode.c,如果配置长文件名模式为 3 (FF_USE_LFN == 3),则必须同时包含 ffsystem.c, 且必须实现 ffsystem.c 中的 ff_memalloc() 和 ff_memfree() 这两个函数。

第二步:修改配置

  根据自己的需要修改 ffconf.h 中的各个配置项!

第三步:实现必要的函数

  首先要实现 diskio.c/h 其中各接口!需要注意的是,一般的使用了 FatFs 的第三方库,都会提供该文件的实现!例如,在 STM32 的USB 驱动库中,有个名为 usbh_msc_fatfs.c 的文件,它其实就是 diskio.c 改了个名字而已。里面的函数就是 diskio.h 所声明的那些函数!也因此在使用 STM32 的USB 驱动库 + FatFs 时,我们不再需要 diskio.c了,但是 diskio.h 仍然是必须的!
下表显示了需要实现的函数(部分需要看自己在ffconf.h 的配置):
在这里插入图片描述
在 FatFs 源码的 diskio.c 中给出的示例是以默认的配置来说的,在 ffconf.h 中,有如下配置:

/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations     驱动器/卷配置
/---------------------------------------------------------------------------*/

/* 要使用的卷(逻辑驱动器)数。 取值 1 ~ 10
 */
#define FF_VOLUMES		1
/* Number of volumes (logical drives) to be used. (1-10) */


/* 这两个宏值用来给每个卷定义一个名字
 *  0: 不启用卷名字,默认值
 *  1 或 2: 对应的卷名由 FF_VOLUME_STRS 给出。 FF_VOLUME_STRS 的个数必须小于 FF_VOLUMES。
 *  名字的规则为  A-Z, a-z and 0-9 组合,且字母开头。不区分大小写!
 *  如果定义为了 >= 1 但是没有定义 FF_VOLUME_STRS,则 用户必须自己定义 const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
 *  
 *  在 ff.c 中,我们可以发现如下语句:
 *  #if FF_STR_VOLUME_ID
 *  #ifdef FF_VOLUME_STRS
 *  static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS};
 *  #endif
 *  #endif
 */
#define FF_STR_VOLUME_ID	0
#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",...
*/

我们必须根据自己的需求来更改。结合上文对于这个文件中每个函数的说明来实现即可!

第四步:实现 get_fattime

  如果在 ffconf.h 中配置了 FF_FS_NORTC == 0,则必须自己实现函数 DWORD get_fattime (void); 该函数返回一个 4 字节的时间戳!例如在上面的 STM32 的 USB 中,在 usbh_msc_fatfs.c 中 有 该函数的实现,但是默认为空(根据配置,用户需要修改该函数实现)!
  如果在 ffconf.h 中配置了 FF_FS_NORTC == 1,则可省略该函数的实现!具体见上文的说明即可!

该函数的格式:DWORD get_fattime (void);

  • 参数:
  • 返回值:当前本地时间应作为包含在DWORD值中的位字段返回。 位字段如下:
    • bit31:25:从 1980 年开始的年份 ,取值 0 ~ 127。例如 37 for 2017)
    • bit24:21:月份(1…12)
    • bit20:16:日期 (1…31)
    • bit15:11:小时(0…23)
    • bit10:5:分钟 (0…59)
    • bit4:0:秒 / 2 ( 0…29, e.g. 25 for 50)

第五步:应用层使用

  经过以上四步之后,就可以在项目中使用 FatFs了。可用的接口全部在于 ff.h 中,其与标准 C 语言中各函数用法基本一致!

error: #20: identifier “BYTE” is undefined

  这个问题应该是是由于没有正确包含头文件导致的。在 R0.13c 的时候,作者更新: Supported stdint.h for C99 and later. (integer.h was included in ff.h),然后删除了原来的 integer.h。这样,diskio.h 中也删除了对于该文件的包含。这样就导致了 diskio.h 中某些数据类型未定义!如下图:
在这里插入图片描述
解决方法就是在 diskio.h 中包含数据类型的定义文件。根据 00history.txt 中的更新说明,现在的数据类型定义就在 ff.h 中。

参考

  1. ST 官网
  2. FatFs 官网

https://itexp.blog.csdn.net/article/details/96127474?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.control

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值