【电子DIY】DIY行车记录仪为游戏机之主控软件开发(7)fatfs文件系统移植

代码见:https://github.com/qinyunti/py32f403-nes.git

一.前言

前面我们实现了FLASH的读写,现在开始就可以移植文件系统了,这里移植比较常用的FATFS。

二.移植过程

2.1添加文件

下载源码

从以下地址http://elm-chan.org/fsw/ff/00index_e.html下载最新源码

214051nv8q48e20mmmkjx8

保留以下文件

214051x49n6zf6nnatd6t9

工程中创建fatfs目录,添加上述文件

214051up777jnpdp6b7vpe

并添加头文件包含路径

214051z07ti0tueiqxruky

2.2适配读写接口

diskio.h

修改diskio.c

先只实现一个设备0,路径以0:开头

#include "ff.h"                       

#include "diskio.h"               

#include "flash_itf.h"

#define DEV_FLASH                0       

disk_status

获取状态,直接返回OK

DSTATUS disk_status (

        BYTE pdrv                /* Physical drive nmuber to identify the drive */

)

{

        switch (pdrv) {

        case DEV_FLASH :

                return RES_OK;

        }

        return STA_NOINIT;

}

disk_initialize

驱动在其他地方已经初始化,这里直接返回OK

DSTATUS disk_initialize (

        BYTE pdrv                                /* Physical drive nmuber to identify the drive */

)

{

        switch (pdrv) {

        case DEV_FLASH :

                return RES_OK;

        }

        return STA_NOINIT;

}

disk_read

这里是按照sector为单位的,转化为字节为单位

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 */

)

{

        switch (pdrv) {

        case DEV_FLASH :

                flash_itf_read(buff, sector*512, count*512);

                return RES_OK;

        }

        return RES_PARERR;

}

disk_write

这里是按照sector为单位的,转化为字节为单位

#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 */

)

{

        switch (pdrv) {

        case DEV_FLASH :

                flash_itf_write((uint8_t*)buff, sector*512, count*512);

                return RES_OK;

        }

        return RES_PARERR;

}

#endif

disk_ioctl

获取容量等,先固定,以后再优化为通过flash接口自动获取

DRESULT disk_ioctl (

        BYTE pdrv,                /* Physical drive nmuber (0..) */

        BYTE cmd,                /* Control code */

        void *buff                /* Buffer to send/receive control data */

)

{

        DRESULT res;

        switch (pdrv) {

        case DEV_FLASH :

                // Process of the command for the RAM drive

                switch(cmd)

                {

                        case CTRL_SYNC:

                                res = RES_OK;

                        break;

                        case GET_SECTOR_SIZE:

                                *(WORD*)buff = 512;

                                res = RES_OK;

                        break;

                        case GET_BLOCK_SIZE:

                                *(WORD*)buff = 4096;

                                res = RES_OK;

                        break;

                        case GET_SECTOR_COUNT:

                                *(DWORD*)buff = 131072;

                                res = RES_OK;

                        break;

                        case CTRL_TRIM:

                                res = RES_OK;

                        break;

                        default:

                                res = RES_PARERR;

                        break;

                }

                return res;

        }

        return RES_PARERR;

}

2.3时间接口

默认需要实现

get_fattime()

我们修改ffconf.h中

#define FF_FS_NORTC                0

#define FF_FS_NORTC                1

不使能时间戳功能。

2.4长文件名支持

ffconf.h中

#define FF_USE_LFN                0改为

#define FF_USE_LFN                3

如果配置为1则使用静态变量,线程不安全。

如果配置为2则使用栈,会占用较大栈容易栈溢出。

如果配置为3则使用动态内存分配,需要实现接口ff_memalloc()和ff_memfree()

我这里配置为3

ffsystem.c中

#if FF_USE_LFN == 3        /* Use dynamic memory allocation */

#include "FreeRTOS.h"

/*------------------------------------------------------------------------*/

/* Allocate/Free a Memory Block                                           */

/*------------------------------------------------------------------------*/

void* ff_memalloc (        /* Returns pointer to the allocated memory block (null if not enough core) */

        UINT msize                /* Number of bytes to allocate */

)

{

        return pvPortMalloc(msize);        /* Allocate a new memory block */

}

void ff_memfree (

        void* mblock        /* Pointer to the memory block to free (no effect if null) */

)

{

        vPortFree(mblock);        /* Free the memory block */

}

#endif

2.5线程安全

ffconf.h中

以下宏配置为1

#define FF_FS_REENTRANT        1

ffsystem.c中

以下宏配置为3使用FreeRTOS,其他不支持的系统需要移植对应互斥量的接口。

#define OS_TYPE        3       

2.6其他配置

配置

#define FF_USE_MKFS                1支持格式化。

三.测试

Main.c中初始化

#include “ff.h”

static FATFS fs;             /* Filesystem object */

static BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */

static void fs_init(void)

{

    FRESULT res;        /* API result code */

                if(FR_OK != (res = f_mount (&fs, "0:", 1)))

          {

                        xprintf("mount err %d, mkfs\r\n",res);

                        res = f_mkfs("0:",0,work,sizeof(work));

                        if(res == 0)

                        {

                                xprintf("mkfs ok\r\n");

                                if(FR_OK == f_mount (&fs, "0:", 1))

                                {

                                        xprintf("mount ok\r\n");

                                }

                                else

                                {

                                        xprintf("mount err\r\n");

                                }

                        }

                        else

                  {

                                xprintf("mkfs err %d\r\n",res);

                        }

                }

                else

                {

                        xprintf("mount ok\r\n");

                }

}

在shell 任务中调用fs_init初始化,因为spi接口使用了互斥量等系统api。

在shell_func.c中

#include "ff.h"

添加函数申明

static void printfilefunc(uint8_t* param);

static void writefilefunc(uint8_t* param);

g_shell_cmd_list_ast中添加两行

  { (uint8_t*)"printfile",    printfilefunc,    (uint8_t*)"printfile path addr size"},  

  { (uint8_t*)"writefile",    writefilefunc,    (uint8_t*)"writefile path addr[hex] hexstr"},

实现如下

void printfilefunc(uint8_t* param)

{

  char path[128];

  uint8_t tmp[16];

  uint32_t addr;

  uint32_t size;

  FRESULT res;

  FIL fil;

  UINT br;

  uint32_t offset = 0;

  if(3 == sscanf((const char*)param, "%*s %s %x %d", path, &addr, &size))

  {

    xprintf("hexdump %s 0x%x %d\r\n",path,addr,size);

    if(FR_OK == (res=f_open(&fil,(const char*)path, FA_READ)))

    {

      xprintf("\r\n");

      do

      {

        br = 0;

        if(FR_OK == (res = f_read(&fil,tmp,(size>16)?16:size,&br)))

        {

          xprintf("%08x ",offset);

          offset+=br;

          for(uint32_t i=0;i<br;i++)

          {

            xprintf("%02x",(uint32_t)tmp);

          }

          xprintf(":");

          for(uint32_t i=0;i<br;i++)

          {

            xprintf("%c",((tmp>0x1F)&&(tmp<0x7F))?(char)tmp:'.');

          }

          xprintf("\r\n");

          size -= br;

        }

        else

        {

          break;

        }

      }while(br > 0);

      if(FR_OK != (res = f_close(&fil)))

      {

        xprintf("close %s err %d\r\n",path,res);

      }

    }

    else

    {

      xprintf("open %s err %d",path,res);

    }

  }

  else

  {

    xprintf("param err");

  }

}

void writefilefunc(uint8_t* param)

{

  char path[128];

  uint8_t hexstr[32+1];

  uint8_t tmp[16];

  uint32_t hexnum = 0;

  uint32_t addr;

  FRESULT res;

  FIL fil;

  UINT bw;

  if(3 == sscanf((const char*)param, "%*s %s %x %s", path, &addr, hexstr))

  {

    xprintf("hexwrite %s 0x%x %s\r\n",path,addr,hexstr);

    if(FR_OK == (res=f_open(&fil,(const char*)path, FA_WRITE | FA_CREATE_ALWAYS)))

    {

      xprintf("\r\n");

      hexnum = str2hex((const char*)hexstr,tmp,32);

      if(hexnum > 0)

      {

        if(FR_OK == (res=(f_lseek(&fil,addr))))

        {

          if(FR_OK != (res = f_write(&fil,tmp,hexnum,&bw)))

          {

            xprintf("write err %d\r\n",res);

          }

        }

        else

        {

          xprintf("seek %d err %d\r\n",addr,res);

        }

      }

      if(FR_OK != (res = f_close(&fil)))

      {

        xprintf("close %s err %d\r\n",path,res);

      }

    }

    else

    {

      xprintf("open %s err %d",path,res);

    }

  }

  else

  {

    xprintf("param err");

  }

}

初始格式化需要一点时间,

writefile 0:/test.bin 0 112233445566778899写入文件

printfile 0:/test.bin 0 16读出文件看到一致

214051kknwv8hhkfrwh13w

四.总结

以上我们完成了FATFS的移植,并基于SHELL进行了文件读写测试,后面就可以继续实现文件的相关操作命令以及文件到导入导出。