FATFS文件系统移植

上文中完成了sd卡的驱动代码,本文陈述如何系统移植文件系统

源码下载

源码可以登录fatfs官网下载:http://elm-chan.org/fsw/ff/archives.html,我这里下载最新版本的源码包,下载完成后解压到本地大致浏览源码包的文件结构,主目录下有documents与source两个文件夹,其中documents中教会用户如何使用fatfs,source中主要包含三部分代码:

  • 物理层接口:diskio.c/diskio.h
  • fatfs源码:ff.c/ff.h
  • fatfs配置文件:ffconf.h
  • 操作系统扩展功能:ffsystem.c
  • 语言编码表:ffunicode.c
    我移植时的需求:不使用操作系统,编码语言使用英文,所以我只用到diskio.c/diskio.h、ff.c/ff.h、ffconf.h这五个文件,并将其添加到自己的工程中

    接口绑定

    上文中我们完成了sd卡的驱动代码,预留了如下几个接口:

    uint8_t SD_WaitReady(void);
    uint8_t SD_Init(void);
    uint32_t SD_GetSectorCount(void);
    uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
    uint8_t SD_WriteDisk(const uint8_t*buf,uint32_t sector,uint8_t cnt);

    我们将其与diskio.c中的接口文件绑定:
    首先需要新建一个我们自己的设备并删除掉代码中的示例设备:
    #define DEV_SD 0
    在获取磁盘状态的代码中新增我们的设备代码(这里我让我的设备状态无论如何返回OK):

    DSTATUS disk_status (
      BYTE pdrv
    )
    {
      DSTATUS stat;
      int result;
    
      switch (pdrv) {
      case DEV_SD :
          stat = RES_OK;
      return stat;
      }
      return STA_NOINIT;
    }

    在磁盘初始化代码中绑定我们的SD卡初始化代码:

    DSTATUS disk_initialize (
      BYTE pdrv                /* Physical drive nmuber to identify the drive */
    )
    {
      DSTATUS stat;
      int result;
    
      switch (pdrv) {
      case DEV_SD :
          result = SD_Init();
          if(0 == result)
          {
              stat = RES_OK;
          }
          return stat;
      }
      return STA_NOINIT;
    }

    在设备读扇区接口中绑定我们的读sd卡设备代码:

    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 res;
      int result;
    
      switch (pdrv) {
          case DEV_SD :
    
          result = SD_ReadDisk(buff, sector, count);
    
          if(0 == result)
          {
              res = RES_OK;
          }
    
          return res;
      }
    
      return RES_PARERR;
    }

    在设备写数据接口中绑定我们的写sd卡数据代码:

    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 res;
      int result;
    
      switch (pdrv) {
          case DEV_SD :
    
          result = SD_WriteDisk(buff, sector, count);
    
          if(0 == result)
          {
              res = RES_OK;
          }
    
          return res;
      }
    
      return RES_PARERR;
    }

    在设备状态获取接口中返回我们的设备状态代码:

    这里总共获取设备的四种状态:

    • 设备是否就绪
    • 设备的扇区数量,取决于内存卡多大
    • 设备的块大小-恒定512
    • 设备的扇区大小-恒定512
    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 DEV_SD :
      switch (cmd) {
          case CTRL_SYNC: 
          if(0 == SD_WaitReady())
          {
              return RES_OK;
          }
          else
          {
              printf("not ready\r\n");
              return RES_NOTRDY;
          }
          case GET_SECTOR_COUNT: *(DWORD * )buff  = SD_GetSectorCount();return RES_OK;
          case GET_SECTOR_SIZE: *(DWORD * )buff  = 512 ;return RES_OK;
          case GET_BLOCK_SIZE: *(WORD * )buff = 512 ;return RES_OK;
      }
          return res;
      }
      return RES_PARERR;
    }

    fatfs配置

    FF_FS_READONLY -是否开启只读功能
    FF_FS_MINIMIZE -是否裁剪功能
    FF_USE_FIND -是否开启文件寻找功能
    FF_USE_MKFS -是否开启格式化功能(重要)
    FF_FS_NORTC -关闭RTC功能(如果使用rtc则需要实现获取时间的接口)
    FF_CODE_PAGE -使用哪种编码语言
    详细的配置说明可以查看官网文档

    文件系统demo测试

    以上完成fatfs的移植,我们就可以在单片机上对SD卡实现PC端一样的文件操作,详细的接口使用还是去看官方文档,我实现了一个demo,挂载sd卡,如果未成功挂载SD卡就将其格式化,然后读取sd卡中的test.jpg,并拷贝一份重命名为copy.jpg:

    uint8_t stat;
    stat=f_mount(&fs,"0:",1);//SD卡挂载
    printf("f_mount:%d\r\n",stat);
    if(stat!=FR_OK)
    {
      printf("f_mkfs :FAT32\r\n");
      stat = f_mkfs("0:", 0,(void*)f_mkfs_buff,sizeof(f_mkfs_buff));
      if(stat!=FR_OK)
      {
          printf("f_mkfs failed\r\n");
      }
      else
      {
          printf("f_mkfs successed\r\n");
      }
    }
    else
    {
      printf("SD Card init successed\r\n");
    }
    
    FIL fp;
      FRESULT res;
    UINT bw;
    UINT br;
    res=f_open(&fp,"0:/test.jpg",FA_READ);
    printf("read test.jpg:%d\r\n",res);
    
    uint32_t size = fp.obj.objsize;
    printf("test.jpg size is :%d\r\n",size);
    
    char *data = (char *)malloc(size);
    res=f_read(&fp,data,size,&br);
    f_close(&fp);
    
    res=f_open(&fp,"0:/copy.jpg",FA_READ|FA_WRITE|FA_CREATE_ALWAYS);
    if(res==FR_OK)
    {
      printf("creat copy.jpg\r\n");
      res=f_write(&fp,data,size,&bw);
      if(res==FR_OK)
      {
        printf("write copy.jpg\r\n");
      }
    }
    else
    {
      printf("error :%d\r\n",res);
    }
    f_close(&fp);
    
    free(data);
    printf("over \r\n");

    最终再将内存卡插在电脑上可以发现存在被拷贝的图片(为什么是大写名称我也不知道,可能遗漏了某些东西):
    1.png

其他可能存在的坑:为了保证格式化为FAT32格式,可是又懒滴写配置文件,在f_mkfs源码中修改默认的格式化类型为FAT32格式,这样可以实现windows系统更快的识别内存卡:
static const MKFS_PARM defopt = {FM_FAT32, 0, 0, 0, 0};
文章目录