最近在一个STM32的项目中,为了方便现场调试的抓取一些运行数据,就想在项目中增加类似于 linux 的串口终端,实现一些基本命令行的调试命令。本着开源优先的原则,一通搜索,最终选定了 xcmd 这个开源库。
XCMD 介绍 https://gitee.com/two_salted_eggs/xcmd.git
它有以下几个优点:
- 移植十分简单
- 资源占用很小
- 支持历史记录
- 支持命令自动补全
- 支持注册快捷键
- 支持 xcmd_cmd_register()/xcmd_key_register()方法注册命令或按键
- 支持 XCMD_EXPORT_CMD()/XCMD_EXPORT_KEY()方法直接导出命令或按键,不需要额外运行注册函数
具体的移植过程看 readme.md 文件即可,参考 example 中的 demo, 不到 20分钟就能把例程在项目上跑起来了。
但是项目提供的 demo 没有启用 ENABLE_XCMD_EXPORT 选项,命令和按键是通过 xcmd_cmd_register()/xcmd_key_register() 在运行时注册。
如下图所示:
通过 xcmd_cmd_register()/xcmd_key_register() 注册命令和按键会有额外的RAM开销,如下图所示,每条命令也会有32bytes(32位的MCU)的链表项。
而通过 XCMD_EXPORT_CMD()/XCMD_EXPORT_KEY() 导出,则可以将命令和按键以常量表的方式放到 FLASH 中。
下面修改例程 “example\stm32\MDK\stm32f4xx” 以说明在 keil mdk 中,启用 ENABLE_XCMD_EXPORT 选项的步骤
-
首先打开工程文件 “Project/test.uvprojx”,编译项目,确保能正确编译完成。
然后将 “Output” 目录下的文件 “test.sct” 复制一份到 “Project” 目录。
打开工程配置页面(Options for Target),切换到 Linker 页面,去掉 “Use Memory Layout from Taget Dialog” 的选中状态,同时通过 ScatterFile 栏的 “...” 选择刚才复制到 “Project” 目录的 “test.sct” 文件。
如下图所示:
-
点击上图中的 “Edit...” 打开 “test.sct” 文件。如下,在 “ER_IROM1” 域添加下面 11~16 行的内容:
下面添加的部分,链接器会按照字典顺序排序。这是在keil MDK中能使用 XCMD_EXPORT_CMD()/XCMD_EXPORT_KEY() 的关键点。
点击查看代码
1. ; *************************************************************
2. ; *** Scatter-Loading Description File generated by uVision ***
3. ; ************************************************************* 5. LR_IROM1 0x08000000 0x00200000 { ; load region size_region
6. ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
7. *.o (RESET, +First)
8. *(InRoot$$Sections)
9. .ANY (+RO)
10. .ANY (+XO)
11. *(a1_xcmd_cmd_list_start)
12. *(a2_xcmd_cmd_list)
13. *(a3_xcmd_cmd_list_end)
14. *(b1_xcmd_key_list_start)
15. *(b2_xcmd_key_list)
16. *(b3_xcmd_key_list_end)
17. }
18. RW_IRAM1 0x20000000 0x00030000 { ; RW data
19. .ANY (+RW +ZI)
20. }
21. }
- 打开 “inc\xcmd.h”, 将下面的代码:
点击查看代码
#define XCMD_EXPORT_CMD(_name, _func, _help) XCMD_USED const xcmd_t XCMD_SECTION("_xcmd_cmd_list") \ xcmd_cmd_##_name={\ .name=#_name, \ .func=_func, \ .help=_help\ };
#define XCMD_EXPORT_KEY(_key, _func, _help) XCMD_USED const xcmd_key_t XCMD_SECTION("_xcmd_key_list") \ xcmd_key_##_key={\ .key=_key, \ .func=_func, \ .help=_help\ };
改为下面所示内容:
点击查看代码
#if defined(__CC_ARM) || defined(__CLANG_ARM)
#define XCMD_EXPORT_CMD(_name, _func, _help) XCMD_USED const xcmd_t XCMD_SECTION("a2_xcmd_cmd_list") \xcmd_cmd_##_name={\.name=#_name, \.func=_func, \.help=_help\};
#define XCMD_EXPORT_KEY(_key, _func, _help) XCMD_USED const xcmd_key_t XCMD_SECTION("b2_xcmd_key_list") \xcmd_key_##_key={\.key=_key, \.func=_func, \.help=_help\};
#else
#define XCMD_EXPORT_CMD(_name, _func, _help) XCMD_USED const xcmd_t XCMD_SECTION("_xcmd_cmd_list") \xcmd_cmd_##_name={\.name=#_name, \.func=_func, \.help=_help\};
#define XCMD_EXPORT_KEY(_key, _func, _help) XCMD_USED const xcmd_key_t XCMD_SECTION("_xcmd_key_list") \xcmd_key_##_key={\.key=_key, \.func=_func, \.help=_help\};
#endif //#if defined(__CC_ARM) || defined(__CLANG_ARM)
- 打开 “src\xcmd_default_cmds.c”, 将下面的代码:
点击查看代码
XCMD_EXPORT_CMD(help, cmd_help, "show this list")
XCMD_EXPORT_CMD(clear, cmd_clear, "clear screen")
XCMD_EXPORT_CMD(keys, cmd_keys, "show keys")
XCMD_EXPORT_CMD(logo, cmd_logo, "show logo")
改为下面的内容:
点击查看代码
#if defined(ENABLE_XCMD_EXPORT) && (defined(__CC_ARM) || defined(__CLANG_ARM)) XCMD_USED const xcmd_t XCMD_SECTION("a1_xcmd_cmd_list_start") _xcmd_cmd_list_start = {"help", cmd_help, "show this list"}; XCMD_USED const xcmd_t XCMD_SECTION("a3_xcmd_cmd_list_end") _xcmd_cmd_list_end = {NULL, NULL, NULL};
#else XCMD_EXPORT_CMD(help, cmd_help, "show this list")
#endif XCMD_EXPORT_CMD(clear, cmd_clear, "clear screen")
XCMD_EXPORT_CMD(keys, cmd_keys, "show keys")
XCMD_EXPORT_CMD(logo, cmd_logo, "show logo")
- 打开 “src\xcmd_default_cmds.c”, 将下面的代码:
点击查看代码
XCMD_EXPORT_KEY(KEY_CTR_H, xcmd_del_char, "backspace")
XCMD_EXPORT_KEY(KEY_BACKSPACE, xcmd_del_char, "delete")
改为下面的内容:
点击查看代码
#if defined(ENABLE_XCMD_EXPORT) && (defined(__CC_ARM) || defined(__CLANG_ARM)) XCMD_USED const xcmd_key_t XCMD_SECTION("b1_xcmd_key_list_start") _xcmd_key_list_start = {KEY_CTR_H, xcmd_del_char, "backspace"}; XCMD_USED const xcmd_key_t XCMD_SECTION("b3_xcmd_key_list_end") _xcmd_key_list_end = {NULL, NULL, NULL};
#else XCMD_EXPORT_KEY(KEY_CTR_H, xcmd_del_char, "backspace")
#endif XCMD_EXPORT_KEY(KEY_BACKSPACE, xcmd_del_char, "delete")
修改后的代码可以参考我 fork 的副本
https://gitee.com/sqqdfny/xcmd/tree/dev-sqqdfny