一、引言
Letter shell是一個(gè)C語(yǔ)言編寫(xiě)的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式設(shè)備。簡(jiǎn)單來(lái)說(shuō)是一個(gè)命令行交互軟件,可以讀取用戶輸入的命令,找到并執(zhí)行命令對(duì)應(yīng)的函數(shù)。本文基于國(guó)科安芯AS32A601開(kāi)發(fā)板,實(shí)現(xiàn)輕量化的shell。
二、文件概述
本項(xiàng)目集成了一個(gè)輕量級(jí)串口命令行 Shell,支持通過(guò) USART0 與主機(jī)交互,以 printf 為統(tǒng)一輸出通道。
-Shell 提供基礎(chǔ)命令( help 、 ver 、 echo 、 led ),可按需擴(kuò)展到 ADC、SPI 等外設(shè)。
目錄與文件
shell.h :Shell 對(duì)外 API 與類(lèi)型。
shell.c :Shell 核心實(shí)現(xiàn)(輸入緩沖、命令解析、調(diào)度)。
shell_cmds.c :示例命令注冊(cè)與實(shí)現(xiàn)。
print.c :將 printf 輸出重定向到 USART0 。
main.c :Shell 初始化與主循環(huán)集成。
serial_cli.ps1 :Windows 交互腳本,便捷串口調(diào)試。
2.1****shell.c
#include "shell.h"
#include
#include
#include
/* RX ring buffer */
static****volatile uint8_t rx_buf[256];
static****volatile uint16_t rx_head = 0; /* write index */
static****volatile uint16_t rx_tail = 0; /* read index */
/* Line buffer */
static****char line_buf[SHELL_MAX_LINE];
static uint16_t line_len = 0;
/* Command registry */
static****const ShellCmd *cmd_table[8];
static****int cmd_table_count[8];
static****int table_used = 0;
void shell_init(void (writer)(**const***char** *buf, int len)) {
rx_head = rx_tail = 0;
line_len = 0;
table_used = 0;
( void )writer; /* output uses printf directly */
}
void shell_register(const ShellCmd *cmds, int count) {
__asm volatile ("fence.i");
if (!cmds || count <= 0) return ;
if (table_used < ( int )( sizeof (cmd_table)/ sizeof (cmd_table[0]))) {
cmd_table[table_used] = cmds;
cmd_table_count[table_used] = count;
table_used++;
}
}
void shell_input_byte(uint8_t b) {
uint16_t next = (uint16_t)((rx_head + 1) & 0xFF);
if (next == rx_tail) {
/* overflow, drop byte */
return ;
}
rx_buf[rx_head] = b;
rx_head = next;
}
static****int tokenize(char *line, char **argv, int max_args) {
int argc = 0;
char *p = line;
while (*p && argc < max_args) {
while (*p == ' ' || *p == 't') p++;
if (!*p) break ;
argv[argc++] = p;
while (*p && *p != ' ' && *p != 't') p++;
if (!*p) break ;
*p++ = '?';
}
return argc;
}
static****void print_prompt( void ) {
printf("rn> ");
}
static****int dispatch(int argc, char **argv) {
if (argc <= 0) return 0;
const****char *name = argv[0];
__asm volatile ("fence.i");
if (strcmp(name, "help") == 0) {
printf("Commands:rn");
for (int t = 0; t < table_used; ++t) {
for (int i = 0; i < cmd_table_count[t]; ++i) {
const ShellCmd *c = &cmd_table[t][i];
printf(" %s - %srn", c->name, c->desc ? c->desc : "");
__asm volatile ("fence.i");
}
}
return 0;
}
for (int t = 0; t < table_used; ++t) {
for (int i = 0; i < cmd_table_count[t]; ++i) {
const ShellCmd *c = &cmd_table[t][i];
if (strcmp(name, c->name) == 0) {
return c->handler(argc, argv);
}
}
}
printf("Unknown command: %srn", name);
return -1;
}
void shell_poll( void ) {
/* Read bytes from ring and build lines */
while (rx_tail != rx_head) {
uint8_t b = rx_buf[rx_tail];
rx_tail = (uint16_t)((rx_tail + 1) & 0xFF);
if (b == 'r') {
/* ignore CR */
continue ;
}
if (b == 'n') {
/* complete line */
line_buf[line_len] = '?';
char *argv[SHELL_MAX_ARGS];
int argc = tokenize(line_buf, argv, SHELL_MAX_ARGS);
if (argc > 0) {
( void )dispatch(argc, argv);
}
line_len = 0;
print_prompt();
continue ;
}
if (b == 'b' || b == 0x7F) {
/* backspace */
if (line_len > 0) line_len--;
continue ;
}
if (line_len < SHELL_MAX_LINE - 1) {
line_buf[line_len++] = ( char )b;
} else {
/* truncate on overflow */
}
}
}
主要函數(shù)分析
**1. **初始化函數(shù)
void shell_init(void (*writer)(const char *buf, int len));
- 初始化緩沖區(qū)指針
- writer 參數(shù)當(dāng)前未使用(直接使用 printf)
**2. **命令注冊(cè)函數(shù)
void shell_register(const ShellCmd *cmds, int count);
- 注冊(cè)一組命令
- fence.i 指令:RISC-V 內(nèi)存屏障,確保指令緩存一致性
**3. **字節(jié)輸入處理
void shell_input_byte(uint8_t b);
- 從串口接收單個(gè)字節(jié)
- 存入環(huán)形緩沖區(qū)
- 處理緩沖區(qū)溢出(丟棄字節(jié))
**4. **主輪詢函數(shù)
void shell_poll(void);
核心處理邏輯 :
- 從環(huán)形緩沖區(qū)讀取字節(jié)
- 處理特殊字符:
- 普通字符存入行緩沖區(qū)
- 行完成后,分詞并調(diào)度執(zhí)行
**5. **分詞函數(shù)
static int tokenize(char *line, char **argv, int max_args);
- 空格/制表符分割命令行
- 支持最大 SHELL_MAX_ARGS 個(gè)參數(shù)
- 原地修改字符串(添加 ? 終止符)
**6. **命令分發(fā)
static int dispatch(int argc, char **argv);
- 內(nèi)置 help 命令:顯示所有注冊(cè)命令
- 遍歷所有命令表查找匹配命令
- 調(diào)用對(duì)應(yīng)的 handler 函數(shù)
2.2 ** shell_cmds.c**
用戶可在該文件中定義函數(shù),并注冊(cè)到命令列表中
#include "shell.h"
#include "led.h"
#include
#include
static****int cmd_ver(int argc, char **argv) {
( void )argc; ( void )argv;
printf("AS32X601 usart_eflash shell v0.1rn");
return 0;
}
static****int cmd_echo(int argc, char **argv) {
for (int i = 1; i < argc; ++i) {
printf("%s%s", argv[i], (i == argc - 1) ? "" : " ");
}
printf("rn");
return 0;
}
static****int cmd_led(int argc, char **argv) {
if (argc < 3) {
printf("Usage: led <1|2|3>rn");
return -1;
}
int idx = argv[2][0] - '0';
if (idx < 1 || idx > 3) {
printf("Invalid LED index: %srn", argv[2]);
return -1;
}
int toggle = (strcmp(argv[1], "toggle") == 0);
int on = (strcmp(argv[1], "on") == 0);
int off = (strcmp(argv[1], "off") == 0);
if (!(toggle || on || off)) {
printf("Invalid action: %srn", argv[1]);
return -1;
}
switch (idx) {
case 1:
if (toggle) LED1_TOGGLE(); else****if (on) LED1_ON(); else****if (off) LED1_OFF();
break ;
case 2:
if (toggle) LED2_TOGGLE(); else****if (on) LED2_ON(); else****if (off) LED2_OFF();
break ;
case 3:
if (toggle) LED3_TOGGLE(); else****if (on) LED3_ON(); else****if (off) LED3_OFF();
break ;
default :
break ;
}
printf("led %s %drn", argv[1], idx);
return 0;
}
static****const ShellCmd default_cmds[] = {
{"ver", "Show shell version", cmd_ver},
{"echo", "Echo back arguments", cmd_echo},
{"led", "Control LEDs: led <1|2|3>", cmd_led},
};
void shell_cmds_init( void ) {
shell_register(default_cmds, ( int )( sizeof (default_cmds)/ sizeof (default_cmds[0])));
}
void shell_info()
{
printf("rn");
printf("rn");
printf (" _ _ _ _ _ _ rn");
printf( "| | _* | | | |* ___ _ __ _| | ___| | |rn");
printf("| | / _ __| / _ ' | / _| ' / _ | |rn");
printf("| |__| __/ |_| || **/ | ** | | | __/ | |rn");
printf("| | | | | | * * / | |* |* * | |* |rn");
printf ("rn");
printf("rn");
printf("Version: 0.1n");
printf("Board: AS32X601n");
printf("Build: " DATE " " TIME "n");
printf("n");
}
- 版本信息命令
static int cmd_ver(int argc, char **argv)
- 顯示固件版本信息
2.回顯命令
static int cmd_echo(int argc, char **argv)
- 打印所有參數(shù)(argv[0] 是命令名本身)
- 正確處理參數(shù)間的空格
3. ** LED **控制命令
static int cmd_led(int argc, char **argv)
- 完整的參數(shù)驗(yàn)證 :參數(shù)數(shù)量、范圍、合法性
- 清晰的錯(cuò)誤提示
- 執(zhí)行反饋 :操作成功后打印確認(rèn)信息
**4. **命令表定義
static const ShellCmd default_cmds
- 結(jié)構(gòu)清晰:命令名、描述、處理函數(shù)
- 包含使用示例(led命令)
**5. **初始化函數(shù)
void shell_cmds_init(void)
- 自動(dòng)計(jì)算命令數(shù)量,避免硬編碼
- 提供清晰的模塊初始化接口
2.3 main.c****部分流程
shell_init(NULL);
shell_cmds_init();
shell_info();
printf("AS32X601 shell readyrnType 'help' to list commands.rn> ");
while (1)
{
if (SET == USART_GetFlagStatus(USART0, USART_FLAG_RXFNE))
{
usart_data = USART_ReceiveData(USART0);
ClearCache();
/* feed incoming byte to shell */
shell_input_byte(usart_data);
}
/* process any pending input and run commands */
shell_poll();
}
輸入路徑:串口接收中斷或輪詢將字節(jié)喂給 shell_input_byte ,Shell維護(hù)環(huán)形緩沖與狀態(tài)機(jī)。
解析執(zhí)行:按行解析命令,匹配已注冊(cè)的命令表或函數(shù)指針,執(zhí)行對(duì)應(yīng)處理例程。
主循環(huán):周期性調(diào)用 shell_poll 以處理緩沖區(qū)中的數(shù)據(jù)與命令
三、開(kāi)發(fā)板驗(yàn)證:
該項(xiàng)目實(shí)現(xiàn)通過(guò)串口分別控制led 1,2,3翻轉(zhuǎn)和回顯功能

審核編輯 黃宇
-
命令行
+關(guān)注
關(guān)注
0文章
83瀏覽量
10779 -
Shell
+關(guān)注
關(guān)注
1文章
375瀏覽量
25493
發(fā)布評(píng)論請(qǐng)先 登錄
ROC rk3588s PC的uboot命令行的啟動(dòng)
涂鴉CLI正式開(kāi)源:讓AI Agent一行命令管理3000+品類(lèi)智能設(shè)備
Kubernetes kubectl命令行工具詳解
命令行阿里千問(wèn)搭建過(guò)程
AS32X601的I2C模塊操作EEPROM詳解
基于 AS32X601 微控制器的定時(shí)器模塊(TIM)技術(shù)研究與應(yīng)用實(shí)踐
所見(jiàn)即所得——Luban-Lite VS Code插件讓開(kāi)發(fā)實(shí)現(xiàn)“命令行自由”
AS32X601芯片F(xiàn)lash擦寫(xiě)調(diào)試技術(shù)解析
AS32X601系列MCU硬件最小系統(tǒng)設(shè)計(jì)與調(diào)試方案探析
【RA-Eco-RA6M4開(kāi)發(fā)板評(píng)測(cè)】移植shell實(shí)現(xiàn)命令交互
淺談wsl --update` 命令行選項(xiàng)無(wú)效的解決方案
AS32X601驅(qū)動(dòng)系列教程 GPIO_點(diǎn)亮LED詳解
AS32X601驅(qū)動(dòng)系列教程 SMU_系統(tǒng)時(shí)鐘詳解
基于AS32X601使用shell命令行終端詳解
評(píng)論