本次研究移植letter-shell。letter-shell一個功能強大的嵌入式shell,是一個C語言編寫的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式設備,以C語言函數(shù)為運行單位,可以通過命令行調(diào)用,運行程序中的函數(shù)。地址:https://github.com/NevermindZZT/letter-shell
功能
- 命令自動補全
- 快捷鍵功能定義
- 命令權(quán)限管理
- 用戶管理
- 變量支持
- 代理函數(shù)和參數(shù)代理解析
下面開始移植:移植流程官方倉庫說明的很清晰,看說明照著做一遍基本就可以了。
//初始化接口,對接串口。
#include "shell_cfg_user.h"
#include "drv_uart.h"
#include "shell.h"
Shell shell;
char shellBuffer[512];
/**
* @brief 用戶shell寫
*
* @param data 數(shù)據(jù)
* @param len 數(shù)據(jù)長度
*
* @return short 實際寫入的數(shù)據(jù)長度
*/
short userShellWrite(char *data, unsigned short len)
{
for(uint32_t i=0;i<len;i++)
stdout_putchar(data[i]);
return len;
}
/**
* @brief 用戶shell讀
*
* @param data 數(shù)據(jù)
* @param len 數(shù)據(jù)長度
*
* @return short 實際讀取到
*/
short userShellRead(char *data, unsigned short len)
{
uint32_t rlen = 0;
for(uint32_t i=0;i<len;i++)
{
if(shell_uart_rx.read_i != shell_uart_rx.write_i)
{
data[rlen++] = shell_uart_rx.buff[shell_uart_rx.read_i++];
shell_uart_rx.read_i &= 0x7f;
}
}
return rlen;
}
/**
* @brief 用戶shell上鎖
*
* @param shell shell
*
* @return int 0
*/
int userShellLock(Shell *shell)
{
return 0;
}
/**
* @brief 用戶shell解鎖
*
* @param shell shell
*
* @return int 0
*/
int userShellUnlock(Shell *shell)
{
return 0;
}
/**
* @brief 用戶shell初始化
*
*/
void userShellInit(void)
{
shell.write = userShellWrite;
shell.read = userShellRead;
// shell.lock = userShellLock;
// shell.unlock = userShellUnlock;
shellInit(&shell, shellBuffer, 512);
}
串口部分:
初始化調(diào)用:userShellInit(); //letter shell
主循環(huán)中調(diào)用:shell_usart_loop();調(diào)用shellHandler()處理串口接收的字符。
void drv_usart_gpio_init(void)
{
usart_config_t USART_InitStructure;
CLOCK_Select(kUART0_Clk_From_MainClk);
RESET_PeripheralReset(kUART0_RST_N_SHIFT_RSTn);
USART_GetDefaultConfig(&USART_InitStructure);
USART_InitStructure.baudRate_Bps = 115200;
USART_InitStructure.enableRx = 1U;
USART_InitStructure.enableTx = 1U;
USART_Init(USART0, &USART_InitStructure, CLOCK_GetMainClkFreq());
//使能UARTx RC中斷
USART_EnableInterrupts(USART0, kUSART_RxReadyInterruptEnable);
//優(yōu)先級,無優(yōu)先級分組
NVIC_SetPriority(USART0_IRQn, 0);
//UARTx中斷使能
NVIC_EnableIRQ(USART0_IRQn);
#if UART_SHELL == LETTER_SHELL
userShellInit(); //letter shell
#elif UART_SHELL == NR_MICRO_SHELL
shell_init();
#endif
}
int stdout_putchar (int ch)
{
while (0 == (USART_GetStatusFlags(USART0) & USART_STAT_TXRDY_MASK));
USART_WriteByte(USART0, (uint8_t)ch);
return ch;
}
int fputc(int ch,FILE *f)
{
return stdout_putchar(ch);
}
fifo_buffer shell_uart_rx=
{
.read_i = 0,
.write_i = 0,
};
void shell_usart_loop(void)
{
if(shell_uart_rx.read_i != shell_uart_rx.write_i)
{
#if UART_SHELL == LETTER_SHELL
shellHandler(&shell, shell_uart_rx.buff[shell_uart_rx.read_i++]); //letter shell
#elif UART_SHELL == NR_MICRO_SHELL
shell(shell_uart_rx.buff[shell_uart_rx.read_i++]);
#endif
shell_uart_rx.read_i &= 0x7f;
}
}
void USART0_IRQHandler(void)
{
uint32_t statusFlag;
uint32_t ch;
statusFlag = USART0->STAT;
if((statusFlag & USART_STAT_RXRDY_MASK) != 0)
{
ch = USART_ReadByte(USART0);
if(((shell_uart_rx.write_i+1)&0x7f) != shell_uart_rx.read_i)
{
shell_uart_rx.buff[shell_uart_rx.write_i++] = ch & 0xff;
shell_uart_rx.write_i &= 0x7f;
}
}
USART0->STAT |= statusFlag;
}
使用方法,倉庫內(nèi)也有說明和例子。
例如添加用戶的定義:
//測試添加用戶:
SHELL_EXPORT_USER(SHELL_CMD_PERMISSION(0xFF), root, 666666, root user);
SHELL_EXPORT_USER(SHELL_CMD_PERMISSION(0x01), LSY, 666666, test user);
這里添加了2個用戶,root和LSY。密碼都是666666.但是2個用戶權(quán)限不同。
letter-shell有2種添加函數(shù)命令方式。
第一種main函數(shù)形式??慈缦吕?,使用int argc, char *argv[]參數(shù),并給函數(shù)添加不同權(quán)限。
默認的letter用戶是使用不了下面函數(shù)命令的,
新加的root擁有下面2個函數(shù)func和func2權(quán)限.
新加的LSY只擁有func的權(quán)限。
//測試添加mian命令
int func(int argc, char *argv[])
{
printf("%d parameter(s)rn", argc);
for (char i = 1; i < argc; i++)
{
printf("%srn", argv[i]);
}
return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0x01)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), func, func, test1);
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0x02)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), func2, func, test2);
//letter:/$ func "hello world"
//2 parameter(s)
//hello world
第二種是普通C函數(shù)形式:
如下例子:主要是函數(shù)參數(shù)方式不同。在輸入命令參數(shù)時的方式就不同。
這個例子設置函數(shù)權(quán)限為0,意思是所有用戶都擁有這個函數(shù)權(quán)限。
//測試添加func命令
int func1(int i, char ch, char *str)
{
printf("input int: %d, char: %c, string: %srn", i, ch, str);
return 0;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), func1, func1, test3);
//letter:/$ func1 666 'A' "hello world"
//input int: 666, char: A, string: hello world
添加變量方式:
這里權(quán)限使用0xFF,標識除了權(quán)限為0的用戶,其他用戶都可以操作這個變量。
uint32_t muzhu_cnt;
SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0xFF) | SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT),muzhu_cnt,&muzhu_cnt,muzhu-count);
復制代碼
下面測試一下效果:
這是默認用戶letter時效果。
下面切換到root用戶看看,直接輸入root回車,然后輸入密碼666666.
下面切換LSY用戶效果:
可以看到不同用戶下,命令根據(jù)權(quán)限的不同。執(zhí)行的命令也不同。
工程添加的文件:shell_port.c,shell_port.h,shell_cfg_user.h為添加的接口文件。
shell_cfg_user.h添加的配置:
#if 0
#define SHELL_TASK_WHILE //是否使用默認shell任務while循環(huán)
#define SHELL_USING_CMD_EXPORT //是否使用命令導出方式
#define SHELL_USING_COMPANION //是否使用shell伴生對象功能
#define SHELL_SUPPORT_END_LINE //是否支持shell尾行模式
#define SHELL_HELP_LIST_USER //是否在輸入命令列表中列出用戶
#define SHELL_HELP_LIST_VAR //是否在輸入命令列表中列出變量
#define SHELL_HELP_LIST_KEY //是否在輸入命令列表中列出按鍵
#define SHELL_ENTER_LF //使用LF作為命令行回車觸發(fā)
#define SHELL_ENTER_CR //使用CR作為命令行回車觸發(fā)
#define SHELL_ENTER_CRLF //使用CRLF作為命令行回車觸發(fā)
#define SHELL_EXEC_UNDEF_FUNC //使用執(zhí)行未導出函數(shù)的功能
#define SHELL_COMMAND_MAX_LENGTH //shell命令最大長度
#define SHELL_PARAMETER_MAX_NUMBER //shell命令參數(shù)最大數(shù)量
#define SHELL_HISTORY_MAX_NUMBER //歷史命令記錄數(shù)量
#define SHELL_DOUBLE_CLICK_TIME //雙擊間隔(ms)
#define SHELL_QUICK_HELP //快速幫助
#define SHELL_MAX_NUMBER //管理的最大shell數(shù)量
#define SHELL_GET_TICK() //獲取系統(tǒng)時間(ms)
#define SHELL_USING_LOCK //是否使用鎖
#define SHELL_MALLOC(size) //內(nèi)存分配函數(shù)(shell本身不需要)
#define SHELL_FREE(obj) //內(nèi)存釋放函數(shù)(shell本身不需要)
#define SHELL_SHOW_INFO //是否顯示shell信息
#define SHELL_CLS_WHEN_LOGIN //是否在登錄后清除命令行
#define SHELL_DEFAULT_USER //shell默認用戶
#define SHELL_DEFAULT_USER_PASSWORD //默認用戶密碼
#define SHELL_LOCK_TIMEOUT //shell自動鎖定超時
#endif
/**
* @brief 是否使用shell伴生對象
* 一些擴展的組件(文件系統(tǒng)支持,日志工具等)需要使用伴生對象
*/
#define SHELL_USING_COMPANION 1
/**
* @brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 1
/**
* @brief 使用LF作為命令行回車觸發(fā)
* 可以和SHELL_ENTER_CR同時開啟
*/
#define SHELL_ENTER_LF 1
/**
* @brief 使用CR作為命令行回車觸發(fā)
* 可以和SHELL_ENTER_LF同時開啟
*/
#define SHELL_ENTER_CR 1
/**
* @brief 使用CRLF作為命令行回車觸發(fā)
* 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同時開啟
*/
#define SHELL_ENTER_CRLF 0
/**
* @brief shell格式化輸入的緩沖大小
* 為0時不使用shell格式化輸入
* @note shell格式化輸入會阻塞shellTask, 僅適用于在有操作系統(tǒng)的情況下使用
*/
//#define SHELL_SCAN_BUFFER 0
/**
* @brief 獲取系統(tǒng)時間(ms)
* 定義此宏為獲取系統(tǒng)Tick,如`HAL_GetTick()`
* @note 此宏不定義時無法使用雙擊tab補全命令help,無法使用shell超時鎖定
*/
//#define SHELL_GET_TICK() HAL_GetTick()
/**
* @brief 使用鎖
* @note 使用shell鎖時,需要對加鎖和解鎖進行實現(xiàn)
*/
//#define SHELL_USING_LOCK 0
/**
* @brief shell內(nèi)存分配
* shell本身不需要此接口,若使用shell伴生對象,需要進行定義
*/
#define SHELL_MALLOC(size) malloc(size)
/**
* @brief shell內(nèi)存釋放
* shell本身不需要此接口,若使用shell伴生對象,需要進行定義
*/
#define SHELL_FREE(obj) free(obj)