MM32 MCU shell实现原理
eetrendMcu 2023-03-20

在前两节中,我们讲解了如何在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 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 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 length; i++)

{

shellDisplayByte(shell, shell->buffer[i]);

}

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 keyFuncNumber; i++)

{

if (base[i].keyCode == data) {

if (base[i].keyFunction) {

base[i].keyFunction(shell);

}

keyDefFind = 1;

}

}

if (keyDefFind == 0)

{

for (short i = 0; 

i

以上就是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的方式。

声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 【立即预约】Keysight直播:搞懂介电常数那些事儿


  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
  • 基于C51单片机实现汽车座椅自动控制系统的软硬件设计

    引言 随着人们生活水平的提高,对汽车座椅的舒适性要求也越来越高,要求对汽车座椅地调节能够更加简单、方便、快捷。目前,汽车座椅位置的调节多采用基于手动调节方式的机械和电动控制两种方式。汽车座椅位置的调节...

    2小时前
  • MCS51单片机程序设计时堆栈的计算方法解析

    用C语言进行MCS51系列单片机程序设计是单片机开发和应用的必然趋势。Keil公司的C51编译器支持经典8051和8051派生产品的版本,通称为Cx51。应该说,Cx51是C语言在MCS51单片机上的扩展,既有C语言的共性,又有它自己...

    2小时前
  • 51单片机定时器工作原理及用法

    TMOD : 控制定时器的工作方式。8个bit,高四位 bit 控制 T1,、低四位 bit 控制 T0。因为定时器有4种工作方式;TMOD = 0x00(工作方式0),TMOD = 0x01(工作方式0),TMOD = 0x02(工作方式2),TMOD = 0x03(工作方式3)。...

    2小时前
  • 51单片机学习单片机之路总结

    学习单片机有一学期了,现在也由51转到STM32了。一直想对51的学习做一个总结。也希望对别人有一些启发。也给后学者提供一些建议。当然本文是我对自己学习过程的总结,若有不对的地方,还请高手指出。 我想,再看本...

    2小时前
  • hot51增强型单片机开发板原理图

    功能要求: 一):绿灯25s倒计时,绿灯过度红灯有5s黄灯时间,红灯25s后直接跳绿灯。 二):按键按下模拟闯红灯输入,产生5s蜂鸣器鸣叫。 开发环境: 软件:Keil uVision4 硬件:HOT51增强型单片机开发板 程序代码:...

    昨天
  • 51单片机的延时子程序

    延时程序在单片机编程中使用非常广泛,但一些读者在学习中不知道延时程序怎么编程,不知道机器周期和指令周期的区别,不知道延时程序指令的用法, ,本文就此问题从延时程序的基本概念、机器周期和指令周期的区别和联系...

    昨天
  • 什么是Flash盘?Flash盘的结构是什么样的?

    Flash是大家常使用的存储之一,对于Flash,大家或多或少有所了解。上篇文章中,小编对Flash闪存的类型有所介绍。为继续增进大家对Flash的认识,本文将对Flash盘、Flash盘结构以及Flash读写操作予以介绍。如果你对本...

    昨天
  • 深谈嵌入式系统,嵌入式系统是如何组成的?

    嵌入式系统在生活中有诸多应用,大家对于嵌入式系统或多或少有所耳闻。在前两篇文章中,小编对嵌入式系统进行过详细介绍。为继续增进大家对嵌入式系统的认识,本文将对嵌入式系统的组成加以说明。如果你对嵌入式系...

    06-27
  • 嵌入式系统秘籍共享,最全嵌入式系统解析

    嵌入式系统的应用十分广泛,因此越来越多的人学习嵌入式系统。由此,在学习嵌入式系统之前,我们应当对嵌入式系统具备一些认识。所以在本文余下部分,小编将对嵌入式系统进行全面解析。如果你对嵌入式系统具有兴趣...

    06-27
  • 51单片机超声波测距程序详解

    51单片机超声波测距程序详解 超声波四通道测距:超声波测距实现分为三大块: 其一是12864带字库的液晶驱动程序: 代码如下: /////////////////12864驱动程序/////////////////////////// //1写数据 void WriteDat...

    06-25
  • 51系列单片机的引脚图

    51系列单片机的引脚图 端子介绍 l P0.0~P0.7 P0口8位双向口线(在引脚的39~32号端子)。 l P1.0~P1.7 P1口8位双向口线(在引脚的1~8号端子)。 l P2.0~P2.7 P2口8位双向口线(在引脚的21~28号端子)。 l P3.0~P3.7 P2口8...

    06-25
  • 51单片机串口通信需要加超时中断吗?

    接收数据时,超过一定时间就算出错. 这个超时的时间是单片机自己算出的吗?超时的时间是由编程序的人定的,他定多长就多长从一段程序开始 实现电脑向 单片机发送一些数据,单片机返回Iget +数据 #include #define u...

    06-25
下载排行榜
更多
评测报告
更多
EE直播间
更多
广告