发新帖本帖赏金 100.00元(功能说明)我要提问
12下一页
返回列表
打印
[MM32生态]

有哪些嵌入式 shell 适合用于单片机上?在这选一个吧

[复制链接]
6898|31
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 yang377156216 于 2022-5-24 19:10 编辑
#申请原创#  @21小跑堂

整体概览
如今,对于做 Linux 开发的研发人员来说,大家都喜欢通过输入指令符来执行一些命令操作,特别享受在黑乎乎的 cmd 窗口中敲击命令然后得到 echo 的感觉;而对于一些极客,他们乐忠于使用树莓派或者其它开源硬件加上 MicroPython 作为脚本开发语言去完成自己的 DIY 创作,很大程度也是因为有交互式解释器(又称 REPL)的存在,该环境能够让调试代码变得很轻松;而对于 MCU 的程序开发来说,除非会使用一些高级的手段(在 RAM 中运行代码),否则每次修改程序调试就需要重新下载,有时候改动点仅仅是几个全局变量参数,这样反复修改程序、重新编译下载就显得很浪费时间,并且在调试电机、电源等高压电源类应用时,如果出现操作错误,还有可能会造成炸机、毁坏电脑等危险。另外,在处理多个 AT 指令的时候,是不是也想有个优雅的框架能够完成此任务?此时是不是会想,如果有一个类似 Linux 的命令行操作环境,可以让开发者通过串口调试助手输入命令然后运行一些调试函数,这样将会极大方便开发进程?那是否需要自己去开发一套这样的环境呢?由于 MCU 存储资源和运算速度等限制,需要做很多处理优化去实现这么个 shell 功能,好在目前其实已经有很多类似的嵌入式 shell 代码框架可以直接拿来用了,只需要掌握如何移植以及如何添加自定义命令即可。为了响应本版一些工程师的呼吁,本次将介绍如何在 MM32 MCU 上使用 shell 框架去做开发,主要分为以下几个部分内容:
  • 有哪些常用的串口/终端工具
  • 有哪些常见嵌入式 shell
  • 移植和使用嵌入式 shell
  • 附件内容
  • 参考资源


一、有哪些常用的串口/终端工具
在嵌入式开发中,串口工具应该是平时工作中打交道最多的了。一般常见的串口工具根据功能划分以下几大类:
  • 纯串口的
  • 带 Modbus RTU 协议功能的
  • 串口网络互转功能的
  • 串口波形显示器
  • 带多种调试助手的集成工具
  • 带 SSH 等远程传输协议的终端工具

一般在选择工具的时候,会考虑功能是否齐全并且能够满足自己本身开发需求?比如有些特殊波特率的设置,数据位长度的设置等等;会考虑是否免费开源,自己能否获取到源码去做修改?会考虑交互的便捷性,以及界面逼格是否高大上,等等。本人将这两天搜集到的一些常用的串口/终端工具全部整理罗列在下面,并且全部放到网盘中,如果大家能够用得上可以尽情下载,有其它好用的也请跟帖上传哟。
  • Tabby
  • SecureCRT
  • MobaXterm
  • Putty
  • XShell
  • SSCOM
  • XCOM
  • Hypertrm
  • 野火/山外多功能调试助手
  • 友善串口调试助手
  • comNG
  • Commix
  • UartX
  • Termius
  • NxShell
  • FinalShell
  • Fish Shell
  • electerm
  • SEGGER Jlink-RTT Viewer
  • vscode EIDE 插件
  • ……

二、有哪些常见嵌入式 shell
在 PC 机上面 shell 的概念是:用户与操作系统间接口的程序,它允许用户向操作系统输入需要执行的命令,并将操作系统的运行结果返回给用户。所有 PC 机都是运行于操作系统之上的,系统中的内核管理着整台计算机的硬件,但是由于内核处于系统的底层,普通用户不能随意操作,不然一个不小心系统就崩溃啦!但我们总还是要让用户操作系统的,怎么办呢?这就需要一个专门的程序,它接受用户输入的命令,然后帮我们与内核沟通,最后让内核完成我们的任务。这个提供用户界面的程序被叫做 shell (壳层)。它提供了一个用户操作系统的入口,一般是通过 shell 去调用其他各种各样的应用程序,最后来达成我们的目的。
在 PC 机上 shell 通常可以分为命令行 shell 与 图形 shell 。顾名思义,前者提供一个 CLI 命令行界面,后者提供一个图形用户界面 GUI。
历史上知名的命令行 shell 有:
  • 适用于 Unix 及类 Unix 系统:

    • sh (Bourne shell),最经典的 Unix shell;
    • bash (Bourne-Again shell),目前绝大多数 Linux 发行版的默认 shell;
    • zsh (Z shell),非常友好;
    • fish (Friendly interactive shell),专注于易用性与友好用户体验的 shell;

  • Windows 下的 cmd.exe (命令提示符) 与 PowerShell。

至于为什么叫做 shell ,与其在系统中的地位很有关联,下面这幅图是不是很像一层壳呢?
而对于嵌入式系统特别是 MCU 软件工程而言,一般用户开发不复杂的应用时会习惯直接跑裸机运行程序,针对这种没有操作系统的程序,如何高效便捷的进行系统调试往往是一个比较令人头疼的问题,而一些常见的嵌入式 shell 程序就是作为一个用户与设备端的连接桥梁的存在,极大的方便了系统的调试。往深了讲,shell 的运行原理是通过在命令行输入命令,shell 通过串口接收数据并且对命令进行解析,然后执行相应的操作。更通俗地来说,就是使用输入的字符串,匹配到对应的函数,然后执行下去。那么,就需要建立一个命令与函数的一一对应的关系,将其定义为一张执行表。
在使用嵌入式 shell 过程中,我整理了一些常用且好用的 shell 程序可以方便大家拿去使用,大致可以分为裸机适用的和 RTOS 自带的组件,它们分别是:
  • nr micro shell
  • Letter shell
  • RTT shell
  • USMART
  • cmd parser
  • FinSH msh
  • FreeRTOS CLI
上面每个 shell 原理都一样,移植的核心也就是分为接口适配以及增加自定义命令,下面逐一介绍。

三、移植和使用嵌入式 shellnr micro shell(https://gitee.com/nrush/nr_micro_shell
它是一个开源的 MCU 级命令行交互组件,前面几篇内容中也大都是用到了这个组件,它具有以下优点:
  • 占用资源少,编译后占用 ROM 不到 4K,RAM 占用 1K 左右,使用简单,灵活方便。使用过程只涉及两个 shell_init() 和 shell() 两个函数,无论是使用 RTOS 还是裸机都可以方便的应用该工具,不需要额外的编码工作。
  • 交互体验好。完全类似于 linux shell 命令行,当串口终端支持 ANSI(如 Hypertrm 终端)时,其不仅支持基本的命令行交互,还提供Tab键命令补全,查询历史命令,方向键移动光标修改功能。
  • 扩展性好。nr_micro_shell 为用户提供自定义命令的标准函数原型,只需要按照命令编写命令函数,并注册命令函数,即可使用命令。
  • nr_micro_shell 不支持 ESC 键等控制键(控制符)。


接着介绍移植步骤。先将源码下载好并且添加到 template 工程中且包含到头文件路径,源码目录结构如下:
在串口初始化以及接收中断中添加以下初始化 nr_micro_shell 和接收命令接口函数,接收函数也可以通过串口轮询方式处理:
shell_init();//shell 初始化
if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
    {
        shell(UART_ReceiveData(UART1));//接收串口命令
        UART_ClearITPendingBit(UART1,  UART_IT_RXIEN);
    }
保证 shell_printf() 可以正常打印字符数据,这里可以直接将其定义为 printf(),接着使用下面两种方式添加自定义命令,一种如下:
const static_cmd_st static_cmd[] =
{
    {"ls", shell_ls_cmd},
    {"test", shell_test_cmd},
    {"blink", blink_led_cmd},
    {"\0", NULL}
};
其中最后一行    {"\0", NULL}  是不允许被删除的,另一种如下:
#define NR_SHELL_USING_EXPORT_CMD
NR_SHELL_CMD_EXPORT(reset, system_reset_cmd);
NR_SHELL_CMD_EXPORT(hwfault, hwfault_test_cmd);
上面两种方式根据自己的喜好去做定义,2 个参数分别表示交互时候的命令以及要被执行的函数,命令允许带其它参数,比如: blink 0 。执行函数可以解析附加的参数,也可以不管它们而做其它的事情。上面我自定义了 3 个函数,分别执行闪灯、软复位以及触发硬件错误动作,对于一些没有复位按键的板子该复位指令显得尤为重要了。下面是函数的具体实现:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] blink_led_cmd
*        blink 0     turn off the led
*        blink 1     turn on the led
*/
void blink_led_cmd(char argc, char *argv)
{
    if (!strcmp("1", &argv[argv[1]]))
    {
        LED1_ON();
        LED2_ON();
        LED3_ON();
        LED4_ON();
        shell_printf("led is ON\r\n");
    }
    else if (!strcmp("0", &argv[argv[1]]))
    {
        LED1_OFF();
        LED2_OFF();
        LED3_OFF();
        LED4_OFF();
        shell_printf("led is OFF\r\n");
    }
}

/**
* [url=home.php?mod=space&uid=247401]@brief[/url] system_reset_cmd
*/
void system_reset_cmd(char argc, char *argv)
{
    NVIC_SystemReset();
}

/**
* [url=home.php?mod=space&uid=247401]@brief[/url] hwfault_test_cmd
*/
void hwfault_test_cmd(char argc, char *argv)
{
    #if 0
    volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
    int x, y, z;

    *SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */

    x = 10;
    y = 0;
    z = x / y;
    printf("z:%d\n", z);
    #endif
    *(uint32_t *)0x32 = 888 ;
}
移植完后可以进行测试,使用前面介绍的串口终端工具:

Letter shell(https://gitee.com/zhang-ge/letter-shell
Letter shell 是一个 C 语言编写的,可以嵌入在程序中的嵌入式 shell,主要面向嵌入式设备,以 C 语言函数为运行单位,可以通过命令行调用,运行程序中的函数。相对2.x版本,Letter shell 3.x 增加了用户管理,权限管理,以及对文件系统的初步支持,此外 3.x 版本修改了命令格式和定义,2.x 版本的工程需要经过简单的修改才能完成迁移,若只需要使用基础功能,可以使用 Letter shell 2.x 版本。
Letter shell 有以下功能特点:
  • 命令自动补全
  • 快捷键功能定义
  • 命令权限管理
  • 用户管理
  • 变量支持
  • 代理函数和参数代理解析

这里仅需要实现基础的交互功能,所以选择使用 2.x 版本作为移植对象,移植的接口与前面尤为相似,主要有以下几个点:
#include "shell_port.h"

SHELL_TypeDef shell;

/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
void shellPortWrite(const char ch)
{
    UART_SendData(UART1, (uint8_t)ch);
    while(UART_GetFlagStatus(UART1, UART_IT_TXIEN) == RESET);
}

shell.write = shellPortWrite;
shellInit(&shell);//初始化阶段

/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
void UART1_IRQHandler(void)
{
    if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
    {
        shellHandler(&shell, UART_ReceiveData(UART1));
        UART_ClearITPendingBit(UART1,  UART_IT_RXIEN);
    }
}
然后可以根据自己的需求,在 shell_cfg.h 中更改各项宏定义配置,比如是否开启登录 shell 密码、命令头符号等等,具体宏定义内容如下:

可以看出,letter shell 不仅能够用于裸机程序,也能够适配上各种操作系统的,只是 shell_Task 与 shell_Handler 的处理方式不同。另外,自定义命令的方式与前面也很相似,这里只列举一条指令:
/******************************************************************************
* @brief  Jump to the specified address
* @param  wUserFlashAddr: 0x08000000 ~ 0x08xxxxxx
* @retval  None
* [url=home.php?mod=space&uid=93590]@Attention[/url]  None
******************************************************************************/
void BOOT_Jump_To_APP(void)
{
    /* 复位所有外设以及去除所有中断标志关闭中断*/
    /* 将中断向量表拷贝到SRAM区 */
    Iap_Jump_To_Address(APPLICATION_ADDRESS);
}

SHELL_EXPORT_CMD(jump, BOOT_Jump_To_APP, Jump to APP);
其中第三个参数为解释说明性语言,前两个参数与 nr_micro_shell 一样。Letter shell 功能挺强大的,如果还用到其它扩展功能,可以根据说明文档迁移到 3.X 版本去。
Segger RTT shell(https://www.segger.com/products/debug-probes/j-link/tools/rtt-viewer/#rtt-viewer-startup
SEGGER 的实时传输(Real Time Transfer, RTT)是嵌入式应用中用户 I/O 交互的一种新技术。J-Link RTT Viewer 是在调试主机上使用 RTT 功能的 Windows GUI 应用程序。它结合了 SWO 和半主机 semihosting 的优点,具有很高的性能,使用 RTT,可以从目标微控制器输出信息,并以非常高的速度向应用程序发送输入,而不会影响目标的实时性。它有以下特性:
  • 与目标应用程序进行双向通信
  • 非常高的传输速度,不影响实时行为
  • 使用调试通道进行通信
  • 目标上不需要额外的硬件或引脚
  • 支持任何J-Link模型
  • 支持ARM Cortex-M0/M0+/M1/M3/M4/M7/M23/M33和Renesas RX100/200/600
  • 提供功能和自由的完整实现代码
  • 通道0上的终端输出
  • 将文本输入发送到通道0
  • 最多16个虚拟终端,只有一个目标通道
  • 控制文本输出:彩色文本,擦除控制台
  • 在通道1上记录数据
RTT 支持两个方向上的多个通道,向上到主机,向下到目标板,可以用于不同的目标,并为用户提供尽可能多的自由。默认实现每个方向使用一个通道,这意味着多个可打印的终端输入和输出。有了 J-Link RTT 查看器,这个通道可以用于多个“虚拟”终端,只需要一个目标缓冲区就可以打印到多个窗口(例如,一个用于标准输出,一个用于错误输出,一个用于调试输出)。例如,可以使用另一个up (to host)通道发送分析或事件跟踪数据。具体移植说明参见附件中的文档,在此不做过多赘述。
USMART
相信熟悉正点原子的朋友都应该熟悉该组件,它是原子哥开发的一款小型 shell 工具,几乎在所有例程中都能见到。由于它是专为 STM32 而设计的,移植到 MM32 上是需要对底层实现方式做些修改的,好在 MM32 的 TIM 和 UART 外设与 STM32 还是很像的,只是需要注意一点,串口接收中断缓存完数据后需要手动清除标志位,而 ST 的是读取数据寄存器会自动清除接收有效标志位的。
下面是移植的关键代码:
#if USMART_ENTIMX_SCAN==1
//复位runtime
//需要根据所移植到的MCU的定时器参数进行修改
void usmart_reset_runtime(void)
{

    TIM_ClearFlag(TIM14, TIM_FLAG_Update); //清除中断标志位
    TIM_SetAutoreload(TIM14, 0XFFFF); //将重装载值设置到最大
    TIM_SetCounter(TIM14, 0);    //清空定时器的CNT
    usmart_dev.runtime = 0;
}
//获得runtime时间
//返回值:执行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms
//需要根据所移植到的MCU的定时器参数进行修改
u32 usmart_get_runtime(void)
{
    if (TIM_GetFlagStatus(TIM14, TIM_FLAG_Update) == SET) //在运行期间,产生了定时器溢出
    {
        usmart_dev.runtime += 0XFFFF;
    }
    usmart_dev.runtime += TIM_GetCounter(TIM14);
    return usmart_dev.runtime;      //返回计数值
}
//下面这两个函数,非USMART函数,放到这里,仅仅方便移植.
//定时器14中断服务程序
void TIM14_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM14, TIM_IT_Update) == SET) //溢出中断
    {
        usmart_dev.scan();  //执行usmart扫描
        TIM_SetCounter(TIM14, 0);    //清空定时器的CNT
        TIM_SetAutoreload(TIM14, 100); //恢复原来的设置
    }
    TIM_ClearITPendingBit(TIM14, TIM_IT_Update); //清除中断标志位
}
//使能定时器14,使能中断.
void Timer14_Init(u16 arr, u16 psc)
{
    NVIC_InitTypeDef   NVIC_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM14, ENABLE); ///使能TIM14时钟

    TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //定时器分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseInit(TIM14, &TIM_TimeBaseInitStructure); //初始化定时器4

    TIM_ITConfig(TIM14, TIM_IT_Update, ENABLE); //允许定时器4更新中断
    TIM_Cmd(TIM14, ENABLE); //使能定时器4

    NVIC_InitStructure.NVIC_IRQChannel = TIM14_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);//配置NVIC
}
#endif
////////////////////////////////////////////////////////////////////////////////////////
//初始化串口控制器
//sysclk:系统时钟(Mhz)
void usmart_init(u8 sysclk)
{
#if USMART_ENTIMX_SCAN==1
    Timer14_Init(1000, (u32)sysclk * 100 - 1); //分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步.
#endif
    usmart_dev.sptype = 1;  //十六进制显示参数
}

//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,   接收完成标志
//bit14,   接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA = 0;     //接收状态标记

void UART1_IRQHandler(void)                    //串口1中断服务程序
{
    u8 Res;

    if (UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
        Res = UART_ReceiveData(UART1);
        UART_ClearITPendingBit(UART1,  UART_IT_RXIEN);
        
        if ((USART_RX_STA & 0x8000) == 0) //接收未完成
        {
            if (USART_RX_STA & 0x4000) //接收到了0x0d
            {
                if (Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始
                else USART_RX_STA |= 0x8000; //接收完成了
            }
            else //还没收到0X0D
            {
                if (Res == 0x0d)USART_RX_STA |= 0x4000;
                else
                {
                    USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res ;
                    USART_RX_STA++;
                    if (USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收
                }
            }
        }
    }
}
实现完底层接口后,在 usmart_config.c 中完成自定义命令的注册以及相关函数的声明及定义,当然内部自带了用于打印所有 usmart 可调用函数 list 命令、用于获取各个函数的入口地址 id 命令、打印 usmart 使用的帮助信息 help/? 命令、以及查看十六进制和十进制数据的 hex 和 dec 命令等等。这里简单的增加了几条自定义命令:
//翻转LED
void led_toggle(void)
{
    LED1_TOGGLE();
}


/**
* @brief reset
*/
void reset(void)
{
    NVIC_SystemReset();
}


/**
* @brief prvHardFaultCommand
*/
void hardfault(void)
{
    *(uint32_t *)0x32 = 888 ;
}

//函数参数调用测试函数
void test_fun(void(*led_toggle)(void))
{
    led_toggle();
}

//函数名列表初始化(用户自己添加)
//用户直接在这里输入要执行的函数名及其查找串
struct _m_usmart_nametab usmart_nametab[] =
{
#if USMART_USE_WRFUNS==1    //如果使能了读写操作
    (void *)read_addr, "u32 read_addr(u32 addr)",
    (void *)write_addr, "void write_addr(u32 addr,u32 val)",
#endif
    (void *)led_toggle,"void led_toggle(void)",
    (void *)reset,"void reset(void)",
    (void *)hardfault,"void hardfault(void)",
    (void *)test_fun,"void test_fun(void(*led_toggle)(void))",
};
在串口助手中输入的字符串需要为 “led_toggle()”、“reset()” 等等,这点与前面几个 shell 不一样,需要注意,且 usmart 还能支持输入函数 ID 去执行该函数,这点设计蛮实用的,只要在使用前查询一下所有函数的 ID 号。实际测试时当然结合原子自己的 XCOM 串口助手来得方便,需要注意勾选换新行:

FinSH msh
FinSH 是 RT-Thread 的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。用户在控制终端输入命令,控制终端通过串口、USB、网络等方式将命令传给设备里的 FinSH,FinSH 会读取设备输入命令,解析并自动扫描内部函数表,寻找对应函数名,执行函数后输出回应,回应通过原路返回,将结果显示在控制终端上。
需要注意的是,该组件需要配合 RT-Thread 任意一个版本来运行,为了简化代码量,可以在 Keil 中以 CMSIS PACK 的方式在 RTE 中进行添加 rt-thread nano 与 msh/finsh。具体添加过程在此略过,主要还是描述移植的接口主要在 board.c 文件中,这里使用了串口接收中断和非中断的 2 种方式实现,用到了一个开关宏定义来做切换:
/*******************************************************************************
* @brief      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
void rt_hw_console_output(const char *str)
{
    rt_size_t i = 0, size = 0;
    char a = '\r';

    size = rt_strlen(str);
    for (i = 0; i < size; i++)
    {
        if (*(str + i) == '\n')
        {
            while((UART1->CSR & UART_IT_TXIEN) == 0);
            UART1->TDR = (a & (u16)0x00FF);
        }
        while((UART1->CSR & UART_IT_TXIEN) == 0);
        UART1->TDR = (*(str + i) & (u16)0x00FF);
   }
}

/*******************************************************************************
* @brief      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
char rt_hw_console_getchar(void)
{
    int ch = -1;

    #ifdef UART_USE_IT
    rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER);//接收信号量
    ch = UART_ReceiveData(UART1);
    #else
    if (UART_GetFlagStatus(UART1, UART_FLAG_RXAVL) != RESET)
    {
        ch = UART_ReceiveData(UART1);
        UART_ClearITPendingBit(UART1,  UART_IT_RXIEN);
    }
    else
    {
        rt_thread_mdelay(10);
    }
    #endif
   
    return ch;
}

#ifdef UART_USE_IT
/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void UART1_IRQHandler(void)
{
    rt_interrupt_enter();
    if(UART_GetITStatus(UART1, UART_ISR_RX) != RESET)
    {
        UART_ReceiveData(UART1);
        UART_ClearITPendingBit(UART1,UART_IT_RXIEN);
        rt_sem_release(&shell_rx_sem);  //释放信号量
    }
    rt_interrupt_leave();
}
#endif
FinSH msh 自定义命令也类似于前面的 shell ,语句样式为:
MSH_CMD_EXPORT(cycle_fix, change blink period);
实际测试用的 SecureCRT ,中间遇到一个问题,默认情况下该工具未设置“ENTER”键为回车换行,而只是回车,需要做如下设置才能让 shell 正常工作:
FreeRTOS CLl(https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_CLI/FreeRTOS_Plus_Command_Line_Interface.html
FreeRTOS+CLI(命令行界面)提供了一种简单、小型、可扩展且 RAM 高效的方法,使 FreeRTOS 应用程序能够处理命令行输入。移植添加 CLI 命令组件所需的步骤如下面所示:

限于篇幅,在此针对移植过程不做过多描述,具体的 FreeRTOS + CLI 的移植细节可以参阅:https://blog.csdn.net/sinat_36568888/article/details/124407768
我移植好的工程结构如下:

对于串口发送,我暂时未参照 ST 的,用的不是中断方式而是直接 printf 方式,大家可以在我工程中屏蔽位置进行发送中断方式实现。另外,实际测试时发现输出结果会输出 2 遍,调试发现了问题所在,这个组件与上面其它几个 shell 不一样,它认的终止符号为回车或者换行而不是回车换行:


最后,对于 cmd_parser 这个命令解析组件的移植和使用在此不做过多探讨和研究了,大家可以参照该链接进行研究:
https://mculover666.blog.csdn.net/article/details/106102372?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-1-106102372-blog-80486733.pc_relevant_baidufeatures_v7&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-1-106102372-blog-80486733.pc_relevant_baidufeatures_v7&utm_relevant_index=2

希望能够帮助到社区里面讨论使用 shell 的用户,在这么多 shell 面前千万不要挑花了眼哟,具体占用资源情况可以打开各个工程进行编译,个人推荐 nr_micro_shell !

四、附件内容
附件资源包中有以下内容可供参考下载:
基于 MM32F0140 的 FreeRTOS+CLI shell 工程 —— 1. MM32F0140_FreeRTOS_CLI_Shell.zip
基于 MM32F0140 的 USMART shell 工程 —— 2. MM32F0140_USMART_Shell.zip
基于 MM32F0140 的 nr_micro_shell 工程  —— 3. MM32F0140_NR_Micro_Shell.zip
基于 MM32F0140 的 nr_micro_shell 工程  —— 3. MM32F0140_NR_Micro_Shell.zip
基于 MM32F0270 的 Letter_Shell 2.X 工程  —— 4. MM32F0270_Letter_Shell.zip
基于 MM32F0140 的 RT-Thread Nano_MSH_Shell 工程  —— 5. MM32F0140_RT-Thread Nano_MSH_Shell.zip
收纳的串口终端工具下载地址 —— 9. 串口终端工具_百度网盘.zip

五、参考资源
本文创作参阅学习了以下下资源,在此声明感谢!
https://www.cnblogs.com/sddai/p/9769086.html
https://zhuanlan.zhihu.com/p/141988605
https://zhuanlan.zhihu.com/p/60329896

1. MM32F0140_FreeRTOS_CLI_Shell.zip (4 MB)
2. MM32F0140_USMART_Shell.zip (1.78 MB)
3. MM32F0140_NR_Micro_Shell.zip (1.84 MB)
4. MM32F0270_Letter_Shell.zip (797.21 KB)
5. MM32F0140_RT-Thread Nano_MSH_Shell.zip (711.93 KB)
6. MM32F0140_RTT_Viewer_Shell.zip (1.92 MB)
9. 串口终端工具_百度网盘.zip (292 Bytes)

   

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2022-05-25
理由:恭喜通过原创文章审核!请多多加油哦!

沙发
天意无罪| | 2022-5-24 21:26 | 只看该作者
码字不易,点个赞,这几个shell都没有用过,很早以前自己写了个shell,自己用,爽歪歪。

使用特权

评论回复
板凳
WoodData| | 2022-5-25 11:36 | 只看该作者
点个赞,不错啊

使用特权

评论回复
地板
海滨消消| | 2022-5-26 15:15 | 只看该作者
码字不易,点个赞

使用特权

评论回复
5
wuliangu| | 2022-5-26 23:54 | 只看该作者
学习了,希望以后能用得到

使用特权

评论回复
6
xiaoqi976633690| | 2022-6-1 20:30 | 只看该作者
谢谢分享...........

使用特权

评论回复
7
caigang13| | 2022-6-2 08:08 | 只看该作者
都没用过,out了啊。

使用特权

评论回复
8
sainuo598| | 2022-6-9 14:15 | 只看该作者
不错,感谢分享

使用特权

评论回复
9
HJH9648| | 2022-6-20 11:20 | 只看该作者
谢谢分享

使用特权

评论回复
10
www5911839| | 2022-6-30 11:08 | 只看该作者
感谢大佬

使用特权

评论回复
11
哀怨的玫瑰| | 2023-2-9 09:45 | 只看该作者
谢谢分享,受益匪浅

使用特权

评论回复
12
pingis58| | 2023-2-9 11:10 | 只看该作者
赞一个,学到了很不错。

使用特权

评论回复
13
wcheng13| | 2023-2-27 19:33 | 只看该作者
用过 RT 和 Letter ,都还行

使用特权

评论回复
14
mollylawrence| | 2023-3-2 15:54 | 只看该作者
网上有很多的教程可以使用的。              

使用特权

评论回复
15
benjaminka| | 2023-3-2 16:30 | 只看该作者
Letter shell是一个体积极小的

使用特权

评论回复
16
houjiakai| | 2023-3-4 21:18 | 只看该作者
letter shell 3.0是一个C语言编写的,可以嵌入在程序中的

使用特权

评论回复
17
cashrwood| | 2023-3-4 21:44 | 只看该作者
实现一个shell难不难?              

使用特权

评论回复
18
ccook11| | 2023-3-4 22:02 | 只看该作者
这个没有使用过 。              

使用特权

评论回复
19
everyrobin| | 2023-3-4 22:40 | 只看该作者
Letter shell               

使用特权

评论回复
20
beacherblack| | 2023-3-5 10:13 | 只看该作者
实现一个shell难不难?              

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

200

帖子

10

粉丝