shell是什么?shell实现原理分析基于MM32 MCU的shell脚本源码

在前两节中,我们讲解了如何在mm32 mcu上使用shell来辅助开发,分别介绍的是通过串口方式和j-link rtt方式的shell,本次课程我们分析源码来讲解shell实现原理。
软件资源如下:
以下为函数初始化配置及相关全局变量定义内容,代码如下:
typedef struct
{
char *command; // shell命令提示符
char buffer[shell_command_max_length]; // shell命令缓冲buffer
unsigned short length; // shell命令长度大小
unsigned short cursor; // shell光标位置偏移
char *param[shell_parameter_max_number]; // shell参数变量
char history[shell_history_max_number][shell_command_max_length]; // 历史记录区域
unsigned short historycount; // 历史记录数量
short historyflag; // 当前记录偏移位置
short historyoffset; // 历史记录偏移大小
shell_commandtypedef *commandbase; // 命令表基地址
unsigned short commandnumber; // 命令数量
int keyfuncbase; // 按键响应表基地址
unsigned short keyfuncnumber; // 按键响应数量
shell_inputmode status; // shell输入状态
unsigned char isactive; //是不是当前激活的shell
shellread read; // shell读函数接口
shellwrite write; // shell写函数接口
}shell_typedef;
如上所示,为对象的定义接口,具体说明看注释,我们需要关注的是shell的读写接口。
void shellinit(shell_typedef *shell)
{
shelldisplay(shell, “\r\n\r\n”);
shelldisplay(shell, “+=========================================================+\r\n”);
shelldisplay(shell, “| (c) copyright 2019 mindmotion |\r\n”);
shelldisplay(shell, “| shell v”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);
#if defined(__cc_arm) || (defined(__armcc_version) && __armcc_version 》= 6000000)
extern const unsigned int shellcommand$$base;
extern const unsigned int shellcommand$$limit;
extern const unsigned int shellvariable$$base;
extern const unsigned int shellvariable$$limit;
shell-》commandbase = (shell_commandtypedef *)(&shellcommand$$base);
shell-》commandnumber = ((unsigned int)(&shellcommand$$limit)
- (unsigned int)(&shellcommand$$base))
/ sizeof(shell_commandtypedef);
#endif
}
上述代码void shellinit(shell_typedef *shell)用来初始化shell对象,首先打印shell界面,然后对shell对象进行初始化为默认状态,然后给shell命令表指定区域和数量。
对于shell输入处理,需要分两种类型判断,一个是正常的字母按键,如a、b、c、d等,一个是功能按键,如方向键等。下面给出两种类型处理代码。
// shell ansi按键处理函数
void shellansi(shell_typedef *shell, char data)
{
switch ((unsigned char)(shell-》status))
{
case shell_ansi_csi:
switch (data)
{
case 0x41: // 键盘方向键向上键
shellhistory(shell, 0);
break;
case 0x42: // 键盘方向键向下键
shellhistory(shell, 1);
break;
case 0x43: // 键盘方向键向右键
if (shell-》cursor 《 shell-》length)
{
shelldisplaybyte(shell, shell-》buffer[shell-》cursor]);
shell-》cursor++;
}
break;
case 0x44: // 键盘方向键向左键
if (shell-》cursor 》 0)
{
shelldisplaybyte(shell, ‘\b’);
shell-》cursor--;
}
break;
default:
break;
}
shell-》status = shell_in_normal;
break;
case shell_ansi_esc:
if (data == 0x5b)
{
shell-》status = shell_ansi_csi;
}
else
{
shell-》status = shell_in_normal;
}
break;
default:
break;
}
}
上述void shellansi(shell_typedef *shell, char data)函数为shellansi处理。
//shell正常按键处理函数
static void shellnormal(shell_typedef *shell, char data)
{
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 (short i = 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 (short i = shell-》cursor - 1; i 《 shell-》length; i++)
{
shelldisplaybyte(shell, shell-》buffer);
}
for (short i = shell-》length - shell-》cursor; i 》 0; i--)
{
shelldisplaybyte(shell, ‘\b’);
}
}
}
else
{
shelldisplay(shell, “\r\nwarnig: command is too long\r\n”);
shelldisplay(shell, shell-》command);
shelldisplay(shell, shell-》buffer);
shell-》cursor = shell-》length;
}
}
基于上述的两个类型代码,即可封装得到shell的处理代码,如下所示:
//shell处理
void shellhandler(shell_typedef *shell, char data) //shell处理函数
{
if (shell-》status == shell_in_normal) //shell工作在正常模式
{
char keydeffind = 0;
shell_keyfunctiondef *base = (shell_keyfunctiondef *)shell-》keyfuncbase;
for (short i = 0; i 《 shell-》keyfuncnumber; i++)
{
if (base.keycode == data) {
if (base.keyfunction) {
base.keyfunction(shell);
}
keydeffind = 1;
}
}
if (keydeffind == 0)
{
for (short i = 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);//shell ansi处理
}
}
以上就是shell的全部介绍,融合两节的代码,如下:
int main(void)
{
int getkey;
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-link rtt模式的shell,方便用户根据自己实际条件来辅助调试代码。
以上实现方式可能会影响mcu的运行效率,我们在本教程中优先考虑提供实现shell的方式。
推荐阅读:shell调试教程之mm32 mcu的j-link rtt方式实现shell功能
推荐阅读:shell调试教程之如何在mm32 mcu上使用shell来辅助开发

BittWare采用FPGA实现I/O开关,每簇通信量大于5
大模型微调样本构造的trick
地平线发布全场景整车智能中央计算芯片征程5
MAX2683低成本高性能3.5GHz上变频器
心疼华为:美国两大运营商相继与华为终止合作
shell是什么?shell实现原理分析基于MM32 MCU的shell脚本源码
unix操作系统常用命令
航锦科技全资子公司获两大FPGA订单 实现了该类产品的国产化替代
一根数据线可以实现多少功能
使用MAX1253/54和MAX1153/54进行温度监测
云计算进军深度学习的方法论
Arduino警灯的制作教程
全桥直流电动机驱动器的的高性能电源控制
什么蓝牙耳机最实用?最实用的蓝牙耳机推荐
DNA存储技术在未来会有它的市场吗
PLC现场实例电气原理图及编程
敦泰营收不断减少,根本原因是大陆智能手机市场呈现衰退趋势
FDD和WCDMA系统发射机的设计及性能分析
三星将扩大自家移动应用处理器搭载比重至60%
2022年中国汽车集团低碳化转型研究报告