找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

697

积分

0

好友

87

主题
发表于 10 小时前 | 查看: 0| 回复: 0

之前的文章中有朋友留言推荐了 CherrySH,今天我们就来一起深入了解一下。

用户留言推荐CherrySH截图

1. CherrySH简介

项目信息 详情
项目名称 CherrySH
定位 嵌入式交互式 Shell
开发语言 C
主仓库 https://github.com/cherry-embedded/CherrySH
许可证 Apache-2.0

1.1 功能

  • 支持 TAB 键补全,包括命令和路径补全
  • 支持历史记录,通过 按键
  • 支持环境变量,需使用 $ 作为前缀,例如 $PATH
  • 支持设定用户名、主机名、路径
  • 支持非阻塞模式,支持裸机和 RTOS
  • 支持光标左右移动,支持 HOMEEND 切换光标
  • 支持组合按键,包括 Ctrl + <key> Alt + <key> F1-F12
  • 支持信号处理,捕获和处理不同的信号,例如Ctrl+C SIGINT 和Ctrl+Z SIGTSTP,可中断当前执行的 shell 任务
  • 支持用户登录,需要实现hash函数,默认strcmp
  • 支持环境变量添加、修改、删除、读出
  • 支持文件系统,FatFS,FileX,LittleFS,RomFS等(TODO)
  • 支持 exit 函数实现终止命令执行以及现场返回并调用设定的handler,利用setjmp实现(裸机)(TODO)
  • 支持作业控制,可以在前台或后台运行命令,并使用相关的控制命令(如fg、bg、jobs)来管理和操作作业(TODO)
  • 支持多用户命令权限(TODO)

一句话说清楚:CherrySH 就是让你在 MCU 上敲命令,还能有方向键、历史、TAB 补全这些体验,而且不用 malloc。

它怎么做到的?两招:

  1. 命令注册:编译时用宏把命令塞进链接器 section,运行时直接遍历这块内存
  2. 行编辑:单独的 readline 模块处理键盘输入,全静态 buffer

CherrySH终端执行LED控制命令

2. 核心原理

2.1 整体架构

CherrySH系统架构流程图

说明:

  • CherryReadLine:负责“怎么把一行字编辑好”(方向键、删除、历史、补全)
  • CherrySH:负责“拿到一行后怎么执行”(解析参数、查命令、调函数)
  • 命令表:编译时自动收集,运行时直接遍历

2.2 关键代码

注册一个命令:
static int my_cmd(int argc, char **argv)
{
    chry_shell_t *csh = (void *)argv[argc + 1];
    csh_printf(csh, "====== 嵌入式大杂烩 ======\r\n");
    return 0;
}

CSH_CMD_EXPORT(my_cmd, );

help命令与my_cmd命令执行结果

这个宏展开后,会在 FSymTab 段里放一个结构体:

// csh.h 里的定义
typedef struct {
    const char *path;   // 命令路径,如 "/bin"
    const char *name;   // 命令名
    const char *usage;  // 简短说明
    const char *help;   // 详细帮助
    int (*func)(int argc, char **argv);  // 函数指针
} chry_syscall_t;

链接器把所有这种结构体收集到一起,形成一个“命令表”。

运行时怎么找命令?

chry_shell_task_repl() 核心逻辑:

// 读一行
char *line = chry_readline(&csh->rl, buffer, size, &linesize);

// 解析成 argc/argv(原地切割,空格变 \0)
argc = chry_shell_parse(line, linesize, argv, MAX_ARG);

// 遍历命令表找匹配
for (call = csh->cmd_tbl_beg; call < csh->cmd_tbl_end; call++) {
    if (路径+名字匹配) {
        call->func(argc, argv);  // 找到就调用
        break;
    }
}

用流程图表示:

CherrySH命令执行流程图

2.3 目录结构

CherrySH/
├── chry_shell.c/h   # Shell 核心(解析、执行、用户管理)
├── csh.h            # 配置 + 导出宏
├── cherryrl/        # ReadLine 模块(行编辑、历史、补全)
├── builtin/         # 内置命令示例(help、clear 等)
└── samples/         # 移植示例(HPM、STM32)

3. 移植步骤

以先楫半导体hpm5301evklite为例。

  • 命令查找采用的是 gcc 的 section 功能,因此,我们需要先修改 linkerscript 文件,增加相关 section,举例 gcc ld 文件:
    .text : {
    .....

    . = ALIGN(4);
    __fsymtab_start = .;
    KEEP(*(FSymTab))
    __fsymtab_end = .;
    . = ALIGN(4);
    __vsymtab_start = .;
    KEEP(*(VSymTab))
    __vsymtab_end = .;
    . = ALIGN(4);
    }
  • 实现字符输入输出函数,接收推荐用中断 + ringbuf的形式
#include "csh.h"

static chry_shell_t csh;

static uint16_t csh_sput_cb(chry_readline_t *rl, const void *data, uint16_t size)
{
    uint16_t i;
    (void)rl;
    for (i = 0; i < size; i++) {
        if (status_success != uart_send_byte(HPM_UART0, ((uint8_t *)data)[i])) {
            break;
        }
    }

    return i;
}

static uint16_t csh_sget_cb(chry_readline_t *rl, void *data, uint16_t size)
{
    uint16_t i;
    (void)rl;
    for (i = 0; i < size; i++) {
        if (status_success != uart_receive_byte(HPM_UART0, (uint8_t *)data + i)) {
            break;
        }
    }

    return i;
}
  • 初始化 shell,参考 samples 中实现
  • 调用 chry_shell_task_execchry_shell_task_repl ,参考 samples 中实现
  • 配置系统环境变量
#define __ENV_PATH "/sbin:/bin"
const char ENV_PATH[] = __ENV_PATH;
CSH_RVAR_EXPORT(ENV_PATH, PATH, sizeof(__ENV_PATH));

#define __ENV_ZERO ""
const char ENV_ZERO[] = __ENV_ZERO;
CSH_RVAR_EXPORT(ENV_ZERO, ZERO, sizeof(__ENV_ZERO));
  • 使用 CSH_CMD_EXPORT 导出命令
static int write_led(int argc, char **argv)
{
    if (argc < 2) {
        printf("usage: write_led <status>\r\n\r\n");
        printf("  status    0 or 1\r\n\r\n");
        return -1;
    }

    board_led_write(atoi(argv[1]) == 0);
    return 0;
}
CSH_CMD_EXPORT(write_led, );

4. 总结

4.1 CherrySH 优缺点

优点:

  • readline 独立,行编辑体验好(方向键、Home/End、Ctrl 组合键都支持)
  • 支持多线程执行模型,长耗时命令可被 Ctrl+C 打断
  • 命令路径机制(/bin、/sbin),适合命令多的场景
  • 无堆内存,资源占用可预测

缺点:

  • 必须改链接脚本,对新手不友好
  • history buffer 必须是 2 的幂,容易踩坑
  • 文档较少,主要靠看 samples 学习

总的来说,CherrySH 为资源受限的嵌入式MCU环境提供了一个功能相当完善的交互式Shell解决方案,其通过编译器链接段来管理命令的机制颇具巧思。如果你想在项目中增加一个友好的调试命令行界面,又不想引入动态内存分配,CherrySH 值得一试。关于嵌入式系统开发中的更多底层技巧和 C/C++ 编程实践,你可以在 云栈社区 找到更多相关的讨论和资源。




上一篇:Python基金收益分析:用pandas计算累计收益与最大回撤
下一篇:Java开发实战:基于Activiti 7工作流引擎实现请假审批OA系统
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 17:47 , Processed in 0.307571 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表