【电子DIY】DIY行车记录仪为游戏机之主控软件开发(8)文件操作shell命令集

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

一.前言

前面我们基于SPI FLASH移植了FATFS,可以进行文件的操作了。为了方便后面调试,我们继续实现文件操作SHELL命令集,包括读写文件,复制,移动,LS查看等等基本命令。

并且移植xmodem,ymodem等,以便进行游戏文件的导入导出。

Xmodem可以参考公众号文章

https://mp.weixin.qq.com/s/QSYKxND3DTyQZ0JtnSqkzQ?token=1631959641&lang=zh_CN

Ymodem可以参考公众号文章

https://mp.weixin.qq.com/s/uDwVYJRDGrH6MZmUXn2Q3A?token=1631959641&lang=zh_CN

二.SHELL文件操作命令集合

ffconf.h

配置支持目录相关接口

#define FF_FS_RPATH                2

#define FF_USE_EXPAND        1

Main.c中实现

void app_fatfs_mkfs(char* path)

{

    FRESULT res;        /* API result code */

                f_unmount(path);

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

    if(res == 0)

    {

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

                        if(FR_OK == f_mount (&fs, path, 1))

                        {

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

                        }

                        else

                        {

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

                        }

    }

    else

    {

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

    }

}

void app_fatfs_mount(char* path)

{

    FRESULT res;        /* API result code */

                res = f_unmount(path);

                if(FR_OK == (res = f_mount (&fs, path, 1)))

                {

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

                }

                else

                {

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

                }

}

添加以下文件

221836i9rw2nnyz2r6gsnn

Shell_func.c中

#include "xmodem.h"

#include "ymodem.h"

#include "md5.h"

以下函数申明

static void printfilefunc(uint8_t* param);

static void writefilefunc(uint8_t* param);

static void rxfilefunc(uint8_t* param);

static void sxfilefunc(uint8_t* param);

static void ryfilefunc(uint8_t* param);

static void syfilefunc(uint8_t* param);

static void lsfilefunc(uint8_t* param);

static void rmfilefunc(uint8_t* param);

static void renamefilefunc(uint8_t* param);

static void touchfunc(uint8_t* param);

static void mkdirfunc(uint8_t* param);

static void pwdfunc(uint8_t* param);

static void cdfunc(uint8_t* param);

static void cpfilefunc(uint8_t* param);

static void md5sumfunc(uint8_t* param);

static void freefunc(uint8_t* param);

static void mkfsfunc(uint8_t* param);

static void mountfsfunc(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"},

  { (uint8_t*)"rxfile",       rxfilefunc,       (uint8_t*)"rxfile name len"},

  { (uint8_t*)"sxfile",       sxfilefunc,       (uint8_t*)"sxfile name len"},

  { (uint8_t*)"ryfile",       ryfilefunc,       (uint8_t*)"ryfile"},

  { (uint8_t*)"syfile",       syfilefunc,       (uint8_t*)"syfile len path1 path2"},

  { (uint8_t*)"ls    ",       lsfilefunc,       (uint8_t*)"ls path"},

  { (uint8_t*)"rm    ",       rmfilefunc,       (uint8_t*)"rm path"},  

  { (uint8_t*)"rename    ",   renamefilefunc,   (uint8_t*)"rename path newpath"},  

  { (uint8_t*)"touch     ",   touchfunc,        (uint8_t*)"touch path size"},  

  { (uint8_t*)"mkdir     ",   mkdirfunc,        (uint8_t*)"mkdir path"},  

  { (uint8_t*)"pwd     ",     pwdfunc,          (uint8_t*)"pwd"},  

  { (uint8_t*)"cd     ",      cdfunc,           (uint8_t*)"cd path"},  

  { (uint8_t*)"cp",           cpfilefunc,       (uint8_t*)"cp srcpath dstpath"},

  { (uint8_t*)"md5sum",       md5sumfunc,       (uint8_t*)"md5sum path"},

  { (uint8_t*)"free",         freefunc,         (uint8_t*)"free path"},

  { (uint8_t*)"mkfs",         mkfsfunc,         (uint8_t*)"mkfs path"},

  { (uint8_t*)"mountfs",      mountfsfunc,      (uint8_t*)"mountfs path"},

代码实现如下

static uint8_t rxtx_buf[1029];

static uint32_t getms(void)

{

    uint32_t ticks = xTaskGetTickCount();

    return ticks / (configTICK_RATE_HZ / 1000);

}

static uint32_t io_read(uint8_t* buffer, uint32_t len)

{

  return uart_read(0,buffer, len);

}

static void io_read_flush(void)

{

  uint8_t tmp;

  while(0 != uart_read(0,&tmp, 1));

}

static uint32_t io_write(uint8_t* buffer, uint32_t len)

{

  uart_send(0,buffer, len);

  return len;

}

static FIL rx_fil;            /* File object */

static int rx_file_open_flag = 0;

static int rx_file_open(char* name)

{

  FRESULT res = f_open(&rx_fil, name, FA_CREATE_NEW | FA_WRITE);

  if (FR_OK == res)

  {

    rx_file_open_flag = 1;

    return 0;

  }

  else

  {

    xprintf("open %s err %d\r\n",name,res);

    return -1;

  }

}

static int rx_file_close(char* name)

{

  (void)name;

  if(rx_file_open_flag != 0)

  {

    rx_file_open_flag = 0;

    FRESULT res = f_close(&rx_fil);

    if(res != FR_OK)

    {

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

    }

    return 0;

  }

  else

  {

    return -1;

  }

}

static uint32_t  rx_file_write(uint32_t addr, uint8_t* buffer, uint32_t len)

{

  (void)addr;

  if(rx_file_open_flag != 0)

  {

    UINT bw;

    FRESULT res = f_write(&rx_fil, buffer, len, &bw);

    if ((bw != len) || (res != FR_OK))

    {

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

    }

    return bw;

  }

  else

  {

    return 0;

  }

}

void rxfilefunc(uint8_t* param)

{

  char name[64];

  uint32_t len;

  int res = 0;

  if(2 == sscanf((const char*)param, "%*s %s %d", name, &len))

  {

    xprintf("rxfile %s %ld\r\n",name,len);

    if(0 == rx_file_open(name))

    {

      xmodem_cfg_st cfg=

      {

          .buffer = rxtx_buf,

          .crccheck = 1,

          .getms = getms,

          .io_read = io_read,

          .io_read_flush = io_read_flush,

          .io_write = io_write,

          .start_timeout = 60,

          .packet_timeout = 2000,

          .ack_timeout = 2000,

          .mem_write = rx_file_write,

          .addr = 0,

          .totallen = len,

      };

      xmodem_init_rx(&cfg);

      while((res = xmodem_rx()) == 0);

      rx_file_close(name);

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

    }

    else

    {

      xprintf("open:%s err\r\n",name);

    }

  }

}

static FIL tx_fil;            /* File object */

static int tx_file_open_flag = 0;

static int tx_file_open(char* name)

{

  FRESULT res = f_open(&tx_fil, name,  FA_READ);

  if (FR_OK == res)

  {

    tx_file_open_flag = 1;

    return 0;

  }

  else

  {

    xprintf("open %s err %d\r\n",name,res);

    return -1;

  }

}

static int tx_file_close(char* name)

{

  (void)name;

  if(tx_file_open_flag != 0)

  {

    tx_file_open_flag = 0;

    FRESULT res = f_close(&tx_fil);

    if(res != FR_OK)

    {

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

    }

    return 0;

  }

  else

  {

    return -1;

  }

}

static uint32_t tx_file_read(uint32_t addr, uint8_t* buffer, uint32_t len)

{

  (void)addr;

  UINT br;

  FRESULT res = f_read(&tx_fil, buffer, len, &br);

  if(res != 0)

  {

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

  }

  return br;

}

void sxfilefunc(uint8_t* param)

{

  char name[64];

  uint32_t len;

  int res = 0;

  if(2 == sscanf((const char*)param, "%*s %s %d", name, &len))

  {

    xprintf("sxfile %s %ld\r\n",name,len);

    if(0 == tx_file_open(name))

    {

      xmodem_cfg_st cfg=

      {

        .buffer = rxtx_buf,

        .plen = 1024,

        .getms = getms,

        .io_read = io_read,

        .io_read_flush = io_read_flush,

        .io_write = io_write,

        .start_timeout = 60,

        .packet_timeout = 1000,

        .ack_timeout = 5000,

        .mem_read = tx_file_read,

        .addr = 0,

        .totallen = len,

      };

      xmodem_init_tx(&cfg);

      while((res = xmodem_tx()) == 0);

      tx_file_close(name);

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

    }

    else

    {

      xprintf("open:%s err\r\n",name);

    }

  }

}

/* List contents of a directory */

static FRESULT list_dir (const char *path)

{

    FRESULT res;

    DIR dir;

    FILINFO fno;

    int nfile, ndir;

    res = f_opendir(&dir, path);                       /* Open the directory */

    if (res == FR_OK) {

        nfile = ndir = 0;

        for (;;) {

            res = f_readdir(&dir, &fno);                   /* Read a directory item */

            if (res != FR_OK || fno.fname[0] == 0) break;  /* Error or end of dir */

            if (fno.fattrib & AM_DIR) {            /* Directory */

                xprintf("   <DIR>   %s\n", fno.fname);

                ndir++;

            } else {                               /* File */

                xprintf("%10u %s\n", fno.fsize, fno.fname);

                nfile++;

            }

        }

        f_closedir(&dir);

        xprintf("%d dirs, %d files.\n", ndir, nfile);

    } else {

        xprintf("Failed to open \"%s\". (%u)\n", path, res);

    }

    return res;

}

void lsfilefunc(uint8_t* param)

{

  char path[128];

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

    list_dir((const char *)path);

  }

  else

  {

    xprintf("param err");

  }

}

void rmfilefunc(uint8_t* param)

{

  FRESULT res;

  char path[128];

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

    if(FR_OK != (res = f_unlink((const char *)path)))

    {

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

    }

  }

  else

  {

    xprintf("param err");

  }

}

void renamefilefunc(uint8_t* param)

{

  FRESULT res;

  char path[128];

  char newpath[128];

  if(2 == sscanf((const char*)param, "%*s %s %s", path, newpath))

  {

    if(FR_OK != (res = f_rename((const char *)path, (const char*)newpath)))

    {

      xprintf("rename %s to %s err %d\r\n",path,newpath,res);

    }

  }

  else

  {

    xprintf("param err");

  }

}

void touchfunc(uint8_t* param)

{

  FIL fil;

  char path[128];

  int size;

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

  {

    FRESULT res = f_open(&fil, path, FA_CREATE_NEW | FA_WRITE);

    if (FR_OK == res)

    {

      if(FR_OK != (res = f_expand(&fil,size,1)))

      {

        xprintf("expand %s size to %d err %d\r\n",path,size,res);

      }

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

      {

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

      }

    }

    else

    {

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

    }

  }

  else

  {

    xprintf("param err");

  }

}

void mkdirfunc(uint8_t* param)

{

  char path[128];

  FRESULT res;

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

    if(FR_OK != (res=f_mkdir((const char*)path)))

    {

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

    }

  }

  else

  {

    xprintf("param err");

  }

}

void pwdfunc(uint8_t* param)

{

  (void)param;

  FRESULT fr;

  TCHAR str[128];

  if(1 == sscanf((const char*)param, "%*s %s", str)){

                if(FR_OK != (fr = f_getcwd(str, 128)))  /* Get current directory path */

                {

                        xprintf("getcwd err %d\r\n",fr);

                }

                else

                {

                        xprintf("%s\r\n",str);

                }

        }else

  {

    xprintf("param err");

  }

}

void cdfunc(uint8_t* param)

{

  char path[128];

  FRESULT res;

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

    if(FR_OK != (res=f_chdir((const char*)path)))

    {

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

    }

  }

  else

  {

    xprintf("param err");

  }

}

/* md5算法参考 https://www.rfc-editor.org/rfc/rfc1321 */

void md5sumfunc(uint8_t* param)

{

  char path[128];

  uint8_t tmp[64];

  uint8_t out[16];

  FIL fil;

  UINT br;

  FRESULT res;

  MD5_CTX ctx;

  MD5Init(&ctx);

  if(1 == sscanf((const char*)param, "%*s %s",path))

  {

    res = f_open(&fil, path, FA_READ);

    if(res == FR_OK)

    {

      do

      {

        res = f_read(&fil, tmp, sizeof(tmp), &br);

        if(res == 0)

        {

          MD5Update(&ctx, tmp, br);

        }

        else

        {

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

          break;

        }

      }while(br > 0);

      MD5Final(out, &ctx);

      for(int i=0; i<16;i++)

      {

        xprintf("%02x",out);

      }

      xprintf("\r\n");

      f_close(&fil);

    }

    else

    {

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

    }

  }

  else

  {

    xprintf("param err");

  }

}

void freefunc(uint8_t* param)

{

  char path[128];

  FRESULT res;

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

    FATFS *fs;

    DWORD fre_clust, fre_sect, tot_sect;

    /* Get volume information and free clusters of drive 1 */

    res = f_getfree(path, &fre_clust, &fs);

    if(res == FR_OK)

    {

      /* Get total sectors and free sectors */

      tot_sect = (fs->n_fatent - 2) * fs->csize;

      fre_sect = fre_clust * fs->csize;

      /* Print the free space (assuming 512 bytes/sector) */

      xprintf("%10lu KiB total drive space.\n%10lu KiB available.\n", tot_sect / 2, fre_sect / 2);

    }

    else

    {

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

    }

  }

  else

  {

    xprintf("param err");

  }

}

extern void app_fatfs_mkfs(char* path);

void mkfsfunc(uint8_t* param)

{

  char path[128];

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

                app_fatfs_mkfs(path);

  }

  else

  {

    xprintf("param err");

  }

}

extern void app_fatfs_mount(char* path);

void mountfsfunc(uint8_t* param)

{

  char path[128];

  if(1 == sscanf((const char*)param, "%*s %s", path))

  {

                app_fatfs_mount(path);

  }

  else

  {

    xprintf("param err");

  }

}

static FIL ymodem_rx_fil;            /* File object */

static int ymodem_rx_file_open_flag = 0;

static int  ymodem_rx_file_start(uint32_t addr, uint8_t** name, uint32_t* len)

{

  (void)addr;

  (void)len;

  int res = 0;

  if(ymodem_rx_file_open_flag == 0)

  {

    FRESULT res = f_open(&ymodem_rx_fil, (const char*)(*name), FA_CREATE_NEW | FA_WRITE);

    if (FR_OK == res)

    {

      ymodem_rx_file_open_flag = 1;

      return 0;

    }

    else

    {

      xprintf("open %s err %d\r\n",name,res);

      return -1;

    }

  }

  return res;

}

static uint32_t  ymodem_rx_file_write(uint32_t addr, uint8_t* buffer, uint32_t len)

{

  (void)addr;

  if(ymodem_rx_file_open_flag != 0)

  {

    UINT bw;

    FRESULT res = f_write(&ymodem_rx_fil, buffer, len, &bw);

    if ((bw != len) || (res != FR_OK))

    {

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

    }

    return bw;

  }

  else

  {

    return 0;

  }

}

static int  ymodem_rx_file_done(void)

{

  if(0 != ymodem_rx_file_open_flag)

  {

    ymodem_rx_file_open_flag = 0;

    return f_close(&ymodem_rx_fil);

  }

  return 0;

}

void ryfilefunc(uint8_t* param)

{

  (void)param;

  int res = 0;

  ymodem_rx_cfg_st cfg=

  {

    .buffer = rxtx_buf,

    .getms = getms,

    .io_read = io_read,

    .io_write = io_write,

    .start_timeout = 60,

    .packet_timeout = 2000,

    .ack_timeout = 1000,

    .mem_start = ymodem_rx_file_start,

    .mem_write = ymodem_rx_file_write,

    .mem_done = ymodem_rx_file_done,

  };

  ymodem_rx_init(&cfg);

  while((res = ymodem_rx()) == YMODEM_RX_ERR_NEED_CONTINUE);

  xprintf("\r\nres:%d\r\n",res);

}

static int8_t ymodem_sfile_name[2][64];

static int ymodem_tx_file_open_flag = 0;

static uint32_t ymodem_tx_file_num = 0;

static FIL ymodem_tx_file;

static uint8_t ymodem_sfile_name_len_att[128];

/* 获取文件名 */

static int  ymodem_tx_file_start(uint32_t addr, uint8_t** name, uint32_t* len)

{

  (void)addr;

  (void)len;

  uint32_t flen;

  uint32_t fnamelen;

  int res = 0;

  if(ymodem_tx_file_open_flag != 0)

  {

    f_close(&ymodem_tx_file);

    ymodem_tx_file_open_flag = 0;

  }

  if(ymodem_tx_file_num >= sizeof(ymodem_sfile_name)/sizeof(ymodem_sfile_name[0]))

  {

    return -1;

  }

  if(0 == (res = f_open(&ymodem_tx_file, (const char*)ymodem_sfile_name[ymodem_tx_file_num], FA_READ)))

  {

    flen =  f_size(&ymodem_tx_file);

    fnamelen = strlen((const char*)ymodem_sfile_name[ymodem_tx_file_num]);

    memcpy(ymodem_sfile_name_len_att,ymodem_sfile_name[ymodem_tx_file_num],fnamelen);

    ymodem_sfile_name_len_att[fnamelen]=0;

    *len = fnamelen + 1 + snprintf((char*)(&ymodem_sfile_name_len_att[fnamelen+1]),sizeof(ymodem_sfile_name_len_att)-(fnamelen+1),"%d %o %d",flen,1715670058,0);

    *name = ymodem_sfile_name_len_att;

    ymodem_tx_file_open_flag = 1;

  }

  ymodem_tx_file_num++;

  return res;

}

static uint32_t ymodem_tx_file_read(uint32_t addr, uint8_t* buffer, uint32_t len)

{

  (void)addr;

  UINT br;

  FRESULT res = f_read(&ymodem_tx_file, buffer, len, &br);

  if(res != 0)

  {

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

  }

  return br;

}

static int  ymodem_tx_file_done(void)

{

  if(0 != ymodem_tx_file_open_flag)

  {

    ymodem_tx_file_open_flag = 0;

    return f_close(&ymodem_tx_file);

  }

  return 0;

}

void syfilefunc(uint8_t* param)

{

  (void)param;

  int plen = 0;

  int res;

  int num = 0;

  ymodem_tx_cfg_st cfg=

  {

    .buffer = rxtx_buf,

    .getms = getms,

    .io_read = io_read,

    .io_write = io_write,

    .start_timeout = 60,

    .packet_timeout = 2000,

    .ack_timeout = 2000,

    .plen = 1024,

    .mem_start = ymodem_tx_file_start,

    .mem_read = ymodem_tx_file_read,

    .mem_done = ymodem_tx_file_done,

  };

  memset(ymodem_sfile_name,0,sizeof(ymodem_sfile_name));

  ymodem_tx_file_num = 0;

  num = sscanf((const char*)param, "%*s %d %s %s", &plen, ymodem_sfile_name[0],ymodem_sfile_name[1]);

  if((num == 2) || (num == 3))

  {

    cfg.plen = plen;

    ymodem_tx_init(&cfg);

    while((res = ymodem_tx()) == YMODEM_TX_ERR_NEED_CONTINUE);

    xprintf("\r\nres:%d\r\n",res);

  }

}

static int cp(const char* src, const char* dst)

{

        FIL fr;

        FIL fw;

        BYTE buf[64] = {0x00};

        FRESULT res;   

        size_t write_size;  /*剩余待写入字节数*/   

        DWORD pos = 0;

        UINT btr;        /*一次试图写入字节数*/

        UINT br;         /*一次实际读到字节数*/

        UINT bw;         /*一次实际写入字节数*/

    UINT size;

        res = f_open(&fr, src, FA_READ);

        if (res != FR_OK)

        {

                return -1;

        }

        res = f_open(&fw, dst, FA_WRITE | FA_CREATE_ALWAYS);

        if (res != FR_OK)

        {

                f_close(&fr);

                return -1;

        }   

        write_size = f_size( &fr );

    size = write_size;

        do

        {   

                btr = (write_size > sizeof(buf)) ? sizeof(buf) : write_size;

        //res = f_lseek(&fr, pos);

                res = f_read (&fr, buf, btr, &br);

        if(res != FR_OK)

        {

            f_close(&fw);

            f_close(&fr);  

            return -1;

        }

        //res = f_lseek(&fw, pos);               

                res = f_write(&fw, buf, br, &bw);

        if((res != FR_OK) || (br != bw))

        {

            f_close(&fw);

            f_close(&fr);

            return -1;

        }

                //f_sync(&fw);

                pos += br;

                write_size -= br;

        xprintf("\b\b%02d", 100 * (pos + 1) / size);

        }while(write_size > 0);

    xprintf("\b\b\b%d\r\n", 100);

        res = f_close(&fw);

        res = f_close(&fr);  

    return 0;

}

static void cpfilefunc(uint8_t * cmdbuf)

{

        int len;

        int res;

        char path[32] = {0x00};

        char dst[32] = {0x00};

        char regexp[32];

        memset((void *)path, 0, sizeof(path));

  len = sscanf((char const *)cmdbuf, "%*s %31s %31s %31s", path,dst,regexp);

        if (len == 2)

        {

                xprintf("\tcopy \"%s\" to \"%s\"...\r\n", path, dst);

                res = cp(path, dst);

                xprintf("f_cp result = %d\r\n", res);

        }

  else

  {}

}

三.测试

查看支持的命令

221836oz4qcaf8msvc7kg7

Ls查看文件

221836mohyo6aizbjrroww

测试导入文件

这里使用crt终端,设置xmodem为1k模式

221836ar9n9ssazt9q469g

输入rxfile 0:/1.jpg 131804回车

其中131804是文件的大小

菜单栏 Transfer->  Send Xmodem ... 选择需要发送的文件1.jpg 发送

115200的波特率平均速度10KB/S 基本跑满了。

注意uart.c中设置对应的串口的fifo大于一帧可以最大优化效率

221836zgmsz35wpfzzsmp0

221836cxx6as9xs0s6sq7s

sxfile 0:/1.jpg 131804

菜单栏 Transfer->  Receive Xmodem ... 选择需要保存的文件11.jpg 接收

221836ilcdw3m0ft0ssf2m

注意接收的文件会在最后不足1024对齐的地方填充0x1A。

其他命令都可以测试,这里不再赘述。

四.总结

实现以上命令之后,我们就可以方便的进行文件的操作了。包括导入导出游戏文件。