最近了解到了Letter Shell这个轻量级的命令行,看了一下官方给出的例程,可以移植到SEGGER RTT上,于是尝试了一下,记录一下过程。
首先从GitHub上拉取Letter Shell的源码:https://github.com/NevermindZZT/letter-shell
建议将Github上的说明文档仔细看一遍再参考我下面的教程。
然后从JLink的安装目录下将SEGGER RTT的库文件拷贝出来,库文件一般在Jlink安装目录/Samples/RTT下面
将RTT的库文件拷贝到你的工程里面,设置好文件路径,在文件中包含RTT的头文件SEGGER_RTT.h ,然后在main函数中调用SEGGER_RTT_printf(),随意打印一些东西,编译下载运行。插上Jlink,用J-Link RTT Viewer连接单片机,如果能够打印出东西,说明RTT已经移植成功。
运行结果如下:
同样将Letter Shell的库文件拷贝到你的工程中,并设置好文件路径。
shell_port.h和.c文件用于shell和外部接口的对接,包括读写函数和初始化shell的函数。这里是将shell对接到了RTT的通道0上面。我的这两个文件如下:
shell_port.h:
c /**
* @file shell_port.h
* @author Letter (NevermindZZT@gmail.com)
* @brief
* @version 0.1
* @date 2019-02-22
*
* @copyright (c) 2019 Letter
*
*/
/*
加在.rodata :
_shell_command_start = .;
KEEP (*(shellCommand))
_shell_command_end = .;
*/
#ifndef __SHELL_PORT_H__
#define __SHELL_PORT_H__
#include "shell.h"
#include <stdbool.h>
extern Shell rttShell;
void rttShellInit(void);
short rttShellRead(char *data, unsigned short len);
short rttShellWrite(char *data, unsigned short len);
#endif
shell_port.c:
c /**
* @file shell_port.c
* @author Letter (NevermindZZT@gmail.com)
* @brief
* @version 0.1
* @date 2019-02-22
*
* @copyright (c) 2019 Letter
*
*/
#include "shell.h"
#include "shell_port.h"
#include "SEGGER_RTT.h"
Shell rttShell;
char rttShellBuffer[512];
/**
* @brief rtt shell写
*
* @param data 数据
* @param len 数据长度
*
* @return short 实际写入的数据长度
*/
short rttShellWrite(char *data, unsigned short len)
{
static int blocked = 0;
unsigned int time = HAL_GetTick();
short avail = 0;
short write = 0;
short wrote = 0;
do {
avail = SEGGER_RTT_GetAvailWriteSpace(0);
if (avail <= 0) {
if (blocked > 10) {
return 0;
}
HAL_Delay(1);
blocked++;
} else {
blocked = 0;
write = avail >= len ? len : avail;
SEGGER_RTT_Write(0, data, write);
data += write;
len -= write;
wrote += write;
}
} while (len > 0 && HAL_GetTick() - time < 10);
return wrote;
}
/**
* @brief rtt shell读
*
* @param data 数据
* @param len 数据长度
*
* @return short 实际读取的数据长度
*/
short rttShellRead(char *data, unsigned short len)
{
return SEGGER_RTT_Read(0, data, len);
}
/**
* @brief 用户shell初始化
*
*/
void rttShellInit(void)
{
rttShell.write = rttShellWrite;
rttShell.read = rttShellRead;
shellInit(&rttShell, rttShellBuffer, 512);
}
然后根据需要修改shell_cfg.h中的配置,我的配置如下:
c/**
* @file shell_cfg.h
* @author Letter (nevermindzzt@gmail.com)
* @brief shell config
* @version 3.0.0
* @date 2019-12-31
*
* @copyright (c) 2019 Letter
*
*/
#include "main.h"
#ifndef __SHELL_CFG_H__
#define __SHELL_CFG_H__
#ifdef SHELL_CFG_USER
#include SHELL_CFG_USER
#endif
#ifndef SHELL_TASK_WHILE
/**
* @brief 是否使用默认shell任务while循环
* 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
* 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
*/
#define SHELL_TASK_WHILE 0
#endif /** SHELL_TASK_WHILE */
#ifndef SHELL_USING_CMD_EXPORT
/**
* @brief 是否使用命令导出方式
* 使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
* 定义shell命令,关闭此宏的情况下,需要使用命令表的方式
*/
#define SHELL_USING_CMD_EXPORT 1
#endif /** SHELL_USING_CMD_EXPORT */
#ifndef SHELL_USING_COMPANION
/**
* @brief 是否使用shell伴生对象
* 一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
*/
#define SHELL_USING_COMPANION 0
#endif /** SHELL_USING_COMPANION */
#ifndef SHELL_SUPPORT_END_LINE
/**
* @brief 支持shell尾行模式
*/
#define SHELL_SUPPORT_END_LINE 0
#endif /** SHELL_SUPPORT_END_LINE */
#ifndef SHELL_HELP_LIST_USER
/**
* @brief 是否在输出命令列表中列出用户
*/
#define SHELL_HELP_LIST_USER 0
#endif /** SHELL_HELP_LIST_USER */
#ifndef SHELL_HELP_LIST_VAR
/**
* @brief 是否在输出命令列表中列出变量
*/
#define SHELL_HELP_LIST_VAR 0
#endif /** SHELL_HELP_LIST_VAR */
#ifndef SHELL_HELP_LIST_KEY
/**
* @brief 是否在输出命令列表中列出按键
*/
#define SHELL_HELP_LIST_KEY 0
#endif /** SHELL_HELP_LIST_KEY */
#ifndef SHELL_HELP_SHOW_PERMISSION
/**
* @brief 是否在输出命令列表中展示命令权限
*/
#define SHELL_HELP_SHOW_PERMISSION 1
#endif /** SHELL_HELP_SHOW_PERMISSION */
#ifndef SHELL_ENTER_LF
/**
* @brief 使用LF作为命令行回车触发
* 可以和SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_LF 1
#endif /** SHELL_ENTER_LF */
#ifndef SHELL_ENTER_CR
/**
* @brief 使用CR作为命令行回车触发
* 可以和SHELL_ENTER_LF同时开启
*/
#define SHELL_ENTER_CR 1
#endif /** SHELL_ENTER_CR */
#ifndef SHELL_ENTER_CRLF
/**
* @brief 使用CRLF作为命令行回车触发
* 不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
*/
#define SHELL_ENTER_CRLF 0
#endif /** SHELL_ENTER_CRLF */
#ifndef SHELL_EXEC_UNDEF_FUNC
/**
* @brief 使用执行未导出函数的功能
* 启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
* @attention 如果地址错误,可能会直接引起程序崩溃
*/
#define SHELL_EXEC_UNDEF_FUNC 0
#endif /** SHELL_EXEC_UNDEF_FUNC */
#ifndef SHELL_PARAMETER_MAX_NUMBER
/**
* @brief shell命令参数最大数量
* 包含命令名在内,超过16个参数并且使用了参数自动转换的情况下,需要修改源码
*/
#define SHELL_PARAMETER_MAX_NUMBER 8
#endif /** SHELL_PARAMETER_MAX_NUMBER */
#ifndef SHELL_HISTORY_MAX_NUMBER
/**
* @brief 历史命令记录数量
*/
#define SHELL_HISTORY_MAX_NUMBER 5
#endif /** SHELL_HISTORY_MAX_NUMBER */
#ifndef SHELL_DOUBLE_CLICK_TIME
/**
* @brief 双击间隔(ms)
* 使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔
*/
#define SHELL_DOUBLE_CLICK_TIME 200
#endif /** SHELL_DOUBLE_CLICK_TIME */
#ifndef SHELL_QUICK_HELP
/**
* @brief 快速帮助
* 作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息
*/
#define SHELL_QUICK_HELP 1
#endif /** SHELL_QUICK_HELP */
#ifndef SHELL_KEEP_RETURN_VALUE
/**
* @brief 保存命令返回值
* 开启后会默认定义一个`RETVAL`变量,会保存上一次命令执行的返回值,可以在随后的命令中进行调用
* 如果命令的`SHELL_CMD_DISABLE_RETURN`标志被设置,则该命令不会更新`RETVAL`
*/
#define SHELL_KEEP_RETURN_VALUE 0
#endif /** SHELL_KEEP_RETURN_VALUE */
#ifndef SHELL_MAX_NUMBER
/**
* @brief 管理的最大shell数量
*/
#define SHELL_MAX_NUMBER 5
#endif /** SHELL_MAX_NUMBER */
#ifndef SHELL_PRINT_BUFFER
/**
* @brief shell格式化输出的缓冲大小
* 为0时不使用shell格式化输出
*/
#define SHELL_PRINT_BUFFER 128
#endif /** SHELL_PRINT_BUFFER */
#ifndef SHELL_SCAN_BUFFER
/**
* @brief shell格式化输入的缓冲大小
* 为0时不使用shell格式化输入
* @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
*/
#define SHELL_SCAN_BUFFER 0
#endif /** SHELL_SCAN_BUFFER */
#ifndef SHELL_GET_TICK
/**
* @brief 获取系统时间(ms)
* 定义此宏为获取系统Tick,如`HAL_GetTick()`
* @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
*/
#define SHELL_GET_TICK() HAL_GetTick()
#endif /** SHELL_GET_TICK */
#ifndef SHELL_USING_LOCK
/**
* @brief 使用锁
* @note 使用shell锁时,需要对加锁和解锁进行实现
*/
#define SHELL_USING_LOCK 0
#endif /** SHELL_USING_LOCK */
#ifndef SHELL_MALLOC
/**
* @brief shell内存分配
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_MALLOC(size) 0
#endif /** SHELL_MALLOC */
#ifndef SHELL_FREE
/**
* @brief shell内存释放
* shell本身不需要此接口,若使用shell伴生对象,需要进行定义
*/
#define SHELL_FREE(obj) 0
#endif /** SHELL_FREE */
#ifndef SHELL_SHOW_INFO
/**
* @brief 是否显示shell信息
*/
#define SHELL_SHOW_INFO 1
#endif /** SHELL_SHOW_INFO */
#ifndef SHELL_CLS_WHEN_LOGIN
/**
* @brief 是否在登录后清除命令行
*/
#define SHELL_CLS_WHEN_LOGIN 1
#endif /** SHELL_CLS_WHEN_LOGIN */
#ifndef SHELL_DEFAULT_USER
/**
* @brief shell默认用户
*/
#define SHELL_DEFAULT_USER "letter-shell"
#endif /** SHELL_DEFAULT_USER */
#ifndef SHELL_DEFAULT_USER_PASSWORD
/**
* @brief shell默认用户密码
* 若默认用户不需要密码,设为""
*/
#define SHELL_DEFAULT_USER_PASSWORD ""
#endif /** SHELL_DEFAULT_USER_PASSWORD */
#ifndef SHELL_LOCK_TIMEOUT
/**
* @brief shell自动锁定超时
* shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
* 设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
* @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
*/
#define SHELL_LOCK_TIMEOUT 0 * 60 * 1000
#endif /** SHELL_LOCK_TIMEOUT */
#ifndef SHELL_USING_FUNC_SIGNATURE
/**
* @brief 使用函数签名
* 使能后,可以在声明命令时,指定函数的签名,shell 会根据函数签名进行参数转换,
* 而不是自动判断参数的类型,如果参数和函数签名不匹配,会停止执行命令
*/
#define SHELL_USING_FUNC_SIGNATURE 0
#endif /** SHELL_USING_FUNC_SIGNATURE */
#ifndef SHELL_SUPPORT_ARRAY_PARAM
/**
* @brief 支持数组参数
* 使能后,可以在命令中使用数组参数,如`cmd [1,2,3]`
* 需要使能 `SHELL_USING_FUNC_SIGNATURE` 宏,并且配置 `SHELL_MALLOC`, `SHELL_FREE`
*/
#define SHELL_SUPPORT_ARRAY_PARAM 0
#endif /** SHELL_SUPPORT_ARRAY_PARAM */
#endif
首先初始化shell,直接调用刚才在shell_port.c中实现的函数:
crttShellInit(); // 初始化RTT Shell
接下来就是调用shell任务。官方说明文档给出了调用shell任务的几种方式:
因为我这边是裸机开发,所以我选择了调用shellHandler的方式,然后在whlie(1)中简单实现一个定时调度的状态机,给shellHandler分配了一个任务:
如果在编译的时候,出现类似的错误:
参考官方文档的说明,添加对应的参数:
因为我使用的是GCC编译器,所以在链接文件中添加如下内容:
解决完上面的问题之后,编译下载,就可以在RTT Viewer中看到shell的界面了:
为了方便使用,我们可以选择将RTT对接到终端,这样就可以直接在终端中使用shell了。因为J-Link RTT Viewer中只能输入单个字符,不便于我们输入一长串指令,也不方便我们修改,所以对接到正经的终端中使用还是挺有必要的。
这里我使用了Letter Shell官方推荐的rtt转发telnet工具Rtt2Telnet:https://github.com/mcujackson/Rtt2Telnet
设置好Rtt2Telnet的目标芯片和连接速度后,点击开启,就会在本地的6198端口上开启RTT转发,使用支持telnet连接的终端即可连接到RTT。我这里使用的是MobaXterm,配置成功的结果如下:
原文链接:https://zxcli.fun/2025/04/26/将你的Letter-Shell对接到SEGGER-RTT上/
本文由原作者授权于本站联合发布
本文作者:自行车等等我
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 Polari_S_tation 版权所有 许可协议。转载请注明出处!