主控MCU: STM32F103ZET6
STM32程序开发IDE: keil5
STM32程序风格: 采用寄存器方式开发,注释齐全,执行效率高,方便移植
硬件包含: 一块STM32F103ZET6系统板、一个SPI接口的SD卡卡槽模块、一张SD卡
这篇文章主要演示FATFS文件系统如何移植到自己的工程,并完成文件的读写。
因为SD卡采用的是SPI模拟时序,所以,其他单片机一样可以照着移植,代码都可以复制粘贴的。
FatFs 是一种完全免费开源的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C 语言编写,所以具有良好的硬件平台独立性,可以移植到 8051、 PIC、 AVR、 SH、 Z80、 H8、 ARM 等系列单片机上而只需做简单的修改。它支持 FATl2、 FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对 8 位单片机和 16 位单片机做了优化。
fatfs模块是ANSI C(C89)编写的。 没有平台的依赖, 编译器只要符合ANSI C标准就可以编译。
fatf模块假设大小的字符/短/长8/16/32位和int是16或32位。 这些数据类型在integer.h文件中定义。这些数据类型在大多数的编译器中定义都符合要求。 如果现有的定义与编译器有任何冲突发生时,需要自己解决。
下载地址:http://elm-chan.org/fsw/ff/00index_e.html
FATFS有两个版本,一个大版本,一个小版本。小版本主要用于8位机(内存小)使用。
下载图:
将下载的源码解压后可以得到两个文件夹: doc 和 src。 doc 里面主要是对 FATFS 的介绍(离线文档—英文和日文),而 src 里面才是我们需要的源码。
其中,与平台无关的是:
- ffconf.h FATFS配置文件
- ff.h 应用层头文件
- ff.c 应用层源文件
- diskio.h 硬件层头文件
- interger.h 数据类型定义头文件
- option 可选的外部功能(比如支持中文等)
与平台相关的代码:
diskio.c 底层接口文件(需要用户提供)
FATFS 模块在移植的时候,我们一般只需要修改 2 个文件,即 ffconf.h 和 diskio.c。
FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们可以通过配置里面的一些选项,来满足自己的需求。
FATFS最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC 上读/写文件那样简单。
中间层 FATFS 模块, 实现了 FAT 文件读/写协议。 FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口 ( disk、I/O) 和供给文件创建修改时间的实时时钟。
移植之前,首先得准备一个能正常编译的工程,并且工程里有SD卡的驱动代码,提供了读写扇区这些函数才能进行FATFS文件系统的正常移植。
关于如何编写SD卡驱动,SD卡的时序介绍、命令介绍等知识点下篇文章再讲解。这篇文章重点是FATFS文件系统的移植过程。
FATFS文件系统源码下载下来,解压之后,移植修改的步骤如下:
打开KEIL工程,添加FATFS文件源码:
加入.h文件主要是方便配。cc936.c 用于支持中文。
注释掉现在不需要的用到的文件,因为我们现在用的是SD卡,与USB,ATA,MMC卡没关系。
并加入一个新的宏 :
#define SD 0
定义SD卡的物理驱动器号为0。
修改 disk_status函数,该函数主要是用来获取磁盘状态。现在未用到,可以直接函数体内代码删除。
修改截图:
代码示例:
- #include "diskio.h" /* fatf底层API */
- #include "sd.h" /* SD卡驱动头文件 */
- /* 定义每个驱动器的物理驱动器号*/
- #define SD 0
-
- /*-----------------------------------------------------------------------*/
- /* 获取设备(磁盘)状态 */
- /*-----------------------------------------------------------------------*/
-
- DSTATUS disk_status (
- BYTE pdrv /* 物理驱动识别 */
- )
- {
- return 0; //该函数现在无需用到,直接返回0
- }
修改disk_initialize函数,添加SD卡的初始化,其他不用到的代码直接删掉,该函数成功返回0,失败返回1。
修改截图:
代码示例:
- /*-----------------------------------------------------------------------*/
- /* 初始化磁盘驱动 */
- /*-----------------------------------------------------------------------*/
-
- DSTATUS disk_initialize (
- BYTE pdrv /* 物理驱动识别 */
- )
- {
- DSTATUS stat;
- int result;
-
- switch (pdrv) {
- case SD : //选择SD卡
- stat=SD_Init(); //初始化SD卡-用户自己提供
- }
- if(stat)return STA_NOINIT; //磁盘未初始化
- return 0; //初始化成功
- }
修改disk_read函数,加入SD卡读任意扇区的函数(需要用户自己提供),其他不用到的选项可以删掉。
修改代码如下:
- /*-----------------------------------------------------------------------*/
- /* 读扇区 */
- /*-----------------------------------------------------------------------*/
- DRESULT disk_read (
- BYTE pdrv, /* 物理驱动编号 - 范围0-9*/
- BYTE *buff, /* 数据缓冲区存储读取数据 */
- DWORD sector, /* 扇区地址*/
- UINT count /* 需要读取的扇区数*/
- )
- {
- DRESULT res;
- int result;
- switch (pdrv) {
- case SD:
- res=SD_Read_Data((u8*)buff,sector,count); //读SD扇区函数--用户提供
- return res; //在此处可以判错误
- }
- return RES_PARERR; //无效参数
- }
修改disk_write 函数,添加写扇区函数:
代码:
- /*-----------------------------------------------------------------------*/
- /* 写扇区 */
- /*-----------------------------------------------------------------------*/
-
- #if _USE_WRITE
- DRESULT disk_write (
- BYTE pdrv, /* 物理驱动号*/
- const BYTE *buff, /* 要写入数据的首地址 */
- DWORD sector, /* 扇区地址 */
- UINT count /* 扇区数量*/
- )
- {
- DRESULT res;
- int result;
-
- switch (pdrv) {
- case SD:
- res=SD_Write_Data((u8*)buff,sector,count); //写入扇区
- return res;
- }
- return RES_PARERR; //无效参数
- }
- #endif
修改disk_ioctl 函数,填充ioctl命令功能。这些功能是标准的命令,在diskio.h有定义。
代码如下:
- /*-----------------------------------------------------------------------*/
- /* 其他函数 */
- /*-----------------------------------------------------------------------*/
-
- #if _USE_IOCTL
- DRESULT disk_ioctl (
- BYTE pdrv, /* 物理驱动号 */
- BYTE cmd, /* 控制码 */
- void *buff /* 发送/接收数据缓冲区地址 */
- )
- {
- DRESULT res;
- int result;
-
- switch (pdrv) {
- case SD:
- switch(cmd)
- {
- case CTRL_SYNC: //等待写过程
- SD_CS(0); //选中SD卡
- if(SD_Wait_Ready())result = RES_ERROR;/*等待卡准备好*/
- else res = RES_OK; //成功
- SD_CS(1); //释放SD卡
- break;
-
- case GET_SECTOR_SIZE://获取扇区大小
- *(DWORD*)buff = 512;
- res = RES_OK; //成功
- break;
-
- case GET_BLOCK_SIZE: //获取块大小
- *(WORD*)buff = 8; //块大小(扇区为单位),一块等于8个扇区
- res = RES_OK;
- break;
-
- case GET_SECTOR_COUNT: //获取总扇区数量
- *(DWORD*)buff = SD_Get_Sector_Count();
- res = RES_OK;
- break;
-
- default: //命令错误
- res = RES_PARERR;
- break;
- }
- return res;
- }
- return RES_PARERR; //返回状态
- }
diskio.c 文件修改完整代码:
- /*-----------------------------------------------------------------------*/
- /* 低级别磁盘I / O模块框架fatf(C)ChaN)2014
- *存储控制模块fatf模块定义了一个API。 */
- /*-----------------------------------------------------------------------*/
-
- #include "diskio.h" /* fatf底层API */
- #include "sd.h" /* SD卡驱动头文件 */
-
- /* 定义每个驱动器的物理驱动器号*/
- #define SD 0
-
- /*-----------------------------------------------------------------------*/
- /* 获取设备(磁盘)状态 */
- /*-----------------------------------------------------------------------*/
-
- DSTATUS disk_status (
- BYTE pdrv /* 物理驱动识别 */
- )
- {
- return 0; //该函数现在无需用到,直接返回0
- }
-
-
-
- /*-----------------------------------------------------------------------*/
- /* 初始化磁盘驱动 */
- /*-----------------------------------------------------------------------*/
-
- DSTATUS disk_initialize (
- BYTE pdrv /* 物理驱动识别 */
- )
- {
- DSTATUS stat;
- int result;
-
- switch (pdrv) {
- case SD : //选择SD卡
- stat=SD_Init(); //初始化SD卡-用户自己提供
- }
- if(stat)return STA_NOINIT; //磁盘未初始化
- return 0; //初始化成功
- }
-
-
- /*-----------------------------------------------------------------------*/
- /* 读扇区 */
- /*-----------------------------------------------------------------------*/
-
- DRESULT disk_read (
- BYTE pdrv, /* 物理驱动编号 - 范围0-9*/
- BYTE *buff, /* 数据缓冲区存储读取数据 */
- DWORD sector, /* 扇区地址*/
- UINT count /* 需要读取的扇区数*/
- )
- {
- DRESULT res;
- int result;
-
- switch (pdrv) {
- case SD:
- res=SD_Read_Data((u8*)buff,sector,count); //读SD扇区函数--用户提供
- return res; //在此处可以判错误
- }
- return RES_PARERR; //无效参数
- }
-
-
-
- /*-----------------------------------------------------------------------*/
- /* 写扇区 */
- /*-----------------------------------------------------------------------*/
-
- #if _USE_WRITE
- DRESULT disk_write (
- BYTE pdrv, /* 物理驱动号*/
- const BYTE *buff, /* 要写入数据的首地址 */
- DWORD sector, /* 扇区地址 */
- UINT count /* 扇区数量*/
- )
- {
- DRESULT res;
- int result;
-
- switch (pdrv) {
- case SD:
- res=SD_Write_Data((u8*)buff,sector,count); //写入扇区
- return res;
- }
- return RES_PARERR; //无效参数
- }
- #endif
-
-
- /*-----------------------------------------------------------------------*/
- /* 其他函数 */
- /*-----------------------------------------------------------------------*/
-
- #if _USE_IOCTL
- DRESULT disk_ioctl (
- BYTE pdrv, /* 物理驱动号 */
- BYTE cmd, /* 控制码 */
- void *buff /* 发送/接收数据缓冲区地址 */
- )
- {
- DRESULT res;
- int result;
-
- switch (pdrv) {
- case SD:
- switch(cmd)
- {
- case CTRL_SYNC: //等待写过程
- SD_CS(0); //选中SD卡
- if(SD_Wait_Ready())result = RES_ERROR;/*等待卡准备好*/
- else res = RES_OK; //成功
- SD_CS(1); //释放SD卡
- break;
-
- case GET_SECTOR_SIZE://获取扇区大小
- *(DWORD*)buff = 512;
- res = RES_OK; //成功
- break;
-
- case GET_BLOCK_SIZE: //获取块大小
- *(WORD*)buff = 8; //块大小--一块等于8个扇区
- res = RES_OK;
- break;
-
- case GET_SECTOR_COUNT: //获取总扇区数量
- *(DWORD*)buff = SD_Get_Sector_Count();
- res = RES_OK;
- break;
-
- default: //命令错误
- res = RES_PARERR;
- break;
- }
- return res;
- }
- return RES_PARERR; //返回状态
- }
- #endif
-
-
- //返回FATFS时间
- //获得时间
- DWORD get_fattime (void)
- {
- return (DWORD)(2017-1980)<<25| //年
- 7<<21| //月
- 27<<16| //日
- 12<<11| //时
- 13<<5| //分
- 14; //秒
- }
-
-
- /*
- Return Value
- Currnet local time is returned with packed into a DWORD value. The bit field is as follows:
- bit31:25
- Year origin from the 1980 (0..127)
- bit24:21
- Month (1..12)
- bit20:16
- Day of the month(1..31)
- bit15:11
- Hour (0..23)
- bit10:5
- Minute (0..59)
- bit4:0
- Second / 2 (0..29)
- */
需要注意的一些宏配置:
- #define _CODE_PAGE 936 //采用中文GBK编码 (64行)
- #define _USE_LFN 3 //动态的堆上工作 (93行)
- #define _MAX_LFN 255 /*_USE_LFN选项开关LFN(长文件名)特性。
- #define _VOLUMES 1 /* 支持的磁盘数量(逻辑驱动器)。 */ (142行)
- #define _MIN_SS 512 (165行)
- #define _MAX_SS 512 /*这些选项配置支持扇区大小的范围。(512,1024, 4096*/
- #define _FS_NORTC 0 /*启用RTC时间功能*/ (202行)
- #define _NORTC_MON 1
- #define _NORTC_MDAY 1
- #define _NORTC_YEAR 2015 //年
- /*需要实现:get_fattime()函数*/
ffconf.h 文件源码(讲解):
- /*---------------------------------------------------------------------------/
- / FatFs - FAT文件系统模块配置文件 R0.11a (C)ChaN, 2015
- /---------------------------------------------------------------------------*/
-
- #define _FFCONF 64180 /* 版本识别*/
-
- /*---------------------------------------------------------------------------/
- / 功能配置
- /---------------------------------------------------------------------------*/
-
- #define _FS_READONLY 0
- /* 这个选项开关只读配置。(0:读/写或1:只读)
- /只读配置删除编写API函数,f_write(),f_sync(),
- / f_unlink(),f_mkdir(),f_chmod(),f_rename(),f_truncate(),f_getfree()
- /写和可选的功能. */
-
-
- #define _FS_MINIMIZE 0
- /*此选项定义删除一些基本的API函数极小化水平。
- /
- / 0:所有基本功能都是激活的。
- / 1:f_stat(),f_getfree(),f_unlink(),f_mkdir(),f_chmod(),f_utime(),
- / f_truncate()和f_rename()函数删除。
- / 2:f_opendir(),f_readdir()和f_closedir()中除了1。
- / 3:f_lseek()函数删除除了2。*/
-
-
- #define _USE_STRFUNC 1
- /*这个选项开关字符串函数,f_gets(),f_putc(),f_puts()和
- / f_printf()。
- /
- / 0:禁用字符串函数。
- / 1:启用没有LF-CRLF转换。
- / 2:启用LF-CRLF(回车换行)转换。*/
-
-
- #define _USE_FIND 0
- /*这个选项开关过滤目录读取特性和相关功能,
- / f_findfirst()和f_findnext()。(0:禁用或1:启用)*/
-
-
- #define _USE_MKFS 1
- /* 这个选项开关f_mkfs()函数。(0:禁用或1:启用) */
-
-
- #define _USE_FASTSEEK 1
- /* 这个选项开关快速寻求功能。(0:禁用或1:启用) */
-
-
- #define _USE_LABEL 1
- /* 磁盘卷标这个选项开关功能,f_getlabel()和f_setlabel()。
- /(0:禁用或1:启用) */
-
-
- #define _USE_FORWARD 0
- /* 这个选项开关f_forward()函数。(0:禁用或1:启用)
- /启用它,也_FS_TINY需要设置为1. */
-
-
- /*---------------------------------------------------------------------------/
- / 语言环境和名称空间配置
- /---------------------------------------------------------------------------*/
-
- #define _CODE_PAGE 936 //采用中文GBK编码
- /* 这个选项指定OEM代码页在目标系统上使用。
- /不正确的代码页的设置会导致文件打开失败.
- /
- / 1 - ASCII (没有扩展字符。Non-LFN cfg。只有)
- / 437 - U.S.
- / 720 - 阿拉伯语
- / 737 - 希腊语;
- / 771 - 阿富汗
- / 775 - 波罗的海
- / 850 - 拉丁1
- / 852 - 拉丁2
- / 855 - 西里尔字母
- / 857 - 土耳其语
- / 860 - 葡萄牙语
- / 861 - 冰岛语
- / 862 - 希伯来人
- / 863 - 加拿大法语
- / 864 - 阿拉伯语
- / 865 - 日耳曼民族的
- / 866 - 俄语
- / 869 - 希腊 2
- / 932 - 日本人 (DBCS)
- / 936 - 简体中文(DBCS)
- / 949 - 韩国人 (DBCS)
- / 950 - 繁体中文(DBCS)
- */
-
-
- #define _USE_LFN 3 //动态的堆上工作
- #define _MAX_LFN 255
- /*_USE_LFN选项开关LFN(长文件名)特性。
- /
- / 0:禁用LFN特性。_MAX_LFN没有影响。
- / 1:启用LFN BSS静态工作缓冲区。总是不是线程安全的。
- / 2:启用LFN与动态缓冲栈上的工作。
- / 3:使LFN与动态缓冲区在堆上工作。
- /
- / 当启用LFN(长文件名)特性,Unicode(选项/ unicode.c)必须处理功能
- /被添加到项目中。LFN工作缓冲区占用(_MAX_LFN + 1)* 2字节。
- /当使用堆栈缓冲区,照顾堆栈溢出。当使用堆
- /工作缓冲区内存,内存管理功能,ff_memalloc()和
- / ff_memfree(),必须添加到项目中。 */
-
-
- #define _LFN_UNICODE 0
- /* 这个选项开关字符编码的API。(0:ANSI / OEM或1:Unicode)
- 路径名/使用Unicode字符串,并设置_LFN_UNICODE启用LFN特性
- /1。这个选项也会影响行为的字符串的I / O功能。
- */
-
-
- #define _STRF_ENCODE 3
- /* 当_LFN(长文件名)_UNICODE是1,这个选项选择文件的字符编码
- /通过字符串读取/写入I /O功能,f_gets(),f_putc(),f_puts和f_printf().
- /
- / 0: ANSI/OEM
- / 1: UTF-16LE
- / 2: UTF-16BE
- / 3: UTF-8
- /
- / 当_LFN_UNICODE = 0时,该选项没有影响。*/
-
- #define _FS_RPATH 0
- /* 这个选项配置相对路径的功能。 /
- / 0:禁用相对路径特性和删除相关功能。
- / 1:启用相对路径特性。f_chdir()和f_chdrive()是可用的。
- / 2:f_getcwd()函数可用除了1。 /
- /注意,目录项读通过f_readdir()这个选项。
- */
-
- /*---------------------------------------------------------------------------/
- / 驱动/卷配置
- /---------------------------------------------------------------------------*/
-
-
- #define _VOLUMES 1
- /* 支持的磁盘数量(逻辑驱动器)。 */
-
-
- #define _STR_VOLUME_ID 0
- #define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
- /* STR_VOLUME_ID选项开关卷ID字符串功能。
- /当_STR_VOLUME_ID设置为1时,也可以使用预先定义的字符串在路径名称/数量。
- 为每个_VOLUME_STRS定义驱动ID字符串
- /逻辑驱动器。条目的数量必须等于_VOLUMES。有效字符
- /驱动ID字符串:a - z和0 - 9。*/
-
-
- #define _MULTI_PARTITION 0
- /* 这个选项开关多分区的特性。在默认情况下(0),每个逻辑驱动器
- /号绑定到相同的物理驱动器号
- /物理驱动器将被安装。当启用分区特性(1),
- /每个逻辑驱动器号是绑定到任意物理驱动器和分区
- /中列出VolToPart[]。还f_fdisk()函数可用. */
-
-
- #define _MIN_SS 512
- #define _MAX_SS 512
- /* 这些选项配置支持扇区大小的范围。(512,1024,
- / 2048或4096)总是为大多数系统设置两个512,卡和所有类型的内存
- /硬盘。但是可能需要更大的值为车载闪存和一些
- /类型的光学媒体。当_MAX_SS大于_MIN_SS,fatf配置
- /变量扇区大小和GET_SECTOR_SIZE命令必须执行 disk_ioctl()函数. */
-
-
- #define _USE_TRIM 0
- /* 这个选项开关ATA-TRIM特性。(0:禁用或1:启用)
- /启用削减特性,也应该实现CTRL_TRIM命令
- / disk_ioctl()函数。*/
-
-
- #define _FS_NOFSINFO 0
- /*
- 如果你需要知道正确的自由空间体积FAT32,设置一些0
- /选项,f_getfree()函数在第一次后体积将迫使山
- /全脂肪扫描。位1控制使用的集群数量分配。 /
- / bit0 = 0:使用免费的集群计算FSINFO如果可用。
- / bit0 = 1:不相信自由FSINFO集群计算。
- / bit1 = 0:最后使用集群可用FSINFO如果数量分配。
- / bit1 = 1:不相信最后分配FSINFO集群数量.
- */
-
-
-
- /*---------------------------------------------------------------------------/
- / 系统配置列表
- /---------------------------------------------------------------------------*/
-
- #define _FS_TINY 0
- /* 这个选项开关小缓冲区配置。(0:正常或1:小)
- /小配置,文件对象的大小(FIL)_MAX_SS减少字节。而不是私人部门从文件对象,缓冲了
- /公共部门缓冲文件系统中的对象(fatf)是用于该文件
- /数据传输. */
-
-
- #define _FS_NORTC 0
- #define _NORTC_MON 1
- #define _NORTC_MDAY 1
- #define _NORTC_YEAR 2015 //年
- /* _FS_NORTC选项开关时间戳的特性。如果系统没有/
- RTC函数或不需要有效的时间戳,_FS_NORTC 1设置为禁用/
- 时间戳的特性。所有对象修改fatf将有一个固定的时间戳。/
- 固定的时间定义为_NORTC_MON _NORTC_MDAY _NORTC_YEAR。
-
- /当启用时间戳特性(_FS_NORTC = = 0),需要实现get_fattime()函数。 /
- 添加到项目RTC读当前时间形式。_NORTC_MON, /
- _NORTC_MDAY和_NORTC_YEAR没有效果。
- /这些选项没有影响只读配置(_FS_READONLY = = 1)。 */
-
-
- #define _FS_LOCK 0
- /* _FS_LOCK选项开关控制复制的文件打开的文件锁定功能
- /和非法操作打开对象。这个选项_FS_READONLY时必须是0
- /是1。 /
- / 0:禁用文件锁定功能。为了避免体积腐败、应用程序
- /应该避免非法打开,删除和重命名的开放对象。
- / > 0:启用文件锁定功能。值定义了多少文件/子目录
- 可以同时打开的/文件锁的控制之下。注意,这个文件独立于re-entrancy /锁功能。 */
-
-
-
- #define _FS_REENTRANT 0
- #define _FS_TIMEOUT 1000
- #define _SYNC_t HANDLE
- /* _FS_REENTRANT选项开关re-entrancy fatf的(线程安全)
- /模块本身。注意,不管这个选项,文件访问不同
- /体积始终是凹角和音量控制功能,f_mount(),f_mkfs()
- /和f_fdisk()函数,总是不凹角。只有文件/目录的访问
- /相同的体积是这个功能的控制。
- /
- / 0:禁用re-entrancy。_FS_TIMEOUT和_SYNC_t没有效果。
- / 1:启用re-entrancy。还提供用户同步处理程序,
- / ff_req_grant(),ff_rel_grant(),ff_del_syncobj()和ff_cre_syncobj()
- /函数,必须添加到项目中。样品中可用
- /选项
- / syscall.c。
- /
- / _FS_TIMEOUT定义超时时间单位的滴答声。
- / _SYNC_t定义了O
- / S依赖同步对象类型。例如处理、ID、OS_EVENT *
- / SemaphoreHandle_t等. .O / S的头文件定义需要
- /包括在ff.c的范围。 */
-
-
- #define _WORD_ACCESS 0
- /* _WORD_ACCESS选项是一个只有依赖于平台的选择。
- 它定义了这个词/访问方法是用来体积上的数据。
- /
- / 0:逐字节的访问。总是兼容所有平台。
- / 1:词的访问。不要选择这个,除非在下列条件。
- /
- / *地址对齐内存访问总是允许所有指令。
- / *字节顺序的记忆是低位优先。
- /
- /如果是这样的情况,_WORD_ACCESS也可以减少代码的大小设置为1。
- /下表显示允许设置某种类型的处理器。
- /
- / ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2
- / Cortex-M3 0 *3 Z80 0/1 V850ES 0/1
- / Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1
- / AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
- / AVR32 0 *1 RL78 0 *2 R32C 0 *2
- / PIC18 0/1 SH-2 0 *1 M16C 0/1
- / PIC24 0 *2 H8S 0 *1 MSP430 0 *2
- / PIC32 0 *1 H8/300H 0 *1 8051 0/1
- /
- /
- * 1:高位优先。 /
- * 2:不支持不连续的内存访问。 /
- * 3:一些编译器生成LDM(逻辑磁盘管理器 ) / STM mem_cpy(内存拷贝)函数。
- */
ff.h文件有动态内存的释放,动态内存申请,时间获取函数接口。
在diskio.c文件实现函数功能:
代码实现如下:
- //动态内存分配
- void* ff_memalloc (UINT msize) /* 分配内存块 */
- {
- return (void*)malloc(msize); //分配空间
- }
-
-
- //动态内存释放
- void ff_memfree (void* mblock) /* 空闲内存块 */
- {
- free(mblock); //释放空间
- }
-
-
- //返回FATFS时间
- //获得时间
- DWORD get_fattime (void)
- {
- //Get_RTC_Timer(); //获取一次RTC时间
- return (RTC_Timer.year-1980)<<25| //年
- RTC_Timer.month<<21| //月
- RTC_Timer.day<<16| //日
- RTC_Timer.hour<<11| //时
- RTC_Timer.minute<<5| //分
- RTC_Timer.sec; //秒
- }
-
- /*
- Return Value
- Currnet local time is returned with packed into a DWORD value. The bit field is as follows:
- bit31:25
- Year origin from the 1980 (0..127)
- bit24:21
- Month (1..12)
- bit20:16
- Day of the month(1..31)
- bit15:11
- Hour (0..23)
- bit10:5
- Minute (0..59)
- bit4:0
- Second / 2 (0..29)
- */
完成了上述的修改,还需要修改堆栈空间,因为长文件支持需要占用堆空间。
修改STM32启动文件如下:
修改完毕之后,给开发板插上SD卡,调用API函数在SD卡创建一个文件,并写入数据,测试是否成功:
- #include "ff.h"
- FATFS fs; // 用户定义的文件系统结构体
- FIL file; // 用户定义的文件系统结构体
- u8 buff[]="123 知识!!";
- int main(void)
- {
- u32 data; //检测SD卡容量
- u8 i,res;
- LED_Init(); //LED灯初始化
- Delay_Init();
- KEY_Init();
- USART1_Init(72,115200);
- USART2_Init(36,115200);
- FLASH_Init();
- Set_Font_addr(); //字库地址初始化
- FSMC_SRAM_Init();
- LCD_Init();
- RTC_Init(); //RTC时钟初始化
- while(SD_Init()) //检测不到SD卡,SD相关硬件初始化
- {
- i=!i;
- LCD_ShowString(60,150,200,16,16,"SD Card Error! Please Check SD Card!!",0xf800);
- Delay_ms(500);
- LED1(i)//DS0闪烁
- }
-
- f_mount(&fs,"0",1); // 注册工作区,驱动器号 0,初始化后其他函数可使用里面的参数
- printf("注册工作区!\n");
-
- if(f_mkfs("0",0,4096)) //格式化SD卡
- {
- printf("格式化失败!!\n");
- }
- else
- {
- printf("格式化成功!!\n");
- }
- res = f_open(&file, "/file.c", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
- if(res==0)
- {
- printf("文件创建成功!!\n");
- }
- else
- {
- printf("文件创建失败!!\n");
- }
- res =f_write(&file,buff,strlen((const char*)buff),&data);
- if(res==0)
- {
- printf("数据写入成功!!\n");
- }
- else
- {
- printf("数据写入失败!!\n");
- }
- printf("成功写入%d字节数据\n",data);
- f_close(&file); //关闭文件
- //_FS_RPATH
-
- while(1)
- {
- Delay_ms(1000);
- LED1(1);
- Delay_ms(500);
- LED1(0);
- }
- }