源碼地址:https://github.com/NevermindZZT/letter-shell
1 Letter shell簡介
熟悉Linux的朋友的都知道,shell包裹在內核之外的人機交互界面,用于用戶和內核之間打交道的功能,類似于windows CMD。 通過Shell將輸入的命令與內核通訊,好讓內核可以控制硬件開正確無誤的操作工作。Shell有著不同的分類,比如Bourne shell(sh),Korn shell(ksh)、C shell (csh)、Bourne-again shell(bash)、tcsh。其中最常用的有csh和bash。Shell本身是一個用C語言編寫的程序,它是用戶使用Unix/Linux的橋梁,用戶的大部分工作都是通過Shell完成的。
然而在嵌入式中,由于資源有限,自然很少使用shell,但隨著MCU的資源越來越豐富,一些適用于嵌入式的shell工具也就問世了,本問將要介紹的是Letter shell,Letter shell是一個體積極小的嵌入式shell,當前最新版本是3.X。
Letter shell有如下功能:
- 命令自動補全,使用TAB鍵補全命令
- 命令幫助,使用help [command]顯示命令幫助
- 幫助補全,輸入命令后雙擊TAB鍵補全命令幫助指令
- 快捷鍵,支持使用CTRL+A~Z組合按鍵直接調用函數
- shell變量,支持在shell中查看和修改變量值,支持變量作為命令參數
- 登錄密碼,支持在shell中使用登錄密碼,支持超時自動鎖定
2 Letter shell移植
Shell是一個命令行交互式形式存在,那最常規的就是使用MCU的串口資源了,當然也可使用USB模擬的虛擬串口。
Letter shell的移植比較簡單,既然需要占用串口資源,那么首先要準備一個裸機工程,該工程需要事先串口的收發,關于串口的實現請參看逼著文章:
標準庫:https://bruceou.blog.csdn.net/article/details/79341769
HAL庫:https://bruceou.blog.csdn.net/article/details/109190370
筆者本文以標準庫為例講解。
1.復制源碼
首先下載letter-shell,然后在工程中新建Letter_shell目錄,將letter-shell目錄下的src的文件復制到工程目錄Middlewares/Letter_shell中。
2.新建接口文件
在工程用戶目錄下新建shell_port.c和shell_port.h文件,當然也可以放在User目錄。
3.配置工程
打開Keil,添加相應的文件。
然后添加相應的頭文件路徑。
接下來就是實現Letter shell的收發。
發送代碼如下:
/**
* @brief 用戶shell寫
*
* @param data 數據
*/
void userShellWrite(char data)
{
USART_SendData(USART1, (uint8_t) data);
/* 等待發送完畢 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
接收采用中斷的方式,代碼如下:
/**
* @brief This function handles USART1 Handler.
* @param None
* @retval None
*/
void USART1_IRQHandler(void)
{
uint8_t ch; //接收中斷緩沖
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ch = USART_ReceiveData(USART1);
//ch = USART1- >DR;
//調用shell處理數據的接口
shellHandler(&shell, ch);
}
}
還需要實現Letter shell初始化接口。
/**
* @brief 用戶shell初始化
*
*/
void userShellInit(void)
{
shell.write = userShellWrite;
shellInit(&shell, shellBuffer, 512);
}
最后在主函數中初始化即可。
/**
* @brief mian
* @param None
* @retval int
*/
int main(void)
{
/* 配置SysTick 為10us中斷一次 */
SysTick_Init();
/* USART1 配置模式為 115200 8-N-1,中斷接收 */
USART1_Config();
userShellInit();
for(;;)
{
Delay_ms(50);
}
}
好了,這就移植完成了,編譯、下載,連接串口1,使用xshell等工具,打印信息如下:
很簡單吧。
3 Letter shell應用
3.1 Letter shell宏定義
在開發Letter shell應用前,需要知道Letter shell的宏定義,其宏定義在shell_cfg.h文件。
#ifndef __SHELL_CFG_H__
#define __SHELL_CFG_H__
/**
* @brief 是否使用默認shell任務while循環,使能宏`SHELL_USING_TASK`后此宏有意義
* 使能此宏,則`shellTask()`函數會一直循環讀取輸入,一般使用操作系統建立shell
* 任務時使能此宏,關閉此宏的情況下,一般適用于無操作系統,在主循環中調用`shellTask()`
*/
#define SHELL_TASK_WHILE 1
/**
* @brief 是否使用命令導出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等導出命令
* 定義shell命令,關閉此宏的情況下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
/**
* @brief 是否使用shell伴生對象
* 一些擴展的組件(文件系統支持,日志工具等)需要使用伴生對象
*/
#define SHELL_USING_COMPANION 0
/**
* @brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 0
/**
* @brief 是否在輸出命令列表中列出用戶
*/
#define SHELL_HELP_LIST_USER 0
/**
* @brief 是否在輸出命令列表中列出變量
*/
#define SHELL_HELP_LIST_VAR 0
/**
* @brief 是否在輸出命令列表中列出按鍵
*/
#define SHELL_HELP_LIST_KEY 0
/**
* @brief 是否在輸出命令列表中展示命令權限
*/
#define SHELL_HELP_SHOW_PERMISSION 1
/**
* @brief 使用LF作為命令行回車觸發
* 可以和SHELL_ENTER_CR同時開啟
*/
#define SHELL_ENTER_LF 1
/**
* @brief 使用CR作為命令行回車觸發
* 可以和SHELL_ENTER_LF同時開啟
*/
#define SHELL_ENTER_CR 1
/**
* @brief 使用CRLF作為命令行回車觸發
* 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同時開啟
*/
#define SHELL_ENTER_CRLF 0
/**
* @brief 使用執行未導出函數的功能
* 啟用后,可以通過`exec [addr] [args]`直接執行對應地址的函數
* @attention 如果地址錯誤,可能會直接引起程序崩潰
*/
#define SHELL_EXEC_UNDEF_FUNC 0
/**
* @brief shell命令參數最大數量
* 包含命令名在內,超過8個參數并且使用了參數自動轉換的情況下,需要修改源碼
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
/**
* @brief 歷史命令記錄數量
*/
#define SHELL_HISTORY_MAX_NUMBER 5
/**
* @brief 雙擊間隔(ms)
* 使能宏`SHELL_LONG_HELP`后此宏生效,定義雙擊tab補全help的時間間隔
*/
#define SHELL_DOUBLE_CLICK_TIME 200
/**
* @brief 管理的最大shell數量
*/
#define SHELL_MAX_NUMBER 5
/**
* @brief shell格式化輸出的緩沖大小
* 為0時不使用shell格式化輸出
*/
#define SHELL_PRINT_BUFFER 128
/**
* @brief shell格式化輸入的緩沖大小
* 為0時不使用shell格式化輸入
* @note shell格式化輸入會阻塞shellTask, 僅適用于在有操作系統的情況下使用
*/
#define SHELL_SCAN_BUFFER 0
/**
* @brief 獲取系統時間(ms)
* 定義此宏為獲取系統Tick,如`HAL_GetTick()`
* @note 此宏不定義時無法使用雙擊tab補全命令help,無法使用shell超時鎖定
*/
#define SHELL_GET_TICK() 0
/**
* @brief shell內存分配
* shell本身不需要此接口,若使用shell伴生對象,需要進行定義
*/
#define SHELL_MALLOC(size) 0
/**
* @brief shell內存釋放
* shell本身不需要此接口,若使用shell伴生對象,需要進行定義
*/
#define SHELL_FREE(obj) 0
/**
* @brief 是否顯示shell信息
*/
#define SHELL_SHOW_INFO 1
/**
* @brief 是否在登錄后清除命令行
*/
#define SHELL_CLS_WHEN_LOGIN 1
/**
* @brief shell默認用戶
*/
#define SHELL_DEFAULT_USER "letter"
/**
* @brief shell默認用戶密碼
* 若默認用戶不需要密碼,設為""
*/
#define SHELL_DEFAULT_USER_PASSWORD ""
/**
* @brief shell自動鎖定超時
* shell當前用戶密碼有效的時候生效,超時后會自動重新鎖定shell
* 設置為0時關閉自動鎖定功能,時間單位為`SHELL_GET_TICK()`單位
* @note 使用超時鎖定必須保證`SHELL_GET_TICK()`有效
*/
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
#endif