本帖最后由 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发扬光大。
|
newB