做过嵌入式Linux开发或使用过桌面Linux系统的童鞋们,肯定对shell命令交互印象比较深刻,然而我们大多数搞嵌入式软件开发的码农都是基于单片机,比如51、STM32等进行开发的,在单片机上能否做个shell命令行交互?答案当然是可以的,在网上类似的文章和代码一搜一箩筐, 基本原理: 监测用户的输入,然后到一个命令查找表里过滤是否可以找到该命令,如果可以则调用对应的处理函数,当然做的好点的话还可以向处理函数传递参数。
主要的数据结构及解析函数定义如下,注意这里函数指针的定义,Argc代表参数的个数,可以为0、1、2...,Argv用于存放具体的参数,可能有些童鞋要问为啥定义成CHAR **,这是因为我们在shell交互窗口输入的内容都会被当作ASCII码字符串,所以只能用CHAR *来指向它们,另外又因为我们可能会输入多个字符串参数(多个参数以空格进行间隔),所以要使用二级指针CHAR **,可能有的同学会发现,我们平时见的标准main函数的原型就是这样定义的
int main(int argc, char *argv[])
char *argv[]与char **argv是等价的,这个就不需要解释了吧,采用这种定义方式可以非常灵活,具体见下面的用法示例:
#define SHELL_MAX_PARA_NUM 20 //最多支持20个命令参数
// 函数指针
typedef UINT8 (* Cmd_Analys_Fun_P)(UINT8 Argc, CHAR **Argv);
typedef struct
{
CHAR *pName;
Cmd_Analys_Fun_P pCmdFunc; // 命令解析函数
} S_Shell_Cmd;
/***************************************************************
* 函数名称: Shell_Proc
* 功能描述: Shell交互处理
* 输入参数:
* 输出参数:
* 返 回 值:
****************************************************************/
UINT8 Shell_Proc(CHAR *ucCmd, UINT8 ucCmdLength)
{
UINT8 Result;
Result = Cmd_Analys(Shell_Cmd, S_NUM(Shell_Cmd), ucCmd, ucCmdLength);
if ((Result EQ 1) || (Result EQ 2))
{
//vConsoleLog("[shell]#");
}
return Result;
}
/***************************************************************
* 函数名称: Cmd_Analys
* 功能描述: 命令解析
* 输入参数:
* 输出参数:
* 返 回 值:
****************************************************************/
LOCAL UINT8 Cmd_Analys(CONST S_Shell_Cmd Shell_Cmd[], UINT8 Num, CHAR Cmd[], UINT8 Len)
{
UINT8 i, j;
UINT8 Argc, Cmd_Len;
CHAR *(Argv[SHELL_MAX_PARA_NUM]);
Cmd_Len = 0;
for (i = 0; i < Len; i++)
{
if ((Cmd[i] EQ 'r') || (Cmd[i] EQ 'n')) // 找到回车换行键, 说明已经输入了一条完整命令
{
Cmd_Len = i; // 记录命令长度
Cmd[i] = '�';
break;
}
else if (Cmd[i] EQ ' ') // 空格全部替换成'�'
{
Cmd[i] = '�';
}
}
if (i EQ Len) // 没有找到命令
{
return 0;
}
if (Cmd_Len EQ 0) // 全部输入的是空格或者回车
{
vConsoleLog("rnShell:"); // 提示输入新的命令
return 1;
}
for (i = 0; i < Num; i++)
{
if (strcmp(Shell_Cmd[i].pName, Cmd) != 0)
{
continue;
}
j = (UINT8)strlen(Cmd);
Argc = 0;
while (j < Cmd_Len)
{
if (Cmd[j] EQ '�' && Cmd[j + 1] != '�') // 前一个是空格,后一个非空格,说明是一个新参数
{
if (Argc < SHELL_MAX_PARA_NUM)
{
Argv[Argc] = &Cmd[j + 1];
Argc++;
}
else
{
break;
}
}
j++;
}
// 执行命令
(*Shell_Cmd[i].pCmdFunc)(Argc, Argv);
break;
}
if (i EQ Num)
{
vConsoleLog("Cmd Error!");
return 2;
}
return 1;
}
**用法1:**只有命令,没有参数
/***************************************************************
* 函数名称: RebootTerminal
* 功能描述: 重启终端
* 输入参数:
* 输出参数:
* 返 回 值:
****************************************************************/
LOCAL UINT8 RebootTerminal(UINT8 argc, CHAR **argv)
{
//发起复位请求
udwResetTimeCounter = 0;
blResetRequestFlag = TRUE;
vConsoleLog("Terminal Prepare Reboot ...");
return 1;
}
**用法2:**命令+1个参数
/***************************************************************
* 函数名称: ConsoleOutputRedirect
* 功能描述: console输出重定向
* 输入参数:
* 输出参数:
* 返 回 值:
****************************************************************/
LOCAL UINT8 ConsoleOutputRedirect(UINT8 argc, CHAR **argv)
{
if (argc != 1)
{
vConsoleLog("miss argumentrn");
return 0;
}
if (!strcmp(argv[0], "on"))
{
ucConsoleRedirectFlag = 1;
vConsoleLog("console output redirect to tcpconsolern");
}
else if (!strcmp(argv[0], "off"))
{
ucConsoleRedirectFlag = 0;
vConsoleLog("console output redirect to localconsolern");
}
else
{
vConsoleLog("error argumentrn");
return 0;
}
return 1;
}
**用法3:**命令+N个参数
/***************************************************************
* 函数名称: SetTerminalTime
* 功能描述: 设置终端时间
* 输入参数:
* 输出参数:
* 返 回 值:
****************************************************************/
LOCAL UINT8 SetTerminalTime(UINT8 argc, CHAR **argv)
{
UINT8 ucTime[6];
if (argc != 6)
{
vConsoleLog("Param Err! argc = %d", argc);
return 0;
}
ucTime[0] = strtoul(argv[0], NULL, 0);
ucTime[1] = strtoul(argv[1], NULL, 0);
ucTime[2] = strtoul(argv[2], NULL, 0);
ucTime[3] = strtoul(argv[3], NULL, 0);
ucTime[4] = strtoul(argv[4], NULL, 0);
ucTime[5] = strtoul(argv[5], NULL, 0);
ucTimeTestFlag = 1;
stCurrentTime.ucYear = ucTime[0];
stCurrentTime.ucMonth = ucTime[1];
stCurrentTime.ucDay = ucTime[2];
stCurrentTime.ucHour = ucTime[3];
stCurrentTime.ucMin = ucTime[4];
stCurrentTime.ucSec = ucTime[5];
vConsoleLog("SetTerminalTime: %02d/%02d/%02d %02d:%02d:%02d", ucTime[0], ucTime[1], ucTime[2],
ucTime[3], ucTime[4], ucTime[5]);
return 1;
}
以上三种用法,基本可以涵盖现实中的各种使用需求!
以上就是shell命令的基本用法,至于如何捕捉用户的输入,方式和方法就很多了,不过常用的就下面的几种情况:
- 终端设备上的串口(这种最常见)
- 终端设备上的网口(稍微有点门槛,后面会专门写一篇STM32的文章介绍这种用法)
- 如果终端设备已经登录了后台主站云平台,直接在云平台上给终端设备下发shell命令
-
嵌入式
+关注
关注
5068文章
19008浏览量
302976 -
Linux
+关注
关注
87文章
11219浏览量
208872 -
函数
+关注
关注
3文章
4303浏览量
62411 -
系统
+关注
关注
1文章
1006浏览量
21310
发布评论请先 登录
相关推荐
评论