【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植

FatFs是一个用于小型嵌入式系统的通用FAT/exFAT文件系统模块。FatFs模块是按照ANSI C(C89)编写的,与磁盘I/O层完全分离。因此,它独立于平台。它可以集成到资源有限的小型微控制器中,如8051、PIC、AVR、ARM、Z80、RX等。此外,还提供了用于微型微控制器的Petit Fatfs模块。

特征

  • DoS/Windows兼容FAT/exFAT文件系统。
  • 平台无关。很容易移植。
  • 程序代码和工作区占用的空间很小。
  • 五花八门配置选项支持:
    • ANSI/OEM或Unicode中的长文件名。
    • exFAT文件系统,64位LBA和GPT用于大型存储。
    • 线程安全的实时操作系统。
    • 多卷(物理驱动器和分区)。
    • 可变扇区大小。
    • 多个代码页,包括DBCS。
    • 只读,可选API,I/O缓冲区等.

这个是fatfs官网的原话翻译过来的,原话如下:

FatFs is a generic FAT/exFAT filesystem module for small embedded systems. The FatFs module is written in compliance with ANSI C (C89) and completely separated from the disk I/O layer. Therefore it is independent of the platform. It can be incorporated into small microcontrollers with limited resource, such as 8051, PIC, AVR, ARM, Z80, RX and etc. Also Petit FatFs module for tiny microcontrollers is available here.

Features

  • DOS/Windows compatible FAT/exFAT filesystem.
  • Platform independent. Easy to port.
  • Very small footprint for program code and work area.
  • Various configuration options to support for:
    • Long file name in ANSI/OEM or Unicode.
    • exFAT filesystem, 64-bit LBA and GPT for huge storeges.
    • Thread safe for RTOS.
    • Multiple volumes (physical drives and partitions).
    • Variable sector size.
    • Multiple code pages including DBCS.
    • Read-only, optional API, I/O buffer and etc...

这里就不多说了,其实就很简单理解,文件系统可以使得我们在存储介质上存储文件方便。

下面进入正题

准备工作

从官网下载一份最新的fatfs源码(官网:http://elm-chan.org/fsw/ff/00index_e.html),下完后解压出来如下:

【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植_第1张图片

准备一个有地层驱动的stm32 keil工程,这里我沿用上篇使用的usb虚拟U盘的代码,这样可以更方便和直观的看到文件本身。

准备好后接下来就是移植过程了。

第一步

把源码复制到工程目录,并添加对应的源码和配置好编译包含的路径

【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植_第2张图片

【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植_第3张图片

接下来就是修改源码了,修改源码其实就是给文件系统操作介质的接口对接上就完了。主要部分是diskio.c

这个下面一共有5个函数需要修改:

disk_status       //获取磁盘状态

disk_initialize   //磁盘初始化

disk_read         //度扇区

disk_write        //写扇区

disk_ioctl        //控制设备指定特性和除了读写外的功能杂项

这里官方给的默认有三个磁盘:

/* 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 */

我这里只用到了一个外部的spi flash,所以这些都没有用。这样的话disk_status和disk_initialize里面直接返回RES_OK即可。

注意在disk_initialize之前一定要初始化好外部的spiflash。

然后就是读/写了,先把代码贴上来:

/*-----------------------------------------------------------------------*/
/* 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 */
)
{
	while(count--){
		W25QXX_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
		sector++;
	}
	return RES_OK;
}



/*-----------------------------------------------------------------------*/
/* 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 */
)
{
	while(count--){
		W25QXX_Write((u8 *)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE);
		sector++;
	}
	return RES_OK;
}

#endif

这个地方也很简单,读写接口一共有四个参数,也有英文注释,第一个是操作的盘符(多个磁盘的时候作区分),第二个是读/写的内容缓存,第三个是操作的起始扇区,第四个是要操作的扇区数量。

这里解释下里面所用到的宏定义:


#define FLASH_SECTOR_SIZE (512)
#define FLASH_SECTOR_CNT	(1024*1024*4/512)
#define FLASH_BLOCK_SIZE (4096/512)

首先是这里的扇区大小,这里定义成512字节,但是使用的spiflash的扇区大小是4K也就是4096字节的,那么这个512是怎么来的呢

在ffconf里面有这样一个宏定义:

#define FF_MIN_SS		512
#define FF_MAX_SS		512
/* 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. */

这是没有做修改的,这里定义了操作扇区的大小是512,当然这个是可以改的,我这里为以后的sd卡做预留,因为sd卡的扇区大小是512,所以这里强制性的把spiflash的扇区大小也定义成512。使用是没有问题的,但是这样的弊端是会造成spiflash更多次的擦除,因为擦除的最小单位是真实的扇区,也就是4096字节。

然后就是块大小了,块大小定义成8(4096/512),真实的扇区大小4k,4k包含有8个512的fatfs扇区;

再有就是扇区数量,这里使用的是8M字节的flash,只把前面的4M做为fatfs访问,后面的预留出来作别的。4M也刚好与U盘工程的4M对应,其实fatfs和U盘是两个不同的东西,他们之间没有必然的联系。举个例子,比如有文件系统而没有U盘的驱动,那么这个时候用fatfs向盘里写个文件,这个文件因为单片机没有U盘的驱动,就不能在pc上直观的看到,但是文件还是存在的。

最有修改的控制函数如下:

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{	
	DRESULT res;
	switch(cmd){
	
		case CTRL_SYNC:
			res = RES_OK;
			break;
		case GET_SECTOR_COUNT:
			*(WORD *)buff = FLASH_SECTOR_CNT;
			res = RES_OK;
			break;
		case GET_SECTOR_SIZE:
			*(WORD *)buff = FLASH_SECTOR_SIZE;
			res = RES_OK;
			break;
		case GET_BLOCK_SIZE:
			*(WORD *)buff = FLASH_BLOCK_SIZE;
			res = RES_OK;
			break;
		case CTRL_TRIM:
			res = RES_OK;
			break;
		
		default: 
			res = RES_PARERR;
			break;			
	}
	return res;
}

这里几个在官方的文档中介绍的很详细

【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植_第4张图片

对应的命令有很多,在文档中有介绍只需要用标准的command就行了。别的对于本实验用不到。这个disk_ioctl函数里面就是告诉fatfs一些磁盘的信息。至此底层就改完了。

接下在主函数里面写测试函数

#include "fy_includes.h"

#include "ff.h"
FATFS fs;
DIR dir;
FIL file;
UINT br,bw;
FRESULT fres;


u8 filebuf[4096];


//USB唤醒中断服务函数
void USBWakeUp_IRQHandler(void) 
{
	EXTI_ClearITPendingBit(EXTI_Line18);//清除USB唤醒中断挂起位
} 
void USB_MSC_Configuration(void)
{
//  Set_System();
  Set_USBClock();
//  Led_Config();
  USB_Interrupts_Config();
  USB_Init();
//  while (bDeviceState != CONFIGURED);

//  USB_Configured_LED();

//  while (1)
//  {}
}

void USB_HP_CAN1_TX_IRQHandler(void)
{
  CTR_HP();
}


void USB_LP_CAN1_RX0_IRQHandler(void)
{
  USB_Istr();
}

int main(void)
{	
	setlocale(LC_ALL,"C");    								//设置本地区域
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); 		//中断分组配置

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);	 //开启AFIO时钟
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);	 //JTAG-DP Disabled and SW-DP Enabled //PA15 & PB3/4设置为普通I/O口
	SPI2_Configuration();
	W25QXX_Configuration();
	Oled_Configuration();	

	fres = f_mount(&fs,"0:",1);
	
	if(fres == FR_NO_FILESYSTEM)
	{
		fres = f_mkfs("0:",NULL,filebuf,4096);
	}
	fres = f_setlabel("0:FATFS TEST");
	
	fres = f_open(&file,"0:/massage.txt",FA_CREATE_NEW | FA_WRITE);
	
	fres = f_write(&file,"Hello world!\r\n\r\n",sizeof("Hello world!\r\n\r\n"),&bw);
	
	fres = f_close(&file);
	

	fres = f_open(&file,"0:/massage.txt", FA_READ );	
	
	fres = f_read(&file,filebuf,5,&br);
		
	fres = f_close(&file);
	
	fres = f_open(&file,"0:/copy.txt",FA_CREATE_NEW | FA_WRITE);
	
	fres = f_write(&file,filebuf,5,&bw);
	
	fres = f_write(&file,"   fatfs test on ff14\r\n By Urien 2019/10/27 !",sizeof("   fatfs test on ff14\r\n By Urien 2019/10/27 !"),&bw);

	fres = f_close(&file);
	
    Mass_Memory_Size[0]=1024*1024*4;	//w25q64->8M
    Mass_Block_Size[0] =512;			//设置SPI FLASH的操作扇区大小为512
    Mass_Block_Count[0]=Mass_Memory_Size[0]/Mass_Block_Size[0];

	USB_MSC_Configuration();
	
	Delay_ms(500);
	
	
	while(1)
	{
			
	}
}



/*********************************************END OF FILE********************************************/

这里面主要用到的函数在官方文档里面都有很详细的说明,这里就不多说了

【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植_第5张图片

测试代码的思路是这样的:

1.挂载flash为磁盘,盘符为0

2.检查挂载的返回结果,如果没有文件系统就格式化一下

3.设置盘符的名称为“FATFS TEST”

4.向根目录里面写一个massage.txt,内容为"Hello world!\r\n\r\n",如果这个文件不存在就创建一个新的 

以上是对文件的写测试

5.打开刚才创建的massage.txt并读取前面的5个字节数据,也就是Hello

6.写一个读出来的Hello到新的copy.txt,方法同理,并写入一些新的数据进去

以上是对文件读和写的测试

这样对文件的读写都测试了。

编译代码,发现有些函数提示没有定义,这个是因为在ffconf里面没有打开对应的宏


#define FF_USE_LABEL	1    //盘符相关
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/  (0:Disable or 1:Enable) */

#define FF_USE_MKFS		1    //格式化
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */

#define FF_FS_READONLY	0    //只读
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/  Read-only configuration removes writing API functions, f_write(), f_sync(),
/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/  and optional writing functions as well. */

再次编译发现没有获得时间戳函数,定位一下可以找到如下代码:

/* Timestamp */
#if FF_FS_NORTC == 1
#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
#error Invalid FF_FS_NORTC settings
#endif
#define GET_FATTIME()	((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
#else
#define GET_FATTIME()	get_fattime()
#endif

这里可以看到如果这个宏定义为1的话就是用fatfs提供的默认时间

#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2019
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/  any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/  the timestamp function. 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). */

默认是2019年1月1日,定义成0的话就提供获取时间戳的接口

#define GET_FATTIME()	get_fattime()

所以就需要自己实现以下这个函数,这里为了简单直接定义1.

测试结果如下:

【单片机笔记】基于STM32硬件平台的最新FATFS 最新版ff14版本的移植_第6张图片

与预期的效果一致。不同的地方是文件名程序里面的小写的,实际却是大写的。这个是因为在ffconf里面没有打开支持长文件名的定义

#define FF_USE_LFN		0

这个后面有机会再加上,因为要增加一些编码,用单片机内部flash就有点够呛。

至此结束!

By Urien 2019年10月27日 22:15:29

 

你可能感兴趣的:(单片机)