本帖最后由 aple0807 于 2022-12-15 18:36 编辑
开发MCU软件最常用的调试方式就是利用调试工具在线调试,打断点观察程序运行数据。但在部分场合不方便一直连接调试工具,特别是产测阶段或者现场维护等操作。此时为程序添加SHELL接口可以极大改善调试速度。
本次使用MM32L0136开发板的串口实现SHELL功能。
本程序SHELL的原理主要借助与编译工具 section 属性特征实现,即链接器会按照section属性值的字符顺序来排列变量。
SHELL核心宏如下:
typedef struct
{
const char *name;
int (*func)(int, void *[]);
} func_info_type;
#define DBG_FUN_BASE_EXPORT(fn, level) \
extern int fn(int argc, void *argv[]); \
OBJ_USED const func_info_type __dbg_func_##fn OBJ_SECTION(".app_fn.dbg." level) = \
{ \
#fn, \
(void *)fn, \
}
#if DBG_EN > 0
#define DBG_FUN_EXPORT(fn) DBG_FUN_BASE_EXPORT(fn, "2." #fn)
#else
#define DBG_FUN_EXPORT(...)
#endif
#define SHELL_FUN_EXPORT(fn) DBG_FUN_BASE_EXPORT(fn, "2." #fn)
使用SHELL_FUN_EXPORT处理的函数,会创建一个结构,包括函数地址和函数名。串口接收到一行数据后会搜索函数名,命中则使用绝对地址调用该函数。
/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url] 搜索指令索引
* @param cmd
* [url=home.php?mod=space&uid=266161]@return[/url] 目标索引,-1标示未搜索到
******************************************************************************/
int dbg_fun_list_find(const char *cmd)
{
int low, high, mid, des = -1;
volatile const func_info_type *volatile funlist_start = &__dbg_func_dbg_fun_list_start;
volatile const func_info_type *volatile funlist_end = &__dbg_func_dbg_fun_list_end;
low = 1;
high = (funlist_end - funlist_start);
while (low <= high)
{
ii8 cval;
mid = (low + high) >> 1;
cval = comp_string(cmd, (funlist_start + mid)->name, 256);
if (cval > 0)
{
low = mid + 1;
}
else if (cval < 0)
{
high = mid - 1;
}
else
{
des = mid;
break;
}
}
return des;
}
/*******************************************************************************
* @brief: 执行命令
* @param *cmd
* [url=home.php?mod=space&uid=266161]@return[/url] {*}
******************************************************************************/
int dbg_fun_list_exe(const char *cmd)
{
int argc, site;
char *argv[10];
char cbuff[256];
volatile const func_info_type *volatile funlist_start = &__dbg_func_dbg_fun_list_start;
copy_str(cmd, cbuff, sizeof(cbuff) - 1);
str_remove_l20_ascii(cbuff);
argc = str_split(cbuff, ' ', argv, arr_len(argv));
site = dbg_fun_list_find(argv[0]);
if (site > 0)
{
(funlist_start + site)->func(argc, (void **)argv);
}
return site;
}
经过以上处理后,如下书写函数就可以在串口助手调用程序的函数了:
int sh_func1(int argc, void *argv[])
{
printf("shell fun1 is called\n");
return 0;
}
SHELL_FUN_EXPORT(sh_func1);
int sh_func2(int argc, void *argv[])
{
printf("shell fun2 is called\n");
return 0;
}
SHELL_FUN_EXPORT(sh_func2);
下面附上工程,有兴趣的小伙伴可以一起研究,改善功能!
L0136DEMO.rar
(1.43 MB)
示例程序使用UART2作为SHELL串口,演示如下:
|