【正点原子STM32】USMART串口调试组件(USMART主要特点、USMART原理、USMART组成、USMART扫描函数、USMART移植、USAMRT使用、USAMRT源码) |
您所在的位置:网站首页 › 正点原子delay函数 › 【正点原子STM32】USMART串口调试组件(USMART主要特点、USMART原理、USMART组成、USMART扫描函数、USMART移植、USAMRT使用、USAMRT源码) |
一、USMART简介 二、USMART原理 三、USMART移植 四、USAMRT使用 五、USAMRT源码 六、总结 一、USMART简介
使用USMART,开发者可以在不修改代码的情况下,通过串口发送指令来调用预先编写的函数,并且可以实时观察函数的返回值或输出结果。这样的设计极大地简化了调试过程,加快了开发速度,提高了代码的可调试性和可维护性。 总的来说,USMART是一个强大的串口调试工具,能够在嵌入式开发中发挥重要作用,特别是在调试和验证阶段。 调用用户编写的函数:可以直接调用绝大部分用户编写的函数,无需修改代码,方便快捷地进行调试和功能验证。 占用资源少:USMART在嵌入式系统中占用的资源较少,最小占用为4KB FLASH和72B SRAM,不会对系统资源造成过大的压力。 支持多种参数类型:支持多种参数类型,包括整数(10进制/16进制)、字符串、函数指针等,满足不同类型参数的调试需求。 函数返回值显示:支持函数返回值的显示,并且可以对返回值的格式进行设置,便于开发者查看和分析函数执行结果。 支持函数执行时间计算:可以计算函数执行的时间,帮助开发者评估函数的执行效率和优化代码。 需要注意的是,USMART不支持浮点数参数,因此在使用过程中需要避免传递浮点数类型的参数。 二、USMART原理深入理解C语言函数指针 用户输入字符串: 用户通过串口输入字符串,通常以回车换行符 “\r\n” 结束。 解析并对比: 系统对用户输入的字符串进行解析,提取出函数名和参数。然后将提取出的函数名与本地函数名表进行对比。 函数名匹配结果: 函数名匹配失败: 如果提取出的函数名与本地函数名表中的函数名不匹配,则提示用户输入的函数名有误或者不存在,报错并等待下一次输入。 函数名匹配成功: 如果提取出的函数名与本地函数名表中的某个函数名匹配成功,则获取该函数名对应的函数指针。然后将参数传递给该函数指针所指向的函数,并执行该函数。 返回执行结果: 执行完函数后,将执行结果返回给用户,通常是函数的返回值或者执行状态。然后等待下一次用户输入。 通过这样的流程,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扫描函数:
获取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组件
输入函数名和参数: 按照程序编写格式输入函数名及参数,并以回车键结束。例如: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.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; /* 在指定地址写入指定的值 */ } #endifusamrt_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);/* 清除中断标志位 */ } #endifusamrt_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);/* 在指定地址写入指定的值 */ #endifusamrt_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中所有的函数参数. */ #endifmain.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 |