打印
[RISC-V MCU 应用开发]

【RISC-V MCU CH32V103测评】基于fatfs的shell终端

[复制链接]
720|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
coslight|  楼主 | 2020-11-30 16:21 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 coslight 于 2020-11-30 16:47 编辑

基于fatfs的shell终端

1.     概述
    前面的帖子已经完成了sd卡驱动,fatfs的移植,shell的移植,这里完成一个最终的测试,基于fatfs的shell终端,可以完成几个简单的文件系统命令,随着研究的深入,可以增加更多的命令支持,只要芯片空间够用。
     Fatfs采用开源软件库,版本R0.14,下载连接:http://elm-chan.org/fsw/ff/arc/ff14.zip
    Shell终端采用开源软件库Letter shell,版本3.0.5 下载连接:https://github.com/NevermindZZT/letter-shell
测试设计目标,
   1)通过CH32V103的SPI接口完成SD卡管理;
   2)移植基于SD卡的Fatfs文件管理系统;
   3)移植Letter shell终端软件;
   4)集成为一个简单的终端工具,可以完成基于Fatfs的文件系统终端,可以实现:
  • 系统支持命令查看,
  • 帮助提示文件,
  • 输入辅助,
  • 查询文件系统概况,
  • pwd当前目录命令,
  • ls当前目录或指定目录内容列表命令,
  • cd改变当前目录命令,
  • 在指定目录下文件创建,写入、读出测试
  • Cat 显示指定目录下指定文本文件内容
  5)预留接口,扩展shell终端对于文件系统及文件的管理能力。
2.     终端软件实现
2.1.   SPI接口的SD卡管理
通过SPI接口的SD卡管理请见我的试用帖:https://bbs.21ic.com/icview-3048056-1-1.html
这里不赘述。
2.2.   Fatfs移植
Fatfs的移植主要是DiskIO接口函数的移植,主要包括如下几个接口函数:
1)SD卡状态获取:
DSTATUS MMC_disk_status(BYTE pdrv)
{
    returnstatus;
}
2) Disk初始化:
    /*-----------------------------------------------------------------------*/
/* Inidializea Drive                                                   */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
    BYTE pdrv            /*Physical drive nmuber to identify the drive */
)
{
    intresult;
    status = RES_OK;
    result = SD_Init_Config();
    if(result== 0xbb)
        status = STA_NODISK;
    else if(result!= 0)
        status = STA_NOINIT;

    returnstatus;
}
3)扇区读操作函数:
/*-----------------------------------------------------------------------*/
/*Read Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

DRESULT disk_read(
    BYTE pdrv,    /* Physical drive nmuberto identify the drive */
    BYTE *buff,       /* Data buffer to store readdata */
    LBA_t sector, /* Start sector in LBA */
    UINT count    /* Number of sectors to read*/
)
{
    DRESULT res;
    res = SD_ReadDisk2(buff, sector, count);
    return res;
}
4)扇区写操作函数:
/*-----------------------------------------------------------------------*/
/*Write Sector(s)                                                      */
/*-----------------------------------------------------------------------*/

#ifFF_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 */
)
{
    DRESULT res;
    res = SD_WriteDisk2((u8*)buff,sector, count);
    return res;
}

#endif

5)Ioctrl接口函数:
/*-----------------------------------------------------------------------*/
/*Miscellaneous Functions                                              */
/*-----------------------------------------------------------------------*/

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) return RES_PARERR;                 /*Check parameter */
    if(status & STA_NOINIT) return RES_NOTRDY;   /* Check if drive is ready */

    res = RES_ERROR;

    switch(cmd) {
    caseCTRL_SYNC :        /*Wait for end of internal write process of the drive */
        if(SD_Select()) res = RES_OK;
        SD_DisSelect();
        break;

    caseGET_SECTOR_COUNT : /* Get drive capacity in unit of sector(DWORD) */
        *(LBA_t*)buff= SD_GetSectorCount();
        if(*(LBA_t*)buff> 0)
            res = RES_OK;
        break;

    caseGET_BLOCK_SIZE :   /*Get erase block size in unit of sector (DWORD) */
        *(DWORD*)buff= SD_GetBlockSize();
        if(*(DWORD*)buff> 0)
            res = RES_OK;
        break;

    caseCTRL_TRIM :    /*Erase a block of sectors (used when _USE_ERASE == 1) */
        res = RES_OK;   /* FatFs does not checkresult of this command */
        break;

    default:
        res = RES_PARERR;
    }

    SD_DisSelect();

    return res;
}
2.3.   Letter shell移植
Letter shell移植请见我的试用帖:https://bbs.21ic.com/icview-3050316-1-1.html
这里不再赘述。
2.4.   shell终端软件功能实现
利用Letter shell提供的接口函数管理方法,实现设计目标中的几个关键接口函数,其中系统支持命令查看,帮助提示文件,输入辅助为shell系统集成功能,这里不再详述。重点为与文件系统有关的几个接口函数的实现。
1)查询文件系统概况
文件系统概况主要展示文件系统类型、文件系统的基本数据(扇区数量,扇区大小等)、卷标、SD中文件和目录数量统计和空间占用、文件系统总容量和可用容量信息。
实现函数如下:
//showvolume status
int tfatfs_vol(intargc, char *argv[])
{
    FATFS*fs;           /* Filesystemobject */
    longp1,p2;
    static const char*ft[] = {"", "FAT12", "FAT16", "FAT32", "exFAT"};
    FRESULTres;  /* APIresult code */

    printf("fatfsshow volume status\r\n");
    if(argc< 1)
    {
        printf("inputdriver num,0!\n\r");
        return -1;
    }
    res = f_getfree(argv[1], (DWORD*)&p1,&fs);
    if(res)
    {
        printf("f_getfree()%d\n\r",res);
        return -1;
    }
    printf("FATtype = %s\n\r", ft[fs->fs_type]);
    printf("Bytes/Cluster= %lu\n\r", (DWORD)fs->csize *512);
    printf("Numberof FATs = %u\n\r", fs->n_fats);
    if(fs->fs_type < FS_FAT32)
        printf("RootDIR entries = %u\n\r", fs->n_rootdir);
    printf("Sectors/FAT= %lu\n\r", fs->fsize);
    printf("Numberof clusters = %lu\n\r", (DWORD)fs->n_fatent -2);
    printf("Volumestart (lba) = %lu\n\r",fs->volbase);
    printf("FATstart (lba) = %lu\n\r",fs->fatbase);
    printf("DIRstart (lba,clustor) = %lu\n\r",fs->dirbase);
    printf("Datastart (lba) = %lu\n\r",fs->database);
#ifFF_USE_LABEL
    res = f_getlabel(argv[1], (char*)Buff,(DWORD*)&p2);
    if(res)
    {
        printf("f_getlabel()%d\n\r",res);
        return -1;
    }
    printf(Buff[0]? "Volume name is %s\n" : "Novolume label\n\r", (char*)Buff);
    printf("VolumeS/N is %04X-%04X\n\r", (DWORD)p2>> 16, (DWORD)p2 & 0xFFFF);
#endif
    AccSize = AccFiles = AccDirs = 0;
    printf("...");
    res = scan_files(argv[1]);
    if(res)
    {
        printf("scan_files()%d\n\r",res);
        return -1;
    }
    printf("\r%ufiles, %lu bytes.\n %u folders.\n\r"
            "%luKiB total disk space.\n\r%lu KiB available.\n\r",
            AccFiles, AccSize, AccDirs,
            (fs->n_fatent - 2)* (fs->csize / 2), (DWORD)p1 *(fs->csize / 2)
    );
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), tfatfs_vol,tfatfs_vol, fatfs show volume status);
    其中用到的几个关键函数为,f_getfree(),f_getlabel()等。

2)pwd当前目录命令
pwd的功能与linux系统中的相似,为列出当前目录路径。利用文件系统的函数f_getcwd()。
       函数功能实现:
       //get current directory
int pwd(intargc, char *argv[])
{
    FRESULTres;  /* APIresult code */
    charLine[256];             /*Console input buffer */

//    printf("fatfsget current directory\r\n");
    res = f_getcwd(Line, sizeofLine);
    if (res== 0)
        printf("%s\n\r",Line);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),pwd, pwd, fatfs get current directory);

3)ls当前目录或指定目录内容列表命令
ls的功能与linux系统中的相似,为列出当前路径或指定路径下文件和目录的列表。利用文件系统函数f_opendir()和f_readdir()。
       函数实现方式为:
       //ls
int ls(intargc, char *argv[])
{
    FATFS*fs;           /* Filesystemobject */
    FRESULTres;  /* APIresult code */
    long p1;
    UINT s1,s2;
    charLine[256];             /*Console input buffer */

//    printf("fatfslist dir\r\n");
    memset(Line,0,256);
    if(argc<= 1)
    {
       f_getcwd(Line, sizeof(Line));
    }
    else
        strncpy(Line,(const char*)argv[1],255);
    res =f_opendir(&Dir, Line);
    if(res)
    {
        printf("scan_files()%d\n\r",res);
        return -1;
    }
    p1 = s1 = s2 =0;
    for(;;)
    {
        res = f_readdir(&Dir, &Finfo);
        if((res != FR_OK) || !Finfo.fname[0]) break;
        if(Finfo.fattrib & AM_DIR) {
            s2++;
        } else {
            s1++;p1 += Finfo.fsize;
        }
        printf("%c%c%c%c%c%u/%02u/%02u %02u:%02u %9lu  %s\n\r",
               (Finfo.fattrib & AM_DIR) ? 'D' : '-',
               (Finfo.fattrib & AM_RDO) ? 'R' : '-',
               (Finfo.fattrib & AM_HID) ? 'H' : '-',
               (Finfo.fattrib & AM_SYS) ? 'S' : '-',
               (Finfo.fattrib & AM_ARC) ? 'A' : '-',
               (Finfo.fdate >> 9) + 1980, (Finfo.fdate>> 5) & 15, Finfo.fdate &31,
               (Finfo.ftime >> 11), (Finfo.ftime>> 5) & 63,
               Finfo.fsize, Finfo.fname);
    }
    printf("%4uFile(s),%10lu bytes total\n\r%4u Dir(s)", s1,p1, s2);
    res =f_getfree(argv[1], (DWORD*)&p1,&fs);
    if (res== FR_OK)
        printf(",%10lu bytes free\n\r", p1 * fs->csize *512);

    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),ls, ls, fatfs ls);

4)cd改变当前目录命令
cd的功能与linux系统中的相似,为改变当前路径或进入指定路径。利用文件系统函数f_chdir()。
函数实现方式为:
//Change current directory
int cd(intargc, char *argv[])
{
    FRESULTres;  /* APIresult code */
    charLine[256];             /*Console input buffer */
    if(argc< 1)
    {
        printf("inputdir,0:!\n\r");
        return -1;
    }
    res = f_chdir(argv[1]);
    if(res== 0)
    {
        res =f_getcwd(Line, sizeof Line);
        if (res== 0)
            printf("%s\n\r",Line);
    }
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),cd, cd, fatfs Change current directory);

5)在指定目录下文件创建,写入、读出测试
函数的测试目的是测试文件系统对于文件的操作,文件读,文件写等。通过命令,在指定目录下生成指定文件名文件,写入给定的字符串,并读出校验(显示写入内容),关闭打开的文件。命令支持,如果文件存在,打开文件从头重新写入新内容(覆盖式);如果文件不存在,创建文件,并从头写入内容。
注:为了保证系统的健壮性,目前最多允许写入12个字符。
函数实现方式为:
int tfatfs_test(intargc, char *argv[])
{
    FILfil;            /*File object */
    FRESULTres;  /* APIresult code */
    UINTbw;            /*Bytes written */
    BYTEmm[50];
    UINT i;

    printf("fatfstest begin:\r\n");
    if(argc<= 2)
    {
        printf("pleaseinput string same as tfatfs_test /main.txt 123\n\r");
        return -1;
    }
    /*Create a file as new */
    res =f_open(&fil, argv[1], FA_CREATE_ALWAYS|FA_WRITE|FA_READ);
    if(res)
    {
        printf("openfile failed.\r\n");
    }
    else
    {
        printf("openfile success.\r\n");
    }
    /*Write a message */
    res =f_write(&fil, argv[2], 12, &bw);
    if (bw== 12)
    {
        printf("writefile success!\r\n");
    }
    else
    {
        printf("writefile failed!\r\n");
    }
    res =f_size(&fil);
    printf("filesize:%d Bytes.\r\n",res);
    memset(mm,0x0,50);
   f_lseek(&fil,0);
    res =f_read(&fil,mm,12,&i);
    if (res== FR_OK)
    {
        printf("readfile success!\r\n");
        printf("readdata length:%d Bytes.\r\n",i);
    }
    else
    {
        printf("readfile failed!\r\n");
    }
    printf("readdata:%s\r\n",(char*)mm);
    /*Close the file */
   f_close(&fil);
    printf("fatfstest finished.\r\n");
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),tfatfs_test, tfatfs_test, tfatfs_test /m.txt 123);
6)cat显示指定目录下指定文本文件内容
cat的功能与linux系统中的相似,显示指定目录下文本文件内容。
    函数实现方式:
   //displaya text file context
int cat(intargc, char *argv[])
{
    FILfil;            /*File object */
    FRESULTres;  /* APIresult code */
    char line[256];
    UINT i;

    if(argc<= 1)
    {
        printf("pleaseinput string same as cat /main.txt\n\r");
        return -1;
    }
    /*Create a file as new */
    res =f_open(&fil, argv[1], FA_READ);
    if(res)
    {
        printf("openfile failed.\r\n");
        return res;
    }
    res =f_size(&fil);
    printf("filesize:%d Bytes.\r\n",res);
    memset(line,0x0,256);
    while(f_read(&fil,line,255,&i)== FR_OK)
    {
        if(i ==0)
            break;
        printf("%s\r\n",line);
        memset(line,0x0,256);
    }
    /*Close the file */
   f_close(&fil);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),cat, cat, fatfs display a text file contex);

3.     运行结果展示
系统运行画面和支持命令列表:
文件系统信息展示: 我这里插了一个32G的卡,格式化为FAT32。
列出当前目录和指定目录内容展示:
    实现了列当前目录,列一级子目录,列二级子目录,列三级子目录等等,系统允许可以列更多级的子目录。
    改变当前目录和获取当前目录功能展示:
    可以看到,通过cd / 切换到根目录,展示了根目录下内容;然后通过cd /main切换到一级子目录,展示了一级子目录main下的内容。
    显示指定目录下指定文本文件内容展示:
可以看到cat命令显示了当前目录下和一级子目录下txt文件中的内容。
    操纵过程的动画展示:      

shell视频演示  https://v.youku.com/v_show/id_XNDk4Njk5NjgyOA==.html

   源码工程: t6.rar (702.87 KB)

4.     总结和展望
通过本次试用,我看到基于RISC-V内核的CH32V103控制器的功能和性能,都已经达到了同级别ARM 内核芯片的水平。可以提供丰富的外设、接口能力、IDE开发环境和仿真器等。但是不足之处也相对较为明显,比如生态系统还没有完全建立,开发环境和仿真器的性能,易用性方面还有待提高,外设库接口还有待进一步完善等。但瑕不掩瑜,CH32V103的出现,给嵌入式应用人员一个更多的选择,让我们将来不会再被别人掐着脖子了,希望RISC-V和CH32V10x发扬光大。


5.png (37.11 KB )

5.png

使用特权

评论回复
评论
zhengfish 2020-12-1 08:55 回复TA
newB 

相关帖子

沙发
coslight|  楼主 | 2020-11-30 16:41 | 只看该作者
实在是不知道怎么才能把视频出入到帖子中

使用特权

评论回复
评论
coslight 2020-12-2 08:07 回复TA
@zhengfish :试了以下,转换后效果不佳,丢帧太多 
zhengfish 2020-12-1 08:56 回复TA
也许可以考虑mp4转为gif后上传。 
板凳
coslight|  楼主 | 2020-12-1 08:08 | 只看该作者
cat命令的展示图跑到了帖子的最后面了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

927

帖子

5

粉丝