MM32 MCU shell实现原理
0 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
评论
  • 【6.29直播】西门子EDA工具在3D IC设计中的应用


  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
  • 怎样才能快速学习8051单片机

    [导读]单片机是微机的一种,现时有8051、AVR、ARM7、ARM9等系列,其中 “8051”是源自于Intel公司的MCS-51系列芯片,但目前不同生产厂商

    06-02
  • 什么是DDR?DDR的分类有哪些?

    存储器可分为易失性存储器和非易失性存储器两类,前者在掉电后会失去记忆的数据,后者即使在切断电源也可以保持数据

    05-31
  • 51单片机RAM 数据存储区、位寻址区、数据缓冲区

    [导读]1.RAM keil C语言编程RAM是程序运行中存放随机变量的数据空间。在keil中编写程序,如果当前模式为small模式,如果总的变量大小未超过12

    05-30
  • STM32使用库函数驱动LED灯编写程序步骤

    [导读] 一、熟悉GPIO结构体以下这个结构体是我从官方手册中获取的:[cpp] view plain copy print?typedef struct{u1

    05-30
  • 你知道Linux下的ds18b20驱动吗?

    [导读]今天在各位前辈已有成就的基础上花了两天时间终于把这个驱动给搞定了,从开始编译成模块看效果,进行调试,再到编译进内核,最后又编译了一个界面出来,虽说大多数

    05-29
  • 一文区分AT89C51和AT89C52

    [导读]AT89C51和AT89C52是单片机的两种型号。主要区别是容量不同。at89c51最多支持4KB的程序,at89c52则最多支持8KB的程序。

    05-29
  • 功能强大的时钟中断应用分析

    [导读]在单片机程序设计中,设置一个好的时钟中断,将能使一个CPU发挥两个CPU的功效,大大方便和简化程序的编制,提高系统的效率与可操作性。我们可以把一些例行的

    05-29
  • 一文告诉你MCS-51单片机有几个工作寄存器?

    [导读]工作寄存器有4组,每组都是8个工作寄存器R0~R7,通过PSW中的RS1、RS0两位来选择使用哪一组,如果不选,默认是选择第0组。

    05-29
  • 一文详解STC89C52单片机

    [导读]STC89C52单片机简介

    05-29
  • AVR单片机时熔丝位配置出现的一些问题分析

    [导读]AVR单片机的熔丝位配置是AVR单片机初学者很容易出错的地方,其实只要注意一些事项,还是能够尽量避免单片机被锁死,即使单片机被锁死,也可以使用一些方法解

    05-29
  • 一文详解STC89C52处理芯片

    [导读]主要性能: 与MCS-51单片机产品兼容 、8K字节在系统可编程Flash存储器、 1000次擦写周期、全静态操作:0Hz~33Hz 、三级加密程序存储

    05-29
  • 一文详细分析51单片机中断问题

    [导读]该寄存器用于设置定时/计数器的工作方式,低四位用于定时器0,高四位用于定时器1。 GATE:门控位。GATE=0时,只要用软件使TCON中的TR0或

    05-29
下载排行榜
更多
广告