[分享][交流]发一个控制台模块
本帖最后由 Gorgon_Meducer 于 2014-5-8 23:12 编辑傻孩子工作室出品
模块简介:
这个控制台模块是一个使用全状态机编写的、移植性强的模块。
模块内置help 与 clear两个命令。
可以静态或者动态扩展需要的命令。
支持上下方向键回溯历史命令功能,历史命令的保存数量可配置。
支持F1 F3键回溯上一条历史命令功能,这个功能可以配置打开或者关闭。
效果图:
准备工作:
你现在需要一个有串口、有两个独立按键、有独立LED指示灯的单片机系统。
为了体现平台无关,请参考文档 《平台搭建》 搭建平台。
该平台提供了串口收发函数、LED控制接口函数、独立按键读取函数。
这样我们就拥有一个共同讨论的基础。
我现在使用的是STM32F103C8T6的板子,提供了一个使用MDK编写的、基于STM32F103C8T6的范例。
基于之前搭建的平台,代码移植起来是非常容易的。
在平台的基础上,使用者需要提供两个函数来使用该模块:写数据函数、读取数据函数。
范例下载:
使用MDK的软件仿真即刻体验 ~虽然不能实现F1 F3 / 方向键上下 / clear命令 等功能
模块下载:
解压后可以看到:模块由console文件夹下的三个文件组成:console.c console.h app_cfg.h
移植console模块:
将模块文件夹复制到你工程的代码文件夹,然后包含console.c。
提供写数据函数与读取数据函数:
范例中:main.c提供了如下函数:
bool console_serial_out(uint8_t chByte)
{
return serial_out(chByte);
}
bool console_serial_in(uint8_t *pchByte)
{
return serial_in(pchByte);
}
在顶层app_cfg.h中通过如下插入宏来注册这两个函数:
// console 读写数据插入宏
#define CONSOLE_WRITE_BYTE(__BYTE) console_serial_out(__BYTE)
extern bool console_serial_out(uint8_t chByte);
#define CONSOLE_READ_BYTE(__BYTE) console_serial_in(__BYTE)
extern bool console_serial_in(uint8_t *pchByte);
该app_cfg.h被console模块中的app_cfg.h包含;进而被console.c包含。
经过以上的步骤,console模块的移植工作就完成了。使用时,需包含头文件console.h
使用console模块:
接口函数:
// console_task 函数
// 返回值: 状态机运行状态
extern fsm_rt_t console_task(void);
// 动态扩展命令消息地图注册函数
// 参数: ptMSG -- 待注册的消息地图首指针
// chMSGNum -- 待注册消息地图中消息的数量
// 返回值:true -- 注册成功
// false -- 参数错误
extern bool console_register_command(const msg_t *ptMSG, uint8_t chMSGNum);
console_task 函数为console模块任务函数,该函数需要在主程序中被反复调用。
console_register_command函数为动态扩展命令消息地图注册函数。
静态扩展命令:
在顶层 app_cfg.h 中提供消息处理函数原型插入宏 CONSOLE_MSG_MAP_FUN_EXTERN
提供方法举例:
#define CONSOLE_MSG_MAP_FUN_EXTERN\
extern fsm_rt_t test_handler(const msg_t *ptMSG, uint8_t *pchStr, uint8_t chNum);
并且提供消息地图插入宏 CONSOLE_MSG_MAP_SET
提供方法举例:
#define CONSOLE_MSG_MAP_SET{ "test", &test_handler, "test -- this is a test command" }
动态扩展命令:
参见范例main.c中声明消息地图:
/* 动态扩展命令消息地图 ------------------------------------------------ */
const static msg_t s_tExpandMSGMap[] = {
{ "expand", &expand_handler, "expand -- this is a expand command" },
{ "abc", &abc_handler, "abc -- this is abc command" }
};
编写处理函数:
// expand 命令的处理函数
// 参数: ptMSG -- 消息指针
// pchStr-- 后续 token 指针
// chNum -- 后续 token 数量
// 返回值:状态机运行状态
static fsm_rt_t expand_handler(const msg_t *ptMSG, uint8_t *pchStr, uint8_t chNum)
{
……
}
// abc 命令的处理函数
// 参数: ptMSG -- 消息指针
// pchStr-- 后续 token 指针
// chNum -- 后续 token 数量
// 返回值:状态机运行状态
static fsm_rt_t abc_handler(const msg_t *ptMSG, uint8_t *pchStr, uint8_t chNum)
{
……
}
注册扩展命令:
console_register_command(s_tExpandMSGMap, UBOUND(s_tExpandMSGMap));
配置历史命令回溯数量:
通过在顶层app_cfg.h中提供插入宏实现:
// 配置记录历史命令数量
#define CONSOLE_HIS_CMD_NUM 10
配置F1 F3回溯功能的开启与关闭:
通过在顶层app_cfg.h中提供插入宏实现:
// 配置命令重复功能
#define CONSOLE_CMD_REPEAT_EN ENABLED
注意事项:
1console_task函数必须在主循环中被反复调用。
2扩展命令的处理函数不能阻塞。
接口文件console.h:
/* console 模块的输出接口文件 */
#ifndef __CONSOLE_H__
#define __CONSOLE_H__
/* 文件包含 ----------------------------------------------------------------- */
#include".\app_cfg.h"
/* 模块说明 ----------------------------------------------------------------- */
// 使用本模块应在上层 app_cfg.h 中提供 写数据插入宏 CONSOLE_WRITE_BYTE(__BYTE)
// 与读取数据插入红 CONSOLE_READ_BYTE(__BYTE)
// 并且提供插入宏展开后的原型
//
// 应在上层 app_cfg.h 中提供用以配置最大命令字符数的 CONSOLE_CMD_CHAR_NUM 插入宏
// 如没有提供该插入宏,会产生一条警告信息;并且默认配置为最大64字符数
//
// 应在上层 app_cfg.h 中提供用以配置是否需要重复功能 ( F1 F3 等 )
// 的插入宏 CONSOLE_CMD_REPEAT_EN ENABLED(1)--启用 DISABLED(0)--禁止
// 如没有提供该插入宏,会产生一条警告信息;并且默认启用该功能
//
// 应在上层 app_cfg.h 中提供用以配置历史命令记录数量的插入宏
// CONSOLE_HIS_CMD_NUM 至少为 1 每一条历史指令都将消耗 CONSOLE_CMD_CHAR_NUM + 2 字节RAM
// 如没有提供该插入宏,会产生一条警告信息;并且默认保存4条历史指令
//
// 应在上层 app_cfg.h 中提供用以配置 token 分隔符的插入宏 CONSOLE_TOKEN_CODE
// 提供方法举例: #define CONSOLE_TOKEN_CODE" ,;- ."
// 如没有提供该插入宏,会产生一条警告系你系;并默认采用空格为 token 分隔符
//
// 静态扩展命令:
// 在上层 app_cfg.h 中提供消息处理函数原型插入宏 CONSOLE_MSG_MAP_FUN_EXTERN
// 提供方法举例:
// #define CONSOLE_MSG_MAP_FUN_EXTERN\
// extern fsm_rt_t test_handler(const msg_t *ptMSG, uint8_t *pchStr, uint8_t chNum);
// 并且提供消息地图插入宏 CONSOLE_MSG_MAP_SET
// 提供方法举例:
// #define CONSOLE_MSG_MAP_SET{ "test", &test_handler, "test -- this is a test command" }
/* 类型定义 ----------------------------------------------------------------- */
typedef struct _msg_t msg_t;
typedef fsm_rt_t MSG_HANDLER(const msg_t *ptMSG, uint8_t *pchStr, uint8_t chNum); // 消息处理函数类型
struct _msg_t {
uint8_t *pchMsgStr; // 消息
MSG_HANDLER *fnHandler; // 消息处理函数
uint8_t *pchHelpStr; // 帮助信息指针
};
/* 输出接口 ----------------------------------------------------------------- */
// console_task 函数
// 返回值: 状态机运行状态
extern fsm_rt_t console_task(void);
// 动态扩展命令消息地图注册函数
// 参数: ptMSG -- 待注册的消息地图首指针
// chMSGNum -- 待注册消息地图中消息的数量
// 返回值:true -- 注册成功
// false -- 参数错误
extern bool console_register_command(const msg_t *ptMSG, uint8_t chMSGNum);
#endif
/* EOF */
接口文件向外部提供了模块的使用说明以及需要用到的数据类型:消息类型的定义以及消息处理函数的原型。
mark ........... 可以有。。。。。。。。 mark!顶一下 {:handshake:} 谢谢楼主分享,有空试一试! 我试图下载了一下,结果打不开MDK,打开后IDE就崩溃,顿时兴趣全无;楼主有空了,看看咋回事?还有别人碰到这问题了吗? kinsno 发表于 2014-5-7 22:22
我试图下载了一下,结果打不开MDK,打开后IDE就崩溃,顿时兴趣全无;楼主有空了,看看咋回事?还有别人碰到 ...
我使用的是4.10版本的MDK;
刚刚我下载了试验一下,结果是可以打开的。 本帖最后由 cslrd 于 2014-5-7 23:29 编辑
同楼上一样,打不开
求楼主支招 请问使用mdk的版本? 刚刚我在另一台电脑上面下载附件,也是可以打开的。
这个MDK的版本是4.12 catwill 发表于 2014-5-8 06:15
请问使用mdk的版本?
MDK版本是4.23是否同C编译器版本有关? 先顶,晚上看看。 顶,很强悍的东西 cslrd 发表于 2014-5-8 07:07
MDK版本是4.23是否同C编译器版本有关?
我也没有遇到过这样的问题……
我只能晚些时候再换台电脑试试看了 catwill 发表于 2014-5-8 07:26
我也没有遇到过这样的问题……
我只能晚些时候再换台电脑试试看了
刚才用uVision4.00a 可以打开,但是不带ARM编译器的。楼主可以用更高版本的MDK试一下是否有问题 我试试我的版本倒是可以打开
另外我把它转成uVision3 格式,大家可以试试能不能打开 train.Uv2
标记,console控制台程序. 特地登录顶起~! 很强大的说 console 我本来打算自己弄一个的。。 cslrd 发表于 2014-5-8 08:55
刚才用uVision4.00a 可以打开,但是不带ARM编译器的。楼主可以用更高版本的MDK试一下是否有问题 ...
如果可以的话,请把出错版本的MDK安装文件发到bbzsdts@126.com
晚些时候我会在我的电脑上试验一下 本帖最后由 cslrd 于 2014-5-8 23:03 编辑
catwill 发表于 2014-5-8 20:08
如果可以的话,请把出错版本的MDK安装文件发到
晚些时候我会在我的电脑上试验一下 ...
安装文件太大了,我就不发了。你如果有时间可以去下载一个4.23版本的测试一下。PS:我下载了18楼提供uv3格式与uv2格式,可以打开。可能是因为LZ提供的是uv4格式的原因 楼主你好,我是个菜鸟,我试了这个程序的仿真,运行的很好……我想请教下为什么要有静态和动态扩展两种命令,能举例说明什么情况用静态,什么情况用动态吗? 静态是编译时决定的
动态是运行时决定的
大部分情况都可以用静态——除非你需要运行时改变的特性 catwill 发表于 2014-7-28 08:42
静态是编译时决定的
动态是运行时决定的
大部分情况都可以用静态——除非你需要运行时改变的特性 ...
谢谢楼主回复!
我明白了静态部分,还想请教下关于动态的问题:
所谓动态是不是说,可以在程序运行的任意时刻进行改变?
如果要进行改变的话,该怎么操作,是通过这个注册函数重新注册改变后的消息吗?
// 注册扩展命令
console_register_command(s_tExpandMSGMap, UBOUND(s_tExpandMSGMap));
我这些问题问的很乱,楼主不要介意哈,因为俺水平太菜啦{:sweat:}
楼主,希望你如果有闲暇时间的话,能再出个关于“需要运行时改变的特性”的范例吧,感觉这个功能很高级啊!(好像现有的范例没体现出这点,也可能是我没看出来{:sweat:} )
相信这样的话,会让更多的人,更容易的去理解你的作品,谢谢!{:lol:} mark。。。。。。 Jmhh247 发表于 2014-7-28 14:08
谢谢楼主回复!
我明白了静态部分,还想请教下关于动态的问题:
所谓动态是不是说,可以在程序运行的任意 ...
所以说大部分的情况使用静态即可。
这个例子确实没有体现出动态的优势;确实没有用到运行时改变的特性。
通过这个
// 注册扩展命令
console_register_command(s_tExpandMSGMap, UBOUND(s_tExpandMSGMap));
即可在运行时改变——写两个消息地图即可 说实话……
我目前也不知道什么时候需要使用动态扩展 catwill 发表于 2014-7-29 14:16
说实话……
我目前也不知道什么时候需要使用动态扩展
哈哈,楼主真坦诚,现在缺这种品质的人太多了{:handshake:} {:lol:}
这样的话,就等楼主知道了再分享一下吧……
另,虽然现在还搞不懂,我还是想请教下楼主,如果是动态的话,需不需要一个删除消息的函数呢?(现在已经有了注册函数){:smile:} catwill 发表于 2014-7-29 14:14
所以说大部分的情况使用静态即可。
这个例子确实没有体现出动态的优势;确实没有用到运行时改变的特性。
...
我刚又看了源码,动态扩展是通过下面这个指针实现的,这样的话应该是不需要删除消息的函数,
就像你说那样,写几个消息,通过注册函数去切换就好……没看仔细啊{:sweat:}
const static msg_t *s_ptExpandMsgMap = NULL; // 动态扩展消息地图指针
static uint8_t s_chExpandMSGNum = 0; // 动态扩展消息数量
这个要支持一下,慢慢研究······· 小宝加油,四哥看好你 名师出高徒,有空再分析下源程序。 好学习下。 分析了一下,不得不说,楼主状态机用的真牛,算是学习了。 想问下楼主,这个消息地图中,// parse 消息地图
typedef struct _msg_t msg_t;
typedef fsm_rt_t MSG_HANDLER(const msg_t *ptMsg, uint8_t *pchStr, uint8_t chNum); // 消息处理函数类型
struct _msg_t {
uint8_t *pchMsgStr; // 消息
MSG_HANDLER *fnHandler; // 消息处理函数
uint8_t *pchHelpStr; // 帮助信息指针
};
const static msg_t c_tMsgMap[] = { // 消息地图
{ "help",&help_handler, "help -- print all command" },
{ "clear", &clear_handler, "clear -- clear the screen" },
CONSOLE_MSG_MAP_SET // 静态扩展命令插入宏
};
那个&help_handler 和这个 &clear_handler 取函数地址为什么要用&,不是取函数名就可以取地址了么? slzm40 发表于 2014-12-24 23:29
想问下楼主,这个消息地图中,
我觉得c语法支持加上&和省略&,
但是加上&,让人第一眼就知道这个一个指针,
阅读源码的时候第一时间能得到更多信息,即便于阅读 y574924080 发表于 2014-12-25 01:12
我觉得c语法支持加上&和省略&,
但是加上&,让人第一眼就知道这个一个指针,
这个想法好,便于阅读,指明取址。 这个非常好,有时间试一下。 太好了,可以把我们用的换成这个了.真心感谢 这个相当给力~~~~ 这个东东,非常好.准备下载下来好好改造. 牛逼,有空研究一下源码。消化消化! 想请教一下楼主
#define TOKEN_START 0
#define TOKEN_CHECK_BREAK 1
#define TOKEN_MOVE 2
#define TOKEN_RESET() do { s_chState = 0; } while(0)
static fsm_rt_t token(uint8_t *pchStr, uint8_t *pchNum)
这个函数有什么作用? 随风允诺2015 发表于 2015-8-7 15:21
想请教一下楼主
#define TOKEN_START 0
#define TOKEN_CHECK_BREAK 1
将CONSOLE_TOKEN_CODE定义的标记转换成'\0' 嗯 不错 控制台模块,mark 一下 这个是好东西 谢谢分享。。有空要试试 呵呵 好东西 是要收藏 楼主,这个控制台占大概用多少ram,我用stm8总是报溢出 淋湿的鸡毛 发表于 2015-8-27 10:36
楼主,这个控制台占大概用多少ram,我用stm8总是报溢出
你可以从以下几个地方来查找原因:
1、存储历史按键的部分是很消耗内存的,比如,如果你要保存过去5条历史输入,就需要64*5的存储器,你可以直接通过宏把这个功能关掉节省内存
2、命令行的最大长度应该也是可以用宏控制的,默认应该是64个字节,你可以改小,如果没有宏可以控制这个,请跟我一起鄙视作者
3、有很多const的东西应该是不占用SRAM的,如果占用了,你可以查查你用的编译器,然后找到对应的关键字确保他们没有占用不必要的SRAM
4、系统应该使用了FIFO来保存输入输出数据,可以缩小缓冲。
经过以上检查,我觉得对内存的占用不应该超过256个字节。 这份源码有做过详细分析。感觉在回车之后,获取到命令,后面就不需要过度的使用状态机。可以做详细的精简, 最后提供的handle接口,形如main那样,
handle(int argc,char *argv[]),在模块内部开一个数组指针,这样在handle里面获参数会更加方便。
slzm40 发表于 2015-8-27 17:14
这份源码有做过详细分析。感觉在回车之后,获取到命令,后面就不需要过度的使用状态机。可以做详细的精 ...
可能是有点过度,但也可以降低对处理器资源的占用,毕竟控制台是一个实时性要求非常低的任务,应该出让更多的
处理器时间给其他的状态机任务。 Gorgon_Meducer 发表于 2015-8-27 17:24
可能是有点过度,但也可以降低对处理器资源的占用,毕竟控制台是一个实时性要求非常低的任务,应该出让更 ...
可能的确是这方面的考虑,有时考虑到,一旦获取命令,就可以对命令进行整体解析,做尽快的回复。的确会占用一定的时间。 我作了比较多的改动,用于自己的工程。类似main参数,意思差不多,稍有点不同。
问下书月底放出来么?
bool LedControl(const void *ptMSG, uint8_t argc, uint8_t *argv[])
{
if(argc != 2 ){
console_print_str(console_led_warning);
return true;
}
if(console_cmp_str(argv, "on")){
console_print_str("led on is done!");
HalLedSet(HAL_LED_ALL,HAL_LED_MODE_ON);
}else if(console_cmp_str(argv, "off")){
console_print_str("led off is done!");
HalLedSet(HAL_LED_ALL,HAL_LED_MODE_OFF);
}else if(console_cmp_str(argv, "flash")){
console_print_str("led off is flash!");
HalLedSet(HAL_LED_ALL,HAL_LED_MODE_FLASH);
}else{
console_print_str(console_led_warning);
}
return true;
}
Gorgon_Meducer 发表于 2015-8-27 14:52
你可以从以下几个地方来查找原因:
1、存储历史按键的部分是很消耗内存的,比如,如果你要保存过去5条 ...
我把历史消息改到一个,最大长度改为16,运行ok。谢谢傻孩子 本帖最后由 Gorgon_Meducer 于 2015-8-28 13:34 编辑
slzm40 发表于 2015-8-27 17:32
可能的确是这方面的考虑,有时考虑到,一旦获取命令,就可以对命令进行整体解析,做尽快的回复。的确会占 ...
Demo月底放出来。关于如何设计多任务的。另外,你把最后一个参数去掉我觉得没必要,毕竟确切地告诉你有多少个参数还是很有帮助的,也避免你重复计算。 Gorgon_Meducer 发表于 2015-8-28 13:32
Demo月底放出来。关于如何设计多任务的。另外,你把最后一个参数去掉我觉得没必要,毕竟确切地告诉你有多 ...
已经月底啦。 最后一个参数没有去掉。。我把它改成argc的名字了,以便和main 相似。只不过是改成总的参数数量,而非后续参数数量。 slzm40 发表于 2015-8-28 16:06
已经月底啦。 最后一个参数没有去掉。。我把它改成argc的名字了,以便和main 相似。只不过是改成总的参 ...
周一见…… {:lol:}
哦哦哦,大事件哦
新书终于来了
mark!顶一下 标记标记。。。 不错,准备试用 支持一下。牛。 顶一下贴~~~ 真是牛人,顶顶顶 支持一下!功能很强大,不过编译了一下,ROM大概占10k左右,RAM大概3K左右。鱼与熊掌不可兼得阿。 你好,你能不能单独把这三个文件私发给我下,公司,家里都不能下载,应该不是网络原因。企鹅407214944,邮件也行。感激不尽! int 发表于 2016-10-22 23:36
支持一下!功能很强大,不过编译了一下,ROM大概占10k左右,RAM大概3K左右。鱼与熊掌不可兼得阿。 ...
你好,能不能把你在这个帖子中下载的三个文件发给我下,我直接在这个帖子上下载,没法下载。非常感谢/407214944企鹅 guo407214944 发表于 2016-12-17 09:30
你好,能不能把你在这个帖子中下载的三个文件发给我下,我直接在这个帖子上下载,没法下载。非常感谢/407 ...
已发到邮箱,估计还是你的网络问题 多谢,学习了 多谢分享~~~~~~{:victory:} 好东西啊 看一次顶一次 感谢分享。。 mark 串口控制台模块
页:
[1]