一、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 < sizeof(sys_cmd_tab) / 4; i++) /* 支持的系统指令 */
{
if (usmart_strcmp(sfname, sys_cmd_tab[i]) == 0)break;
}
switch (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 < usmart_dev.fnum; i++)USMART_PRINTF("%s\r\n", usmart_dev.funs[i].name);
USMART_PRINTF("\r\n");
break;
case 3: /* 查询ID */
USMART_PRINTF("\r\n");
USMART_PRINTF("-------------------------函数 ID --------------------------- \r\n");
for (i = 0; i < usmart_dev.fnum; i++)
{
usmart_get_fname((char *)usmart_dev.funs[i].name, sfname, &pnum, &rval); /* 得到本地函数名 */
USMART_PRINTF("%s id is:\r\n0X%08X\r\n", sfname, (unsigned int)usmart_dev.funs[i].func); /* 显示ID */
}
USMART_PRINTF("\r\n");
break;
case 4: /* hex指令 */
USMART_PRINTF("\r\n");
usmart_get_aparm(str, sfname, &i);
if (i == 0) /* 参数正常 */
{
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 < usmart_dev.fnum; i++)
{
sta = usmart_get_fname((char *)usmart_dev.funs[i].name, sfname, &spnum, &rval); /* 得到本地函数名及参数个数 */
if (sta)return sta; /* 本地解析有误 */
if (usmart_strcmp(sfname, rfname) == 0) /* 相等 */
{
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 < pnum; i++) /* 输出参数 */
{
if (usmart_dev.parmtype & (1 << 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 <= '9' && *p >= '0') || ((*str == '-' || *str == '+') && bnum == 0) || (*p <= 'F' && *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 < 3)return 2; /* 位数小于3,直接退出.因为0X就占了2个,如果0X后面不跟数据,则该数据非法. */
if (*p == '0' && (*(p + 1) == 'X')) /* 必须以'0X'开头. */
{
p += 2; /* 偏移到数据起始地址. */
bnum -= 2; /* 减去偏移量 */
}
else
{
return 3; /* 起始头的格式不对 */
}
}
else if (bnum == 0)
{
return 4; /* 位数为0,直接退出. */
}
while (1)
{
if (bnum)bnum--;
if (*p <= '9' && *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) < 5) /* 最多记录5个字符 */
{
if (pcnt == 0)pcnt |= 0X80; /* 置位最高位,标记开始接收返回值类型 */
if (((pcnt & 0x7f) == 4) && (*strtemp != '*'))break; /* 最后一个字符,必须是* */
fpname[pcnt & 0x7f] = *strtemp; /* 记录函数的返回值类型 */
pcnt++;
}
else if (pcnt == 0X85)
{
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 < 5) /* 当第一个参数来时,为了避免统计void类型的参数,必须做判断. */
{
fpname[fplcnt] = *strtemp; /* 记录参数特征. */
fplcnt++;
}
temp++; /* 得到有效参数(非空格) */
}
if (fover == 1 && temp == 2)
{
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 <= '9') || *str == '-' || *str == '+' || (*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F') || *str == 'X' || *str == 'x')
{
if (enout)break; /* 找到了下一个参数,直接退出. */
if (*str >= 'a')
{
*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 < num; i++)
{
temp += usmart_dev.plentbl[i];
}
return temp;
}
/**
* @brief 从str中得到函数参数
* @param str : 源字符串
* @param parn : 参数的多少.0表示无参数 void类型
* @retval 0,成功;其他,错误代码.
*/
uint8_t usmart_get_fparam(char *str, uint8_t *parn)
{
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 < MAX_PARM; i++)
{
usmart_dev.plentbl[i] = 0; /* 清空参数长度表 */
}
while (*str != '(') /* 偏移到参数开始的地方 */
{
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 << n); /* 标记数字 */
usmart_dev.plentbl[n] = 4; /* 该参数的长度为4 */
n++; /* 参数增加 */
if (n > MAX_PARM)return USMART_PARMOVER; /* 参数太多 */
}
break;
case 1:/* 字符串 */
len = usmart_strlen(tstr) + 1; /* 包含了结束符'\0' */
usmart_strcopy(tstr, (char *)&usmart_dev.parm[usmart_get_parmpos(n)]); /* 拷贝tstr数据到usmart_dev.parm[n] */
usmart_dev.parmtype |= 1 << n; /* 标记字符串 */
usmart_dev.plentbl[n] = len; /* 该参数的长度为len */
n++;
if (n > MAX_PARM)return USMART_PARMOVER; /* 参数太多 */
break;
case 0XFF:/* 错误 */
return USMART_PARMERR; /* 参数错误 */
}
if (*str == ')' || *str == '\0')break; /* 查到结束标志了. */
}
*parn = n; /* 记录参数的个数 */
return USMART_OK; /* 正确得到了参数 */
}
usamrt.h
#ifndef __USMART_H
#define __USMART_H
#include "./USMART/usmart_port.h"
#define USMART_OK 0 /* 无错误 */
#define USMART_FUNCERR 1 /* 函数错误 */
#define USMART_PARMERR 2 /* 参数错误 */
#define USMART_PARMOVER 3 /* 参数溢出 */
#define USMART_NOFUNCFIND 4 /* 未找到匹配函数 */
#define SP_TYPE_DEC 0 /* 10进制参数显示 */
#define SP_TYPE_HEX 1 /* 16进制参数显示 */
/* 函数名列表 */
struct _m_usmart_nametab
{
void *func; /* 函数指针 */
const char *name; /* 函数名(查找串) */
};
/* usmart控制管理器 */
struct _m_usmart_dev
{
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);
}
}
六、总结