在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) { } }
本章节内容还没有完,请继续参考下一章节内容