离线
TA的每日心情 | 慵懒 2021-7-23 17:16 |
---|
签到天数: 17 天 [LV.4]
|
有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 草帽王子 于 2021-11-30 01:37 编辑
前面教程中介绍过SPI读写FLASH,此外在论坛中看到很多朋友在此基础上运行FatFs文件系统,参考各位朋友帖子,整理汇总一下,写一个帖子。
1、FatFs简介
FatFs是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051, PIC, AVR, ARM, Z80, RX等等,不需要做任何修改。
关于FatFs的具体介绍以及FatFs文件系统的源码,可去FatFs官网浏览下载,链接如下:FatFs - Generic FAT Filesystem Module
下载解压打开文件系统源码文件,可以看见里面包含两个文件夹,如下图。其中,documents文件夹下是一些帮助文档,source文件夹下是文件系统源码。本章教程主要用到source文件夹下源码文件,因此对其做主要介绍。
- diskio.c文件:是FatFs和disk I/O模块接口文件,是与平台相关的代码,需要用户根据存储介质来编写函数。
- diskio.h文件:是FatFs和disk I/O模块的公共包含文件,不需要用户修改。
- ff.c文件:是FatFs模块源码,不需要用户修改。
- ff.h文件:是FatFs和应用程序模块的通用包含文件,不需要用户修改。
- ffconf.h文件:是FatFs模块的配置文件,需要用户根据需求来配置。
- ffsystem.c文件,是可选的操作系统对接的各接口相关实现示例。
- ffunicode.c文件,是可选的Unicode相关的转换函数,包含了多语言支持需要用到的文件和转换函数。
根据上述描述,因此我们在使用FatFs的时候只需要对diskio.c、ffconf.h、ffunicode.c三个文件进行配置修改即可。
2、硬件设计
本章教程使用SPI读写FLASH,由原理图可知,其片选信号通过电阻R11连接,但开发板默认R11是断开的,因此在使用FLASH之前需将R11短接。
3、软件设计
本次教程在SPI读写FLASH程序基础上进行,本章教程将FatFs源码添加到工程之后对diskio.c、ffconf.h、ffunicode.c三个文件进行配置修改,在此只介绍diskio.c文件的修改,其他见工程。
diskio.c文件
- /*-----------------------------------------------------------------------*/
- /* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
- /*-----------------------------------------------------------------------*/
- /* If a working storage control module is available, it should be */
- /* attached to the FatFs via a glue function rather than modifying it. */
- /* This is an example of glue functions to attach various exsisting */
- /* storage control modules to the FatFs module with a defined API. */
- /*-----------------------------------------------------------------------*/
- #include "diskio.h" /* Declarations of disk functions */
- /* Definitions of physical drive number for each drive */
- //#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
- //#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
- //#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
- /* 为每个设备定义一个物理编号 */
- #define ATA 1 // 预留SD卡使用
- #define SPI_FLASH 0 // 外部SPI Flash
- /*-----------------------------------------------------------------------*/
- /* Get Drive Status */
- /*-----------------------------------------------------------------------*/
- DSTATUS disk_status (
- BYTE pdrv /* Physical drive nmuber to identify the drive */
- )
- {
- DSTATUS status = STA_NOINIT;
- switch (pdrv)
- {
- case ATA: /* SD CARD */
- break;
- case SPI_FLASH:
- /* SPI Flash状态检测:读取SPI Flash 设备ID */
- if(W25Q16 == SPI_Flash_ReadID())
- {
- /* 设备ID读取结果正确 */
- status &= ~STA_NOINIT;
- }
- else
- {
- /* 设备ID读取结果错误 */
- status = STA_NOINIT;;
- }
- break;
- default:
- status = STA_NOINIT;
- break;
- }
- return status;
- }
- /*-----------------------------------------------------------------------*/
- /* Inidialize a Drive */
- /*-----------------------------------------------------------------------*/
- DSTATUS disk_initialize (
- BYTE pdrv /* Physical drive nmuber to identify the drive */
- )
- {
- DSTATUS status = STA_NOINIT;
- switch (pdrv)
- {
- case ATA: /* SD CARD */
- break;
- case SPI_FLASH: /* SPI Flash */
- /* 初始化SPI Flash */
- SPI_Flash_Init();
- /* 获取SPI Flash芯片状态 */
- status=disk_status(SPI_FLASH);
- break;
- default:
- status = STA_NOINIT;
- break;
- }
- return status;
- }
- /*-----------------------------------------------------------------------*/
- /* Read Sector(s) */
- /*-----------------------------------------------------------------------*/
- DRESULT disk_read (
- BYTE pdrv, /* Physical drive nmuber to identify the drive */
- BYTE *buff, /* Data buffer to store read data */
- LBA_t sector, /* Start sector in LBA */
- UINT count /* Number of sectors to read */
- )
- {
- DRESULT status = RES_PARERR;
- switch (pdrv)
- {
- case ATA: /* SD CARD */
- break;
- case SPI_FLASH:
- SPI_Flash_Read(buff, sector <<12, count<<12);
- status = RES_OK;
- break;
- default:
- status = RES_PARERR;
- break;
- }
- return status;
- }
- /*-----------------------------------------------------------------------*/
- /* Write Sector(s) */
- /*-----------------------------------------------------------------------*/
- #if FF_FS_READONLY == 0
- DRESULT disk_write (
- BYTE pdrv, /* Physical drive nmuber to identify the drive */
- const BYTE *buff, /* Data to be written */
- LBA_t sector, /* Start sector in LBA */
- UINT count /* Number of sectors to write */
- )
- {
- DRESULT status = RES_PARERR;
- if (!count) {
- return RES_PARERR; /* Check parameter */
- }
- switch (pdrv)
- {
- case ATA: /* SD CARD */
- break;
- case SPI_FLASH:
- SPI_Flash_Erase_Sector(sector);
- SPI_Flash_Write((uint8_t *)buff,sector<<12,count<<12);
- status = RES_OK;
- break;
- default:
- status = RES_PARERR;
- break;
- }
- return status;
- }
- #endif
- /*-----------------------------------------------------------------------*/
- /* Miscellaneous Functions */
- /*-----------------------------------------------------------------------*/
- DRESULT disk_ioctl (
- BYTE pdrv, /* Physical drive nmuber (0..) */
- BYTE cmd, /* Control code */
- void *buff /* Buffer to send/receive control data */
- )
- {
- DRESULT status = RES_PARERR;
- switch (pdrv)
- {
- case ATA: /* SD CARD */
- break;
- case SPI_FLASH:
- switch (cmd)
- {
- /* 扇区数量:1536*4096/1024/1024=6(MB) */
- case GET_SECTOR_COUNT:
- *(DWORD * )buff = 512;
- break;
- /* 扇区大小 */
- case GET_SECTOR_SIZE :
- *(WORD * )buff = 4096;
- break;
- /* 同时擦除扇区个数 */
- case GET_BLOCK_SIZE :
- *(DWORD * )buff = 1;
- break;
- }
- status = RES_OK;
- break;
- default:
- status = RES_PARERR;
- break;
- }
- return status;
- }
- DWORD get_fattime(void) {
- /* 返回当前时间戳 */
- return ((DWORD)(2015 - 1980) << 25) /* Year 2015 */
- | ((DWORD)1 << 21) /* Month 1 */
- | ((DWORD)1 << 16) /* Mday 1 */
- | ((DWORD)0 << 11) /* Hour 0 */
- | ((DWORD)0 << 5) /* Min 0 */
- | ((DWORD)0 >> 1); /* Sec 0 */
- }
复制代码 关于程序理解可见注释。
Main.c文件
- /********************************** (C) COPYRIGHT *******************************
- * File Name : main.c
- * Author : WCH
- * Version : V1.0.0
- * Date : 2020/04/30
- * Description : Main program body.
- *******************************************************************************/
- #include "debug.h"
- #include "spi.h"
- #include "diskio.h" /* FatFs lower layer API */
- #include "ff.h" /* FatFs lower layer API */
- #include "string.h"
- FATFS fs; /* FatFs文件系统对象 */
- FIL fnew; /* 文件对象 */
- FRESULT res_flash; /* 文件操作结果 */
- DIR dire; // 目录对象
- FILINFO fnow; // 定义静态文件信息结构对象
- UINT fnum; /* 文件成功读写数量 */
- BYTE ReadBuffer[1024]={0}; /* 读缓冲区 */
- BYTE WriteBuffer[] = /* 写缓冲区*/
- "这是一个基于CH32V103串行Flash的fatfs的评测实验\r\n";
- BYTE work[FF_MAX_SS];
- FRESULT scan_files (
- char* path /* Start node to be scanned (***also used as work area***) */
- );
- /*******************************************************************************
- * Function Name : main
- * Description : Main program.
- * Input : None
- * Return : None
- *******************************************************************************/
- int main(void)
- {
- char pathBuff[256]; //定义路径数组
- char filename[20];
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- Delay_Init();
- USART_Printf_Init(115200);
- SPI_Flash_Init();
- printf("****** 这是一个SPI FLASH 文件系统实验 ******\r\n");
- printf("FLASH正在整体擦除....\r\n");
- SPI_Flash_Erase_Chip();
- printf("FLASH整体擦除完毕....\r\n");
- //在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
- //初始化函数调用流程如下
- //f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
- //f_mount:注册或注销一个工作区域
- //f_mount 函数有三个形参,
- //第 一个参数是指向 FATFS 变量指针,如果赋值为 NULL 可以取消物理设备挂载。
- //第二个参数为逻辑设备编号,使用设备根路径表示,与物理设备编号挂钩,在diskio.c文件中我们定义 SPI Flash 芯片物理编号为 0,所以这里使用“0:”。
- //第三个参数可选 0 或 1, 1 表示立即挂载, 0 表示不立即挂载,延迟挂载。
- //f_mount 函数会返回一个 FRESULT 类型值,指示运行情况。
- //返回值:FR_OK (0),函数成功 FR_INVALID_DRIVE,驱动器无效
- res_flash = f_mount(&fs,"0:",1);
- if(res_flash==FR_OK)
- {
- printf("》文件系统挂载成功\r\n");
- }
- else
- {
- printf("!!文件系统挂载失败:(%d)\r\n",res_flash);
- }
- /*----------------------- 格式化测试 -----------------*/
- /* 如果没有文件系统就格式化创建创建文件系统 */
- //如果 f_mount 函数返回值为 FR_NO_FILESYSTEM,说明没有 FAT 文件系统,比如新出厂的 SPI Flash 芯片就没有 FAT 文件系统。我们就必须对物理设备进行格式化处理。
- if(res_flash == FR_NO_FILESYSTEM)
- {
- printf("》FLASH还没有文件系统,即将进行格式化...\r\n");
- /* 格式化 */
- //f_mkfs:在驱动器上创建一个文件系统
- //f_mkfs 函数有三个形参,
- //第一个参数为逻辑设备编号;
- //第二参数可选 0 或者 1, 0 表示设备为一般硬盘, 1 表示设备为软盘。
- //第三个参数指定扇区大小,如果为 0,表示通过代码 disk_ioctl 函数获取。格式化成功后需要先取消挂载原来设备,再重新挂载设备
- res_flash=f_mkfs("0:",0,work,sizeof(work));
- if(res_flash == FR_OK)
- {
- printf("》FLASH已成功格式化文件系统。\r\n");
- /* 格式化后,先取消挂载 */
- res_flash = f_mount(NULL,"0:",1);
- /* 重新挂载 */
- res_flash = f_mount(&fs,"0:",1);
- }
- else
- {
- printf("《《格式化失败。》》\r\n");
- while(1);
- }
- }
- else if(res_flash!=FR_OK)
- {
- printf("!!外部Flash挂载文件系统失败。(%d)\r\n",res_flash);
- printf("!!可能原因:SPI Flash初始化不成功。\r\n");
- while(1);
- }
- else
- {
- printf("》文件系统挂载成功,可以进行读写测试\r\n");
- }
- /*----------------------- 文件系统测试:创建文件目录 -------------------*/
- //strcpy:strcpy把含有'\0'结束符的字符串复制到另一个地址空间,返回值的类型为char*
- strcpy(filename, "123");
- //f_mkdir:创建一个目录
- res_flash = f_mkdir(filename);
- if(res_flash==FR_OK)
- {
- printf("》文件夹%s创建成功\r\n",filename);
- }
- else if(res_flash==FR_EXIST)
- {
- printf("!!文件夹已存在:(%d)\r\n",res_flash);
- }
- else
- {
- printf("!!文件夹创建失败:(%d)\r\n",res_flash);
- }
- strcpy(filename, "123/456");
- res_flash = f_mkdir(filename);
- if(res_flash==FR_OK)
- {
- printf("》文件夹%s创建成功\r\n",filename);
- }
- else if(res_flash==FR_EXIST)
- {
- printf("!!文件夹已存在:(%d)\r\n",res_flash);
- }
- else
- {
- printf("!!文件夹创建失败:(%d)\r\n",res_flash);
- }
- strcpy(filename, "123/456/789");
- res_flash = f_mkdir(filename);
- if(res_flash==FR_OK)
- {
- printf("》文件夹%s创建成功\r\n",filename);
- }
- else if(res_flash==FR_EXIST)
- {
- printf("!!文件夹已存在:(%d)\r\n",res_flash);
- }
- else
- {
- printf("!!文件夹创建失败:(%d)\r\n",res_flash);
- }
- /*----------------------- 文件系统测试:写测试 -------------------*/
- /* 打开文件,每次都以新建的形式打开,属性为可写 */
- printf("\r\n****** 即将进行文件写入测试... ******\r\n");
- //f_open:打开或创建一个文件 FA_CREATE_ALWAYS:创建一个新文件,如果文件已存在,则它将被被截断并覆盖
- res_flash = f_open(&fnew, "0:123/456/789/CH32V103x.txt",FA_CREATE_ALWAYS | FA_WRITE );
- if ( res_flash == FR_OK )
- {
- printf("》打开/创建CH32V103x.txt文件成功,向文件写入数据。\r\n");
- /* 将指定存储区内容写入到文件内 */
- //f_write:写文件
- res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
- if(res_flash==FR_OK)
- {
- printf("》文件写入成功,写入字节数据:%d\r\n",fnum);
- printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
- }
- else
- {
- printf("!!文件写入失败:(%d)\n",res_flash);
- }
- /* 不再读写,关闭文件 */
- //f_close:关闭一个文件
- f_close(&fnew);
- }
- else
- {
- printf("!!打开/创建文件失败。\r\n");
- }
- /*------------------- 文件系统测试:读测试 --------------------------*/
- printf("****** 即将进行文件读取测试... ******\r\n");
- //f_open:打开或创建一个文件 FA_OPEN_EXISTING:打开一个文件,如果文件不存在,则打开失败(默认)
- res_flash = f_open(&fnew, "0:123/456/789/CH32V103x.txt",FA_OPEN_EXISTING | FA_READ);
- if(res_flash == FR_OK)
- {
- printf("》打开文件成功。\r\n");
- //f_read:读文件
- res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
- if(res_flash==FR_OK)
- {
- printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
- printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);
- }
- else
- {
- printf("!!文件读取失败:(%d)\n",res_flash);
- }
- }
- else
- {
- printf("!!打开文件失败。\r\n");
- }
- /* 不再读写,关闭文件 */
- f_close(&fnew);
- printf("*************** 文件信息获取测试 **************\r\n");
- /* 操作完成,停机 */
- printf("》开始扫描文件目录....\r\n");
- strcpy(pathBuff, "");
- scan_files(pathBuff);
- /* 不再使用文件系统,取消挂载文件系统 */
- //f_mount:注册或注销一个工作区域
- f_mount(NULL,"0:",1);
- while(1)
- {
- }
- }
- FRESULT scan_files (
- char* path /* Start node to be scanned (***also used as work area***) */
- )
- {
- FRESULT res;
- DIR dir;
- UINT i;
- static FILINFO fno;
- //f_opendir:打开一个目录
- res = f_opendir(&dir, path); /* Open the directory */
- if (res == FR_OK)
- {
- for (;;)
- {
- //f_readdir:读取目录条目
- res = f_readdir(&dir, &fno); /* Read a directory item */
- if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
- if (fno.fattrib & AM_DIR)//假如是文件夹
- {
- /* It is a directory */
- i = strlen(path); //计算出路径长度
- sprintf(&path[i], "/%s", fno.fname); //文件名加到路径后面
- res = scan_files(path); /* Enter the directory */
- if (res != FR_OK) break;
- path[i] = 0;
- }
- else
- { /* It is a file. */
- printf("%s/%s\r\n", path, fno.fname);
- }
- }
- //f_closedir:关闭一个已经打开的目录
- f_closedir(&dir);
- }
- return res;
- }
复制代码 Main.c文件主要进行文件创建、读写测试等。
4、下载验证
将编译好的程序下载到开发板并复位,串口打印如下:
107、Flash-FatFs.rar
(1.32 MB, 下载次数: 70)
完
|
上一篇: 第一百零七章:CH32V103应用教程——PD0、1引脚的复用下一篇: 恭喜CH32V307入选《第十七届全国大学生智能车竞赛》编队车MCU
|