草帽王子 发表于 2021-4-25 21:27:05

第二十六章:CH32V103应用教程——FATFS文件系统(SD卡)

本帖最后由 草帽王子 于 2021-9-10 17:10 编辑

本章教程将使用FATFS文件系统来管理SD卡,实现对SD卡文件读写等基本功能。


1、FATFS简介及相关函数介绍

FATFS 是一种免费开源的、且面向小型嵌入式系统的一种通用FAT文件系统。其采用标准C语言编写并完全独立于底层I/O介质,具有良好的硬件平**立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM 等系列单片机上而只需做简单的修改。它支持 FATl2、FATl6和FAT32等格式,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。

本章教程在上一章SD卡教程的基础上,将FATFS文件系统代码移植到SD卡工程中,并对其进行读写测试。关于FATFS文件系统源码可从FATFS官网进行下载。

FATFS源码文件夹src文件夹主要包含以下几个文件:


[*]option文件夹下文件为一些可选外部c文件,包含多语言支持需要用到的文件和转换函数;
[*]diskio.c文件是 FATFS移植最关键的文件,它为文件系统提供了最底层的访问SD卡SPI接口的方法。其包含底层存储介质的操作函数,这些函数需要用户自己实现,主要添加底层驱动函数。
[*]diskio.h定义了FATFS用到的宏,以及diskio.c文件内与底层硬件接口相关的函数声明。
[*]integer.h文件中包含了一些数值类型定义。
[*]ff.c文件是FATFS核心文件,是文件管理的实现方法。该文件独立于底层介质操作文件的函数,利用这些函数实现文件的读写。
[*]ffconf.h这个头文件包含了对FATFS功能配置的宏定义, 通过修改这些宏定义就可以裁剪FATFS的功能。

关于FATFS文件系统模块的移植,我们只需把上述几个文件添加到SD卡工程中并进行一些修改即可。


2、硬件设计

本章教程主要在SD卡基础上进行FATFS文件系统模块的移植,所需资源与上一章一致。


3、软件设计

本章教程在上一章SD卡测试基础上进行FATFS文件系统模块的移植,将FATFS源码文件夹下文件添加工工程之后,只需进行以下修改即可:

1、由于本款开发板芯片ROM容量限制原因,option文件夹中选用文件ccsbcs.c文件;

2、对ffconf.h文件进行以下修改:


[*]#define _USE_MKFS1,这个用来定时是否使能格式化,本章需要用到,所以设置这里为1。
[*]#define _CODE_PAGE 437,此选项指定要在目标系统上使用的OEM代码页,错误的代码页设置可能会导致文件打开失败,此处选择U.S.,语言类型选择为英文,437,此处需要和ccsbcs.c文件同步使用;
[*]#define _USE_LFN   1,该选项用于设置是否支持长文件名(还需要_CODE_PAGE 支持),取值范围为 0~3。0,表示不支持长文件名,1~3 是支持长文件名,但是存储地方不一样,我们选择使用1,通过启用BSS上的静态工作缓冲区启用LFN;
[*]#define _VOLUMES   2,用于设置FATFS支持的逻辑设备数目,我们设置为 2,即支持2个设备;
[*]#define _MIN_SS    512,指定扇区缓冲最小值;
[*]#define _MAX_SS   4096,指定扇区缓冲最大值。

3、关于diskio.c文件,其具体程序如下:

#include "diskio.h"
#include "sd.h"
#include "malloc.h"


#define SD_CARD0//SD卡,卷标为0
#define EX_FLASH 1//外部flash,卷标为1

#define FLASH_SECTOR_SIZE   512

//初始化磁盘
DSTATUS disk_initialize (
    BYTE pdrv               /* Physical drive nmuber (0..) */
)
{
    u8 res=0;
    switch(pdrv)
    {
      case SD_CARD://SD卡
            res = SD_Initialize();//SD_Initialize()
            if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
            {
                SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
            }
            break;
      case EX_FLASH://外部flash
            break;
      default:
            res=1;
    }
    if(res)returnSTA_NOINIT;
    else return 0; //初始化成功
}

//获得磁盘状态
DSTATUS disk_status (
    BYTE pdrv       /* Physical drive nmuber (0..) */
)
{
    return 0;
}

//读扇区
//drv:磁盘编号0~9
//*buff:数据接收缓冲首地址
//sector:扇区地址
//count:需要读取的扇区数
DRESULT disk_read (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE *buff,   /* Data buffer to store read data */
    DWORD sector,   /* Sector address (LBA) */
    UINT count      /* Number of sectors to read (1..128) */
)
{
    u8 res=0;
    if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误
    switch(pdrv)
    {
      case SD_CARD://SD卡
            res=SD_ReadDisk(buff,sector,count);
            if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
            {
                SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
            }
            break;
      case EX_FLASH://外部flash
            res=0;
            break;
      default:
            res=1;
    }
   //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
    if(res==0x00)return RES_OK;
    else return RES_ERROR;
}

//写扇区
//drv:磁盘编号0~9
//*buff:发送数据首地址
//sector:扇区地址
//count:需要写入的扇区数
#if _USE_WRITE
DRESULT disk_write (
    BYTE pdrv,          /* Physical drive nmuber (0..) */
    const BYTE *buff,   /* Data to be written */
    DWORD sector,       /* Sector address (LBA) */
    UINT count          /* Number of sectors to write (1..128) */
)
{
    u8 res=0;
    if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误
    switch(pdrv)
    {
      case SD_CARD://SD卡
            res=SD_WriteDisk((u8*)buff,sector,count);
            break;
      case EX_FLASH://外部flash
            res=0;
            break;
      default:
            res=1;
    }
    //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
    if(res == 0x00)return RES_OK;
    else return RES_ERROR;
}
#endif


//其他表参数的获得
//drv:磁盘编号0~9
//ctrl:控制代码
//*buff:发送/接收缓冲区指针
#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;
    if(pdrv==SD_CARD)//SD卡
    {
      switch(cmd)
      {
            case CTRL_SYNC:
                SD_CS_L;
                if(SD_WaitReady()==0)res = RES_OK;
                else res = RES_ERROR;
                SD_CS_H;
                break;
            case GET_SECTOR_SIZE:
                *(WORD*)buff = 512;
                res = RES_OK;
                break;
            case GET_BLOCK_SIZE:
                *(WORD*)buff = 8;
                res = RES_OK;
                break;
            case GET_SECTOR_COUNT:
                *(DWORD*)buff = SD_GetSectorCount();
                res = RES_OK;
                break;
            default:
                res = RES_PARERR;
                break;
      }
    }
    else res=RES_ERROR;//其他的不支持
    return res;
}
#endif
//获得时间
//User defined function to give a current time to fatfs module      */
//31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
//15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
DWORD get_fattime (void)
{
    return 0;
}diskio.c文件主要进行磁盘初始化以及进行获取磁盘状态和读写扇区等操作;

4、关于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 "sd.h"
#include "ff.h"


//定义变量
FATFS fs;                     /* FatFs文件系统对象 */
FIL fnew;                     /* 文件对象 */
FRESULT res_sd;               /* 文件操作结果 */
UINT fnum;                  /* 文件成功读写数量 */
BYTE ReadBuffer={0};    /* 读缓冲区 */
BYTE WriteBuffer[] =          /* 写缓冲区*/
"欢迎使用沁恒CH32V103开发板,新建文件系统测试文件\r\n";

/*******************************************************************************
* Function Name: main
* Description    : Main program.
* Input          : None
* Return         : None
*******************************************************************************/
int main(void)
{
    u32 sd_size;

      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
      USART_Printf_Init(115200);

      printf("SystemClk:%d\r\n",SystemCoreClock);
      printf("This is SD FATFS example\r\n");

      if(SD_Detect()==0)
      {
            printf("未检测SD卡插入!\n");
      }
      else
      {
            printf("已检测SD卡插入!\n");

            if(SD_Initialize())
            {
                printf("SD卡初始化出错,请检查!!\n");
                Delay_Ms(500);
            }
            else
            {
                printf("SD卡初始化完成!\n");
                sd_size=SD_GetSectorCount();//得到扇区数
                printf("SD Card Size(MB):%d\n",sd_size>>11);
            }
      }

    //在外部SPI SD挂载文件系统,文件系统挂载时会对SPI设备初始化
    res_sd = f_mount(&fs,"0:",1);

    /*----------------------- 格式化测试 ---------------------------*/
      /* 如果没有文件系统就格式化创建创建文件系统 */
      if(res_sd == FR_NO_FILESYSTEM)
      {
            printf("》SD卡还没有文件系统,即将进行格式化...\r\n");
      /* 格式化 */
            res_sd=f_mkfs("0:",0,0);

            if(res_sd == FR_OK)
            {
                printf("》SD卡已成功格式化文件系统。\r\n");
          /* 格式化后,先取消挂载 */
                res_sd = f_mount(NULL,"0:",1);
          /* 重新挂载   */
                res_sd = f_mount(&fs,"0:",1);
            }
            else
            {
                printf("《《格式化失败。》》res_sd =%d\r\n",res_sd);
                while(1);
            }
      }
      else if(res_sd!=FR_OK)
      {
      printf("!!SD卡挂载文件系统失败。(%d)\r\n",res_sd);
      printf("!!可能原因:SD卡初始化不成功。\r\n");
            while(1);
      }
      else
      {
      printf("》文件系统挂载成功,可以进行读写测试\r\n");
      }
      /*----------------------- 文件系统测试:写测试 -----------------------------*/
            /* 打开文件,如果文件不存在则创建它 */
            printf("\r\n****** 即将进行文件写入测试... ******\r\n");
            res_sd = f_open(&fnew, "0:FatFs读写测试文件.txt",FA_CREATE_ALWAYS | FA_WRITE );
            if ( res_sd == FR_OK )
            {
                printf("》打开/创建FatFs读写测试文件.txt文件成功,向文件写入数据。\r\n");
            /* 将指定存储区内容写入到文件内 */
                res_sd=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
            if(res_sd==FR_OK)
            {
            printf("》文件写入成功,写入字节数据:%d\n",fnum);
            printf("》向文件写入的数据为:\r\n%s\r\n",WriteBuffer);
            }
            else
            {
            printf("!!文件写入失败:(%d)\n",res_sd);
            }
                /* 不再读写,关闭文件 */
            f_close(&fnew);
            }
            else
            {
                printf("!!打开/创建文件失败。\r\n");
            }

      while(1)
    {
      }
}

main.c文件主要进行相关函数初始化以及读取输出相关信息。


4、下载验证

将编译好的程序下载到开发板并复位,当未插入SD卡时,串口打印情况具体如下:

插入SD卡并复位后,串口打印情况如下:

FATFS.rar附件下载

链接:https://pan.baidu.com/s/1jaXEXdeeosZ54ArMM3w5Hw
提取码:y4x4
复制这段内容后打开百度网盘手机App,操作更方便哦



页: [1]
查看完整版本: 第二十六章:CH32V103应用教程——FATFS文件系统(SD卡)