基于AS32X601使用shell命令行终端详解

电子说

1.4w人已加入

描述

一、引言

Letter shell是一个C语言编写的,可以嵌入在程序中的嵌入式shell,主要面向嵌入式设备。简单来说是一个命令行交互软件,可以读取用户输入的命令,找到并执行命令对应的函数。本文基于国科安芯AS32A601开发板,实现轻量化的shell。

二、文件概述

本项目集成了一个轻量级串口命令行 Shell,支持通过 USART0 与主机交互,以 printf 为统一输出通道。

-Shell 提供基础命令( help 、 ver 、 echo 、 led ),可按需扩展到 ADC、SPI 等外设。

目录与文件

shell.h :Shell 对外 API 与类型。

shell.c :Shell 核心实现(输入缓冲、命令解析、调度)。

shell_cmds.c :示例命令注册与实现。

print.c :将 printf 输出重定向到 USART0 。

main.c :Shell 初始化与主循环集成。

serial_cli.ps1 :Windows 交互脚本,便捷串口调试。

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 */

}

}

}

主要函数分析

**1. **初始化函数

void shell_init(void (*writer)(const char *buf, int len));

  • 初始化缓冲区指针
  • writer 参数当前未使用(直接使用 printf)

**2. **命令注册函数

void shell_register(const ShellCmd *cmds, int count);

  • 注册一组命令
  • fence.i 指令:RISC-V 内存屏障,确保指令缓存一致性

**3. **字节输入处理

void shell_input_byte(uint8_t b);

  • 从串口接收单个字节
  • 存入环形缓冲区
  • 处理缓冲区溢出(丢弃字节)

**4. **主轮询函数

void shell_poll(void);

核心处理逻辑

  • 从环形缓冲区读取字节
  • 处理特殊字符:
  • 普通字符存入行缓冲区
  • 行完成后,分词并调度执行

**5. **分词函数

static int tokenize(char *line, char **argv, int max_args);

  • 空格/制表符分割命令行
  • 支持最大 SHELL_MAX_ARGS 个参数
  • 原地修改字符串(添加 � 终止符)

**6. **命令分发

static int dispatch(int argc, char **argv);

  • 内置 help 命令:显示所有注册命令
  • 遍历所有命令表查找匹配命令
  • 调用对应的 handler 函数

2.2 ** shell_cmds.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");

}

  1. 版本信息命令

static int cmd_ver(int argc, char **argv)

  • 显示固件版本信息

2.回显命令

static int cmd_echo(int argc, char **argv)

  • 打印所有参数(argv[0] 是命令名本身)
  • 正确处理参数间的空格

3. ** LED **控制命令

static int cmd_led(int argc, char **argv)

  • 完整的参数验证 :参数数量、范围、合法性
  • 清晰的错误提示
  • 执行反馈 :操作成功后打印确认信息

**4. **命令表定义

static const ShellCmd default_cmds

  • 结构清晰:命令名、描述、处理函数
  • 包含使用示例(led命令)

**5. **初始化函数

void shell_cmds_init(void)

  • 自动计算命令数量,避免硬编码
  • 提供清晰的模块初始化接口

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();

}

输入路径:串口接收中断或轮询将字节喂给 shell_input_byte ,Shell维护环形缓冲与状态机。

解析执行:按行解析命令,匹配已注册的命令表或函数指针,执行对应处理例程。

主循环:周期性调用 shell_poll 以处理缓冲区中的数据与命令

三、开发板验证:

该项目实现通过串口分别控制led 1,2,3翻转和回显功能

Shell

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分