STM32F1移植FATFS文件系统

为了使单片机能在复位或掉电重启后还能保存之前的参数或数据,就需要用到一些非易失存储器,如ROM、FLASH等,本文利用STM32F103的SPI功能挂接外部FLASH的方法实现该功能。
选用的FLASH参数及连接引脚如下:

关于STM32读写外部FLASH的程序可参考野火的相关教程,这里不再赘述。

STM32利用外部FLASH直接存储数据存在许多缺点,如难以记录有效数据的位置,难以确定
存储介质的剩余空间,以及应以何种格式来解读数据等,所以寻求完善的文件系统来管理数据是必要的,这里采用FATFS文件系统,记录其移植到STM32的步骤。

一、FATFS简介
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。
    满足RTOS的线程安全。
    支持多个卷(物理驱动器和分区)。
    支持可变扇区大小。
    支持包括DBCS等的多个代码页。
    只读,可选API,I / O缓冲区等。

官网地址:http://elm-chan.org/fsw/ff/00index_e.html
目前最新版本:R0.14(2019年10月14日发布)

二、FATFS文件系统的程序结构
下图是具备FatFs模块的嵌入式系统的典型配置,但非特定配置,显示了FatFs文件系统的程序调用关系。
STM32F1移植FATFS文件系统_第1张图片
对于单个存储器和多个存储器的调用结构如下图:
STM32F1移植FATFS文件系统_第2张图片
移植FatFs只需要编写所用的磁盘I/O功能,并且可以裁剪功能,例如,只读配置不需要任何写入功能。下表显示了FatFs功能与配置选项的对应关系。
STM32F1移植FATFS文件系统_第3张图片
在移植过程中主要编写disk_status、disk_initialize、disk_read、disk_write、disk_ioctl 这几个功能函数。

三、FATFS文件系统移植至STM32
1、移植准备
从官网下载源码后解压,documents文件夹存放FatFs的功能说明,source中存放源代码
STM32F1移植FATFS文件系统_第4张图片
STM32F1移植FATFS文件系统_第5张图片
将源代码添加到读写外部FLASH的Keil工程,如图:
STM32F1移植FATFS文件系统_第6张图片
2、修改diskio.c中的磁盘I/O函数

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "./flash/bsp_spi_flash.h"//包含FLASH读写的文件

/* Definitions of physical drive number for each drive */
#define SPI_FLASH		0	/* 外部FLASH */


/*-----------------------------------------------------------------------*/
/* Get Drive Status  获取设备状态                                                    */
/*-----------------------------------------------------------------------*/

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

	switch (pdrv) 
	{
		case SPI_FLASH :
			//读取FLASH的ID
			if(sFLASH_ID == SPI_FLASH_ReadID())
			{
				//设备ID读取正确
				stat &= ~STA_NOINIT;
			}
			else
			{
				//设备ID读取错误
				stat = STA_NOINIT;
			}
			break;
			
		default:
			stat = STA_NOINIT;
	}
	
	return stat;
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive   初始化设备                                                 */
/*-----------------------------------------------------------------------*/

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

	switch (pdrv) 
	{
		case SPI_FLASH :
			//初始化FLASH
			SPI_FLASH_Init();
			//延时
			i = 500;
			while(--i);
			//唤醒FLASH
			SPI_Flash_WAKEUP();
			//获取FLASH状态
			stat = disk_status(SPI_FLASH);
			break;
		
		default:
			stat = STA_NOINIT;
	}
	
	return stat;
}



/*-----------------------------------------------------------------------*/
/* 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 */
)
{
	DRESULT res = RES_PARERR;

	switch (pdrv) 
	{
		case SPI_FLASH :
			SPI_FLASH_BufferRead(buff, sector << 12,count << 12);
			res = RES_OK;
			break;
		
		default:
			res = RES_PARERR;
	}

	return res;
}



/*-----------------------------------------------------------------------*/
/* 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 */
)
{
	DRESULT res = RES_PARERR;
	
	//如果写入的扇区个数为0,则报错
	if(!count)
	{
		return RES_PARERR;
	}

	switch (pdrv) 
	{
		case SPI_FLASH :
			SPI_FLASH_SectorErase(sector << 12);
			SPI_FLASH_BufferWrite((uint8_t *)buff, sector << 12, count << 12);
			res = RES_OK;
			break;
		
		default:
			res = RES_PARERR;
	}

	return res;
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Function  设备控制                                       */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res = RES_PARERR;

	switch (pdrv) 
	{
		case SPI_FLASH :
			switch(cmd)
			{
				//扇区数量
				case GET_SECTOR_COUNT:
					*(DWORD * )buff = 4096;
					break;
				//扇区大小
				case GET_SECTOR_SIZE:
					*(WORD * )buff = 4096;
					break;
				//同时擦除扇区个数
				case GET_BLOCK_SIZE:
					*(DWORD * )buff = 1;
					break;
			}
			res = RES_OK;
			break;
			
		default:
				res = RES_PARERR;
	}

	return res;
}


3、配置ffconf.h

//启用 f_mkfs() 函数,用以创建FAT/exFAT卷
#define FF_USE_MKFS		1
//支持简体中文文件名
#define FF_CODE_PAGE	936
//在STACK上启用具有动态工作缓冲区的长文件名
#define FF_USE_LFN		2
//仅有一个存储器
#define FF_VOLUMES		1
//指定扇区大小的最小值和最大值,
#define FF_MIN_SS		512
#define FF_MAX_SS		4096
//不使用RTC
#define FF_FS_NORTC		1

4、编写main.c进行测试

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./flash/bsp_spi_flash.h"
#include "ff.h"


FATFS fs;							/* FatFs文件系统对象 */
FIL fnew;							/* 文件对象 */
FRESULT res_flash;                	/* 文件操作结果 */
UINT fnum;            				/* 文件成功读写数量 */
BYTE ReadBuffer[1024]={0};        	/* 读缓冲区 */
BYTE WriteBuffer[] = "FatFs文件系统测试\r\n"; 	/* 写缓冲区*/
BYTE work[FF_MAX_SS];

int main(void)
{	
	/* 初始化调试串口,一般为串口 */
	USART_Config();	
  	printf("****** FatFs文件系统实验 ******\r\n");
  
	//在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
	//初始化函数调用流程如下
	//f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
	res_flash = f_mount(&fs,"0:",1);
	
/*----------------------- 格式化测试 -----------------*/  
	/* 如果没有文件系统就格式化创建创建文件系统 */
	if(res_flash == FR_NO_FILESYSTEM)
	{
		printf("》FLASH还没有文件系统,即将进行格式化...\r\n");
    	/* 格式化 */
		res_flash = f_mkfs("0:",0,work,sizeof work);			
		
		if(res_flash == FR_OK)
		{
			printf("》FLASH已成功格式化文件系统。\r\n");
      		/* 格式化后,先取消挂载 */
			res_flash = f_mount(NULL,"0:",1);			
      		/* 重新挂载	*/			
			res_flash = f_mount(&fs,"0:",1);
		}
		else
		{
			printf("《《格式化失败。(%d)》》\r\n",res_flash);
			while(1);
		}
	}
  else if(res_flash!=FR_OK)
  {
    printf("!!外部Flash挂载文件系统失败。(%d)\r\n",res_flash);
    printf("!!可能原因:SPI Flash初始化不成功。\r\n");
		while(1);
  }
  else
  {
    printf("》文件系统挂载成功,可以进行读写测试\r\n");
  }
  
/*----------------------- 文件系统测试:写测试 -------------------*/
	/* 打开文件,每次都以新建的形式打开,属性为可写 */
	printf("\r\n****** 即将进行文件写入测试... ******\r\n");	
	res_flash = f_open(&fnew, "0:FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
	if ( res_flash == FR_OK )
	{
		printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
    /* 将指定存储区内容写入到文件内 */
		res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
    if(res_flash==FR_OK)
    {
      printf("》文件写入成功,写入字节数据:%d\n",fnum);
      printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
    }
    else
    {
      printf("!!文件写入失败:(%d)\n",res_flash);
    }    
		/* 不再读写,关闭文件 */
    f_close(&fnew);
	}
	else
	{	
		printf("!!打开/创建文件失败。\r\n");
	}
	
/*------------------- 文件系统测试:读测试 --------------------------*/
	printf("****** 即将进行文件读取测试... ******\r\n");
	res_flash = f_open(&fnew, "0:FatFs读写测试文件.txt",FA_OPEN_EXISTING | FA_READ); 	 
	if(res_flash == FR_OK)
	{
		printf("》打开文件成功。\r\n");
		res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum); 
    if(res_flash==FR_OK)
    {
      printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
      printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);	
    }
    else
    {
      printf("!!文件读取失败:(%d)\n",res_flash);
    }		
	}
	else
	{
		printf("!!打开文件失败。\r\n");
	}
	/* 不再读写,关闭文件 */
	f_close(&fnew);	
  
	/* 不再使用文件系统,取消挂载文件系统 */
	f_mount(NULL,"0:",1);
  
  	/* 操作完成,停机 */
	while(1)
	{
	}
}

5、测试结果
对于一块空的FLASH,程序调用 f_mkfs() 进行格式化
STM32F1移植FATFS文件系统_第7张图片
对于已存在文件系统的FLASH不再格式化,直接进行文件读写操作
STM32F1移植FATFS文件系统_第8张图片
END

你可能感兴趣的:(STM32)