【正点原子STM32】USMART串口调试组件(USMART主要特点、USMART原理、USMART组成、USMART扫描函数、USMART移植、USAMRT使用、USAMRT源码)

您所在的位置:网站首页 正点原子delay函数 【正点原子STM32】USMART串口调试组件(USMART主要特点、USMART原理、USMART组成、USMART扫描函数、USMART移植、USAMRT使用、USAMRT源码)

【正点原子STM32】USMART串口调试组件(USMART主要特点、USMART原理、USMART组成、USMART扫描函数、USMART移植、USAMRT使用、USAMRT源码)

2024-03-18 20:08| 来源: 网络整理| 查看: 265

一、USMART简介 二、USMART原理 三、USMART移植 四、USAMRT使用 五、USAMRT源码 六、总结

一、USMART简介

在这里插入图片描述 USMART是一个串口调试组件,旨在提高代码调试效率。通过USMART,用户可以直接通过串口调用已编写的函数,并且可以随意修改函数参数,从而快速进行代码调试和功能验证。

使用USMART,开发者可以在不修改代码的情况下,通过串口发送指令来调用预先编写的函数,并且可以实时观察函数的返回值或输出结果。这样的设计极大地简化了调试过程,加快了开发速度,提高了代码的可调试性和可维护性。

总的来说,USMART是一个强大的串口调试工具,能够在嵌入式开发中发挥重要作用,特别是在调试和验证阶段。 在这里插入图片描述 USMART具有以下主要特点:

调用用户编写的函数:可以直接调用绝大部分用户编写的函数,无需修改代码,方便快捷地进行调试和功能验证。

占用资源少:USMART在嵌入式系统中占用的资源较少,最小占用为4KB FLASH和72B SRAM,不会对系统资源造成过大的压力。

支持多种参数类型:支持多种参数类型,包括整数(10进制/16进制)、字符串、函数指针等,满足不同类型参数的调试需求。

函数返回值显示:支持函数返回值的显示,并且可以对返回值的格式进行设置,便于开发者查看和分析函数执行结果。

支持函数执行时间计算:可以计算函数执行的时间,帮助开发者评估函数的执行效率和优化代码。

需要注意的是,USMART不支持浮点数参数,因此在使用过程中需要避免传递浮点数类型的参数。

二、USMART原理

深入理解C语言函数指针 在这里插入图片描述 USMART的工作原理可以概括为以下几个步骤:

用户输入字符串: 用户通过串口输入字符串,通常以回车换行符 “\r\n” 结束。

解析并对比: 系统对用户输入的字符串进行解析,提取出函数名和参数。然后将提取出的函数名与本地函数名表进行对比。

函数名匹配结果:

函数名匹配失败: 如果提取出的函数名与本地函数名表中的函数名不匹配,则提示用户输入的函数名有误或者不存在,报错并等待下一次输入。

函数名匹配成功: 如果提取出的函数名与本地函数名表中的某个函数名匹配成功,则获取该函数名对应的函数指针。然后将参数传递给该函数指针所指向的函数,并执行该函数。

返回执行结果: 执行完函数后,将执行结果返回给用户,通常是函数的返回值或者执行状态。然后等待下一次用户输入。

通过这样的流程,USMART实现了通过串口调用用户编写的函数,方便快捷地进行代码调试和功能验证。 在这里插入图片描述 USMART由以下几个文件组成,每个文件负责不同的功能:

usmart.c/h:

核心文件,负责处理命令并与外部进行交互。实现了命令解析、函数调用等核心功能。

usmart_config.c:

函数管理文件,用户可以在此文件中添加需要由USMART管理的函数。通过添加函数声明和对应的函数指针,使得USMART能够识别并调用用户编写的函数。

usmart_port.c/h:

移植文件,用于USMART的移植。用户根据具体的硬件平台和串口配置,修改这些文件,以适配不同的系统环境和串口设定。

usmart_str.c/h:

字符串处理文件,负责字符串的转换、参数获取等操作。提供了一些字符串处理的函数,用于解析用户输入的命令和参数。

通过修改usmart_port.c/h文件可以完成USMART的移植工作,确保其能够在特定的硬件平台上正常运行。而通过修改usmart_config.c文件,用户可以添加自己想要由USMART管理的函数,从而实现对这些函数的串口调用。

USMART扫描函数:

在这里插入图片描述 在这里插入图片描述

/** * @brief 获取输入数据流(字符串) * @note 该函数用于从串口接收缓冲区获取输入数据流(字符串),以供USMART解析函数名及参数等信息。 * @retval char *: 若串口接收完成且有数据,返回接收到的数据流的首地址;否则返回0。 * 注意:返回值为0表示没有接收到数据。 */ char *usmart_get_input_string(void) { uint8_t len; char *pbuf = 0; if (g_usart_rx_sta & 0x8000) /* 判断串口是否接收完成 */ { len = g_usart_rx_sta & 0x3fff; /* 获取接收到的数据长度 */ g_usart_rx_buf[len] = '\0'; /* 在数据末尾添加结束符 */ pbuf = (char*)g_usart_rx_buf; /* 将接收到的数据流的首地址赋值给pbuf */ g_usart_rx_sta = 0; /* 清除接收状态,以便开启下一次接收 */ } return pbuf; /* 返回接收到的数据流的首地址或0 */ } /** * @brief USMART扫描函数 * @note 通过调用该函数实现USMART的各个控制。该函数需要每隔一定时间被调用一次, * 以及时执行从串口发过来的各个函数。可以在中断里面调用,从而实现自动管理。 * 如果非正点原子用户,则USART_RX_STA和USART_RX_BUF[]需要用户自己实现。 * @param 无 * @retval 无 */ void usmart_scan(void) { uint8_t sta, len; char *pbuf = 0; pbuf = usmart_get_input_string(); /* 获取数据数据流 */ if (pbuf == 0) return ; /* 数据流为空,直接返回 */ sta = usmart_dev.cmd_rec(pbuf); /* 解析函数信息 */ if (sta == 0) { usmart_dev.exe(); /* 执行函数 */ } else { len = usmart_sys_cmd_exe(pbuf); /* 执行系统命令 */ if (len != USMART_FUNCERR) sta = len; if (sta) { switch (sta) { case USMART_FUNCERR: USMART_PRINTF("函数错误!\r\n"); break; case USMART_PARMERR: USMART_PRINTF("参数错误!\r\n"); break; case USMART_PARMOVER: USMART_PRINTF("参数太多!\r\n"); break; case USMART_NOFUNCFIND: USMART_PRINTF("未找到匹配的函数!\r\n"); break; } } } } 三、USMART移植

在这里插入图片描述 USMART移植的步骤如下:

获取USMART组件:

首先,从正点原子等资源处获取完整的USMART组件,包括核心文件(usmart.c/h)、函数管理文件(usmart_config.c)、移植文件(usmart_port.c/h)以及字符串处理文件(usmart_str.c/h)。

添加到工程:

将获取到的全部组件添加到您的工程中,并设置好路径关联,以确保编译器能够正确地找到这些文件。

适配硬件:

修改移植文件(usmart_port.c/h),以适配您的硬件平台。主要修改调试串口和定时器相关的配置,确保USMART能够在您的硬件环境中正常工作。

添加执行函数:

在函数管理文件(usmart_config.c)中添加或修改需要由USMART管理的执行函数。添加函数声明和函数指针,使得USMART能够识别并调用您所添加的函数。

通过串口交互:

完成移植后,将移植好的USMART组件烧录到目标设备中。通过串口与目标设备交互,可以测试和调用您添加的函数,并进行调试和验证。

通过以上步骤,您可以成功地将USMART移植到您的目标设备中,并通过串口进行交互,实现对特定函数的调用和执行。

1. 获取USMART组件

在这里插入图片描述

2. 添加到工程

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

发现还缺少HAL库定时器支持

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3. 适配硬件

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

4. 添加执行函数

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

5. 通过串口交互

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

四、USAMRT使用

在这里插入图片描述 USMART是一个串口调试组件,通过串口与嵌入式系统进行交互。以下是USMART的使用说明:

输入函数名和参数:

按照程序编写格式输入函数名及参数,并以回车键结束。例如:function_name arg1 arg2 ...

系统命令:

? 或 help: 获取帮助信息。list: 获取可用的函数列表。id: 获取可用函数的ID列表。hex: 将参数以16进制显示,后跟空格+数字即执行进制转换。dec: 将参数以10进制显示,后跟空格+数字即执行进制转换。runtime 1: 开启函数运行计时。runtime 0: 关闭函数运行计时。

示例:

输入 ? 或 help 可以获取帮助信息。输入 list 可以查看可用的函数列表。输入 function_name arg1 arg2 ... 执行特定函数,并根据需要输入参数。输入 hex value 或 dec value 可以将值转换为16进制或10进制。输入 runtime 1 开启函数运行计时,runtime 0 则关闭。

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

五、USAMRT源码

usamrt.c

#include "./USMART/usmart.h" #include "./USMART/usmart_str.h" #include "./USMART/usmart_port.h" /* 系统命令 */ char *sys_cmd_tab[] = { "?", "help", "list", "id", "hex", "dec", "runtime", }; /** * @brief 处理系统指令 * @param str : 字符串指针 * @retval 0,成功处理;其他,错误代码; */ uint8_t usmart_sys_cmd_exe(char *str) { uint8_t i; char sfname[MAX_FNAME_LEN]; /* 存放本地函数名 */ uint8_t pnum; uint8_t rval; uint32_t res; res = usmart_get_cmdname(str, sfname, &i, MAX_FNAME_LEN); /* 得到指令及指令长度 */ if (res)return USMART_FUNCERR; /* 错误的指令 */ str += i; for (i = 0; i case 0: case 1: /* 帮助指令 */ USMART_PRINTF("\r\n"); #if USMART_USE_HELP USMART_PRINTF("------------------------USMART V3.5------------------------ \r\n"); USMART_PRINTF(" USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 \r\n"); USMART_PRINTF("它,你可以通过串口助手调用程序里面的任何函数,并执行.因此,你可\r\n"); USMART_PRINTF("以随意更改函数的输入参数(支持数字(10/16进制,支持负数)、字符串\r\n"), USMART_PRINTF("、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持\r\n"), USMART_PRINTF("函数返回值显示.支持参数显示进制设置功能,支持进制转换功能.\r\n"); USMART_PRINTF("技术支持:www.openedv.com\r\n"); USMART_PRINTF("USMART有7个系统命令(必须小写):\r\n"); USMART_PRINTF("?: 获取帮助信息\r\n"); USMART_PRINTF("help: 获取帮助信息\r\n"); USMART_PRINTF("list: 可用的函数列表\r\n\n"); USMART_PRINTF("id: 可用函数的ID列表\r\n\n"); USMART_PRINTF("hex: 参数16进制显示,后跟空格+数字即执行进制转换\r\n\n"); USMART_PRINTF("dec: 参数10进制显示,后跟空格+数字即执行进制转换\r\n\n"); USMART_PRINTF("runtime:1,开启函数运行计时;0,关闭函数运行计时;\r\n\n"); USMART_PRINTF("请按照程序编写格式输入函数名及参数并以回车键结束.\r\n"); USMART_PRINTF("--------------------------正点原子------------------------- \r\n"); #else USMART_PRINTF("指令失效\r\n"); #endif break; case 2: /* 查询指令 */ USMART_PRINTF("\r\n"); USMART_PRINTF("-------------------------函数清单--------------------------- \r\n"); for (i = 0; i i = usmart_str2num(sfname, &res); /* 记录该参数 */ if (i == 0) /* 进制转换功能 */ { USMART_PRINTF("HEX:0X%X\r\n", res); /* 转为16进制 */ } else if (i != 4)return USMART_PARMERR; /* 参数错误. */ else /* 参数显示设定功能 */ { USMART_PRINTF("16进制参数显示!\r\n"); usmart_dev.sptype = SP_TYPE_HEX; } } else return USMART_PARMERR; /* 参数错误. */ USMART_PRINTF("\r\n"); break; case 5: /* dec指令 */ USMART_PRINTF("\r\n"); usmart_get_aparm(str, sfname, &i); if (i == 0) /* 参数正常 */ { i = usmart_str2num(sfname, &res); /* 记录该参数 */ if (i == 0) /* 进制转换功能 */ { USMART_PRINTF("DEC:%lu\r\n", (unsigned long)res); /* 转为10进制 */ } else if (i != 4) { return USMART_PARMERR; /* 参数错误. */ } else /* 参数显示设定功能 */ { USMART_PRINTF("10进制参数显示!\r\n"); usmart_dev.sptype = SP_TYPE_DEC; } } else { return USMART_PARMERR; /* 参数错误. */ } USMART_PRINTF("\r\n"); break; case 6: /* runtime指令,设置是否显示函数执行时间 */ USMART_PRINTF("\r\n"); usmart_get_aparm(str, sfname, &i); if (i == 0) /* 参数正常 */ { i = usmart_str2num(sfname, &res); /* 记录该参数 */ if (i == 0) /* 读取指定地址数据功能 */ { if (USMART_ENTIMX_SCAN == 0) { USMART_PRINTF("\r\nError! \r\nTo EN RunTime function,Please set USMART_ENTIMX_SCAN = 1 first!\r\n");/* 报错 */ } else { usmart_dev.runtimeflag = res; if (usmart_dev.runtimeflag) { USMART_PRINTF("Run Time Calculation ON\r\n"); } else { USMART_PRINTF("Run Time Calculation OFF\r\n"); } } } else { return USMART_PARMERR; /* 未带参数,或者参数错误 */ } } else { return USMART_PARMERR; /* 参数错误. */ } USMART_PRINTF("\r\n"); break; default:/* 非法指令 */ return USMART_FUNCERR; } return 0; } /** * @brief 初始化USMART组件 * @param tclk: 定时器的工作频率(单位:Mhz) * @retval 无 */ void usmart_init(uint16_t tclk) { #if USMART_ENTIMX_SCAN == 1 usmart_timx_init(1000, tclk * 100 - 1); #endif usmart_dev.sptype = 1; /* 十六进制显示参数 */ } /** * @brief 从str中解析并获取函数名、ID及参数信息 * @param str: 字符串指针. * @retval 0,识别成功;其他,错误代码. */ uint8_t usmart_cmd_rec(char *str) { uint8_t sta, i, rval; /* 状态 */ uint8_t rpnum, spnum; char rfname[MAX_FNAME_LEN]; /* 暂存空间,用于存放接收到的函数名 */ char sfname[MAX_FNAME_LEN]; /* 存放本地函数名 */ sta = usmart_get_fname(str, rfname, &rpnum, &rval); /* 得到接收到的数据的函数名及参数个数 */ if (sta)return sta; /* 错误 */ for (i = 0; i if (spnum > rpnum)return USMART_PARMERR;/* 参数错误(输入参数比源函数参数少) */ usmart_dev.id = i; /* 记录函数ID. */ break; /* 跳出. */ } } if (i == usmart_dev.fnum)return USMART_NOFUNCFIND; /* 未找到匹配的函数 */ sta = usmart_get_fparam(str, &i); /* 得到函数参数个数 */ if (sta)return sta; /* 返回错误 */ usmart_dev.pnum = i; /* 参数个数记录 */ return USMART_OK; } /** * @brief USMART执行函数 * @note * 该函数用于最终执行从串口收到的有效函数. * 最多支持10个参数的函数,更多的参数支持也很容易实现.不过用的很少.一般5个左右的参数的函数已经很少见了. * 该函数会在串口打印执行情况.以:"函数名(参数1,参数2...参数N)=返回值".的形式打印. * 当所执行的函数没有返回值的时候,所打印的返回值是一个无意义的数据. * * @param 无 * @retval 无 */ void usmart_exe(void) { uint8_t id, i; uint32_t res; uint32_t temp[MAX_PARM]; /* 参数转换,使之支持了字符串 */ char sfname[MAX_FNAME_LEN]; /* 存放本地函数名 */ uint8_t pnum, rval; id = usmart_dev.id; if (id >= usmart_dev.fnum)return; /* 不执行. */ usmart_get_fname((char *)usmart_dev.funs[id].name, sfname, &pnum, &rval); /* 得到本地函数名,及参数个数 */ USMART_PRINTF("\r\n%s(", sfname); /* 输出正要执行的函数名 */ for (i = 0; i USMART_PRINTF("%c", '"'); USMART_PRINTF("%s", usmart_dev.parm + usmart_get_parmpos(i)); USMART_PRINTF("%c", '"'); temp[i] = (uint32_t) & (usmart_dev.parm[usmart_get_parmpos(i)]); } else /* 参数是数字 */ { temp[i] = *(uint32_t *)(usmart_dev.parm + usmart_get_parmpos(i)); if (usmart_dev.sptype == SP_TYPE_DEC) { USMART_PRINTF("%ld", (long)temp[i]); /* 10进制参数显示 */ } else { USMART_PRINTF("0X%X", temp[i]); /* 16进制参数显示 */ } } if (i != pnum - 1)USMART_PRINTF(","); } USMART_PRINTF(")"); #if USMART_ENTIMX_SCAN==1 usmart_timx_reset_time(); /* 计时器清零,开始计时 */ #endif switch (usmart_dev.pnum) { case 0: /* 无参数(void类型) */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(); break; case 1: /* 有1个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0]); break; case 2: /* 有2个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1]); break; case 3: /* 有3个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2]); break; case 4: /* 有4个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3]); break; case 5: /* 有5个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3], temp[4]); break; case 6: /* 有6个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3], temp[4], \ temp[5]); break; case 7: /* 有7个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3], temp[4], \ temp[5], temp[6]); break; case 8: /* 有8个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3], temp[4], \ temp[5], temp[6], temp[7]); break; case 9: /* 有9个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3], temp[4], \ temp[5], temp[6], temp[7], temp[8]); break; case 10:/* 有10个参数 */ res = (*(uint32_t(*)())usmart_dev.funs[id].func)(temp[0], temp[1], temp[2], temp[3], temp[4], \ temp[5], temp[6], temp[7], temp[8], temp[9]); break; } #if USMART_ENTIMX_SCAN==1 usmart_timx_get_time(); /* 获取函数执行时间 */ #endif if (rval == 1) /* 需要返回值. */ { if (usmart_dev.sptype == SP_TYPE_DEC)USMART_PRINTF("=%lu;\r\n", (unsigned long)res); /* 输出执行结果(10进制参数显示) */ else USMART_PRINTF("=0X%X;\r\n", res); /* 输出执行结果(16进制参数显示) */ } else USMART_PRINTF(";\r\n"); /* 不需要返回值,直接输出结束 */ if (usmart_dev.runtimeflag) /* 需要显示函数执行时间 */ { USMART_PRINTF("Function Run Time:%d.%1dms\r\n", usmart_dev.runtime / 10, usmart_dev.runtime % 10); /* 打印函数执行时间 */ } } /** * @brief USMART扫描函数 * @note * 扫描串口,实现USMART的各个控制 * 通过调用该函数,实现USMART的各个控制.该函数需要每隔一定时间被调用一次 * 以及时执行从串口发过来的各个函数. * 本函数可以在中断里面调用,从而实现自动管理. * 如果非正点原子用户,则USART_RX_STA和USART_RX_BUF[]需要用户自己实现 * * @param 无 * @retval 无 */ void usmart_scan(void) { uint8_t sta, len; char *pbuf = 0; pbuf = usmart_get_input_string(); /* 获取数据数据流 */ if (pbuf == 0) return ; /* 数据流空, 直接返回 */ sta = usmart_dev.cmd_rec(pbuf); /* 得到函数各个信息 */ if (sta == 0) { usmart_dev.exe(); /* 执行函数 */ } else { len = usmart_sys_cmd_exe(pbuf); if (len != USMART_FUNCERR)sta = len; if (sta) { switch (sta) { case USMART_FUNCERR: USMART_PRINTF("函数错误!\r\n"); break; case USMART_PARMERR: USMART_PRINTF("参数错误!\r\n"); break; case USMART_PARMOVER: USMART_PRINTF("参数太多!\r\n"); break; case USMART_NOFUNCFIND: USMART_PRINTF("未找到匹配的函数!\r\n"); break; } } } } #if USMART_USE_WRFUNS == 1 /* 如果使能了读写操作 */ /** * @brief 读取指定地址的值 * @param addr: 指定地址 * @retval 读取到的值 */ uint32_t read_addr(uint32_t addr) { return *(uint32_t *)addr; /* 读取指定地址的值 */ } /** * @brief 在指定地址写入指定的值 * @param addr: 指定地址 * @param val: 要写入的值 * @retval 无 */ void write_addr(uint32_t addr, uint32_t val) { *(uint32_t *)addr = val; /* 在指定地址写入指定的值 */ } #endif

usamrt_port.c

#include "./USMART/usmart.h" #include "./USMART/usmart_port.h" TIM_HandleTypeDef g_timx_usmart_handle; /* 定时器句柄 */ /** * @brief 获取输入数据流(字符串) * @note USMART通过解析该函数返回的字符串以获取函数名及参数等信息 * @param 无 * @retval * @arg 0, 没有接收到数据 * @arg 其他,数据流首地址(不能是0) */ char *usmart_get_input_string(void) { uint8_t len; char *pbuf = 0; if (g_usart_rx_sta & 0x8000) /* 串口接收完成? */ { len = g_usart_rx_sta & 0x3fff; /* 得到此次接收到的数据长度 */ g_usart_rx_buf[len] = '\0'; /* 在末尾加入结束符. */ pbuf = (char*)g_usart_rx_buf; g_usart_rx_sta = 0; /* 开启下一次接收 */ } return pbuf; } /* 如果使能了定时器扫描, 则需要定义如下函数 */ #if USMART_ENTIMX_SCAN == 1 /** * 移植注意:本例是以stm32为例,如果要移植到其他mcu,请做相应修改. * usmart_reset_runtime,清除函数运行时间,连同定时器的计数寄存器以及标志位一起清零.并设置重装载值为最大,以最大限度的延长计时时间. * usmart_get_runtime,获取函数运行时间,通过读取CNT值获取,由于usmart是通过中断调用的函数,所以定时器中断不再有效,此时最大限度 * 只能统计2次CNT的值,也就是清零后+溢出一次,当溢出超过2次,没法处理,所以最大延时,控制在:2*计数器CNT*0.1ms.对STM32来说,是:13.1s左右 * 其他的:USMART_TIMX_IRQHandler和Timer4_Init,需要根据MCU特点自行修改.确保计数器计数频率为:10Khz即可.另外,定时器不要开启自动重装载功能!! */ /** * @brief 复位runtime * @note 需要根据所移植到的MCU的定时器参数进行修改 * @param 无 * @retval 无 */ void usmart_timx_reset_time(void) { __HAL_TIM_CLEAR_FLAG(&g_timx_usmart_handle, TIM_FLAG_UPDATE); /* 清除中断标志位 */ __HAL_TIM_SET_AUTORELOAD(&g_timx_usmart_handle, 0XFFFF); /* 将重装载值设置到最大 */ __HAL_TIM_SET_COUNTER(&g_timx_usmart_handle, 0); /* 清空定时器的CNT */ usmart_dev.runtime = 0; } /** * @brief 获得runtime时间 * @note 需要根据所移植到的MCU的定时器参数进行修改 * @param 无 * @retval 执行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms */ uint32_t usmart_timx_get_time(void) { if (__HAL_TIM_GET_FLAG(&g_timx_usmart_handle, TIM_FLAG_UPDATE) == SET) /* 在运行期间,产生了定时器溢出 */ { usmart_dev.runtime += 0XFFFF; } usmart_dev.runtime += __HAL_TIM_GET_COUNTER(&g_timx_usmart_handle); return usmart_dev.runtime; /* 返回计数值 */ } /** * @brief 定时器初始化函数 * @param arr:自动重装载值 * psc:定时器分频系数 * @retval 无 */ void usmart_timx_init(uint16_t arr, uint16_t psc) { USMART_TIMX_CLK_ENABLE(); g_timx_usmart_handle.Instance = USMART_TIMX; /* 通用定时器4 */ g_timx_usmart_handle.Init.Prescaler = psc; /* 分频系数 */ g_timx_usmart_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 向上计数器 */ g_timx_usmart_handle.Init.Period = arr; /* 自动装载值 */ g_timx_usmart_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&g_timx_usmart_handle); HAL_TIM_Base_Start_IT(&g_timx_usmart_handle); /* 使能定时器和定时器中断 */ HAL_NVIC_SetPriority(USMART_TIMX_IRQn,3,3); /* 设置中断优先级,抢占优先级3,子优先级3 */ HAL_NVIC_EnableIRQ(USMART_TIMX_IRQn); /* 开启ITM中断 */ } /** * @brief USMART定时器中断服务函数 * @param 无 * @retval 无 */ void USMART_TIMX_IRQHandler(void) { if(__HAL_TIM_GET_IT_SOURCE(&g_timx_usmart_handle,TIM_IT_UPDATE)==SET)/* 溢出中断 */ { usmart_dev.scan(); /* 执行usmart扫描 */ __HAL_TIM_SET_COUNTER(&g_timx_usmart_handle, 0);; /* 清空定时器的CNT */ __HAL_TIM_SET_AUTORELOAD(&g_timx_usmart_handle, 100);/* 恢复原来的设置 */ } __HAL_TIM_CLEAR_IT(&g_timx_usmart_handle, TIM_IT_UPDATE);/* 清除中断标志位 */ } #endif

usamrt_config.c

#include "./USMART/usmart.h" #include "./USMART/usmart_str.h" /******************************************************************************************/ /* 用户配置区 * 这下面要包含所用到的函数所申明的头文件(用户自己添加) */ #include "./SYSTEM/sys/sys.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LCD/lcd.h" extern void led_set(uint8_t sta); extern void test_fun(void(*ledset)(uint8_t), uint8_t sta); /* 函数名列表初始化(用户自己添加) * 用户直接在这里输入要执行的函数名及其查找串 */ struct _m_usmart_nametab usmart_nametab[] = { #if USMART_USE_WRFUNS == 1 /* 如果使能了读写操作 */ (void *)read_addr, "uint32_t read_addr(uint32_t addr)", (void *)write_addr, "void write_addr(uint32_t addr,uint32_t val)", #endif (void *)delay_ms, "void delay_ms(uint16_t nms)", (void *)delay_us, "void delay_us(uint32_t nus)", (void *)lcd_clear, "void lcd_clear(uint16_t color)", (void *)lcd_fill, "void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color)", (void *)lcd_draw_line, "void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)", (void *)lcd_draw_circle, "lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color)", (void *)lcd_draw_rectangle, "void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)", (void *)lcd_show_num, "void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color)", (void *)lcd_show_xnum, "void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color)", (void *)lcd_show_string, "void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color)", (void *)lcd_draw_point, "void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)", (void *)lcd_read_point, "uint16_t lcd_read_point(uint16_t x, uint16_t y)", (void *)lcd_display_dir, "void lcd_display_dir(uint8_t dir)", (void *)led_set, "void led_set(uint8_t sta)", (void *)test_fun, "void test_fun(void(*ledset)(uint8_t), uint8_t sta)", }; /******************************************************************************************/ /* 函数控制管理器初始化 * 得到各个受控函数的名字 * 得到函数总数量 */ struct _m_usmart_dev usmart_dev = { usmart_nametab, usmart_init, usmart_cmd_rec, usmart_exe, usmart_scan, sizeof(usmart_nametab) / sizeof(struct _m_usmart_nametab), /* 函数数量 */ 0, /* 参数数量 */ 0, /* 函数ID */ 1, /* 参数显示类型,0,10进制;1,16进制 */ 0, /* 参数类型.bitx:,0,数字;1,字符串 */ 0, /* 每个参数的长度暂存表,需要MAX_PARM个0初始化 */ 0, /* 函数的参数,需要PARM_LEN个0初始化 */ };

usamrt_str.c

#include "./USMART/usmart.h" #include "./USMART/usmart_str.h" /** * @brief 对比字符串str1和str2 * @param str1: 字符串1首地址(指针) * @param str2: 字符串2首地址(指针) * @retval 0,相等; 1,不相等; */ uint8_t usmart_strcmp(char *str1, char *str2) { while (1) { if (*str1 != *str2)return 1; /* 不相等 */ if (*str1 == '\0')break; /* 对比完成了. */ str1++; str2++; } return 0;/* 两个字符串相等 */ } /** * @brief 把src的内容copy到dst * @param src: 源地址 * @param dst: 目的地址 * @retval 0,相等; 1,不相等; */ void usmart_strcopy(char *src, char *dst) { while (1) { *dst = *src; /* 拷贝 */ if (*src == '\0')break; /* 拷贝完成了. */ src++; dst++; } } /** * @brief 得到字符串的长度(字节) * @param str: 字符串指针 * @retval 字符串的长度 */ uint8_t usmart_strlen(char *str) { uint8_t len = 0; while (1) { if (*str == '\0')break; /* 拷贝完成了. */ len++; str++; } return len; } /** * @brief 平方函数, m^n * @param m: 底数 * @param n: 指数 * @retval m的n次方 */ uint32_t usmart_pow(uint8_t m, uint8_t n) { uint32_t result = 1; while (n--)result *= m; return result; } /** * @brief 把字符串转为数字 * @note * 支持16进制转换,但是16进制字母必须是大写的,且格式为以0X开头的. * 支持负数 * @param str: 字符串指针 * @param res: 转换完的结果存放地址. * @retval 结果如下: * @arg 0, 成功转换完成 * @arg 1, 数据格式错误 * @arg 2, 16进制位数为0 * @arg 3, 起始格式错误 * @arg 4, 十进制位数为0 */ uint8_t usmart_str2num(char *str, uint32_t *res) { uint32_t t; int tnum; uint8_t bnum = 0; /* 数字的位数 */ char *p; uint8_t hexdec = 10;/* 默认为十进制数据 */ uint8_t flag = 0; /* 0,没有符号标记;1,表示正数;2,表示负数. */ p = str; *res = 0; /* 清零. */ while (1) { /* 参数合法性检测 */ if ((*p = '0') || ((*str == '-' || *str == '+') && bnum == 0) || (*p = 'A') || (*p == 'X' && bnum == 1)) { if (*p >= 'A')hexdec = 16; /* 字符串中存在字母,为16进制格式. */ if (*str == '-') { flag = 2; /* 偏移掉符号 */ str += 1; } else if (*str == '+') { flag = 1; /* 偏移掉符号 */ str += 1; } else { bnum++; /* 位数增加. */ } } else if (*p == '\0') { break; /* 碰到结束符,退出 */ } else { return 1; /* 不全是十进制或者16进制数据. */ } p++; } p = str; /* 重新定位到字符串开始的地址. */ if (hexdec == 16) /* 16进制数据 */ { if (bnum return 3; /* 起始头的格式不对 */ } } else if (bnum == 0) { return 4; /* 位数为0,直接退出. */ } while (1) { if (bnum)bnum--; if (*p = '0')t = *p - '0'; /* 得到数字的值 */ else t = *p - 'A' + 10; /* 得到A~F对应的值 */ *res += t * usmart_pow(hexdec, bnum); p++; if (*p == '\0')break; /* 数据都查完了. */ } if (flag == 2) /* 是负数? */ { tnum = -*res; *res = tnum; } return 0; /* 成功转换 */ } /** * @brief 得到指令名 * @param str : 源字符串 * @param cmdname : 指令名 * @param nlen : 指令名长度 * @param maxlen : 最大长度(做限制,指令不可能太长的) * @retval 0,成功;其他,失败. */ uint8_t usmart_get_cmdname(char *str, char *cmdname, uint8_t *nlen, uint8_t maxlen) { *nlen = 0; while (*str != ' ' && *str != '\0') /* 找到空格或者结束符则认为结束了 */ { *cmdname = *str; str++; cmdname++; (*nlen)++; /* 统计命令长度 */ if (*nlen >= maxlen)return 1; /* 错误的指令 */ } *cmdname = '\0';/* 加入结束符 */ return 0; /* 正常返回 */ } /** * @brief 获取下一个字符(当中间有很多空格的时候,此函数直接忽略空格,找到空格之后的第一个字符) * @param str : 字符串指针 * @retval 下一个字符 */ uint8_t usmart_search_nextc(char *str) { str++; while (*str == ' ' && str != 0)str++; return *str; } /** * @brief 从str中得到函数名 * @param str : 源字符串指针 * @param fname : 获取到的函数名字指针 * @param pnum : 函数的参数个数 * @param rval : 是否需要显示返回值(0,不需要;1,需要) * @retval 0,成功;其他,错误代码. */ uint8_t usmart_get_fname(char *str, char *fname, uint8_t *pnum, uint8_t *rval) { uint8_t res; uint8_t fover = 0; /* 括号深度 */ char *strtemp; uint8_t offset = 0; uint8_t parmnum = 0; uint8_t temp = 1; char fpname[6]; /* void+X+'/0' */ uint8_t fplcnt = 0; /* 第一个参数的长度计数器 */ uint8_t pcnt = 0; /* 参数计数器 */ uint8_t nchar; /* 判断函数是否有返回值 */ strtemp = str; while (*strtemp != '\0') /* 没有结束 */ { if (*strtemp != ' ' && (pcnt & 0X7F) break; } strtemp++; } if (pcnt) /* 接收完了 */ { fpname[pcnt & 0x7f] = '\0'; /* 加入结束符 */ if (usmart_strcmp(fpname, "void") == 0) { *rval = 0; /* 不需要返回值 */ } else { *rval = 1; /* 需要返回值 */ } pcnt = 0; } res = 0; strtemp = str; while (*strtemp != '(' && *strtemp != '\0') /* 此代码找到函数名的真正起始位置 */ { strtemp++; res++; if (*strtemp == ' ' || *strtemp == '*') { nchar = usmart_search_nextc(strtemp); /* 获取下一个字符 */ if (nchar != '(' && nchar != '*')offset = res; /* 跳过空格和*号 */ } } strtemp = str; if (offset)strtemp += offset + 1; /* 跳到函数名开始的地方 */ res = 0; nchar = 0; /* 是否正在字符串里面的标志,0,不在字符串;1,在字符串; */ while (1) { if (*strtemp == 0) { res = USMART_FUNCERR; /* 函数错误 */ break; } else if (*strtemp == '(' && nchar == 0) { fover++; /* 括号深度增加一级 */ } else if (*strtemp == ')' && nchar == 0) { if (fover) { fover--; } else { res = USMART_FUNCERR; /* 错误结束,没收到'(' */ } if (fover == 0)break; /* 到末尾了,退出 */ } else if (*strtemp == '"') { nchar = !nchar; } if (fover == 0) /* 函数名还没接收完 */ { if (*strtemp != ' ') /* 空格不属于函数名 */ { *fname = *strtemp; /* 得到函数名 */ fname++; } } else /* 已经接受完了函数名了. */ { if (*strtemp == ',') { temp = 1; /* 使能增加一个参数 */ pcnt++; } else if (*strtemp != ' ' && *strtemp != '(') { if (pcnt == 0 && fplcnt temp++; /* 防止重复增加 */ parmnum++; /* 参数增加一个 */ } } strtemp++; } if (parmnum == 1) /* 只有1个参数. */ { fpname[fplcnt] = '\0'; /* 加入结束符 */ if (usmart_strcmp(fpname, "void") == 0)parmnum = 0; /* 参数为void,表示没有参数. */ } *pnum = parmnum;/* 记录参数个数 */ *fname = '\0'; /* 加入结束符 */ return res; /* 返回执行结果 */ } /** * @brief 从str中得到一个函数的参数 * @param str : 源字符串指针 * @param fparm : 参数字符串指针 * @param ptype : 参数类型 * @arg 0 ,数字 * @arg 1 ,字符串 * @arg 0XFF,参数错误 * @retval * @arg 0, 已经无参数了 * @arg 其他,下一个参数的偏移量. */ uint8_t usmart_get_aparm(char *str, char *fparm, uint8_t *ptype) { uint8_t i = 0; uint8_t enout = 0; uint8_t type = 0; /* 默认是数字 */ uint8_t string = 0; /* 标记str是否正在读 */ while (1) { if (*str == ',' && string == 0)enout = 1; /* 暂缓立即退出,目的是寻找下一个参数的起始地址 */ if ((*str == ')' || *str == '\0') && string == 0)break; /* 立即退出标识符 */ if (type == 0) /* 默认是数字的 */ { /* 数字串检测 */ if ((*str >= '0' && *str = 'a' && *str = 'A' && *str *fparm = *str - 0X20; /* 小写转换为大写 */ } else { *fparm = *str; /* 小写或者数字保持不变 */ } fparm++; } else if (*str == '"') /* 找到字符串的开始标志 */ { if (enout)break; /* 找到,后才找到",认为结束了. */ type = 1; string = 1; /* 登记STRING 正在读了 */ } else if (*str != ' ' && *str != ',') /* 发现非法字符,参数错误 */ { type = 0XFF; break; } } else /* string类 */ { if (*str == '"')string = 0; if (enout)break; /* 找到了下一个参数,直接退出. */ if (string) /* 字符串正在读 */ { if (*str == '\\') /* 遇到转义符(不复制转义符) */ { str++; /* 偏移到转义符后面的字符,不管什么字符,直接COPY */ i++; } *fparm = *str; /* 小写或者数字保持不变 */ fparm++; } } i++; /* 偏移量增加 */ str++; } *fparm = '\0'; /* 加入结束符 */ *ptype = type; /* 返回参数类型 */ return i; /* 返回参数长度 */ } /** * @brief 得到指定参数的起始地址 * @param num : 第num个参数,范围0~9. * @retval 该参数的起始地址 */ uint8_t usmart_get_parmpos(uint8_t num) { uint8_t temp = 0; uint8_t i; for (i = 0; i uint8_t i, type; uint32_t res; uint8_t n = 0; uint8_t len; char tstr[PARM_LEN + 1]; /* 字节长度的缓存,最多可以存放PARM_LEN个字符的字符串 */ for (i = 0; i str++; if (*str == '\0')return USMART_FUNCERR; /* 遇到结束符了 */ } str++; /* 偏移到"("之后的第一个字节 */ while (1) { i = usmart_get_aparm(str, tstr, &type); /* 得到第一个参数 */ str += i; /* 偏移 */ switch (type) { case 0: /* 数字 */ if (tstr[0] != '\0') /* 接收到的参数有效 */ { i = usmart_str2num(tstr, &res); /* 记录该参数 */ if (i)return USMART_PARMERR; /* 参数错误. */ *(uint32_t *)(usmart_dev.parm + usmart_get_parmpos(n)) = res; /* 记录转换成功的结果. */ usmart_dev.parmtype &= ~(1 struct _m_usmart_nametab *funs; /* 函数名指针 */ void (*init)(uint16_t tclk); /* 初始化 */ uint8_t (*cmd_rec)(char *str); /* 识别函数名及参数 */ void (*exe)(void); /* 执行 */ void (*scan)(void); /* 扫描 */ uint8_t fnum; /* 函数数量 */ uint8_t pnum; /* 参数数量 */ uint8_t id; /* 函数id */ uint8_t sptype; /* 参数显示类型(非字符串参数):0,10进制;1,16进制; */ uint16_t parmtype; /* 参数的类型 */ uint8_t plentbl[MAX_PARM]; /* 每个参数的长度暂存表 */ uint8_t parm[PARM_LEN]; /* 函数的参数 */ uint8_t runtimeflag; /* 0,不统计函数执行时间;1,统计函数执行时间,注意:此功能必须在USMART_ENTIMX_SCAN使能的时候,才有用 */ uint32_t runtime; /* 运行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms */ }; extern struct _m_usmart_nametab usmart_nametab[]; /* 在usmart_config.c里面定义 */ extern struct _m_usmart_dev usmart_dev; /* 在usmart_config.c里面定义 */ void usmart_init(uint16_t tclk); /* 初始化 */ uint8_t usmart_cmd_rec(char*str); /* 识别 */ void usmart_exe(void); /* 执行 */ void usmart_scan(void); /* 扫描 */ uint32_t read_addr(uint32_t addr); /* 读取指定地址的值 */ void write_addr(uint32_t addr,uint32_t val);/* 在指定地址写入指定的值 */ #endif

usamrt_port.h

#ifndef __USMART_PORT_H #define __USMART_PORT_H #include "./SYSTEM/sys/sys.h" #include "./SYSTEM/usart/usart.h"

usamrt_str.h

#ifndef __USMART_STR_H #define __USMART_STR_H #include "./USMART/usmart_port.h" uint8_t usmart_get_parmpos(uint8_t num); /* 得到某个参数在参数列里面的起始位置 */ uint8_t usmart_strcmp(char *str1, char *str2); /* 对比两个字符串是否相等 */ uint32_t usmart_pow(uint8_t m, uint8_t n); /* M^N次方 */ uint8_t usmart_str2num(char *str, uint32_t *res); /* 字符串转为数字 */ uint8_t usmart_get_cmdname(char *str, char *cmdname, uint8_t *nlen, uint8_t maxlen); /* 从str中得到指令名,并返回指令长度 */ uint8_t usmart_get_fname(char *str, char *fname, uint8_t *pnum, uint8_t *rval); /* 从str中得到函数名 */ uint8_t usmart_get_aparm(char *str, char *fparm, uint8_t *ptype); /* 从str中得到一个函数参数 */ uint8_t usmart_get_fparam(char *str, uint8_t *parn); /* 得到str中所有的函数参数. */ #endif

main.c

#include "./SYSTEM/sys/sys.h" #include "./SYSTEM/usart/usart.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LED/led.h" #include "./BSP/LCD/lcd.h" #include "./USMART/usmart.h" /* LED状态设置函数 */ void led_set(uint8_t sta) { LED1(sta); } /* 函数参数调用测试函数 */ void test_fun(void(*ledset)(uint8_t), uint8_t sta) { ledset(sta); } int main(void) { HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ usmart_dev.init(72); /* 初始化USMART */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ lcd_show_string(30, 50, 200, 16, 16, "STM32", RED); lcd_show_string(30, 70, 200, 16, 16, "USMART TEST", RED); lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED); while (1) { LED0_TOGGLE(); /* LED0(RED) 闪烁 */ delay_ms(500); } } 六、总结

在这里插入图片描述 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3