FATFS R0.14b最新版移植到STM32

FATFS R0.14b移植到STM32

FATFS简介

FatFs 是一种完全免费开源的 FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C 语言编写,所以具有良好的硬件平台独立性,可以移植到 8051、 PIC、 AVR、 SH、 Z80、 H8、 ARM 等系列单片机上而只需做简单的修改。它支持 FATl2、 FATl6 和 FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对 8 位单片机和 16 位单片机做了优化。

特点

  1. Windows兼容的FAT文件系统
  2. 不依赖于平台,易于移植
  3. 代码和工作区占用空间非常小
  4. 多种配置选项
  5. 多卷(物理驱动器和分区)
  6. 多ANSI/OEM代码页,包括DBCS
  7. 在ANSI/OEM或Unicode中长文件名的支持
  8. RTOS的支持
  9. 多扇区大小的支持
  10. 只读,最少API,I/O缓冲区等等

移植性

fatfs模块是ANSI C(C89)编写的。 没有平台的依赖, 编译器只要符合ANSI C标准就可以编译。

数据类型

fatf模块假设大小的字符/短/长8/16/32位和int是16或32位。 这些数据类型在integer.h文件中定义。这些数据类型在大多数的编译器中定义都符合要求。 如果现有的定义与编译器有任何冲突发生时,需要自己解决。

FATFS移植准备

本文档移植的开发环境与芯片型号:

目标芯片:STM32F405RGT6

编译软件:KEIL5.27

移植文件系统之前,需要有一个完整的工程,并且有完整的SD卡驱动。

还需要准备SD一张。

 FATFS源码下载

下载地址:FatFs - Generic FAT Filesystem Module

写这篇文档时,官网最新的版本是:R0.14b

官网截图:

FATFS有两个版本,一个大版本,一个小版本。小版本主要用于8位机(内存小)使用。

下载图:

FATFS源码文件介绍

将下载的源码解压后可以得到两个文件夹: documents和 source。 documents里面主要是对 FATFS 的介绍(离线文档—英文和日文),而 source里面才是我们需要的源码,其中,与平台无关的是:

  1. ffconf.h     FATFS配置文件
  2. ff.h        应用层头文件
  3. ff.c        应用层源文件
  4. diskio.h    硬件层头文件
  5. ffunicode.c 外部功能(比如支持中文等)
  6. ffsystem.c  可选O/S相关函数的例子

与平台相关的代码:

        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) 和供给文件创建修改时间的实时时钟。

移植文件系统步骤

1. 把下载的FATFS源码拷贝到具备SD(MMC)卡驱动的工程并解压。

 

2. 打开KEIL工程,添加FATFS文件源码:

3. 添加FATFS相关头文件路径

 

4. 编译工程,出现以下错误,然后进行修改。

5. 修改diskio.c文件

   1) 添加头文件及宏

注释掉现在不需要的用到的文件,因为我们现在用的是SD卡,与RAM,MMC,USB卡没关系。

添加SD卡驱动对应的头文件(可能存在不同的写法)。我的为:

#include "sd_driver.h"

并加入一个新的宏 :

#define  DEV_SD  0

定义SD卡的物理驱动器号为0。 

2) 修改 disk_status函数,该函数主要是用来获取磁盘状态。现在未用到,可以直接函数体内代码删除。

修改截图:

代码示例:

#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "sd_driver.h"

#define DEV_SD		0	/* Example: Map SDCard to physical drive 0 */
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	return RES_OK;
}

3) 修改disk_initialize函数,添加SD卡的初始化,其他不用到的代码直接删掉,该函数成功返回0,失败返回1。

修改截图:

代码示例:

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat;

	switch (pdrv) {
	case DEV_SD :
		stat = SD_Initialize(); //SD卡初始化,用户自己提供
		return stat;
	}
	return STA_NOINIT;
}

4) 修改disk_read函数,加入SD卡读任意扇区的函数(需要用户自己提供),其他不用到的选项可以删掉。

修改代码如下:

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_SD:
		res = SD_ReadDisk(buff, sector, count);//SD卡读扇区函数
		return res;
	}

	return RES_PARERR;
}

5) 修改disk_write 函数,添加写扇区函数:

代码:

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;
	switch (pdrv) {
	case DEV_SD :	
		res = SD_WriteDisk(( BYTE *)buff, sector, count);//SD卡写扇区
		return res;
	}
	return RES_PARERR;
}

6) 修改disk_ioctl 函数,填充ioctl命令功能。这些功能是标准的命令,在diskio.h有定义。

代码如下:


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_SD:
		  switch(cmd)
	    {
		    case CTRL_SYNC://等待写过程
				res = RES_OK; 
		        break;	 
		    case GET_SECTOR_SIZE://获取扇区大小
				*(DWORD*)buff = 512; 
		        res = RES_OK;
		        break;	 
		    case GET_BLOCK_SIZE:	//获取块大小
				*(WORD*)buff = 4096;
		        res = RES_OK;
		        break;	 
		    case GET_SECTOR_COUNT:	//SD卡扇区数量
		        *(unsigned int *)buff = SD_GetSectorCount();break;
		        res = RES_OK;
		        break;
		    default:
		        res = RES_PARERR;
		        break;
	    }
		return res;
	}
	return RES_PARERR;
}

6. 修改ffconf.h文件

1) 修改编码格式

#define _CODE_PAGE 936   //采用中文GBK编码 

    

2) 修改LFN支持

#define   _USE_LFN    3     //动态的堆上工作        

#define   _MAX_LFN  255   /*_USE_LFN选项开关LFN(长文件名)特性。

7. 实现动态内存分配函数

在ffsystem.c文件有动态内存的释放,动态内存申请,在此文件中添加动态内存的释放,动态内存申请函数所需要的头文件。

 

8. 实现时间函数

此时编译工程,出现以下错误:

 

此错误原因是没有在ff.h文件中定义时间获取函数。实现此函数可使用STM32的RTC功能,如果没有实现RTC,可以直接返回0。

没有RTC。

 

有RTC。需要根据自行的RTC进行修改。

 示例代码:

/*********************************************************************************************************
* 函 数 名 : get_fattime
* 功能说明 : 获取时间,创建文件或更新文件时需要用到
* 形    参 : 无
* 返 回 值 : 时间数据
* 备    注 : User defined function to give a current time to fatfs module      
			 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31)                                                                                                                                                                                                                                          
			 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2)   
*********************************************************************************************************/                                                                                                                                                                                                                                          
DWORD get_fattime (void)
{		
	DWORD date = 0;
	
	RTC_GetTimeDate();
	date =
	( (
	(RTC_DateStruct.RTC_Year+20) << 25) |
	(RTC_DateStruct.RTC_Month << 21 ) |
	(RTC_DateStruct.RTC_Date << 16 ) |
	(RTC_TimeStruct.RTC_Hours << 11 ) |
	(RTC_TimeStruct.RTC_Minutes << 5 ) |
	(RTC_TimeStruct.RTC_Seconds )
	);
	return date;
}	

9. 修改堆栈空间  

完成了上述的修改,还需要修改堆栈空间,因为长文件支持需要占用堆空间。

修改STM32启动文件如下:

 

至此,已经完成了FATFS的移植。

​​​​​​​FATFS示例

修改完毕之后,给开发板插上SD卡,调用API函数在SD卡创建一个文件,并写入数据,测试是否成功:

示例:

#include "led.h"
#include "key.h"
#include "systick.h"
#include "usart1.h"
#include "sd_driver.h"
#include "ff.h"

int main()
{	
	FATFS fs;
	FRESULT res;
	FIL fp;
	UINT bw;
	UINT br;
	u8 readBuf[13]={0};
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//配置中断分组,只配置一次
	Systick_Inti(168);			//系统滴答初始化
	Usart1_Init(115200);		//初始化USART1,波特率为115200
	Key_Init();			//按键端口初始化

	/*FATFS测试*/
	//1. 注册
	res = f_mount(&fs,"0",1);
	if(res ==FR_OK)
	{
		printf("SD卡注册成功\r\n");
	}else{
		printf("SD卡注册失败\r\n");
	}
	//2.打开或者创建
	res = f_open(&fp,"chenxingxing.txt",FA_CREATE_ALWAYS|FA_WRITE);
	if(res ==FR_OK)
	{
		printf("成功打开或创建文件\r\n");
	}else{
		printf("打开失败\r\n");
	}
	//3.写数据
	res = f_write(&fp,"hello world",12,&bw);
	if(res ==FR_OK)
	{
		printf("成功写入%d个字节\r\n",bw);
	}else{
		printf("写入文件失败\r\n");
	}
	//4.关闭文件
	f_close(&fp); //关闭文件

	//5.以只读方式打开文件
	f_open(&fp,"chenxingxing.txt",FA_READ);
	
	//6.读取文件
	res = f_read(&fp,readBuf,12,&br);
	if(res ==FR_OK)
	{
		printf("成功读取%d个字节,读到的内容为:%s\r\n",br,readBuf);
	}else{
		printf("读取文件失败\r\n");
	}
	while(1)
	{

	}
}

测试结果:

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值