(十五)使用任务通知实现命令行解释器
虽然这是介绍FreeRTOS系列的文章,但这篇文章偏重于命令行解释器的实现。这一方面是因为任务通知使用起来非常简单,另一方面也因为对于嵌入式程序来说,使用命令行解释器来辅助程序调试是非常有用的。程序调试是一门技术,基本上我们需要两种调试手段,一种是可以单步仿真的硬件调试器,另外一种是可以长期监视程序状态的状态输出,可以通过串口、显示屏等等手段输出异常信息或者某些关键点。这里的命令行解释器就属于后者。
本文实现的命令行解释器具有以下特性:
- 支持十进制参数,识别负号;
- 支持十六进制参数,十六进制以‘0x’开始;
- 命令名长度可定义,默认最大20个字符;
- 参数数目可定义,默认最多8个参数;
- 命令名和参数之间以空格隔开,空格个数任意;
- 整条命令以回车换行符结束;
- 整条命令最大长度可定义,默认64字节,包括回车换行符;
- 如果使用SecureCRT串口工具(推荐),支持该软件的控制字符,比如退格键、左移键、右移键等。
一个带参数的命令格式如下所示:
参数名 <参数1> <参数2> … <参数3>[回车换行符]
1.编码风格
FreeRTOS的编码标准及风格见《FreeRTOS系列第4篇---FreeRTOS编码标准及风格指南》,但我自己的编码风格跟FreeRTOS并不相同,并且我也不打算改变我当前坚持使用的编码风格。所以在这篇或者以后的文章中可能会在一个程序中看到两种不同的编码风格,对于涉及FreeRTOS的代码,我尽可能使用FreeRTOS建议的编码风格,与FreeRTOS无关的代码,我仍然使用自己的编码风格。我可以保证,两种编码风格决不会影响程序的可读性,编写良好可读性的代码,是我一直注重并坚持的。
2.一些准备工作
2.1串口硬件驱动
命令行解释器使用一个硬件串口,需要外部提供两个串口底层函数:一个是串口初始化函数init_cmd_uart(),用于初始化串口波特率、中断等事件;另一个是发送单个字符函数my_putc()。此外,命令行为串口接收中断服务程序提供函数fill_rec_buf(),用于保存接收到的字符,当收到回车换行符后,该函数向命令行分析任务发送通知。
MY_DEBUGF(CMD_LINE_DEBUG,("第%d个参数:%d\n",i+1,arg[i]));
3.使用任务通知
我们将会创建一个任务,用来分析接收到的命令,如果命令有效则调用命令实现函数。这个任务名字为vTaskCmdAnalyze()。串口接收中断用于接收命令,如果接收到回车换行符,则向任务vTaskCmdAnalyze()发送任务通知,表明已经接收到一条完整命令,任务可以去处理了。
示意框图如图3-1所示。
4.数据结构
命令行解释器程序需要涉及两个数据结构:一个与命令有关,包括命令的名字、命令的最大参数数目、命令的回调函数类型、命令帮助信息等;另一个与分析命令有关,包括接收命令字符缓冲区、存放参数缓冲区等。
4.1与命令有关的数据结构
定义如下:
typedef struct {
char const *cmd_name; //命令字符串
int32_t max_args; //最大参数数目
void (*handle)(int argc,void * cmd_arg); //命令回调函数
char *help; //帮助信息
}cmd_list_struct;
需要说明一下命令回调函数的参数,argc保存接收到的参数数目,cmd_arg指向参数缓冲区,目前只支持32位的整形参数,这在绝大多数嵌入式场合是足够的。
4.2与分析命令有关数据结构
定义如下:
#define ARG_NUM 8 //命令中允许的参数个数
#define CMD_LEN 20 //命令名占用的最大字符长度
#define CMD_BUF_LEN 60 //命令缓存的最大长度
typedef struct {
char rec_buf[CMD_BUF_LEN]; //接收命令缓冲区
char processed_buf[CMD_BUF_LEN]; //存储加工后的命令(去除控制字符)
int32_t cmd_arg[ARG_NUM]; //保存命令的参数
}cmd_analyze_struct;
缓冲区的大小使用宏来定义,通过更改相应的宏定义,可以设置整条命令的最大长度、命令参数最大数目等。
5.串口接收中断处理函数
本文使用的串口软件是SecureCRT,在这个软件下敲击的任何键盘字符,都会立刻通过串口硬件发送出去,这与Telnet类似。所以我们无需使用串口的FIFO,每接收到一个字符就产生一次中断。串口中断与硬件关系密切,所以命令行解释器提供了一个与硬件无关的函数fill_rec_buf(),每当串口中断接收到一个字符,就以收到的字符为参数调用这个函数。 fill_rec_buf()函数主要操作变量cmd_analyze,变量的声明原型为:
cmd_analyze_struct cmd_analyze;
函数fill_rec_buf()的实现代码为:
/*提供给串口中断服务程序,保存串口接收到的单个字符*/
void fill_rec_buf(char data)
{
//接收数据
static uint32_t rec_count=0;
cmd_analyze.rec_buf[rec_count]=data;
if(0x0A==cmd_analyze.rec_buf[rec_count] && 0x0D==cmd_analyze.rec_buf[rec_count-1])
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
rec_count=0;
/*收到一帧数据,向命令行解释器任务发送通知*/
vTaskNotifyGiveFromISR (xCmdAnalyzeHandle,&xHigherPriorityTaskWoken);
/*是否需要强制上下文切换*/
portYIELD_FROM_ISR(xHigherPriorityTaskWoken );
}
else
{
rec_count++;
/*防御性代码,防止数组越界*/
if(rec_count>=CMD_BUF_LEN)
{
rec_count=0;
}
}
}
6.命令行分析任务
命令行分析任务大部分时间都会因为等待任务通知而处于阻塞状态。当接收到一个通知后,任务首先去除命令行中的无效字符和控制字符,然后找出命令名并分析参数数目、将参数转换成十六进制数并保存到参数缓冲区中,最后检查命令名和参数是否合法,如果合法则调用命令回调函数处理本条命令。
6.1去除无效字符和控制字符
在这个函数cmd_arg_analyze()中,调用了字符转整形函数string_to_dec()。我们只支持整形参数,这里给出一个字符转整形函数的简单实现,可以识别负号和十六进制的前缀’0x’。在这个函数中调用了三个C库函数,分别是isdigit()、isxdigit()和tolower(),因此需要包含头文件#include <ctype.h>。函数string_to_dec()实现代码如下:
在讲数据结构的时候,我们定义了与命令有关的数据结构。每条命令需要包括命名名、最大参数、命令回调函数、帮助等信息,这里要将每条命令组织成列表的形式。
如果要定义自己的命令,只需要按照6.3节的格式编写命令回调函数,然后将命令名、参数数目、回调函数和帮助信息按照本节格式加入到命令表中即可。
有了上面的基础,命令行分析任务实现起来就非常轻松了,源码如下:
7.使用的串口工具
要用于本文介绍的命令行解释器,要对SecureCRT软件做一些设置。
选择Serial功能、设置端口、波特率、校验等,特别要注意的是不要勾选任何流控制选项,如图2-1所示。
依次点击菜单栏的“选项”---“会话选项”,在弹出的“会话选项”界面中,点击左边树形菜单的“终端”---“仿真”---“模式”,在右边的仿真模式区域选中“换行”和“新行模式”,如图2-2所示。
依次点击菜单栏的“选项”---“会话选项”,在弹出的“会话选项”界面中,点击左边树形菜单的“终端”---“仿真”---“高级”,在右边的“高级仿真”区域,选中“本地回显”,如图2-3所示。
8.测试
我们通过6.3节和6.4接定义了两个命令,第一条命令的名字为”hello”,这是一个无参数命令,直接输出字符串”Hello world!”。第二条命令的名字为”arg”,是一个带参数命令,输出每个参数的值。下面对这两个命令进行测试。
8.1无参数命令测试
设置好SecureCRT软件,输入字符”hello”后,按下回车键,设备会返回字符串”Hello world!”。如图8-1所示。
图8-1:无参数命令测试
8.2带参数命令测试
设置好SecureCRT软件,输入字符”arg 1 2 -3 0x0a”后,按下回车键,设备会返回每个参数值。如图8-2所示。
图8-2:带参数命令测试
(十五)使用任务通知实现命令行解释器相关推荐
- Linux 下五个顶级的开源命令行 Shell
这个世界上有两种 Linux 用户:敢于冒险的和态度谨慎的. 其中一类用户总是本能的去尝试任何能够戳中其痛点的新选择.他们尝试过不计其数的窗口管理器.系统发行版和几乎所有能找到的桌面插件. 另一类用户 ...
- cad命令栏还原默认_CAD十五个必学的命令 掌握后能走天下了
时常有人问,怎样学CAD. 这个问题实在是太难回答了,认识界面是学,二维设计也是学,三维设计也是学,二次开发也是学... 如果按照普遍所理解的那样,懂绘图就算学会了,那就事情就容易办了. 绘图用得最多 ...
- 操作系统课程设计---实验十 简单shell命令行解释器的设计与实现
实验十 简单shell命令行解释器的设计与实现 完整课程设计源码及其报告查看:陈陈的操作系统课程设计 1.实验目的 本实验主要目的在于进一步学会如何在 Linux 系统下使用进程相关的系统调用,了解 ...
- 操作系统课设之简单 shell 命令行解释器的设计与实现
前言 课程设计开始了,实验很有意思,写博客总结学到的知识 白嫖容易,创作不易,学到东西才是真 本文原创,创作不易,转载请注明!!! 本文链接 个人博客:https://ronglin.fun/arch ...
- linux初始:命令行解释器(shell)、权限
目录 命令行解释器 什么是命令行解释器(shell) 命令行解释器的作用 权限 权限的种类 结合用户去理解权限 如何查看权限 用户和用户组 如何看懂权限 如何更改权限 权限对于文件或文件夹的影响 权限 ...
- GitHub 五万星登顶,命令行的艺术!
今天给大家推荐一个GitHub开源项目<The Art of Command Line(命令行的艺术)>,这个开源项目雄踞了 GitHub TOP 周榜,直接以 53972 Star 登上 ...
- 百战RHCE(第十五战:Linux进阶命令十二-主机名和域名解析极简管理)
哈喽哈喽哈喽,大家好啊,很高兴大家能看到这篇文章! 首先,本人目前是计算机专业的大一学生,基于对Linux操作系统的爱好,参与了RHCE的培训班,而我这次编写的 <百战RHCE>文章,是基 ...
- 芝诺数解|「十五」考研路漫漫,行则将至——重庆考研分析报告
前言 又一年的考研大战即将拉开帷幕,9月下旬报名,11月上旬陆续开始现场确认,2020年考研倒计时不足40天.作为求学生涯中的又一重要考试,考研受到广大学子的普遍关注.数据显示,近10年考研人数年年增 ...
- C shell命令行解释器
实现简单的shell命令解释器: <blockquote> 1)shell内部命令处理:cd,exit等 2)shell外部命令处理 3)I/O重定向: 4)管道: 5)其他功能 /*Au ...
- linux 复制指定类型,用Linux命令行实现删除和复制指定类型的文件
(一)Linux 删除当前目录及子目录中所有某种类型的文件 方法1 : 此方法不能处理目录中带空格的那些. rm -rf `find . -name "*.example"` Li ...
最新文章
- 嵌入式linux开发中常见的虚拟机和主机的文件共享问题
- 此任务要求应用程序具有提升的权限
- 什么是typora,什么是markdown?利用typora编写markdown文本
- static、final、static final 用法
- ubuntu18.10终端的方块改成竖线
- SiameseRPN++分析
- 使用mocha进行测试 区块链
- MAC使用homeBrew安装Redis
- 还是原来的配方和味道!《英雄联盟》手游界面再曝光...
- 写在前面--点燃酱爆心中的那团火
- “我曾经的小项目比我在软件行业十年产生的影响还要大”
- linux设备模型之tty驱动架构分析,linux设备模型之uart驱动架构分析
- 拓端tecdat|Matlab马尔可夫链蒙特卡罗法(MCMC)估计随机波动率(SV,Stochastic Volatility) 模型
- 7.3 超标量流水线
- 数字货币智能合约:分析以太坊信标链
- 无法获得 VMCI 驱动程序的版本: 句柄无效解决方法
- 如何证明pi是无理数
- linux find命令 括号,Linux中find命令细节详解
- 如何利用SFTP在远程服务器中保障文件传输安全
- LFY-SpringBoot1【课程概述、springboot2概述】
热门文章
- IDEA项目名称的中文和数字乱码文字
- PageOffice常用功能之-OA系统中的文档在线编辑及流转
- 信用风险频发背后:11月约600亿信用债发行取消
- 四字母net域名值钱吗?四字母域名取名有什么技巧?
- 滴滴裁员2000人:老板辞退你,从来都不是因为钱
- 中南大学计算机学院2021复试名单,2021年中南大学研究生拟录取名单整理汇总(各学院)...
- 如果你对未来还有点迷茫不妨来看一下,必看的软件测试指引!!!
- chrome插件之网页翻译插件
- [研一上]人脸属性迁移文献梳理(1)
- 腾讯QQ会员中心g_tk32算法【C#版】