完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 MMCU5721167 于 2020-1-14 18:56 编辑
在前两节中,我们讲解了如何在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, "rnrn"); shellDisplay(shell, "+=========================================================+rn"); shellDisplay(shell, "| (C) COPYRIGHT 2019 MindMotion |rn"); shellDisplay(shell, "| shell v"SHELL_VERSION" |rn"); shellDisplay(shell, "| Build: "__DATE__" "__TIME__" |rn"); shellDisplay(shell, "+=========================================================+rn"); 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, "rnWarnig: Command is too longrn"); 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的方式。 |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
2259个成员聚集在这个小组
加入小组灵动微电子MM32全系列MCU产品应用手册,库函数和例程和选型表
11986 浏览 3 评论
【MM32 eMiniBoard试用连载】+基于OLED12864的GUI---U8G2
6032 浏览 1 评论
【MM32 eMiniBoard试用连载】移植RT-Thread至MM32L373PS
11154 浏览 0 评论
【MM32 eMiniBoard测评报告】+ 开箱 + 初探
4625 浏览 1 评论
灵动微课堂(第106讲) | MM32 USB功能学习笔记 —— WinUSB设备
4366 浏览 1 评论
[MM32软件] MM32F002使用内部flash存储数据怎么操作?
1702浏览 1评论
898浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-25 05:22 , Processed in 0.503049 second(s), Total 43, Slave 35 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号