搜索

[其他] shell实现原理分析基于MM32 MCU的shell脚本源码

[复制链接]
61|2
 楼主 | 2021-1-5 13:59 | 显示全部楼层 |阅读模式
shell实现原理
以下为函数初始化配置及相关全局变量定义内容,代码如下:
typedefstruct
{
char*command;//shell命令提示符
charbuffer[SHELL_COMMAND_MAX_LENGTH];//shell命令缓冲buffer
unsignedshortlength;//shell命令长度大小
unsignedshortcursor;//shell光标位置偏移
char*param[SHELL_PARAMETER_MAX_NUMBER];//shell参数变量
charhistory[SHELL_HISTORY_MAX_NUMBER][SHELL_COMMAND_MAX_LENGTH];//历史记录区域
unsignedshorthistoryCount;//历史记录数量
shorthistoryFlag;//当前记录偏移位置
shorthistoryOffset;//历史记录偏移大小
SHELL_CommandTypeDef*commandBase;//命令表基地址
unsignedshortcommandNumber;//命令数量
intkeyFuncBase;//按键响应表基地址
unsignedshortkeyFuncNumber;//按键响应数量
SHELL_InputModestatus;//shell输入状态
unsignedcharisAcTIve;//是不是当前激活的shell
shellReadread;//shell读函数接口
shellWritewrite;//shell写函数接口
}SHELL_TypeDef;
如上所示,为对象的定义接口,具体说明看注释,我们需要关注的是shell的读写接口。
voidshellInit(SHELL_TypeDef*shell)
{
shelldisplay(shell,“\r\n\r\n”);
shellDisplay(shell,“+=========================================================+\r\n”);
shellDisplay(shell,“|(C)COPYRIGHT2019MindMoTIon|\r\n”);
shellDisplay(shell,“|shellv”SHELL_VERSION“|\r\n”);
shellDisplay(shell,“|Build:”__DATE__“”__TIME__“|\r\n”);
shellDisplay(shell,“+=========================================================+\r\n”);
shell-》length=0;
shell-》cursor=0;
shell-》historyCount=0;
shell-》historyFlag=0;
shell-》historyOffset=0;
shell-》status=SHELL_IN_NORMAL;
shell-》command=SHELL_DEFAULT_COMMAND;
shell-》isAcTIve=0;
shellAdd(shell);
shellDisplay(shell,shell-》command);
#IFdefined(__CC_ARM)||(defined(__ARMCC_VERSION)&&__ARMCC_VERSION》=6000000)
externconstunsignedintshellCommand$$Base;
externconstunsignedintshellCommand$$Limit;
externconstunsignedintshellVariable$$Base;
externconstunsignedintshellVariable$$Limit;
shell-》commandBase=(SHELL_CommandTypeDef*)(&shellCommand$$Base);
shell-》commandNumber=((unsignedint)(&shellCommand$$Limit)
-(unsignedint)(&shellCommand$$Base))
/sizeof(SHELL_CommandTypeDef);
#endif
}
上述代码voidshellInit(SHELL_TypeDef*shell)用来初始化shell对象,首先打印shell界面,然后对shell对象进行初始化为默认状态,然后给shell命令表指定区域和数量。
对于shell输入处理,需要分两种类型判断,一个是正常的字母按键,如A、B、C、D等,一个是功能按键,如方向键等。下面给出两种类型处理代码。
//shellansi按键处理函数
voidshellAnsi(SHELL_TypeDef*shell,chardata)
{
switch((unsignedchar)(shell-》status))
{
caseSHELL_ANSI_CSI
switch(data)
{
case0x41://键盘方向键向上键
shellHistory(shell,0);
break;
case0x42://键盘方向键向下键
shellHistory(shell,1);
break;
case0x43://键盘方向键向右键
if(shell-》cursor《shell-》length)
{
shellDisplayByte(shell,shell-》buffer[shell-》cursor]);
shell-》cursor++;
}
break;
case0x44://键盘方向键向左键
if(shell-》cursor》0)
{
shellDisplayByte(shell,‘\b’);
shell-》cursor--;
}
break;
default:
break;
}
shell-》status=SHELL_IN_NORMAL;
break;
caseSHELL_ANSI_ESC:
if(data==0x5B)
{
shell-》status=SHELL_ANSI_CSI;
}
else
{
shell-》status=SHELL_IN_NORMAL;
}
break;
default:
break;
}
}
上述voidshellAnsi(SHELL_TypeDef*shell,chardata)函数为shellAnsi处理。
//shell正常按键处理函数
staticvoidshellNormal(SHELL_TypeDef*shell,chardata)
{
if(data==0)
{
return;
}
if(shell-》length《SHELL_COMMAND_MAX_LENGTH-1)
{
if(shell-》length==shell-》cursor)
{
shell-》buffer[shell-》length++]=data;
shell-》cursor++;
shellDisplayByte(shell,data);
}
else
{
for(shorti=shell-》length-shell-》cursor;i》0;i--)
{
shell-》buffer[shell-》cursor+i]=shell-》buffer[shell-》cursor+i-1];
}
shell-》buffer[shell-》cursor++]=data;
shell-》buffer[++shell-》length]=0;
for(shorti=shell-》cursor-1;i《shell-》length;i++)
{
shellDisplayByte(shell,shell-》buffer);
}
for(shorti=shell-》length-shell-》cursor;i》0;i--)
{
shellDisplayByte(shell,‘\b’);
}
}
}
else
{
shellDisplay(shell,“\r\nWarnig:Commandistoolong\r\n”);
shellDisplay(shell,shell-》command);
shellDisplay(shell,shell-》buffer);
shell-》cursor=shell-》length;
}
}
基于上述的两个类型代码,即可封装得到shell的处理代码,如下所示:
//shell处理
voidshellHandler(SHELL_TypeDef*shell,chardata)//shell处理函数
{
if(shell-》status==SHELL_IN_NORMAL)//shell工作在正常模式
{
charkeyDefFind=0;
SHELL_KeyFunctionDef*base=(SHELL_KeyFunctionDef*)shell-》keyFuncBase;
for(shorti=0;i《shell-》keyFuncNumber;i++)
{
if(base.keyCode==data){
if(base.keyFunction){
base.keyFunction(shell);
}
keyDefFind=1;
}
}
if(keyDefFind==0)
{
for(shorti=0;
i《sizeof(shellDefaultKeyFunctionList)/sizeof(SHELL_KeyFunctionDef);
i++)
{
if(shellDefaultKeyFunctionList.keyCode==data){
if(shellDefaultKeyFunctionList.keyFunction){
shellDefaultKeyFunctionList.keyFunction(shell);
}
keyDefFind=1;
}
}
}
if(keyDefFind==0)
{
shellNormal(shell,data);
}
}
else
{
shellAnsi(shell,data);//shellansi处理
}
}
以上就是shell的全部介绍,融合两节的代码,如下:
intmain(void)
{
intGetKey;
delay_init();
LED_Init();
uart_nvic_init(115200);//串口初始化为115200
//uart_shell.read=shellRead;
uart_shell.write=Uart_PutChar;
shellInit(&uart_shell);
/*配置通道0,上行配置*/
SEGGER_RTT_ConfigUpBuffer(0,“RTTUP”,NULL,0,SEGGER_RTT_MODE_NO_BLOCK_SKIP);
/*配置通道0,下行配置*/
SEGGER_RTT_ConfigDownBuffer(0,“RTTDOWN”,NULL,0,SEGGER_RTT_MODE_NO_BLOCK_SKIP);
//rtt_shell.read=shellRead;
rtt_shell.write=RTT_PutChar;
shellInit(&rtt_shell);
while(1)
{
if(SEGGER_RTT_HasKey())
{
GetKey=SEGGER_RTT_GetKey();
shellHandler(&rtt_shell,GetKey);
}
}
}
通过上述代码,可以同时支持串口方式和J-LinkRTT模式的shell,方便用户根据自己实际条件来辅助调试代码。
以上实现方式可能会影响MCU的运行效率,我们在本教程中优先考虑提供实现shell的方式。

使用特权

评论回复
| 2021-1-6 08:13 | 显示全部楼层
学习了,感谢楼主分享。

使用特权

评论回复
| 2021-1-21 15:25 | 显示全部楼层
支持串口方式和J-LinkRTT模式的shell,使用很方便啊

使用特权

评论回复
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 我要提问 投诉建议 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

在线客服 快速回复 返回顶部 返回列表