第十二章
Shell移植
命令行外殼(shell),提供一套供用戶在命令行的操作接口,主要用於調試、查看系統信息。在大部分嵌入式系統中,一般開發調試都使用硬件調試器和printf日誌打印,在有些情況下,這兩種方式並不是那麼好用。比如對於多任務系統,我們想知道某個時刻系統中的任務運行狀態、手動控制系統狀態。如果有一個shell,就可以輸入命令,直接相應的函數執行獲得需要的信息,或者控制程序的行爲。這無疑會十分方便。
接下來我們就要通過移植的方式實現兩種shell。
第一種是我從rtthread中精簡下來,原來在STC12C系列的裸機上運行的Wing。
第二種是我從網上找到的在STM32裸機和ucos上運行的Qshell。
Wing
Wing是我在學習rtthread的finsh的時候看懂了原理,照着寫的一個非常小,動能非常簡單的shell工具,需要使用超級終端來進行交互,普通的串口工具坑能不太好用。
它的原理就是在串口每次接收到字符時進行相應的處理,比如tab或者回車鍵等,調用RTWingProcess函數來進行具體邏輯處理。
當遇到回車鍵時就將buff中的字符串進行對比,如果有與RTWfunc.c中相同的字符串就調用相應的函數指針,實現shell的簡單功能。
最初的wing中只實現了hello和version的功能,就是打印hello和版本信息,後來我又寫了一個status的函數,就是打印當前的系統信息。
int status(int argc, char *argv[])
{
u8 i;
OS_SS *ss = get_sys_statistics();
argc = argc;
argv = argv;
printf("\r\nID\tCPU\tSTATUS\tUSED STACK\r\n");
for (i=0; i<TASK_SIZE; i++) {
if (ss[i].OSSSStatus != OS_STAT_DEFAULT) {//任務已
printf("%bu\t%bu%%\t", i, ss[i].OSSSCyclesTot);
switch (ss[i].OSSSStatus) {
case OS_STAT_RUNNING:
printf("RUN");
break;
case OS_STAT_RDY:
printf("RDY");
break;
case OS_STAT_SLEEP:
printf("SLEEP");
break;
case OS_STAT_SUSPEND:
printf("SUSPEND");
break;
case OS_STAT_DEAD:
printf("DEAD");
break;
case OS_STAT_MUTEX:
printf("MUTEX");
break;
case OS_STAT_SEM:
printf("SEM");
break;
case OS_STAT_MSGQ:
printf("MSGQ");
break;
case OS_STAT_FLAG:
printf("FLAG");
break;
default:
printf("unknown");
}
printf("\t%bu\r\n", ss[i].OSSSMaxUsedStk);
}
}
return 0;
}
並且註冊到數組中
const struct CLI_RTWING rtwing_syscall[] = {
RTWING_FUNCTION_EXPORT(hello, hello rtwing!),
RTWING_FUNCTION_EXPORT(version, rtwing version!),
RTWING_FUNCTION_EXPORT(status, get tasks status!),
};
這樣就可以在超級終端中使用status查看系統信息了。
SecureCRT的輸出如下:
STC15F2K60S2 RT-OS Test Prgramme!
RTWing 0.0.2 Bulid Sep 21 2019 10:35:31
2014 Develop RZ
[RTWing]:
hello hello rtwing!
version rtwing version!
status get tasks status!
[RTWing]: status
ID CPU STATUS USED STACK
0 100% RUN 19
1 0% SLEEP 19
return 0x0000
[RTWing]:
這樣一來你可以根據自己的需要寫一些與任務交互的函數,用來測試或者調試。
Qshell
移植失敗,原因是這個工程最大的亮點是函數或者變量不需要自己去創建數組或者鏈表,因爲他是用了keil MDK的絕對段定位(__attribute__ section),這樣就可以把需要定位的變量保存到這個段中,然後通過段名$$Base就能找到段的起始位置,這樣就可以一個一個的遍歷所有數據。
但是在keil C51中只是找到了幾種絕對定位的方法,並不能滿足這個工程的需求,如果像上一個那樣用數組實現又難免有點重複工作。
所以到這裏全部的工作基本完成,如果從頭學習的人會發現這裏有一個大大的問題就是沒有處理中斷嵌套和中斷中任務相關函數的實現,在這裏就不一一講解了,因爲大體與函數中基本相同,只要弄明白中斷上下文環境能做什麼不能做什麼,這些函數都是重複勞動了。