1. letter-shell簡介
letter shell是一個C語言編寫的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式設備。
說得直白點他就是一個命令行交互軟件,可以讀取用戶輸入的命令,找到并執行命令對應的函數。
letter-shell的功能十分強大,目前主要功能有:
命令自動補全
快捷鍵功能定義
命令權限管理
用戶管理
變量支持
代理函數和參數代理解析
下面是letter-shell運行起來的效果圖:
2. 獲取源碼
我們是要把letter-shell,移植到極海APM32F4的MCU上面運行,所以我們需要獲取到極海的APM32F4的SDK包,以及letter-shell的源碼。
letter-shell 開源項目源碼:
可以到簡介,給出的作者的github官網下載。如果因為網速的原因,也可以到gitee上面下載,gitee也有很多關于letter-shell的源碼,
3. APM32F4上移植letter-shell過程
3.1 準備一份可以通過串口打印信息的工程
我們把官網的APM32F4 SDK下載下來后,然后我們選擇一個串口中斷的例程,如下:
然后,把這個例程不需要的代碼去掉,只留下串口相關的初始化代碼,還有printf重定向的代碼就行了。
編譯下載到板子之后,可以看到串口正常輸出打印信息,就說明代碼正常。
3.2 向工程添加letter-shell源碼
letter-shell源碼目錄如下:
我們只需要把src目錄下的源碼復制到對應工程目錄下即可。
我這里就復制到對應工程的 Middlewaresletter-shell 目錄下。
3.3 在keil-MDK中添加源碼和文件包含路徑
打開keil的項目管理窗口,然后添加我們剛剛復制的letter-shell的源碼目錄src的所有文件:
添加文件之后,再添加letter-shell的文件包含路徑:
點擊OK,退出。這個時候源碼相當于添加完成,這是編譯是可以通過的,沒警告和錯誤。但是還不能正常使用letter-shell,因為還沒有添加移植的接口函數。
3.4 添加shell_port.c文件,提供讀寫接口函數
我們還需要提供letter-shell的讀寫接口函數,這樣letter-shell才能通過串口輸出字符,或者通過串口獲取輸入字符。
在letter-shell的源碼目錄下,demo目錄中,已經提供了基于stm32 freeRTOS的讀寫接口,我們可以把該文件復制到我們的工程目錄下,然后在該文件基礎上進行改寫:
1、在shell_port.c中,我們主要實現shell的寫函數即可,代碼如下:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 用戶shell寫
*
* @param data 數據
* @param len 數據長度
*
* [url=home.php?mod=space&uid=266161]@return[/url] short 實際寫入的數據長度
*/
short userShellWrite(char *data, unsigned short len)
{
unsigned short temp = len;
while (temp--)
{
/* send a byte of data to the serial port */
USART_TxData(USART1, *data++);
/* wait for the data to be send */
while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
}
return len;
}
2、關于讀函數,我們可以不用實現,因為我們使用的是串口中斷方式接收字符,不需要實現讀函數。我們只需要在串口中斷函數中,調用shellHandler即可。串口中斷代碼如下:
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] This function handles USART1 RX interrupt Handler
*
* @param None
*
* @retval None
*
* @note
*/
void USART1_IRQHandler(void)
{
uint8_t ch;
if (USART_ReadIntFlag(USART1, USART_INT_RXBNE) == SET)
{
ch = USART_RxData(USART1);
shellHandler(&shell, ch);
}
}
3、提供letter-shell的初始化函數,該函數其實主要就是初始化shell結構體。因為我們只用到寫函數,所以只提供了寫接口。具體代碼如下:
Shell shell;
char shellBuffer[512];
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 用戶shell初始化
*
*/
void userShellInit(void)
{
shell.write = userShellWrite;
shellInit(&shell, shellBuffer, 512);
}
3.5 main函數初始化letter-shell
當我們把接口函數都提供了之后,就只需要在main函數調用 letter-shell 的初始化函數 userShellInit 即可。main函數代碼如下:
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Main program
*
* @param None
*
* @retval None
*/
int main(void)
{
/* USART Initialization */
USART_Config_T usartConfigStruct;
usartConfigStruct.baudRate = 115200;
usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
usartConfigStruct.mode = USART_MODE_TX_RX;
usartConfigStruct.parity = USART_PARITY_NONE;
usartConfigStruct.stopBits = USART_STOP_BIT_1;
usartConfigStruct.wordLength = USART_WORD_LEN_8B;
APM_EVAL_COMInit(COM1, &usartConfigStruct);
APM_EVAL_COMInit(COM2, &usartConfigStruct);
/* Enable USART1 RXBNE interrput */
USART_EnableInterrupt(EVAL_COM1, USART_INT_RXBNE);
USART_ClearStatusFlag(EVAL_COM1, USART_FLAG_RXBNE);
NVIC_EnableIRQRequest(EVAL_COM1_IRQn,1,0);
userShellInit();
while(1)
{
}
}
主要就是初始化串口之后,就調用 userShellInit 初始化letter-shell。
到這里,我們就完成了letter-shell的移植了,編譯下載可以看到如下效果:
可以看到letter-shell支持了一些默認命令。
3.6 letter-shell的配置文件shell_cfg.h
該文件也是在letter-shell的src目錄下,shell_cfg.h文件中包含了所有用于配置shell的宏,在使用前,可根據需要進行配置。我們工程是使用的默認配置,基本的功能也有,要想使用其他功能,可能需要先打開某個配置宏,定義的含義如下:
4. 自定義自己的命令
4.1 與導出自定義命令相關的宏
letter-shell支持定義自己的命令,并且把該命令導出到shell終端中,以供我們在命令行下使用自定義的命令。
在使用letter-shell自定義命令時,要先檢查 shell_cfg.h 文件是否開啟了命令導出功能。
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 是否使用命令導出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等導出命令
* 定義shell命令,關閉此宏的情況下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
就是這個宏需要定義為 1 。開啟了這個宏,我們就可以編寫自己的命令函數,然后導出到shell終端了。
導出自定義命令的宏在 shell.h 文件中定義,其代碼如下:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] shell 命令定義
*
* @param _attr 命令屬性
* @param _name 命令名
* @param _func 命令函數
* @param _desc 命令描述
*/
#define SHELL_EXPORT_CMD(_attr, _name, _func, _desc)
const char shellCmd##_name[] = #_name;
const char shellDesc##_name[] = #_desc;
SHELL_USED const ShellCommand
shellCommand##_name SHELL_SECTION("shellCommand") =
{
.attr.value = _attr,
.data.cmd.name = shellCmd##_name,
.data.cmd.function = (int (*)())_func,
.data.cmd.desc = shellDesc##_name
}
這里作者加入了命令屬性的參數,主要就是定義該命令的權限,類型,是否使用返回值輸出等等(詳細的屬性定義可以去看源碼),其他參數就是命令名,對應的命令函數名,已經該命令的描述。
4.2 編寫一個命令測試函數
這里我編寫一個測試命令函數,代碼如下:
/* 自定義命令測試函數 */
int test_func(int a, char *str)
{
printf("%d ", a);
printf("%s ", str);
return 0;
}
/* 導出到命令列表里 */
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), test_cmd, test_func, test cmd);
然后編譯運行,可以看到多了一個test_cmd命令:
可以看到我們運行這個命令的時候,輸出了我們代碼的打印內容。
根據作者的reamme文件,目前 letter shell 3.x 版本,命令函數參數只支持整數,字符,字符串參數,以及在某些情況下的浮點參數直接傳遞給執行命令的函數。浮點型參數是在哪些情況才支持,可以閱讀作者的文檔。
另外,參數的個數,是有一個宏配置的:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] shell命令參數最大數量
* 包含命令名在內,超過16個參數并且使用了參數自動轉換的情況下,需要修改源碼
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
默認只支持8個參數,當然我們可以修改支持更多參數個數。
審核編輯:劉清
-
嵌入式
+關注
關注
5095文章
19189瀏覽量
307993 -
C語言
+關注
關注
180文章
7615瀏覽量
137841 -
Shell
+關注
關注
1文章
366瀏覽量
23475
原文標題:APM32芯得 EP.25 | 基于APM32F4移植使用letter-shell命令行終端
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導體】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
在STM32實現命令行
Mini shell命令行調試工具的相關資料分享
【CW32飯盒派開發板試用體驗】+ 串口letter-shell 移植
mini shell命令行調試工具(單片機、c語言)
![mini <b class='flag-5'>shell</b><b class='flag-5'>命令行</b>調試工具(單片機、c語言)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論