查看: 1386|回复: 0
收起左侧

第一百零八章:CH32V103应用教程——文件系统FatFs

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-9-10 16:03:42 | 显示全部楼层 |阅读模式

    有人预言,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文件夹下源码文件,因此对其做主要介绍。
    CH32V CH573单片机芯片-第一百零八章:CH32V103应用教程——文件系统FatFsrisc-v单片机中文社区(1)

    CH32V CH573单片机芯片-第一百零八章:CH32V103应用教程——文件系统FatFsrisc-v单片机中文社区(2)

    • 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短接。
    CH32V CH573单片机芯片-第一百零八章:CH32V103应用教程——文件系统FatFsrisc-v单片机中文社区(3)

    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、下载验证

    将编译好的程序下载到开发板并复位,串口打印如下:
    CH32V CH573单片机芯片-第一百零八章:CH32V103应用教程——文件系统FatFsrisc-v单片机中文社区(4)
    CH32V CH573单片机芯片-第一百零八章:CH32V103应用教程——文件系统FatFsrisc-v单片机中文社区(5) 107、Flash-FatFs.rar (1.32 MB, 下载次数: 70)






    上一篇:第一百零七章:CH32V103应用教程——PD0、1引脚的复用
    下一篇:恭喜CH32V307入选《第十七届全国大学生智能车竞赛》编队车MCU
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-3-28 20:15 , Processed in 0.518720 second(s), 48 queries .

    快速回复 返回顶部 返回列表