单片机shell命令_适用于单片机的小型类shell的命令行软件

在单片机中实现类似shell的命令行工具

如果在单片机编程过程中有一个类似linux的shell命令工具可以通过串口调试助手输入命令然后运行一些调试函数,将会为编程提供极大的帮助。

本文旨在提供一个十分便于移植和十分简单有效的shell解决方法。

在移植时只需提供shellGetChar函数和shellSendChar函数,函数编写尽量简单、高效甚至可以简单的移植到类似51单片机这样的8位处理器上。

首先编写shell最基础的东西,从串口获取到一行字符串,其特点如下。

由’\r\n’ 或者’\n’结尾。

在超级终端上输入字符需要再返回给终端

需要对退格和删除键进行特殊处理

#include "shell.h"

char shellLine[100] = {SHELL_LINE_MAX_LEN}; /*用于存储从串口接收到的字符串*/

char *shellParam[SHELL_LINE_MAX_LEN] = {0}; /*用于存储接收到的参数(包括命令名字)*/

extern T_ShellCmd* sysCmd[];

uint8_t shellGetChar(char *recCh)

{

/*需要自己提供获取一个字符的函数,获取到字符返回1,反之返回0 */

return scanf("%c",recCh);

}

void shellSendChar(char ch)

{

printf("%c",ch);/*需要提供发送一个字符的函数*/

}

/*

*用于从串口获取一条以回车换行结尾的命令

*/

uint8_t shellGetOneLine(char *line, uint8_t maxLen)

{

char getChar;

static uint8_t count=0; /*用于记录除特殊字符外的其他有效字符的数量*/

if(shellGetChar(&getChar))

{

if(count>=maxLen) /*长度超限*/

{

count = 0; /*清零计数器以便后续使用*/

return 1; /*返回有效标志*/

}

line[count] = getChar; /*记录数据*/

switch(getChar)

{

case 0x08:

case 0x7F: /*退格键或者删除键*/

{

if(count>0)

{

count--; /*删除上一个接收到的字符*/

}

}break;

case '\r':

case '\n': /*接收到回车换行,证明已经收到一个完整的命令*/

{

line[count] = '\0'; /*添加字符串结束符,刚好可以去掉'\r'或者'\n'*/

count = 0; /*清零计数器以便后续使用*/

return 1; /*返回有效标志*/

}break;

default:

count++;

}

shellSendChar(getChar); /*把收到的字符输出到串口*/

}

return 0;

}

处理好接收一行命令的函数后,再编写函数将输入的一行字符串转换成命令名和参数,需要使用sting.h 中的strtok函数,使用此函数我们可以简便的提取到相关的参数字符。

/*从命令字符串中解析到命令和其参数

* 获取到的paramArry[0]为要允许的命令名

* 其他的为命令参数

* 返回值为获取到的参数的个数(包括一个命令名)

*/

uint8_t shellGetParam(char* line, char *paramArry[], uint8_t arryLen)

{

uint8_t i,ret;

char *ptr = NULL;

ptr = strtok(line, " ");

for(i=0; ptr!=NULL &&i

{

paramArry[i] = ptr;

ptr = strtok(NULL, ",");

}

ret = i;

return ret;

}

现在我们可以从串口获取到一节命令字符串,并且可以解析得到命令的名字和参数,接下来就是要去通过命令名字去查找我们预先设置好的命令数组,找到相应的命令函数,然后运行即可。

在我们学习C语言的时候,我们有时候会发现main函数会是这个样子,是带参数的。百科上面的解释argc argv百科

int main(int argc, char* argv[])

{

}

因此我定义的命令函数的原型为

typedef int (*T_ShellFun)(int argc, char*argv[]);

还需要去设计一个结构体去将命令名和命令函数连接起来

typedef struct

{

char* name; /*命令的名字*/

char* help; /*帮助描述*/

T_ShellFun fun; /*命令函数*/

}T_ShellCmd;

这里并没有去定义命了的参数个数,在解析完用户输入,运行命令函数时也就不会去检查用户输入的参数的个数。以便实现以下类似功能

ls

不输入参数显示当前文件夹下的所有文件和文件夹

ls \user\

输入了参数,显示user目录下的文件和文件夹

定义好了命令描述结构体,和命令函数模板,就可以定义命令了

extern T_ShellCmd *sysCmd[];

int helpCmdFun(int argc, char*argv[]) /*命令函数*/

{

uint8_t i;

for(i=0; sysCmd[i]; i++)

{

printf("%-15s %s\r\n",sysCmd[i]->name, sysCmd[i]->help);

}

}

T_ShellCmd helpCmd= /*命令描述*/

{

.name = "help",

.help = "show all cmd list",

.fun = helpCmdFun

};

/*例程,此例程序可以接收两个参数,int a,float b*/

int paramTestCmdFun(int argc, char *argv[]) /*命令函数*/

{

uint8_t reti,retf;

int vali;

float valf;

printf("get param num %d\r\n", argc); /*如果用户输入的参数不够cmd函数使用,将传入一个默认的地址,其内容为 '\0' */

vali = shellStr2Int(argv[0], &reti); /*提供的字符串转int函数*/

valf = shellStr2Float(argv[1], &retf); /**/

printf("int[%d]:%d\r\n", reti,vali );

printf("float[%d]:%f\r\n",retf,valf );

return argc;

}

T_ShellCmd paramTestCmd = /*命令描述结构体*/

{

.name= "test",

.help = "(int a=0, float b=0) show tow param val", /*参数提示,使用函数参数书写方式,有等于号表明默认参数,无参数不提示参数项*/

.fun = paramTestCmdFun

};

定义好了一个命令之后我们可以定义一个数组,专门用来存放所有的命令描述结构体

T_ShellCmd *sysCmd[] =

{

&helpCmd, /*只存放命令结构体的指针减少对存储的占用*/

&paramTestCmd, /*按照help的方式建立的其他命令*/

NULL /*用于标记命令数组的结尾*/

};

最后编写shellMain以及上面用到的shellStr2Int和shellStr2Float函数,

uint8_t shellMain(void)

{

uint8_t paramNum = 0;

if(shellGetOneLine(shellLine, SHELL_LINE_MAX_LEN))

{

paramNum = shellGetParam(shellLine, shellParam, SHELL_PARAM_MAX_NUM);

if(paramNum)

{

uint8_t i=0;

for(i=0; sysCmd[i]; i++) /*查找命令名字*/

{

if(strcmp(sysCmd[i]->name, shellParam[0]) == 0)

{

int value = sysCmd[i]->fun(paramNum-1, &shellParam[1]); /*运行命令函数*/

printf("value %d = 0x%x\r\n", value, value); /*打印运行结果*/

return 1;

}

}

if(sysCmd[i-1] == NULL) /*没有找到命令*/

{

printf("C interp: unknown symbol name \'%s\' \r\n",shellLine); /*打印错误信息*/

}

}

printf("->");

}

return 0;

}

/*

* 提供int字符串转int

*/

int shellStr2Int(const char *str, uint8_t* ok)

{

int ret;

if(str == NULL)

{

ok = false;

return 0;

}

*ok=(uint8_t)sscanf(str,"%d", &ret);

if(*ok != 1)

{

*ok = false;

return 0;

}

return ret;

}

/*

* 提供flaot字符串转浮点数功能

*/

float shellStr2Float(const char *str, uint8_t* ok)

{

float ret;

if(str == NULL)

{

ok = false;

return 0;

}

*ok=(uint8_t)sscanf(str,"%f", &ret);

if(*ok != 1)

{

*ok = false;

return 0.0;

}

return ret;

}

/*

* 提供16进制字符串转数字的功能

*/

int shellStr2Hex(const char *str, uint8_t* ok)

{

int ret;

if(str == NULL)

{

ok = false;

return 0;

}

*ok=(uint8_t)sscanf(str,"%X", &ret);

if(*ok != 1)

{

*ok = false;

return 0;

}

return ret;

}

标签:shell,return,命令,int,uint8,char,单片机,命令行,ok

来源: https://blog.csdn.net/qq_38901733/article/details/88114837

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值