源码地址:https:github。comNevermindZZTlettershell1Lettershell简介 熟悉Linux的朋友的都知道,shell包裹在内核之外的人机交互界面,用于用户和内核之间打交道的功能,类似于windowsCMD。通过Shell将输入的命令与内核通讯,好让内核可以控制硬件开正确无误的操作工作。Shell有着不同的分类,比如Bourneshell(sh),Kornshell(ksh)、Cshell(csh)、Bourneagainshell(bash)、tcsh。其中最常用的有csh和bash。Shell本身是一个用C语言编写的程序,它是用户使用UnixLinux的桥梁,用户的大部分工作都是通过Shell完成的。 然而在嵌入式中,由于资源有限,自然很少使用shell,但随着MCU的资源越来越丰富,一些适用于嵌入式的shell工具也就问世了,本问将要介绍的是Lettershell,Lettershell是一个体积极小的嵌入式shell,当前最新版本是3。X。 Lettershell有如下功能:命令自动补全,使用TAB键补全命令命令帮助,使用help〔command〕显示命令帮助帮助补全,输入命令后双击TAB键补全命令帮助指令快捷键,支持使用CTRLAZ组合按键直接调用函数shell变量,支持在shell中查看和修改变量值,支持变量作为命令参数登录密码,支持在shell中使用登录密码,支持超时自动锁定2Lettershell移植 Shell是一个命令行交互式形式存在,那最常规的就是使用MCU的串口资源了,当然也可使用USB模拟的虚拟串口。 Lettershell的移植比较简单,既然需要占用串口资源,那么首先要准备一个裸机工程,该工程需要事先串口的收发,关于串口的实现请参看逼着文章: 标准库:https:bruceou。blog。csdn。netarticledetails79341769 HAL库:https:bruceou。blog。csdn。netarticledetails109190370 笔者本文以标准库为例讲解。 1。复制源码 首先下载lettershell,然后在工程中新建Lettershell目录,将lettershell目录下的src的文件复制到工程目录MiddlewaresLettershell中。 2。新建接口文件 在工程用户目录下新建shellport。c和shellport。h文件,当然也可以放在User目录。 3。配置工程 打开Keil,添加相应的文件。 然后添加相应的头文件路径。 接下来就是实现Lettershell的收发。 发送代码如下:brief用户shell写paramdata数据voiduserShellWrite(chardata){USARTSendData(USART1,(uint8t)data);等待发送完毕while(USARTGetFlagStatus(USART1,USARTFLAGTC)RESET);} 接收采用中断的方式,代码如下:briefThisfunctionhandlesUSART1Handler。paramNoneretvalNonevoidUSART1IRQHandler(void){uint8tch;接收中断缓冲if(USARTGetITStatus(USART1,USARTITRXNE)!RESET){chUSARTReceiveData(USART1);chUSART1DR;调用shell处理数据的接口shellHandler(shell,ch);}} 还需要实现Lettershell初始化接口。brief用户shell初始化voiduserShellInit(void){shell。writeuserShellWrite;shellInit(shell,shellBuffer,512);} 最后在主函数中初始化即可。briefmianparamNoneretvalintintmain(void){配置SysTick为10us中断一次SysTickInit();USART1配置模式为1152008N1,中断接收USART1Config();userShellInit();for(;;){Delayms(50);}} 好了,这就移植完成了,编译、下载,连接串口1,使用xshell等工具,打印信息如下: 很简单吧。3Lettershell应用3。1Lettershell宏定义 在开发Lettershell应用前,需要知道Lettershell的宏定义,其宏定义在shellcfg。h文件。ifndefSHELLCFGHdefineSHELLCFGHbrief是否使用默认shell任务while循环,使能宏SHELLUSINGTASK后此宏有意义使能此宏,则shellTask()函数会一直循环读取输入,一般使用操作系统建立shell任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用shellTask()defineSHELLTASKWHILE1brief是否使用命令导出方式使能此宏后,可以使用SHELLEXPORTCMD()等导出命令定义shell命令,关闭此宏的情况下,需要使用命令表的方式defineSHELLUSINGCMDEXPORT1brief是否使用shell伴生对象一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象defineSHELLUSINGCOMPANION0brief支持shell尾行模式defineSHELLSUPPORTENDLINE0brief是否在输出命令列表中列出用户defineSHELLHELPLISTUSER0brief是否在输出命令列表中列出变量defineSHELLHELPLISTVAR0brief是否在输出命令列表中列出按键defineSHELLHELPLISTKEY0brief是否在输出命令列表中展示命令权限defineSHELLHELPSHOWPERMISSION1brief使用LF作为命令行回车触发可以和SHELLENTERCR同时开启defineSHELLENTERLF1brief使用CR作为命令行回车触发可以和SHELLENTERLF同时开启defineSHELLENTERCR1brief使用CRLF作为命令行回车触发不可以和SHELLENTERLF或SHELLENTERCR同时开启defineSHELLENTERCRLF0brief使用执行未导出函数的功能启用后,可以通过exec〔addr〕〔args〕直接执行对应地址的函数attention如果地址错误,可能会直接引起程序崩溃defineSHELLEXECUNDEFFUNC0briefshell命令参数最大数量包含命令名在内,超过8个参数并且使用了参数自动转换的情况下,需要修改源码defineSHELLPARAMETERMAXNUMBER8brief历史命令记录数量defineSHELLHISTORYMAXNUMBER5brief双击间隔(ms)使能宏SHELLLONGHELP后此宏生效,定义双击tab补全help的时间间隔defineSHELLDOUBLECLICKTIME200brief管理的最大shell数量defineSHELLMAXNUMBER5briefshell格式化输出的缓冲大小为0时不使用shell格式化输出defineSHELLPRINTBUFFER128briefshell格式化输入的缓冲大小为0时不使用shell格式化输入noteshell格式化输入会阻塞shellTask,仅适用于在有操作系统的情况下使用defineSHELLSCANBUFFER0brief获取系统时间(ms)定义此宏为获取系统Tick,如HALGetTick()note此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定defineSHELLGETTICK()0briefshell内存分配shell本身不需要此接口,若使用shell伴生对象,需要进行定义defineSHELLMALLOC(size)0briefshell内存释放shell本身不需要此接口,若使用shell伴生对象,需要进行定义defineSHELLFREE(obj)0brief是否显示shell信息defineSHELLSHOWINFO1brief是否在登录后清除命令行defineSHELLCLSWHENLOGIN1briefshell默认用户defineSHELLDEFAULTUSERletterbriefshell默认用户密码若默认用户不需要密码,设为defineSHELLDEFAULTUSERPASSWORDbriefshell自动锁定超时shell当前用户密码有效的时候生效,超时后会自动重新锁定shell设置为0时关闭自动锁定功能,时间单位为SHELLGETTICK()单位note使用超时锁定必须保证SHELLGETTICK()有效defineSHELLLOCKTIMEOUT0601000endif shellcfg。h文件已经注释了,笔者就不再赘述了。3。2Lettershell内置命令 在Lettershell中默认内置了一些shell命令,在shell中输入help后回车或者直接按下Tab键,就可以打印当前系统支持的所有命令。 按下Tab键后可以列出当前支持的所有命令。以下为按下Tab键后打印出来的当前支持的所有显示Lettershell中的命令,左边是命令名称,右边是关于命令的描述: 自定义shell命令 自定义的shell命令,可以在shell模式下被运行,将一个命令导出到shell模式可以使用如下宏接口:SHELLEXPORTCMD(attr,name,func,desc) 参数 描述 attr 命令属性 name 命令名 func 命令函数 desc 命令描述 原型如下:defineSHELLEXPORTCMD(attr,name,func,desc)constcharshellCmdname〔〕name;constcharshellDescname〔〕desc;constShellCommandshellCommandnameSECTION(shellCommand){。attr。valueattr,。data。cmd。nameshellCmdname,。data。cmd。function(int()())func,。data。cmd。descshellDescname} 导出无参数命令时,函数的入参为void,示例如下:voidhello(void){printf(hellolettershell!);}导出到命令列表里SHELLEXPORTCMD(SHELLCMDPERMISSION(0)SHELLCMDTYPE(SHELLTYPECMDFUNC),hello,hello,hello); 系统运行起来后,在shell控制台按tab键可以看到导出的命令,运行hello命令,运行结果如下所示: 导出有参数的命令时,还可传入参数。导出有参数命令示例如下:voidparametertest(intnum,charstr){printf(parametertest:numd,strs!r,num,str);}导出到命令列表里SHELLEXPORTCMD(SHELLCMDPERMISSION(0)SHELLCMDTYPE(SHELLTYPECMDFUNC),parametertest,parametertest,parametertest); 系统运行起来后,在shell控制台按tab键可以看到导出的命令,运行parametertest命令,运行结果如下所示: 值得注意的是,命令参数的最大个数在中shellcfg。h配置,默认最大是8,但是命令占了一个参数,因此用户可用的应该是7个。briefshell命令参数最大数量包含命令名在内,超过8个参数并且使用了参数自动转换的情况下,需要修改源码defineSHELLPARAMETERMAXNUMBER8 Lettershell不仅可以使用命令的方式运行程序,还可像Linux的终端一样,还能通过上下键能选择历史命令,历史命令的个数默认最大为5个。brief历史命令记录数量defineSHELLHISTORYMAXNUMBER5 非常的方便,Lettershell很强大,还有很多功能可玩,本文先讲到这里,后面再深入讲解Lettershell的设计思想。