day23:FatFs文件系統移植到STM32(案例:以FATFS文件系統的方式對SPI_FLASH進行讀寫操作)


在day22章節的基礎上添加FatFs模塊:https://www.cnblogs.com/josephcnblog/articles/9249787.html

在本章的末尾會附上所有的代碼

運行原理:方便代碼移植,調用底層接口函數:f_mount(),此函數在ff.c文件中

 

工程結構:

 

1、去FatFs文件系統官網下載文件系統庫函數源碼:http://elm-chan.org/fsw/ff/00index_e.html

2、解壓后

3、新建工程,在User目錄下創建文件夾,命名為fatfs,拷貝2中的所有文件和目錄到fatfs目錄下

4、批量導入文件。回到工程,點下面的圖標,創建新組FatFs,並添加文件

創建了一個新組:

5、添加頭文件路徑,點下面的圖標,添加fatfs目錄

6、編譯本文件,出錯:找不到文件,把下面三個.h文件注釋掉。

再編譯,把warning的都注釋掉:

再編譯,還是有warning,不管。編譯所有文件,出錯:diskio.c文件中沒有定義get_fattime()函數

 

從FatFs文件系統官網知道,需要包含如下函數接口:

查看diskio.c文件沒有get_fattime函數,所以需要添加該函數,打開diskio.h文件:只有5個函數接口,並沒有get_fattime

添加:(點擊官網的get_fattime鏈接進入函數),添加進來

在diskio.c中添加:

// 返回時間
DWORD get_fattime (void)
{
	return 0;
}

 再編譯,把warning的全部注釋掉,再編譯就不會報錯和報警了。

 7、在diskio.c中修改下面代碼

改成:

並把報錯的地方都改過來

(1)獲取設備狀態:修改diskio.c

/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	uint32_t FlashID = 0;
	DSTATUS stat = 0;
//	int result;

	switch (pdrv) {
	case SD_CARD :
//		result = ATA_disk_status();

		// translate the reslut code here

		return stat;

	case SPI_FLASH :		
//		result = MMC_disk_status();

		/* ======================== 添加的代碼 ======================== */
		/* 獲取SPI_FLASH設備的狀態 */
		FlashID = SPI_FLASH_Read_Flash_ID();
	
		/* 判斷設備是否初始化 */
		if(FlashID == sFLASH_ID)
		{
			stat = 0;			// 成功
		}	
		else				
		{
			// 失敗,“或上”是因為失敗的原因有很多種,可以同時表達很多狀態
			stat |= STA_NOINIT;	
		}
		/* ======================== 添加的代碼 ======================== */

		return stat;	
		
	case USB :
//		result = USB_disk_status();

		// translate the reslut code here

		return stat;
	}
	return STA_NOINIT;
}

(2)初始化設備

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

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

	switch (pdrv) {
	case SD_CARD :
//		result = ATA_disk_initialize();

		// translate the reslut code here

		return stat;

	case SPI_FLASH :
		/* ======================== 添加的代碼 ======================== */
		/* SPI_FLASH設備初始化 */
		SPI_FLASH_Init();
	
		// SPI_FLASH:物理設備號,還有SD_CARD、USB兩種
		return disk_status(SPI_FLASH);
		/* ======================== 添加的代碼 ======================== */

	case USB :
//		result = USB_disk_initialize();

		// translate the reslut code here

		return stat;
	}
	return STA_NOINIT;
}

(3)讀取SPI_FLASH的數據

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address in LBA */
	UINT count		/* Number of sectors to read */
)
{
	DRESULT res;
//	int result;

	switch (pdrv) {
	case SD_CARD :
		// translate the arguments here

//		result = ATA_disk_read(buff, sector, count);

		// translate the reslut code here

		return res;

	case SPI_FLASH :
		/* ======================== 添加的代碼 ======================== */
		/*
		buff:讀取SPI_FLASH數據,buff是將讀取的數據放到buff指針變量中
		sector:扇區,一個扇區有4096個字節,比如要讀第0扇區,那么讀數據的開始地址是:0*4096
				如果要讀第1扇區,那么讀數據的開始地址是:1*4096
		count:是要讀取的數據長度,也要乘以4096,表示讀的是多少個字節
		*/
		SPI_FLASH_BufferRead(buff, sector*4096, count*4096);
		
		// 讀取完就返回OK
		return RES_OK;
		/* ======================== 添加的代碼 ======================== */

	case USB :
		// translate the arguments here

//		result = USB_disk_read(buff, sector, count);

		// translate the reslut code here

		return res;
	}

	return RES_PARERR;
}

(4)SPI_FLASH寫入數據

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if _USE_WRITE
DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector address in LBA */
	UINT count			/* Number of sectors to write */
)
{
	DRESULT res;
//	int result;

	switch (pdrv) {
	case SD_CARD :
		// translate the arguments here

//		result = ATA_disk_write(buff, sector, count);

		// translate the reslut code here

		return res;

	case SPI_FLASH :
		/* ======================== 添加的代碼 ======================== */
		// 寫之前先擦除
		SPI_FLASH_Erase_Sector(sector*4096);
		
		// 寫入數據
		SPI_FLASH_BufferWrite((u8*)buff, sector*4096, count*4096);

		// 如果要寫的嚴謹,這里要判斷成功或失敗,返回不同的狀態碼,這里省事就不深究了
		return RES_OK;
		/* ======================== 添加的代碼 ======================== */

	case USB :
		// translate the arguments here

//		result = USB_disk_write(buff, sector, count);

		// translate the reslut code here

		return res;
	}

	return RES_PARERR;
}
#endif

(5)disk_ioctl函數

5.1 打開ff.c下的ffconf.h,修改:這是支持格式化操作的命令

5.2扇區大小,在下面可以修改扇區的大小,這里把最大的扇區改成4096,最小的保持不變

改成:

(6)disk_ioctl函數:ioctl是設備驅動程序中對設備的I/O通道進行管理的函數

 此函數用於操作底層的屬性,比如扇區數目,扇區大小,塊大小等等的屬性設置。

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions    ioctl是設備驅動程序中對設備的I/O通道進行管理的函數                                           */
/*-----------------------------------------------------------------------*/

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

	switch (pdrv) {
	case SD_CARD :

		// Process of the command for the ATA drive

		return res;

	case SPI_FLASH :	
		/* ======================== 添加的代碼 ======================== */
		switch(cmd)
		{
			case GET_SECTOR_COUNT:		// 多少個扇區
				// buff是void*類型指針(空指針),根據官網的disk_ioctl函數參數轉換不同返回指針
				*(DWORD*)buff = 2048;	// SPI_FLASH有8M字節,每個扇區有4096個字節,即有2048個扇區
				break;	
			
			case GET_SECTOR_SIZE:		// 每個扇區大小
				*(WORD*)buff = 4096;	// 每個扇區有4096個字節
				break;
			
			case GET_BLOCK_SIZE:		// 塊大小
				*(DWORD*)buff = 1;		// 設置為1,表示每次擦除1個扇區
				break;
		}
		res = RES_OK;
		return res;
		/* ======================== 添加的代碼 ======================== */

	case USB :

		// Process of the command the USB drive

		return res;
	}

	return RES_PARERR;
}
#endif

編譯,沒有錯誤,沒有警告,接下來就是寫文件系統的移植測試程序了,主要是往SPI_FLASH中寫入數據,讀取數據等操作。

 

============================== FatFs文件系統移植測試

main.c

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include "ff.h"		// 添加文件系統的庫文件


FATFS fs;
FRESULT res;

int main(void)
{	
	/* ============================ 初始化 =========================== */
	// 串口初始化
	DEBUG_USART_Config();
	
	printf("\r\n 這是文件系統移植實驗 \r\n");
	
	/*
		移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口
		fs:	文件系統指針
		path:	SD_CARD(0)/SPI_FLASH(1)/USB(2)
		opt:	0:Do not mount (delayed mount), 1:Mount immediately
	*/
	res = f_mount(&fs, "1:", 1);
	if(res != FR_OK)
	{
		printf("\r\n mount res = %d \r\n", res);
	}
	
	while(1)
	{
	
	}
}

燒錄進去,測試結果:按復位鍵RESET

輸出11,查看源碼知道,說的是無效的設備,這是因為文件系統支持的設備數量不夠。

 

查看ffconf.h文件:這里的#define _VOLUMES  1  ,表示文件系統支持的設備數量,

因為我們在diskio.c中設置了如下的設備:0表示SD卡,1表示SPI_FLASH,2表示USB,總共支持3中類型的設備,但是SD卡排在SPI_FLASH的前面,支持的數量又只有1個,所以

就默認是SD卡了

需要把支持的設備數量改成大於2才會讀取到SPI_FLASH,所以在ffconf.h中改成:

保存,再編譯,燒錄到板子,測試結果:

不再輸出11,說明上面的問題解決了。現在輸出了13,查看源碼:說的是SPI_FLASH這個設備沒有掛在文件系統

解決方法:需要先對文件系統進行格式化操作,才能實現文件系統掛在到SPI_FLASH

查看FatFs文件系統源碼,使用函數f_mkfs進行格式化。

main.c

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include "ff.h"		// 添加文件系統的庫文件


FATFS fs;
FRESULT res;

int main(void)
{	
	/* ============================ 初始化 =========================== */
	// 串口初始化
	DEBUG_USART_Config();
	
	printf("\r\n 這是文件系統移植實驗 \r\n");
	
	/*
		移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口
		fs:	文件系統指針
		path:	SD_CARD(0)/SPI_FLASH(1)/USB(2)
		opt:	0:Do not mount (delayed mount), 1:Mount immediately
	*/
	res = f_mount(&fs, "1:", 1);
	if(res == FR_NO_FILESYSTEM)		// SPI_FLASH設備沒有掛在文件系統
	{
		// 文件系統格式化
		res = f_mkfs("1:", 0, 0);
		
		// 再次判斷
		if(res != FR_OK)
		{
			printf("\r\n f_mkfs res = %d \r\n", res);
		}
	}
	
	while(1)
	{
	
	}
}

實驗結果:

說明格式化成功。

下面是使用文件系統函數對文件的操作

 main.c

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include "ff.h"		// 添加文件系統的庫文件


FATFS 		fs;
FRESULT 	res;
FIL			fp;
char		write_buf[] = "這是文件系統測試寫入的數據";
UINT		bw;


int main(void)
{	
	/* ============================ 初始化 =========================== */
	// 串口初始化
	DEBUG_USART_Config();
	
	printf("\r\n 這是文件系統移植實驗 \r\n");
	
	/*
		移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口
		fs:	文件系統指針
		path:	SD_CARD(0)/SPI_FLASH(1)/USB(2)
		opt:	0:Do not mount (delayed mount), 1:Mount immediately
	*/
	res = f_mount(&fs, "1:", 1);
	if(res == FR_NO_FILESYSTEM)		// SPI_FLASH設備沒有掛在文件系統
	{
		printf("\r\n before f_mkfs:res = %d\r\n", res);
		
		// 文件系統格式化
		res = f_mkfs("1:", 0, 0);
		
		// 再次判斷
		if(res != FR_OK)
		{
			printf("\r\n f_mkfs res = %d \r\n", res);
		}
	}
	else if(res == FR_OK)
	{
		// 文件系統移植成功,下面是對文件的操作:創建,寫入,關閉,,,
		printf("\r\n f_mount	res = %d\r\n", res);
		
		/* f_open函數:創建文件
		fp:文件指針
		1:test.txt:文件名
		第三個參數是文件操作的權限,讀/寫/不管文件是否存在都創建(存在就覆蓋)
		*/
		res = f_open(&fp, "1:test.txt", FA_READ|FA_WRITE|FA_CREATE_ALWAYS);
		printf("\r\n f_open		res = %d\r\n", res);
		
		/* f_write函數:向文件中寫入數據
		fp:文件指針
		write_buf:要寫入文件的數據
		第三個參數:寫入多少個字節
		第四個參數:指針,返回已經寫入到文件的字節數
		*/
		res = f_write(&fp, write_buf, sizeof(write_buf), &bw);
		printf("\r\n f_write	res = %d\r\n", res);
		
		/* f_close:關閉文件,使用f_open必須使用f_close,不然會有緩存
		fp:文件指針
		*/
		res = f_close(&fp);
		printf("\r\n f_close	res = %d\r\n", res);
	}
	
	while(1)
	{
	
	}
}

實驗結果:res全部返回0,說明f_mount,f_open,f_write,f_close這四個函數都執行成功了,現在SPI_FLASH設備掛載的FatFs文件系統

已經起作用了,而且在SPI_FLASH設備上的第0扇區中創建了test.txt文件,並寫入了數據。

讀文件數據

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include "ff.h"		// 添加文件系統的庫文件


FATFS 		fs;
FRESULT 	res;
FIL			fp;
char		write_buf[] = "這是文件系統測試寫入的數據";
UINT		bw;
char		read_buf[1024] = {0};
UINT		br;
char		filename[] = "1:test.txt";

int main(void)
{	
	/* ============================ 初始化 =========================== */
	// 串口初始化
	DEBUG_USART_Config();
	
	printf("\r\n 這是文件系統移植實驗 \r\n");
	
	/*
		移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口
		fs:	文件系統指針
		path:	SD_CARD(0)/SPI_FLASH(1)/USB(2)
		opt:	0:Do not mount (delayed mount), 1:Mount immediately
	*/
	res = f_mount(&fs, "1:", 1);
	if(res == FR_NO_FILESYSTEM)		// SPI_FLASH設備沒有掛在文件系統
	{
		printf("\r\n before f_mkfs:res = %d\r\n", res);
		
		// 文件系統格式化
		res = f_mkfs("1:", 0, 0);
		
		// 再次判斷
		if(res != FR_OK)
		{
			printf("\r\n f_mkfs res = %d \r\n", res);
		}
	}
	else if(res == FR_OK)
	{
		// 文件系統移植成功,下面是對文件的操作:創建,寫入,關閉,,,
		printf("\r\n f_mount	res = %d\r\n", res);
		
		/* f_open函數:創建文件
		fp:文件指針
		1:test.txt:文件名
		第三個參數是文件操作的權限,讀/寫/不管文件是否存在都創建(存在就覆蓋)
		*/
		res = f_open(&fp, filename, FA_READ|FA_WRITE|FA_CREATE_ALWAYS);
		printf("\r\n f_open		res = %d\r\n", res);
		
		/* f_write函數:向文件中寫入數據
		fp:文件指針
		write_buf:要寫入文件的數據
		第三個參數:寫入多少個字節
		第四個參數:指針,返回已經寫入到文件的字節數
		*/
		res = f_write(&fp, write_buf, sizeof(write_buf), &bw);
		printf("\r\n f_write	res = %d\r\n", res);
		
		/* f_close:關閉文件,使用f_open必須使用f_close,不然會有緩存
		fp:文件指針
		*/
		res = f_close(&fp);
		printf("\r\n f_close	res = %d\r\n", res);
		
		/* f_read函數:讀文件數據,讀剛剛寫入的數據
		fp:文件指針
		read_buf:將讀取到的數據存儲到這個數組中
		1024:打算讀取多少個字節數據
		br:實際讀取回來的數據字節數
		*/
		// 讀之前需要打開文件:只讀權限(文件存在就讀取)
		f_open(&fp, filename, FA_READ|FA_OPEN_EXISTING);
		// 開始讀取文件數據
		res = f_read(&fp, read_buf, 1024, &br);
		printf("\r\n f_read		res = %d,br = %d,sizeof write_buf = %d,", res, br, sizeof(write_buf));
		printf("read_buf = %s\r\n", read_buf);
		// 讀取完文件后要關閉文件
		res = f_close(&fp);
		printf("\r\n f_close	res = %d\r\n", res);
	}
	
	while(1)
	{
	
	}
}

實驗結果:f_read函數執行成功,成功將上面寫入到SPI_FLASH第0扇區寫入的文件數據讀取回來

如果將文件名設置的很長:

char		filename[] = "1:testhhhhhhhhhhhhhhhhhh.txt";

實驗結果:在f_open的時候就出錯,出錯代碼:6,查看源碼:FR_INVALID_NAME  /* (6) The path name format is invalid */,說是無效的文件名

解決方法:在ffconf.h中修改,改成1就支持長文件名

編譯報錯:

在FatFs目錄先添加文件:User\fatfs\option\ccsbcs.c

打開ffconf.h文件,並修改:表示支持英文形式的長文件名

 

再編譯,就不會報錯了。再測試:可以正常讀取了

 

============================== 程序清單 =============================================

工程結構:

bsp_usart.h

#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "stm32f10x.h"
#include "stdio.h"

// ----------------------- 串口1-USART1
// 使用哪個串口(串口1..5)
#define  DEBUG_USARTx                   USART1					
// APB2串口的同步時鍾
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1	
// APB2系統時鍾(因為串口USART1是掛載到APB2總線上的,所以要打開APB2總線的時鍾)
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd	
// 串口通信的波特率
#define  DEBUG_USART_BAUDRATE           19200


// ----------------------- USART GPIO 引腳宏定義
// GPIO引腳
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)	
// APB2系統時鍾(因為串口USART1是掛載到APB2總線上的,所以要打開APB2總線的時鍾)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd	

// GPIO引腳,發送接PA9,接收接PA10   
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   		
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

/* 串口調試配置函數:配置串口的相關參數,使能串口 */
void DEBUG_USART_Config(void);

/* 發送一個字節 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch);

/* 發送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str);

#endif		/* __BSP_USART_H__ */

 bsp_usart.c

 

#include "./usart/bsp_usart.h"

/* 串口中斷配置函數 */
//static void NVIC_Configuration(void)
//{
//	NVIC_InitTypeDef NVIC_InitStructure;
//  
//	/* 嵌套向量中斷控制器組選擇 */
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//  
//	/* 配置USART為中斷源 */
//	NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
//	/* 搶斷優先級*/
//	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//	/* 子優先級 */
//	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//	/* 使能中斷 */
//	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//	
//	/* 初始化配置NVIC */
//	NVIC_Init(&NVIC_InitStructure);
//}

/* 串口調試配置函數:配置串口的相關參數,使能串口 */
void DEBUG_USART_Config(void)
{
	/* 結構體變量聲明 */
	GPIO_InitTypeDef GPIO_InitStructure;		// GPIO
	USART_InitTypeDef USART_InitStructure;		// USART
	
	/* ------------ 第一步:初始化GPIO */
	// 打開串口GPIO的時鍾
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);	
	
	// 將USART Tx的GPIO配置為推挽復用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;		// 引腳
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				// 模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			// 速率
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);	// 初始化結構體
	
	// 將USART Rx的GPIO配置為浮空輸入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	/* ------------ 第二步:配置串口的初始化結構體 */
	// 打開串口外設的時鍾
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
	/* 配置串口的工作參數 */
	// 波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 針數據字長
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 校驗位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 工作模式,收發一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	/* -------------------------------------------------------- */
	// 串口中斷優先級配置
	//NVIC_Configuration();	
	
	// 使能串口接收中斷
	//USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	/* -------------------------------------------------------- */
	
	/* ------------ 第三步:使能串口 */
	USART_Cmd(DEBUG_USARTx, ENABLE);
}

/* 發送一個字節 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch)
{
	/* 發送一個字節數據到USART */
	USART_SendData(pUSARTx, ch);
		
	/* 等待發送數據寄存器為空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/* 發送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str)
{
	unsigned int k=0;
	do 
	{
		Usart_SendByte(pUSARTx, *(str + k));
		k++;
	} while(*(str + k)!='\0');
  
	/* 等待發送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

/* 重定向c庫函數printf到串口,重定向后可使用printf函數 */
int fputc(int ch, FILE *f)
{
	/* 發送一個字節數據到串口 */
	USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
	/* 等待發送完畢 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
	return (ch);
}

/* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */
int fgetc(FILE *f)
{
	/* 等待串口輸入數據 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

	return (int)USART_ReceiveData(DEBUG_USARTx);
}

 bsp_spi_flash.h

 

#ifndef __BSP_SPI_FLASH_H__
#define __BSP_SPI_FLASH_H__

#include "stm32f10x.h"
#include "stdio.h"

/* =================== GPIO引腳定義 ==================== */
#define	SPI_FLASH_GPIO_CLK           	RCC_APB2Periph_GPIOA	
#define	SPI_FLASH_GPIO_APBxClkCmd		RCC_APB2PeriphClockCmd

/* =================== SPI外設接口定義 ==================== */
#define	SPI_FLASHx						SPI1
#define	SPI_FLASH_CLK                	RCC_APB2Periph_SPI1
#define	SPI_FLASH_APBxClkCmd         	RCC_APB2PeriphClockCmd						

/* =================== SPI-FLASH引腳定義 ==================== */
// CS(NSS)引腳
#define	SPI_FLASH_NSS_GPIO_PORT			GPIOA
#define SPI_FLASH_NSS_GPIO_PIN			GPIO_Pin_4

// SCK引腳
#define	SPI_FLASH_SCK_GPIO_PORT			GPIOA
#define SPI_FLASH_SCK_GPIO_PIN			GPIO_Pin_5

// MISO引腳
#define	SPI_FLASH_MISO_GPIO_PORT		GPIOA
#define SPI_FLASH_MISO_GPIO_PIN			GPIO_Pin_6

// MOSI引腳
#define	SPI_FLASH_MOSI_GPIO_PORT		GPIOA
#define SPI_FLASH_MOSI_GPIO_PIN			GPIO_Pin_7

/* =================== 常量定義 ==================== */
#define SPI_FLASH_WAIT_TIMEOUT			10000		// SPI-FLASH超時時間

#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256

/* =================== FLASH ID ==================== */
//#define  sFLASH_ID              		0xef3015   		//W25X16
//#define  sFLASH_ID              		0xef4015	 	//W25Q16
//#define  sFLASH_ID              		0Xef4018   		//W25Q128
#define  sFLASH_ID              		0Xef4017    	//W25Q64

/***************** 指令定義-開頭 *****************/
#define W25X_WriteEnable		      	0x06 
#define W25X_WriteDisable		      	0x04 
#define W25X_ReadStatusReg		    	0x05 
#define W25X_WriteStatusReg		    	0x01 
#define W25X_ReadData			        0x03 
#define W25X_FastReadData		      	0x0B 
#define W25X_FastReadDual		      	0x3B 
#define W25X_PageProgram		      	0x02 
#define W25X_BlockErase			      	0xD8 
#define W25X_SectorErase		      	0x20 
#define W25X_ChipErase			      	0xC7 
#define W25X_PowerDown			      	0xB9 
#define W25X_ReleasePowerDown	    	0xAB 
#define W25X_DeviceID			        0xAB 
#define W25X_ManufactDeviceID   		0x90 
#define W25X_JedecDeviceID		    	0x9F

/* =================== WIP(busy)標志,FLASH內部正在寫入 ==================== */
#define WIP_Flag                  		0x01
#define Dummy_Byte                		0xFF

/* =================== 函數宏定義 ==================== */
#define SPI_FLASH_NSS_LOW()				GPIO_ResetBits(SPI_FLASH_NSS_GPIO_PORT,SPI_FLASH_NSS_GPIO_PIN);
#define SPI_FLASH_NSS_HIGH()			GPIO_SetBits(SPI_FLASH_NSS_GPIO_PORT,SPI_FLASH_NSS_GPIO_PIN);


/*信息輸出*/
#define FLASH_DEBUG_ON         1

#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\
											if(FLASH_DEBUG_ON)\
											printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                         }while(0)


/* =================== SPI-FLASH相關函數 ==================== */
/* SPI-FLASH初始化 */
void SPI_FLASH_Init(void);

/* 發送一個字節 */
uint8_t SPI_FLASH_Send_Data(uint8_t data);

/* 接收一個字節 */
uint8_t SPI_FLASH_Receive_Data(void);

/* 讀取FLASH_ID */
uint32_t SPI_FLASH_Read_Flash_ID(void);

/* 讀取Device ID */
uint32_t SPI_FLASH_Read_Device_ID(void);

/* 擦除一個扇區 */
void SPI_FLASH_Erase_Sector(uint32_t addr);

/* 寫使能 */
void SPI_FLASH_Write_Enable(void);

 /**
 * @brief	讀取FLASH數據
  * @param	pBuffer,存儲讀出數據的指針
  * @param	ReadAddr,讀取地址
  * @param	NumByteToRead,讀取數據長度
  * @retval	無
  */
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead);

/* 向SPI扇區寫入數據(一次最多寫入256個字節) */
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite);

 /**
 * @brief  對FLASH寫入數據,調用本函數寫入數據前需要先擦除扇區
  * @param	pBuffer,要寫入數據的指針
  * @param  WriteAddr,寫入地址
  * @param  NumByteToWrite,寫入數據長度
  * @retval 無
  */
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite);


#endif		/* __BSP_SPI_FLASH_H__ */

bsp_spi_flash.c

#include "./spi/bsp_spi_flash.h"

uint16_t time_out;

/* SPI-FLASH初始化 */
void SPI_FLASH_Init(void)
{
	// 結構體變量聲明
	GPIO_InitTypeDef GPIO_InitStructure;		// GPIO
	SPI_InitTypeDef SPI_InitStructure;			// SPI
	
	/* =========================== 第一步:打開時鍾 =========================== */
	// 打開SPI GPIO的時鍾
	SPI_FLASH_GPIO_APBxClkCmd(SPI_FLASH_GPIO_CLK, ENABLE);	
	
	// 打開SPI外設的時鍾
	SPI_FLASH_APBxClkCmd(SPI_FLASH_CLK, ENABLE);
	
	/* =========================== 第二步:配置引腳 =========================== */
	// 配置CS(NSS)引腳
	GPIO_InitStructure.GPIO_Pin = SPI_FLASH_NSS_GPIO_PIN;		
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;				
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			
	GPIO_Init(SPI_FLASH_NSS_GPIO_PORT, &GPIO_InitStructure);	
	
	// 配置SCK引腳
	GPIO_InitStructure.GPIO_Pin = SPI_FLASH_SCK_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(SPI_FLASH_SCK_GPIO_PORT, &GPIO_InitStructure);
	
	// MISO引腳
	GPIO_InitStructure.GPIO_Pin = SPI_FLASH_MISO_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(SPI_FLASH_MISO_GPIO_PORT, &GPIO_InitStructure);
	
	// MOSI引腳
	GPIO_InitStructure.GPIO_Pin = SPI_FLASH_MOSI_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(SPI_FLASH_MOSI_GPIO_PORT, &GPIO_InitStructure);
	
	/* =========================== 第三步:配置SPI工作模式 =========================== */
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;		// 二分頻
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;		// 第一邊沿采樣
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;			// CPOL和CPHA都為0,即模式0
	SPI_InitStructure.SPI_CRCPolynomial = 0;			// 這個參數不要求,但也要配置,否則報錯
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	// 8個數據位
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;		// 雙線全雙工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	// 高位先行
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		// STM32配置成主機,FLASH等其他外設為從機
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;			// 軟件控制
	// SPI初始化
	SPI_Init(SPI_FLASHx, &SPI_InitStructure);
	
	/* =========================== 第四步:使能SPI =========================== */
	SPI_Cmd(SPI_FLASHx, ENABLE);
}

/* 出錯時調用回調函數返回錯誤代碼(錯誤信息) */
uint8_t SPI_Timeout_CallBack(uint8_t data)
{
	printf("\r\n SPI檢測超時,錯誤代碼:%d \r\n", data);
	return 0;
}

/* 
功能:發送一個字節 
data:要發送的數據
返回:發送過程中接收回來的數據
*/
uint8_t SPI_FLASH_Send_Data(uint8_t data)
{
	uint8_t read_temp;
	
	// 檢測TXE
	time_out = SPI_FLASH_WAIT_TIMEOUT;
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)	// 發送緩沖區為空,可以向里面發送數據
	{
		if(time_out-- == 0)		// 超時
		{
			return SPI_Timeout_CallBack(1);
		}
	}
	SPI_I2S_SendData(SPI1, data);	// 發送數據
	
	// 檢測RXNE
	time_out = SPI_FLASH_WAIT_TIMEOUT;
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)	// 接收緩沖區為空,可以讀取里面的數據
	{
		if(time_out-- == 0)		// 超時
		{
			return SPI_Timeout_CallBack(2);
		}
	}
	read_temp = (uint8_t)SPI_I2S_ReceiveData(SPI1);			// 接收數據
	
	return read_temp;
}

/* 
功能:接收一個字節 
返回:接收到的數據
說明:根據時序圖寫
*/
uint8_t SPI_FLASH_Receive_Data(void)
{
	return SPI_FLASH_Send_Data(Dummy_Byte);
}

/* 讀取JEDEC_ID */
uint32_t SPI_FLASH_Read_Flash_ID(void)
{
	uint32_t id;
	
	// 拉低NSS,開始讀取數據
	SPI_FLASH_NSS_LOW();
	
	// 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸
	SPI_FLASH_Send_Data(W25X_JedecDeviceID);
	
	// 讀取數據
	id = SPI_FLASH_Receive_Data();
	
	// id左移八位,騰出低八位繼續接收數據
	id <<= 8;
	// 繼續接收數據
	id |= SPI_FLASH_Receive_Data();
	
	// id左移八位,騰出低八位繼續接收數據
	id <<= 8;
	// 繼續接收數據
	id |= SPI_FLASH_Receive_Data();
	
	// 拉高NSS,結束讀取數據
	SPI_FLASH_NSS_HIGH();
	
	// 返回讀取的id
	return id;
}

/* 讀取Device_ID */
uint32_t SPI_FLASH_Read_Device_ID(void)
{
  u32 Temp = 0;

  /* Select the FLASH: Chip Select low */
  SPI_FLASH_NSS_LOW();

  /* Send "RDID " instruction */
  SPI_FLASH_Send_Data(W25X_DeviceID);
  SPI_FLASH_Send_Data(Dummy_Byte);
  SPI_FLASH_Send_Data(Dummy_Byte);
  SPI_FLASH_Send_Data(Dummy_Byte);
  
  /* Read a byte from the FLASH */
  Temp = SPI_FLASH_Send_Data(Dummy_Byte);

  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_NSS_HIGH();

  return Temp;
}

/* 寫使能 */
void SPI_FLASH_Write_Enable(void)
{
	// 拉低NSS,開始讀取數據
	SPI_FLASH_NSS_LOW();
	
	// 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸
	SPI_FLASH_Send_Data(W25X_WriteEnable);		// 發送06h指令進行使能操作
	
	// 拉高NSS,結束讀取數據
	SPI_FLASH_NSS_HIGH();
}

/* 功能:讀取狀態寄存器,用於檢測忙碌還是空閑 */
void SPI_FLASH_WaitForWriteEnd(void)
{
	uint8_t status;
	
	// 拉低NSS,開始讀取數據
	SPI_FLASH_NSS_LOW();
	
	// 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸
	SPI_FLASH_Send_Data(W25X_ReadStatusReg);		// 發送05h指令進行讀取狀態操作
	
	// 讀取狀態
	do
	{
		status = SPI_FLASH_Receive_Data();
	} while(status & WIP_Flag);		// BUSY標志S7-S0,如果S0這個位是1表示忙碌,0表示空閑,所以只要檢測這一位即可		
	
	// 拉高NSS,結束讀取數據
	SPI_FLASH_NSS_HIGH();
}

/* 
功能:擦除一個扇區 
addr:扇區地址
說明:根據SPI說明文檔,在擦除或寫入之前,要先使能寫命令
*/
void SPI_FLASH_Erase_Sector(uint32_t addr)
{
	// 等待其他操作完成之后再進行擦除操作
	SPI_FLASH_WaitForWriteEnd();
	
	// 在擦除之前進行使能操作
	SPI_FLASH_Write_Enable();
	
	/* 發送要擦出的二十四位地址碼 */
	// 拉低NSS,開始讀取數據
	SPI_FLASH_NSS_LOW();
	
	// 發送一個數據,觸發SCK時鍾,這個數據不作為有效數據傳輸
	SPI_FLASH_Send_Data(W25X_SectorErase);		// 發送20h指令進行擦除操作
	
	// 開始發送地址:一次發送八位,移位操作
	SPI_FLASH_Send_Data((addr & 0xFF0000) >> 16);
	SPI_FLASH_Send_Data((addr & 0xFF00) >> 8);
	SPI_FLASH_Send_Data((addr & 0xFF));
	
	// 拉高NSS,結束讀取數據
	SPI_FLASH_NSS_HIGH();
	
	// 檢測是否已經擦除完
	SPI_FLASH_WaitForWriteEnd();
}

 /**
  * @brief	讀取FLASH數據
  * @param	pBuffer,存儲讀出數據的指針
  * @param	ReadAddr,讀取地址
  * @param	NumByteToRead,讀取數據長度
  * @retval	無
  */
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
  /* 選擇FLASH: CS低電平 */
  SPI_FLASH_NSS_LOW();

  /* 發送 讀 指令 */
  SPI_FLASH_Send_Data(W25X_ReadData);

  /* 發送 讀 地址高位 */
  SPI_FLASH_Send_Data((ReadAddr & 0xFF0000) >> 16);
  /* 發送 讀 地址中位 */
  SPI_FLASH_Send_Data((ReadAddr& 0xFF00) >> 8);
  /* 發送 讀 地址低位 */
  SPI_FLASH_Send_Data(ReadAddr & 0xFF);
	
	/* 讀取數據 */
  while (NumByteToRead--) /* while there is data to be read */
  {
    /* 讀取一個字節*/
    *pBuffer = SPI_FLASH_Send_Data(Dummy_Byte);
    /* 指向下一個字節緩沖區 */
    pBuffer++;
  }

  /* 停止信號 FLASH: CS 高電平 */
  SPI_FLASH_NSS_HIGH();
}

/**
  * @brief  對FLASH寫入數據,調用本函數寫入數據前需要先擦除扇區
  * @param	pBuffer,要寫入數據的指針
  * @param  WriteAddr,寫入地址
  * @param  NumByteToWrite,寫入數據長度
  * @retval 無
  */
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
	
	/*mod運算求余,若writeAddr是SPI_FLASH_PageSize整數倍,運算結果Addr值為0*/
  Addr = WriteAddr % SPI_FLASH_PageSize;
	
	/*差count個數據值,剛好可以對齊到頁地址*/
  count = SPI_FLASH_PageSize - Addr;
	/*計算出要寫多少整數頁*/
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
	/*mod運算求余,計算出剩余不滿一頁的字節數*/
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
	
	/* Addr=0,則WriteAddr 剛好按頁對齊 aligned  */
  if (Addr == 0)
  {
		/* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0) 
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    { 
			/*先把整數頁都寫了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
			/*若有多余的不滿一頁的數據,把它寫完*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
	/* 若地址與 SPI_FLASH_PageSize 不對齊  */
  else 
  {
		/* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0)
    {
			/*當前頁剩余的count個位置比NumOfSingle小,一頁寫不完*/
      if (NumOfSingle > count) 
      {
        temp = NumOfSingle - count;
				/*先寫滿當前頁*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
				
        WriteAddr +=  count;
        pBuffer += count;
				/*再寫剩余的數據*/
        SPI_FLASH_PageWrite( pBuffer, WriteAddr,temp);
      }
      else /*當前頁剩余的count個位置能寫完NumOfSingle個數據*/
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
			/*地址不對齊多出的count分開處理,不加入這個運算*/
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
			
			/* 先寫完count個數據,為的是讓下一次要寫的地址對齊 */
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
			
			/* 接下來就重復地址對齊的情況 */
      WriteAddr +=  count;
      pBuffer += count;
			/*把整數頁都寫了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
			/*若有多余的不滿一頁的數據,把它寫完*/
      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

/**
  * @brief  對FLASH按頁寫入數據,調用本函數寫入數據前需要先擦除扇區
  * @param	pBuffer,要寫入數據的指針
  * @param WriteAddr,寫入地址
  * @param  NumByteToWrite,寫入數據長度,必須小於等於SPI_FLASH_PerWritePageSize
  * @retval 無
  */
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  /* 發送FLASH寫使能命令 */
  SPI_FLASH_Write_Enable();

  /* 選擇FLASH: CS低電平 */
  SPI_FLASH_NSS_LOW();
  /* 寫頁寫指令*/
  SPI_FLASH_Send_Data(W25X_PageProgram);
  /*發送寫地址的高位*/
  SPI_FLASH_Send_Data((WriteAddr & 0xFF0000) >> 16);
  /*發送寫地址的中位*/
  SPI_FLASH_Send_Data((WriteAddr & 0xFF00) >> 8);
  /*發送寫地址的低位*/
  SPI_FLASH_Send_Data(WriteAddr & 0xFF);

  if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  {
     NumByteToWrite = SPI_FLASH_PerWritePageSize;
     FLASH_ERROR("SPI_FLASH_PageWrite too large!"); 
  }

  /* 寫入數據*/
  while (NumByteToWrite--)
  {
    /* 發送當前要寫入的字節數據 */
    SPI_FLASH_Send_Data(*pBuffer);
    /* 指向下一字節數據 */
    pBuffer++;
  }

  /* 停止信號 FLASH: CS 高電平 */
  SPI_FLASH_NSS_HIGH();

  /* 等待寫入完畢*/
  SPI_FLASH_WaitForWriteEnd();
}

diskio.c

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2014        */
/*-----------------------------------------------------------------------*/
/* 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 "diskio.h"		/* FatFs lower layer API */
#include "./spi/bsp_spi_flash.h"
//#include "usbdisk.h"	/* Example: Header file of existing USB MSD control module */
//#include "atadrive.h"	/* Example: Header file of existing ATA harddisk control module */
//#include "sdcard.h"		/* Example: Header file of existing MMC/SDC contorl module */

/* Definitions of physical drive number for each drive */
#define SD_CARD			0	
#define SPI_FLASH		1	
#define USB				2	


/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	uint32_t FlashID = 0;
	DSTATUS stat = 0;
//	int result;

	switch (pdrv) {
	case SD_CARD :
//		result = ATA_disk_status();

		// translate the reslut code here

		return stat;

	case SPI_FLASH :		
//		result = MMC_disk_status();

		/* ======================== 添加的代碼 ======================== */
		/* 獲取SPI_FLASH設備的狀態 */
		FlashID = SPI_FLASH_Read_Flash_ID();
	
		/* 判斷設備是否初始化 */
		if(FlashID == sFLASH_ID)
		{
			stat = 0;			// 成功
		}	
		else				
		{
			// 失敗,“或上”是因為失敗的原因有很多種,可以同時表達很多狀態
			stat |= STA_NOINIT;	
		}
		/* ======================== 添加的代碼 ======================== */

		return stat;	
		
	case USB :
//		result = USB_disk_status();

		// translate the reslut code here

		return stat;
	}
	return STA_NOINIT;
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

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

	switch (pdrv) {
	case SD_CARD :
//		result = ATA_disk_initialize();

		// translate the reslut code here

		return stat;

	case SPI_FLASH :
		/* ======================== 添加的代碼 ======================== */
		/* SPI_FLASH設備初始化 */
		SPI_FLASH_Init();
	
		// SPI_FLASH:物理設備號,還有SD_CARD、USB兩種
		return disk_status(SPI_FLASH);
		/* ======================== 添加的代碼 ======================== */

	case USB :
//		result = USB_disk_initialize();

		// translate the reslut code here

		return stat;
	}
	return STA_NOINIT;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address in LBA */
	UINT count		/* Number of sectors to read */
)
{
	DRESULT res;
//	int result;

	switch (pdrv) {
	case SD_CARD :
		// translate the arguments here

//		result = ATA_disk_read(buff, sector, count);

		// translate the reslut code here

		return res;

	case SPI_FLASH :
		/* ======================== 添加的代碼 ======================== */
		/*
		buff:讀取SPI_FLASH數據,buff是將讀取的數據放到buff指針變量中
		sector:扇區,一個扇區有4096個字節,比如要讀第0扇區,那么讀數據的開始地址是:0*4096
				如果要讀第1扇區,那么讀數據的開始地址是:1*4096
		count:是要讀取的數據長度,也要乘以4096,表示讀的是多少個字節
		*/
		SPI_FLASH_BufferRead(buff, sector*4096, count*4096);
		
		// 讀取完就返回OK
		return RES_OK;
		/* ======================== 添加的代碼 ======================== */

	case USB :
		// translate the arguments here

//		result = USB_disk_read(buff, sector, count);

		// translate the reslut code here

		return res;
	}

	return RES_PARERR;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if _USE_WRITE
DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector address in LBA */
	UINT count			/* Number of sectors to write */
)
{
	DRESULT res;
//	int result;

	switch (pdrv) {
	case SD_CARD :
		// translate the arguments here

//		result = ATA_disk_write(buff, sector, count);

		// translate the reslut code here

		return res;

	case SPI_FLASH :
		/* ======================== 添加的代碼 ======================== */
		// 寫之前先擦除
		SPI_FLASH_Erase_Sector(sector*4096);
		
		// 寫入數據
		SPI_FLASH_BufferWrite((u8*)buff, sector*4096, count*4096);

		// 如果要寫的嚴謹,這里要判斷成功或失敗,返回不同的狀態碼,這里省事就不深究了
		return RES_OK;
		/* ======================== 添加的代碼 ======================== */

	case USB :
		// translate the arguments here

//		result = USB_disk_write(buff, sector, count);

		// translate the reslut code here

		return res;
	}

	return RES_PARERR;
}
#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions    ioctl是設備驅動程序中對設備的I/O通道進行管理的函數                                           */
/*-----------------------------------------------------------------------*/

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

	switch (pdrv) {
	case SD_CARD :

		// Process of the command for the ATA drive

		return res;

	case SPI_FLASH :	
		/* ======================== 添加的代碼 ======================== */
		switch(cmd)
		{
			case GET_SECTOR_COUNT:		// 多少個扇區
				// buff是void*類型指針(空指針),根據官網的disk_ioctl函數參數轉換不同返回指針
				*(DWORD*)buff = 2048;	// SPI_FLASH有8M字節,每個扇區有4096個字節,即有2048個扇區
				break;	
			
			case GET_SECTOR_SIZE:		// 每個扇區大小
				*(WORD*)buff = 4096;	// 每個扇區有4096個字節
				break;
			
			case GET_BLOCK_SIZE:		// 塊大小
				*(DWORD*)buff = 1;		// 設置為1,表示每次擦除1個扇區
				break;
		}
		res = RES_OK;
		return res;
		/* ======================== 添加的代碼 ======================== */

	case USB :

		// Process of the command the USB drive

		return res;
	}

	return RES_PARERR;
}
#endif

// 返回時間
DWORD get_fattime (void)
{
	return 0;
}

 main.c

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./spi/bsp_spi_flash.h"
#include "ff.h"		// 添加文件系統的庫文件


FATFS 		fs;
FRESULT 	res;
FIL			fp;
char		write_buf[] = "這是文件系統測試寫入的數據";
UINT		bw;
char		read_buf[1024] = {0};
UINT		br;
char		filename[] = "1:testhhhhhhhhhhhhhhhhhh.txt";

int main(void)
{	
	/* ============================ 初始化 =========================== */
	// 串口初始化
	DEBUG_USART_Config();
	
	printf("\r\n 這是文件系統移植實驗 \r\n");
	
	/*
		移植文件系統到物理設備上,這樣才能使用文件系統的各個函數接口
		fs:	文件系統指針
		path:	SD_CARD(0)/SPI_FLASH(1)/USB(2)
		opt:	0:Do not mount (delayed mount), 1:Mount immediately
	*/
	res = f_mount(&fs, "1:", 1);
	if(res == FR_NO_FILESYSTEM)		// SPI_FLASH設備沒有掛在文件系統
	{
		printf("\r\n before f_mkfs:res = %d\r\n", res);
		
		// 文件系統格式化
		res = f_mkfs("1:", 0, 0);
		
		// 再次判斷
		if(res != FR_OK)
		{
			printf("\r\n f_mkfs res = %d \r\n", res);
		}
	}
	else if(res == FR_OK)
	{
		// 文件系統移植成功,下面是對文件的操作:創建,寫入,關閉,,,
		printf("\r\n f_mount	res = %d\r\n", res);
		
		/* f_open函數:創建文件
		fp:文件指針
		1:test.txt:文件名
		第三個參數是文件操作的權限,讀/寫/不管文件是否存在都創建(存在就覆蓋)
		*/
		res = f_open(&fp, filename, FA_READ|FA_WRITE|FA_CREATE_ALWAYS);
		printf("\r\n f_open		res = %d\r\n", res);
		
		/* f_write函數:向文件中寫入數據
		fp:文件指針
		write_buf:要寫入文件的數據
		第三個參數:寫入多少個字節
		第四個參數:指針,返回已經寫入到文件的字節數
		*/
		res = f_write(&fp, write_buf, sizeof(write_buf), &bw);
		printf("\r\n f_write	res = %d\r\n", res);
		
		/* f_close:關閉文件,使用f_open必須使用f_close,不然會有緩存
		fp:文件指針
		*/
		res = f_close(&fp);
		printf("\r\n f_close	res = %d\r\n", res);
		
		/* f_read函數:讀文件數據,讀剛剛寫入的數據
		fp:文件指針
		read_buf:將讀取到的數據存儲到這個數組中
		1024:打算讀取多少個字節數據
		br:實際讀取回來的數據字節數
		*/
		// 讀之前需要打開文件:只讀權限(文件存在就讀取)
		f_open(&fp, filename, FA_READ|FA_OPEN_EXISTING);
		// 開始讀取文件數據
		res = f_read(&fp, read_buf, 1024, &br);
		printf("\r\n f_read		res = %d,br = %d,sizeof write_buf = %d,", res, br, sizeof(write_buf));
		printf("read_buf = %s\r\n", read_buf);
		// 讀取完文件后要關閉文件
		res = f_close(&fp);
		printf("\r\n f_close	res = %d\r\n", res);
	}
	
	while(1)
	{
	
	}
}

本章節內容還沒有完,請繼續參考下一章節內容

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM