在前兩節中,我們講解了如何在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來輔助開發
?
評論
查看更多