[RISC-V MCU 应用开发] 第一百零八章、CH32V103应用教程——文件系统FatFs

[复制链接]
11134|18
 楼主| RISCVLAR 发表于 2021-8-19 17:12 | 显示全部楼层 |阅读模式
本帖最后由 RISCVLAR 于 2021-8-19 17:12 编辑

CH32V103应用教程——文件系统FatFs

前面教程中介绍过SPI读写FLASH,此外在论坛中看到很多朋友在此基础上运行FatFs文件系统,参考各位朋友帖子,整理汇总一下,写一个帖子。

1、FatFs简介
FatFs是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平台。它可以嵌入到资源有限的微控制器中,如 8051, PIC, AVR, ARM, Z80, RX等等,不需要做任何修改。
关于FatFs的具体介绍以及FatFs文件系统的源码,可去FatFs官网浏览下载,链接如下:http://elm-chan.org/fsw/ff/00index_e.html
下载解压打开文件系统源码文件,可以看见里面包含两个文件夹,如下图。其中,documents文件夹下是一些帮助文档,source文件夹下是文件系统源码。本章教程主要用到source文件夹下源码文件,因此对其做主要介绍。
图片1.png
图片2.png
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.png

3软件设计
本次教程在SPI读写FLASH程序基础上进行,本章教程将FatFs源码添加到工程之后对diskio.c、ffconf.h、ffunicode.c三个文件进行配置修改,在此只介绍diskio.c文件的修改,其他见工程。
diskio.c文件
  1. /*-----------------------------------------------------------------------*/
  2. /* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
  3. /*-----------------------------------------------------------------------*/
  4. /* If a working storage control module is available, it should be        */
  5. /* attached to the FatFs via a glue function rather than modifying it.   */
  6. /* This is an example of glue functions to attach various exsisting      */
  7. /* storage control modules to the FatFs module with a defined API.       */
  8. /*-----------------------------------------------------------------------*/

  9. #include "diskio.h"     /* Declarations of disk functions */

  10. /* Definitions of physical drive number for each drive */
  11. //#define DEV_RAM       0   /* Example: Map Ramdisk to physical drive 0 */
  12. //#define DEV_MMC       1   /* Example: Map MMC/SD card to physical drive 1 */
  13. //#define DEV_USB       2   /* Example: Map USB MSD to physical drive 2 */

  14. /* 为每个设备定义一个物理编号 */
  15. #define ATA             1     // 预留SD卡使用
  16. #define SPI_FLASH       0     // 外部SPI Flash
  17. /*-----------------------------------------------------------------------*/
  18. /* Get Drive Status                                                      */
  19. /*-----------------------------------------------------------------------*/

  20. DSTATUS disk_status (
  21.     BYTE pdrv       /* Physical drive nmuber to identify the drive */
  22. )
  23. {

  24.     DSTATUS status = STA_NOINIT;

  25.     switch (pdrv)
  26.     {
  27.         case ATA:   /* SD CARD */
  28.             break;

  29.         case SPI_FLASH:
  30.           /* SPI Flash状态检测:读取SPI Flash 设备ID */
  31.           if(W25Q16 == SPI_Flash_ReadID())
  32.           {
  33.             /* 设备ID读取结果正确 */
  34.             status &= ~STA_NOINIT;
  35.           }
  36.           else
  37.           {
  38.             /* 设备ID读取结果错误 */
  39.             status = STA_NOINIT;;
  40.           }
  41.           break;

  42.         default:
  43.             status = STA_NOINIT;
  44.             break;
  45.     }
  46.     return status;
  47. }



  48. /*-----------------------------------------------------------------------*/
  49. /* Inidialize a Drive                                                    */
  50. /*-----------------------------------------------------------------------*/

  51. DSTATUS disk_initialize (
  52.     BYTE pdrv               /* Physical drive nmuber to identify the drive */
  53. )
  54. {
  55.       DSTATUS status = STA_NOINIT;
  56.       switch (pdrv)
  57.       {
  58.           case ATA:            /* SD CARD */
  59.               break;

  60.           case SPI_FLASH:    /* SPI Flash */
  61.             /* 初始化SPI Flash */
  62.               SPI_Flash_Init();
  63.             /* 获取SPI Flash芯片状态 */
  64.             status=disk_status(SPI_FLASH);
  65.             break;

  66.           default:
  67.             status = STA_NOINIT;
  68.             break;
  69.       }
  70.       return status;
  71. }



  72. /*-----------------------------------------------------------------------*/
  73. /* Read Sector(s)                                                        */
  74. /*-----------------------------------------------------------------------*/

  75. DRESULT disk_read (
  76.     BYTE pdrv,      /* Physical drive nmuber to identify the drive */
  77.     BYTE *buff,     /* Data buffer to store read data */
  78.     LBA_t sector,   /* Start sector in LBA */
  79.     UINT count      /* Number of sectors to read */
  80. )
  81. {
  82.     DRESULT status = RES_PARERR;
  83.     switch (pdrv)
  84.     {
  85.         case ATA:   /* SD CARD */
  86.             break;

  87.         case SPI_FLASH:
  88.             SPI_Flash_Read(buff, sector <<12, count<<12);
  89.           status = RES_OK;
  90.             break;

  91.         default:
  92.             status = RES_PARERR;
  93.             break;
  94.     }
  95.     return status;
  96. }



  97. /*-----------------------------------------------------------------------*/
  98. /* Write Sector(s)                                                       */
  99. /*-----------------------------------------------------------------------*/

  100. #if FF_FS_READONLY == 0

  101. DRESULT disk_write (
  102.     BYTE pdrv,          /* Physical drive nmuber to identify the drive */
  103.     const BYTE *buff,   /* Data to be written */
  104.     LBA_t sector,       /* Start sector in LBA */
  105.     UINT count          /* Number of sectors to write */
  106. )
  107. {
  108.       DRESULT status = RES_PARERR;
  109.       if (!count) {
  110.           return RES_PARERR;      /* Check parameter */
  111.       }

  112.       switch (pdrv)
  113.       {
  114.           case ATA:   /* SD CARD */
  115.               break;

  116.           case SPI_FLASH:
  117.             SPI_Flash_Erase_Sector(sector);
  118.             SPI_Flash_Write((uint8_t *)buff,sector<<12,count<<12);
  119.             status = RES_OK;
  120.             break;

  121.           default:
  122.             status = RES_PARERR;
  123.             break;
  124.       }
  125.       return status;
  126. }

  127. #endif


  128. /*-----------------------------------------------------------------------*/
  129. /* Miscellaneous Functions                                               */
  130. /*-----------------------------------------------------------------------*/

  131. DRESULT disk_ioctl (
  132.     BYTE pdrv,      /* Physical drive nmuber (0..) */
  133.     BYTE cmd,       /* Control code */
  134.     void *buff      /* Buffer to send/receive control data */
  135. )
  136. {
  137.     DRESULT status = RES_PARERR;
  138.     switch (pdrv)
  139.     {
  140.         case ATA:   /* SD CARD */
  141.             break;

  142.         case SPI_FLASH:
  143.             switch (cmd)
  144.             {
  145.                 /* 扇区数量:1536*4096/1024/1024=6(MB) */
  146.                 case GET_SECTOR_COUNT:
  147.                     *(DWORD * )buff = 512;
  148.                     break;
  149.                 /* 扇区大小  */
  150.                 case GET_SECTOR_SIZE :
  151.                     *(WORD * )buff = 4096;
  152.                     break;
  153.                 /* 同时擦除扇区个数 */
  154.                 case GET_BLOCK_SIZE :
  155.                     *(DWORD * )buff = 1;
  156.                     break;
  157.             }
  158.             status = RES_OK;
  159.             break;

  160.         default:
  161.             status = RES_PARERR;
  162.             break;
  163.     }
  164.     return status;
  165. }

  166. DWORD get_fattime(void) {
  167.     /* 返回当前时间戳 */
  168.     return    ((DWORD)(2015 - 1980) << 25)  /* Year 2015 */
  169.             | ((DWORD)1 << 21)              /* Month 1 */
  170.             | ((DWORD)1 << 16)              /* Mday 1 */
  171.             | ((DWORD)0 << 11)              /* Hour 0 */
  172.             | ((DWORD)0 << 5)                 /* Min 0 */
  173.             | ((DWORD)0 >> 1);              /* Sec 0 */
  174. }
关于程序理解可见注释。
Main.c文件
  1. /********************************** (C) COPYRIGHT *******************************
  2. * File Name          : main.c
  3. * Author             : WCH
  4. * Version            : V1.0.0
  5. * Date               : 2020/04/30
  6. * Description        : Main program body.
  7. *******************************************************************************/
  8. #include "debug.h"
  9. #include "spi.h"
  10. #include "diskio.h"     /* FatFs lower layer API */
  11. #include "ff.h"         /* FatFs lower layer API */
  12. #include "string.h"

  13. FATFS fs;                         /* FatFs文件系统对象 */
  14. FIL fnew;                         /* 文件对象 */
  15. FRESULT res_flash;                /* 文件操作结果 */
  16. DIR dire;                         // 目录对象
  17. FILINFO fnow;                     // 定义静态文件信息结构对象
  18. UINT fnum;                        /* 文件成功读写数量 */
  19. BYTE ReadBuffer[1024]={0};        /* 读缓冲区 */
  20. BYTE WriteBuffer[] =              /* 写缓冲区*/
  21. "这是一个基于CH32V103串行Flash的fatfs的评测实验\r\n";

  22. BYTE work[FF_MAX_SS];

  23. FRESULT scan_files (
  24.     char* path        /* Start node to be scanned (***also used as work area***) */
  25. );
  26. /*******************************************************************************
  27. * Function Name  : main
  28. * Description    : Main program.
  29. * Input          : None
  30. * Return         : None
  31. *******************************************************************************/
  32. int main(void)
  33. {
  34.     char pathBuff[256]; //定义路径数组
  35.     char filename[20];

  36.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  37.     Delay_Init();
  38.     USART_Printf_Init(115200);
  39.     SPI_Flash_Init();

  40.     printf("****** 这是一个SPI FLASH 文件系统实验 ******\r\n");
  41.     printf("FLASH正在整体擦除....\r\n");
  42.     SPI_Flash_Erase_Chip();
  43.     printf("FLASH整体擦除完毕....\r\n");

  44.     //在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化
  45.     //初始化函数调用流程如下
  46.     //f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
  47.     //f_mount:注册或注销一个工作区域
  48.     //f_mount 函数有三个形参,
  49.     //第 一个参数是指向 FATFS 变量指针,如果赋值为 NULL 可以取消物理设备挂载。
  50.     //第二个参数为逻辑设备编号,使用设备根路径表示,与物理设备编号挂钩,在diskio.c文件中我们定义 SPI Flash 芯片物理编号为 0,所以这里使用“0:”。
  51.     //第三个参数可选 0 或 1, 1 表示立即挂载, 0 表示不立即挂载,延迟挂载。
  52.     //f_mount 函数会返回一个 FRESULT 类型值,指示运行情况。
  53.     //返回值:FR_OK (0),函数成功     FR_INVALID_DRIVE,驱动器无效
  54.     res_flash = f_mount(&fs,"0:",1);

  55.     if(res_flash==FR_OK)
  56.     {
  57.       printf("》文件系统挂载成功\r\n");
  58.     }
  59.     else
  60.     {
  61.       printf("!!文件系统挂载失败:(%d)\r\n",res_flash);
  62.     }

  63.     /*----------------------- 格式化测试 -----------------*/
  64.     /* 如果没有文件系统就格式化创建创建文件系统 */
  65.     //如果 f_mount 函数返回值为 FR_NO_FILESYSTEM,说明没有 FAT 文件系统,比如新出厂的 SPI Flash 芯片就没有 FAT 文件系统。我们就必须对物理设备进行格式化处理。
  66.     if(res_flash == FR_NO_FILESYSTEM)
  67.     {
  68.         printf("》FLASH还没有文件系统,即将进行格式化...\r\n");
  69.         /* 格式化 */
  70.         //f_mkfs:在驱动器上创建一个文件系统
  71.         //f_mkfs 函数有三个形参,
  72.         //第一个参数为逻辑设备编号;
  73.         //第二参数可选 0 或者 1, 0 表示设备为一般硬盘, 1 表示设备为软盘。
  74.         //第三个参数指定扇区大小,如果为 0,表示通过代码 disk_ioctl 函数获取。格式化成功后需要先取消挂载原来设备,再重新挂载设备
  75.         res_flash=f_mkfs("0:",0,work,sizeof(work));

  76.         if(res_flash == FR_OK)
  77.         {
  78.             printf("》FLASH已成功格式化文件系统。\r\n");
  79.             /* 格式化后,先取消挂载 */
  80.             res_flash = f_mount(NULL,"0:",1);
  81.             /* 重新挂载   */
  82.             res_flash = f_mount(&fs,"0:",1);
  83.         }
  84.         else
  85.         {
  86.             printf("《《格式化失败。》》\r\n");
  87.             while(1);
  88.         }

  89.     }
  90.     else if(res_flash!=FR_OK)
  91.     {
  92.         printf("!!外部Flash挂载文件系统失败。(%d)\r\n",res_flash);
  93.         printf("!!可能原因:SPI Flash初始化不成功。\r\n");
  94.         while(1);
  95.     }
  96.     else
  97.     {
  98.         printf("》文件系统挂载成功,可以进行读写测试\r\n");
  99.     }

  100.     /*----------------------- 文件系统测试:创建文件目录 -------------------*/
  101.     //strcpy:strcpy把含有'\0'结束符的字符串复制到另一个地址空间,返回值的类型为char*
  102.     strcpy(filename, "123");
  103.     //f_mkdir:创建一个目录
  104.     res_flash = f_mkdir(filename);
  105.     if(res_flash==FR_OK)
  106.     {
  107.         printf("》文件夹%s创建成功\r\n",filename);
  108.     }
  109.     else if(res_flash==FR_EXIST)
  110.     {
  111.         printf("!!文件夹已存在:(%d)\r\n",res_flash);
  112.     }
  113.     else
  114.     {
  115.         printf("!!文件夹创建失败:(%d)\r\n",res_flash);
  116.     }

  117.     strcpy(filename, "123/456");
  118.     res_flash = f_mkdir(filename);
  119.     if(res_flash==FR_OK)
  120.     {
  121.         printf("》文件夹%s创建成功\r\n",filename);
  122.     }
  123.     else if(res_flash==FR_EXIST)
  124.     {
  125.         printf("!!文件夹已存在:(%d)\r\n",res_flash);
  126.     }
  127.     else
  128.     {
  129.         printf("!!文件夹创建失败:(%d)\r\n",res_flash);
  130.     }

  131.     strcpy(filename, "123/456/789");
  132.     res_flash = f_mkdir(filename);
  133.     if(res_flash==FR_OK)
  134.     {
  135.         printf("》文件夹%s创建成功\r\n",filename);
  136.     }
  137.     else if(res_flash==FR_EXIST)
  138.     {
  139.         printf("!!文件夹已存在:(%d)\r\n",res_flash);
  140.     }
  141.     else
  142.     {
  143.         printf("!!文件夹创建失败:(%d)\r\n",res_flash);
  144.     }

  145.     /*----------------------- 文件系统测试:写测试 -------------------*/
  146.     /* 打开文件,每次都以新建的形式打开,属性为可写 */
  147.     printf("\r\n****** 即将进行文件写入测试... ******\r\n");
  148.     //f_open:打开或创建一个文件     FA_CREATE_ALWAYS:创建一个新文件,如果文件已存在,则它将被被截断并覆盖
  149.     res_flash = f_open(&fnew, "0:123/456/789/CH32V103x.txt",FA_CREATE_ALWAYS | FA_WRITE );
  150.     if ( res_flash == FR_OK )
  151.     {
  152.         printf("》打开/创建CH32V103x.txt文件成功,向文件写入数据。\r\n");
  153.         /* 将指定存储区内容写入到文件内 */
  154.         //f_write:写文件
  155.         res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
  156.     if(res_flash==FR_OK)
  157.     {
  158.         printf("》文件写入成功,写入字节数据:%d\r\n",fnum);
  159.         printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
  160.     }
  161.     else
  162.     {
  163.         printf("!!文件写入失败:(%d)\n",res_flash);
  164.     }
  165.         /* 不再读写,关闭文件 */
  166.         //f_close:关闭一个文件
  167.         f_close(&fnew);
  168.     }
  169.     else
  170.     {
  171.         printf("!!打开/创建文件失败。\r\n");
  172.     }

  173.     /*------------------- 文件系统测试:读测试 --------------------------*/
  174.     printf("****** 即将进行文件读取测试... ******\r\n");
  175.     //f_open:打开或创建一个文件     FA_OPEN_EXISTING:打开一个文件,如果文件不存在,则打开失败(默认)
  176.     res_flash = f_open(&fnew, "0:123/456/789/CH32V103x.txt",FA_OPEN_EXISTING | FA_READ);
  177.     if(res_flash == FR_OK)
  178.     {
  179.         printf("》打开文件成功。\r\n");
  180.         //f_read:读文件
  181.         res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
  182.     if(res_flash==FR_OK)
  183.     {
  184.         printf("》文件读取成功,读到字节数据:%d\r\n",fnum);
  185.         printf("》读取得的文件数据为:\r\n%s \r\n", ReadBuffer);
  186.     }
  187.     else
  188.     {
  189.         printf("!!文件读取失败:(%d)\n",res_flash);
  190.     }
  191.     }
  192.     else
  193.     {
  194.         printf("!!打开文件失败。\r\n");
  195.     }
  196.     /* 不再读写,关闭文件 */
  197.     f_close(&fnew);

  198.     printf("*************** 文件信息获取测试 **************\r\n");
  199.      /* 操作完成,停机 */
  200.     printf("》开始扫描文件目录....\r\n");
  201.     strcpy(pathBuff, "");
  202.     scan_files(pathBuff);

  203.     /* 不再使用文件系统,取消挂载文件系统 */
  204.     //f_mount:注册或注销一个工作区域
  205.     f_mount(NULL,"0:",1);
  206.     while(1)
  207.     {

  208.     }
  209. }

  210. FRESULT scan_files (
  211.     char* path        /* Start node to be scanned (***also used as work area***) */
  212. )
  213. {
  214.     FRESULT res;
  215.     DIR dir;
  216.     UINT i;
  217.     static FILINFO fno;

  218.     //f_opendir:打开一个目录
  219.     res = f_opendir(&dir, path);                       /* Open the directory */
  220.     if (res == FR_OK)
  221.     {
  222.         for (;;)
  223.         {
  224.             //f_readdir:读取目录条目
  225.             res = f_readdir(&dir, &fno);                   /* Read a directory item */
  226.             if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */
  227.             if (fno.fattrib & AM_DIR)//假如是文件夹
  228.             {
  229.                 /* It is a directory */
  230.                 i = strlen(path);                          //计算出路径长度
  231.                 sprintf(&path[i], "/%s", fno.fname);       //文件名加到路径后面
  232.                 res = scan_files(path);                    /* Enter the directory */
  233.                 if (res != FR_OK) break;
  234.                 path[i] = 0;
  235.             }
  236.             else
  237.             {                                       /* It is a file. */
  238.                 printf("%s/%s\r\n", path, fno.fname);
  239.             }
  240.         }
  241.         //f_closedir:关闭一个已经打开的目录
  242.         f_closedir(&dir);
  243.     }
  244.     return res;
  245. }

Main.c文件主要进行文件创建、读写测试等。

4下载验证
将编译好的程序下载到开发板并复位,串口打印如下:
图片4.png

107、Flash-FatFs.rar

1.32 MB, 下载次数: 62

kkzz 发表于 2021-10-2 11:47 | 显示全部楼层
fatfs 会很费单片机内存吗  
hudi008 发表于 2021-10-2 11:47 | 显示全部楼层
fatfs单片机能读写csv文件吗
lzmm 发表于 2021-10-2 11:47 | 显示全部楼层
znfat 与fatfs哪个好  
minzisc 发表于 2021-10-2 11:47 | 显示全部楼层
有什么适合单片机的文件系统?  
selongli 发表于 2021-10-2 11:48 | 显示全部楼层
用sd卡 存储单片机数据  
fentianyou 发表于 2021-10-2 11:48 | 显示全部楼层
用FATFS文件系统   
xiaoyaodz 发表于 2021-10-2 11:48 | 显示全部楼层
怎么对单片机内部flash进行坏块检测?
febgxu 发表于 2021-10-2 11:48 | 显示全部楼层
单片机一般提供的ROM和RAM都比较小。  
pixhw 发表于 2021-10-2 11:49 | 显示全部楼层
FAT文件系统的SD卡单片机读写方法详解
sdlls 发表于 2021-10-2 11:49 | 显示全部楼层
支持sd卡和flash吗?        

评论

支持  发表于 2021-10-8 10:59
kkzz 发表于 2022-11-3 21:43 | 显示全部楼层
fatfs会占用多大的内存?如何降低ram占用量
wilhelmina2 发表于 2022-11-3 21:59 | 显示全部楼层
能不能利用FatFs和ch32实现U盘的功能呢?
modesty3jonah 发表于 2022-11-5 09:49 | 显示全部楼层
FatFs支持多大的sd卡呢?              
geraldbetty 发表于 2022-11-5 10:15 | 显示全部楼层
ch32v307是否有sdio的接口呢?
PeerS01 发表于 2023-1-14 22:16 | 显示全部楼层
应该出个 u盘的读写就好了
trucyw 发表于 2024-3-20 14:22 | 显示全部楼层
Spi flash file system.
f_mount disk0 =3!
!!文件系统挂载失败:(3)
!!外部Flash挂载文件系统失败.(3)
!!可能原因:SPI Flash初始化不成功.
提示如上的原因是什么呢???
forgot 发表于 2024-3-21 14:13 | 显示全部楼层
FatFs是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

296

帖子

45

粉丝
快速回复 在线客服 返回列表 返回顶部