1. letter-shell簡介
letter shell是一個C語言編寫的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式設(shè)備。
說得直白點(diǎn)他就是一個命令行交互軟件,可以讀取用戶輸入的命令,找到并執(zhí)行命令對應(yīng)的函數(shù)。
letter-shell的功能十分強(qiáng)大,目前主要功能有:
命令自動補(bǔ)全
快捷鍵功能定義
命令權(quán)限管理
用戶管理
變量支持
代理函數(shù)和參數(shù)代理解析
下面是letter-shell運(yùn)行起來的效果圖:
2. 獲取源碼
我們是要把letter-shell,移植到極海APM32F4的MCU上面運(yùn)行,所以我們需要獲取到極海的APM32F4的SDK包,以及l(fā)etter-shell的源碼。
letter-shell 開源項目源碼:
可以到簡介,給出的作者的github官網(wǎng)下載。如果因為網(wǎng)速的原因,也可以到gitee上面下載,gitee也有很多關(guān)于letter-shell的源碼,
3. APM32F4上移植letter-shell過程
3.1 準(zhǔn)備一份可以通過串口打印信息的工程
我們把官網(wǎng)的APM32F4 SDK下載下來后,然后我們選擇一個串口中斷的例程,如下:
然后,把這個例程不需要的代碼去掉,只留下串口相關(guān)的初始化代碼,還有printf重定向的代碼就行了。
編譯下載到板子之后,可以看到串口正常輸出打印信息,就說明代碼正常。
3.2 向工程添加letter-shell源碼
letter-shell源碼目錄如下:
我們只需要把src目錄下的源碼復(fù)制到對應(yīng)工程目錄下即可。
我這里就復(fù)制到對應(yīng)工程的 Middlewaresletter-shell 目錄下。
3.3 在keil-MDK中添加源碼和文件包含路徑
打開keil的項目管理窗口,然后添加我們剛剛復(fù)制的letter-shell的源碼目錄src的所有文件:
添加文件之后,再添加letter-shell的文件包含路徑:
點(diǎn)擊OK,退出。這個時候源碼相當(dāng)于添加完成,這是編譯是可以通過的,沒警告和錯誤。但是還不能正常使用letter-shell,因為還沒有添加移植的接口函數(shù)。
3.4 添加shell_port.c文件,提供讀寫接口函數(shù)
我們還需要提供letter-shell的讀寫接口函數(shù),這樣letter-shell才能通過串口輸出字符,或者通過串口獲取輸入字符。
在letter-shell的源碼目錄下,demo目錄中,已經(jīng)提供了基于stm32 freeRTOS的讀寫接口,我們可以把該文件復(fù)制到我們的工程目錄下,然后在該文件基礎(chǔ)上進(jìn)行改寫:
1、在shell_port.c中,我們主要實(shí)現(xiàn)shell的寫函數(shù)即可,代碼如下:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 用戶shell寫
*
* @param data 數(shù)據(jù)
* @param len 數(shù)據(jù)長度
*
* [url=home.php?mod=space&uid=266161]@return[/url] short 實(shí)際寫入的數(shù)據(jù)長度
*/
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、關(guān)于讀函數(shù),我們可以不用實(shí)現(xiàn),因為我們使用的是串口中斷方式接收字符,不需要實(shí)現(xiàn)讀函數(shù)。我們只需要在串口中斷函數(shù)中,調(diào)用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的初始化函數(shù),該函數(shù)其實(shí)主要就是初始化shell結(jié)構(gòu)體。因為我們只用到寫函數(shù),所以只提供了寫接口。具體代碼如下:
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函數(shù)初始化letter-shell
當(dāng)我們把接口函數(shù)都提供了之后,就只需要在main函數(shù)調(diào)用 letter-shell 的初始化函數(shù) userShellInit 即可。main函數(shù)代碼如下:
/*!
* [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)
{
}
}
主要就是初始化串口之后,就調(diào)用 userShellInit 初始化letter-shell。
到這里,我們就完成了letter-shell的移植了,編譯下載可以看到如下效果:
可以看到letter-shell支持了一些默認(rèn)命令。
3.6 letter-shell的配置文件shell_cfg.h
該文件也是在letter-shell的src目錄下,shell_cfg.h文件中包含了所有用于配置shell的宏,在使用前,可根據(jù)需要進(jìn)行配置。我們工程是使用的默認(rèn)配置,基本的功能也有,要想使用其他功能,可能需要先打開某個配置宏,定義的含義如下:
4. 自定義自己的命令
4.1 與導(dǎo)出自定義命令相關(guān)的宏
letter-shell支持定義自己的命令,并且把該命令導(dǎo)出到shell終端中,以供我們在命令行下使用自定義的命令。
在使用letter-shell自定義命令時,要先檢查 shell_cfg.h 文件是否開啟了命令導(dǎo)出功能。
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 是否使用命令導(dǎo)出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等導(dǎo)出命令
* 定義shell命令,關(guān)閉此宏的情況下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
就是這個宏需要定義為 1 。開啟了這個宏,我們就可以編寫自己的命令函數(shù),然后導(dǎo)出到shell終端了。
導(dǎo)出自定義命令的宏在 shell.h 文件中定義,其代碼如下:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] shell 命令定義
*
* @param _attr 命令屬性
* @param _name 命令名
* @param _func 命令函數(shù)
* @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
}
這里作者加入了命令屬性的參數(shù),主要就是定義該命令的權(quán)限,類型,是否使用返回值輸出等等(詳細(xì)的屬性定義可以去看源碼),其他參數(shù)就是命令名,對應(yīng)的命令函數(shù)名,已經(jīng)該命令的描述。
4.2 編寫一個命令測試函數(shù)
這里我編寫一個測試命令函數(shù),代碼如下:
/* 自定義命令測試函數(shù) */
int test_func(int a, char *str)
{
printf("%d ", a);
printf("%s ", str);
return 0;
}
/* 導(dǎo)出到命令列表里 */
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), test_cmd, test_func, test cmd);
然后編譯運(yùn)行,可以看到多了一個test_cmd命令:
可以看到我們運(yùn)行這個命令的時候,輸出了我們代碼的打印內(nèi)容。
根據(jù)作者的reamme文件,目前 letter shell 3.x 版本,命令函數(shù)參數(shù)只支持整數(shù),字符,字符串參數(shù),以及在某些情況下的浮點(diǎn)參數(shù)直接傳遞給執(zhí)行命令的函數(shù)。浮點(diǎn)型參數(shù)是在哪些情況才支持,可以閱讀作者的文檔。
另外,參數(shù)的個數(shù),是有一個宏配置的:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] shell命令參數(shù)最大數(shù)量
* 包含命令名在內(nèi),超過16個參數(shù)并且使用了參數(shù)自動轉(zhuǎn)換的情況下,需要修改源碼
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
默認(rèn)只支持8個參數(shù),當(dāng)然我們可以修改支持更多參數(shù)個數(shù)。
審核編輯:劉清
-
嵌入式
+關(guān)注
關(guān)注
5144文章
19575瀏覽量
315767 -
C語言
+關(guān)注
關(guān)注
180文章
7630瀏覽量
140739 -
Shell
+關(guān)注
關(guān)注
1文章
372瀏覽量
24074
原文標(biāo)題:APM32芯得 EP.25 | 基于APM32F4移植使用letter-shell命令行終端
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
在STM32實(shí)現(xiàn)命令行
Mini shell命令行調(diào)試工具的相關(guān)資料分享
【CW32飯盒派開發(fā)板試用體驗】+ 串口letter-shell 移植
caxa命令行中的應(yīng)用
mini shell命令行調(diào)試工具(單片機(jī)、c語言)

評論