STM32中实现shell控制台(命令解析实现)

文章目录

在嵌入式系统的命令行控制台(Shell)中,命令解析模块扮演着关键角色。它负责:

  1. 接收字符串命令;
  2. 拆分命令参数;
  3. 查找匹配的命令函数;
  4. 调用命令对应的处理函数。

本文基于 cmd.c 实现讲解一个简单而高效的命令注册与执行框架。


一、核心设计思想

命令系统基于以下数据结构和接口实现:

  • 命令表(cmd_table):保存所有注册命令;
  • 命令函数指针(cmd_func_t):指向具体执行逻辑;
  • cmd_execute():接收命令字符串,拆分参数并调用对应命令函数;
  • cmd_register():注册命令;
  • cmd_find():通过命令名查找。

二、命令系统实现详解(含完整注释)

c 复制代码
#include "cmd.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define CMD_TABLE_MAX 32  // 最多支持 32 个命令

// 命令表:用于保存所有注册的命令
static cmd_t cmd_table[CMD_TABLE_MAX];
static int cmd_count = 0; // 当前注册命令数

1. 示例命令函数实现

c 复制代码
// help 命令:打印帮助信息
int cmd_help(int argc, char *argv[])
{
    printf("help: Show this message\r\n");
    // 可扩展:遍历 cmd_table 打印所有命令和说明
    return 0;
}

// echo 命令:回显输入参数
int cmd_echo(int argc, char *argv[])
{
    for (int i = 1; i < argc; i++)
    {
        printf("%s ", argv[i]);
    }
    printf("\r\n");
    return 0;
}

2. 初始化命令系统

c 复制代码
// 初始化命令系统:注册内置命令
void cmd_init(void)
{
    cmd_register("help", cmd_help, "Show help");
    cmd_register("echo", cmd_echo, "Echo args");
}

3. 命令注册函数

c 复制代码
// 注册命令:添加命令名、函数指针和帮助信息到命令表
int cmd_register(const char *name, cmd_func_t func, const char *help)
{
    if (cmd_count >= CMD_TABLE_MAX)
        return -1; // 命令表满了,注册失败

    // 复制命令名到表项(限制最大长度)
    strncpy(cmd_table[cmd_count].name, name, CMD_NAME_LEN - 1);
    cmd_table[cmd_count].name[CMD_NAME_LEN - 1] = '\0';

    // 设置函数指针和帮助信息
    cmd_table[cmd_count].func = func;
    cmd_table[cmd_count].help = help;

    cmd_count++; // 更新命令数量
    return 0;
}

4. 命令查找函数

c 复制代码
// 查找命令:通过命令名在命令表中查找
cmd_t *cmd_find(const char *name)
{
    for (int i = 0; i < cmd_count; i++)
    {
        if (strcmp(cmd_table[i].name, name) == 0)
            return &cmd_table[i];  // 找到并返回指针
    }
    return NULL;  // 未找到
}

5. 命令执行函数

c 复制代码
// 执行命令行字符串:拆分参数并调用命令函数
int cmd_execute(const char *cmdline)
{
    if (cmdline == NULL || *cmdline == '\0')
        return -1;  // 空命令行,忽略

    // 使用 buf 保存一份可修改的命令行
    char buf[128];
    strncpy(buf, cmdline, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';

    char *argv[CMD_MAX_ARGS];  // 参数数组
    int argc = 0;

    // 使用 strtok 拆分参数
    char *token = strtok(buf, " ");
    while (token && argc < CMD_MAX_ARGS)
    {
        argv[argc++] = token;
        token = strtok(NULL, " ");
    }

    if (argc == 0)
        return -1;  // 没有有效参数

    // 查找对应命令
    cmd_t *cmd = cmd_find(argv[0]);
    if (!cmd)
    {
        printf("Unknown command: %s\r\n", argv[0]);
        return -1;  // 未知命令
    }

    // 调用命令函数,传递 argc 和 argv
    return cmd->func(argc, argv);
}

三、命令结构体(cmd_t)

c 复制代码
// cmd.h 中结构定义示例
#define CMD_NAME_LEN  16
#define CMD_MAX_ARGS  8

typedef int (*cmd_func_t)(int argc, char *argv[]); // 命令处理函数类型

typedef struct {
    char name[CMD_NAME_LEN];   // 命令名称
    cmd_func_t func;           // 命令处理函数
    const char *help;          // 帮助字符串
} cmd_t;

四、运行效果示例

假设输入如下命令:

shell 复制代码
echo Hello STM32

Shell 处理流程如下:

  1. 输入字符拼接成字符串;

  2. 回车后传入 cmd_execute()

  3. strtok 拆分为 argv = {"echo", "Hello", "STM32"}

  4. 查表找到 cmd_echo

  5. 调用 cmd_echo(argc=3, argv)

  6. 控制台输出:

    复制代码
    Hello STM32

五、小结

这个命令系统具备以下优点:

  • 轻量级:适合裸机或RTOS;
  • 易扩展 :添加命令只需实现函数并调用 cmd_register()
  • 通用接口:命令参数解析和传递简洁统一;
  • 结构清晰:注册、查找、执行职责分离。

适用于嵌入式项目中需要人机交互或调试接口的场景,例如串口控制、调试参数设置、模块测试等。


相关推荐
Do vis8244 小时前
STM32第十六天蓝牙模块
stm32·单片机·嵌入式硬件
学不动CV了4 小时前
ARM单片机启动流程(二)(详细解析)
c语言·arm开发·stm32·单片机·51单片机
猫猫的小茶馆6 小时前
【STM32】通用定时器基本原理
c语言·stm32·单片机·嵌入式硬件·mcu·51单片机
jingshaoqi_ccc7 小时前
stm32的USART使用DMA配置成循环模式时发送和接收有着本质区别
stm32·单片机·嵌入式硬件
小宋同学在不断学习11 小时前
stm32-掌握SPI原理(一)
stm32·单片机·spi
is081511 小时前
STM32的 syscalls.c 和 sysmem.c
c语言·stm32·嵌入式硬件
学不动CV了12 小时前
数据结构---链表结构体、指针深入理解(三)
c语言·arm开发·数据结构·stm32·单片机·链表
星辰pid21 小时前
STM32实现四自由度机械臂(SG90舵机)多功能控制(软件篇freertos)
stm32·单片机·嵌入式硬件·机械臂
小殷学长1 天前
【单片机毕业设计17-基于stm32c8t6的智能倒车监测系统】
stm32·单片机·课程设计
花落已飘1 天前
STM32中实现shell控制台(shell窗口输入实现)
stm32·单片机·嵌入式硬件