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文件系統的程序調用關係。
在這裏插入圖片描述
對於單個存儲器和多個存儲器的調用結構如下圖:
在這裏插入圖片描述
移植FatFs只需要編寫所用的磁盤I/O功能,並且可以裁剪功能,例如,只讀配置不需要任何寫入功能。下表顯示了FatFs功能與配置選項的對應關係。
在這裏插入圖片描述
在移植過程中主要編寫disk_status、disk_initialize、disk_read、disk_write、disk_ioctl 這幾個功能函數。

三、FATFS文件系統移植至STM32
1、移植準備
從官網下載源碼後解壓,documents文件夾存放FatFs的功能說明,source中存放源代碼
在這裏插入圖片描述
在這裏插入圖片描述
將源代碼添加到讀寫外部FLASH的Keil工程,如圖:
在這裏插入圖片描述
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() 進行格式化
在這裏插入圖片描述
對於已存在文件系統的FLASH不再格式化,直接進行文件讀寫操作
在這裏插入圖片描述
END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章