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