【STM32】RTT-Studio中HAL库开发教程十四:MSMART串口组件

文章目录


一、USMART简介

1.简介

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

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

  总的来说,USMART是一个强大的串口调试工具,能够在嵌入式开发 中发挥重要作用,特别是在调试和验证阶段。

2.主要特点

USMART具有以下主要特点(不支持浮点型参数):

  • 调用用户编写的函数:可以直接调用绝大部分用户编写的函数,无需修改代码,方便快捷地进行调试和功能验证。
  • 占用资源少:USMART在嵌入式系统中占用的资源较少,最小占用为4KB FLASH和72B SRAM,不会对系统资源造成过大的压力。
  • 支持多种参数类型:支持多种参数类型,包括整数(10进制/16进制)、字符串、函数指针等,满足不同类型参数的调试需求。
  • 函数返回值显示:支持函数返回值的显示,并且可以对返回值的格式进行设置,便于开发者查看和分析函数执行结果。
  • 支持函数执行时间计算:可以计算函数执行的时间,帮助开发者评估函数的执行效率和优化代码。

二、USMART内容

1.工作原理

USMART的工作原理可以概括为以下几个步骤:

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

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

  • 函数名匹配结果

    • 函数名匹配失败:如果提取出的函数名与本地函数名表中的函数名不匹配,则提示用户输入的函数名有误或者不存在,报错并等待下一次输入。
    • 函数名匹配成功:如果提取出的函数名与本地函数名表中的某个函数名匹配成功,则获取该函数名对应的函数指针。然后将参数传递给该函数指针所指向的函数,并执行该函数。
  • 返回执行结果:执行完函数后,将执行结果返回给用户,通常是函数的返回值或者执行状态。然后等待下一次用户输入。

通过这样的流程,USMART实现了通过串口调用用户编写的函数,方便快捷地进行代码调试和功能验证。

2.USMART组成

USMART由以下几个文件组成,每个文件负责不同的功能:

  1. usmart.c/h:核心文件,负责处理命令并与外部进行交互。实现了命令解析、函数调用等核心功能。
  2. usmart_config.c:函数管理文件,用户可以在此文件中添加需要由USMART管理的函数。通过添加函数声明和对应的函数指针,使得USMART能够识别并调用用户编写的函数。
  3. usmart_port.c/h:移植文件,用于USMART的移植。用户根据具体的硬件平台和串口配置,修改这些文件,以适配不同的系统环境和串口设定。
  4. usmart_str.c/h:字符串处理文件,负责字符串的转换、参数获取等操作。提供了一些字符串处理的函数,用于解析用户输入的命令和参数。

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

入口函数:

c 复制代码
/**
 * @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移植的步骤如下:

  1. 获取USMART组件:首先,从正点原子等资源处获取完整的USMART组件,包括核心文件(usmart.c/h)、函数管理文件(usmart_config.c)、移植文件(usmart_port.c/h)以及字符串处理文件(usmart_str.c/h)。
  2. 添加到工程:将获取到的全部组件添加到您的工程中,并设置好路径关联,以确保编译器能够正确地找到这些文件。
  3. 适配硬件:修改移植文件(usmart_port.c/h),以适配您的硬件平台。主要修改调试串口和定时器相关的配置,确保USMART能够在您的硬件环境中正常工作。
  4. 添加执行函数:在函数管理文件(usmart_config.c)中添加或修改需要由USMART管理的执行函数。添加函数声明和函数指针,使得USMART能够识别并调用您所添加的函数。
  5. 通过串口交互:完成移植后,将移植好的USMART组件烧录到目标设备中。通过串口与目标设备交互,可以测试和调用您添加的函数,并进行调试和验证。

1.移植步骤

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

(1)获取USMART组件

(2)添加到工程

(3)适配硬件

(4)添加执行函数

(5)通过串口交互


四、完整代码

1.main.c

c 复制代码
#include <stdio.h>
#include <board.h>

#include "usmart.h"

int main(void)
{
    usart_init(115200);
    usmart_dev.init(84);

    while (1)
    {
        wait_ms(1000);
    }

    return 0;
}

2.usmart.h

c 复制代码
/**
 ****************************************************************************************************
 * @file        usmart.h
 * @author      正点原子团队(ALIENTEK)
 * @version     V3.5
 * @date        2020-12-20
 * @brief       USMART 串口调试组件
 *
 *              USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 它,你可以通过串口助手调用程
 *              序里面的任何函数,并执行.因此,你可以随意更改函数的输入参数(支持数字(10/16进制,支持负数)
 *              、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返 回值显示.
 *              V2.1版本以后新增hex和dec两个指令.他们可以用于设置函数参数的显示格式.也可以用于数据的进
 *              制转换,例如:
 *              输入"hex 100"  会在串口调试助手上看到 HEX 0X64.
 *              输入"dec 0X64" 会在串口调试助手上看到 DEC 100.
 *   @note
 *              USMART资源占用情况@MDK 3.80A@2.0版本:
 *              FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
 *              SRAM:72字节(最少的情况下)
 *              SRAM计算公式:   SRAM=PARM_LEN+72-4  其中PARM_LEN必须大于等于4.
 *              应该保证堆栈不小于100个字节.
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 * 
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * 
 * V3.4之前版本详细修改说明见USMART文件夹下的:readme.txt
 * 
 * V3.4 20200324
 * 1, 新增usmart_port.c和usmart_port.h,用于管理USMART的移植,方便修改
 * 2, 修改变量命名方式为: uint8_t, uint16_t, uint32_t
 * 3, 修改usmart_reset_runtime为usmart_timx_reset_time
 * 4, 修改usmart_get_runtime为usmart_timx_get_time
 * 5, 修改usmart_scan函数实现方式,改成由usmart_get_input_string获取数据流
 * 6, 修改printf函数为USMART_PRINTF宏定义
 * 7, 修改定时扫描相关函数,改用宏定义方式,方便移植
 *
 * V3.5 20201220
 * 1,修改部分代码以支持AC6编译器
 ****************************************************************************************************
 */

#ifndef __USMART_H
#define __USMART_H

#include "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

3.usmart.c

c 复制代码
/**
 ****************************************************************************************************
 * @file        usmart.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V3.5
 * @date        2020-12-20
 * @brief       USMART 串口调试组件
 *
 *              USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 它,你可以通过串口助手调用程
 *              序里面的任何函数,并执行.因此,你可以随意更改函数的输入参数(支持数字(10/16进制,支持负数)
 *              、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返 回值显示.
 *              V2.1版本以后新增hex和dec两个指令.他们可以用于设置函数参数的显示格式.也可以用于数据的进
 *              制转换,例如:
 *              输入"hex 100"  会在串口调试助手上看到 HEX 0X64.
 *              输入"dec 0X64" 会在串口调试助手上看到 DEC 100.
 *   @note
 *              USMART资源占用情况@MDK 3.80A@2.0版本:
 *              FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
 *              SRAM:72字节(最少的情况下)
 *              SRAM计算公式:   SRAM=PARM_LEN+72-4  其中PARM_LEN必须大于等于4.
 *              应该保证堆栈不小于100个字节.
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 * 
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * 
 * V3.4之前版本详细修改说明见USMART文件夹下的:readme.txt
 * 
 * V3.4 20200324
 * 1, 新增usmart_port.c和usmart_port.h,用于管理USMART的移植,方便修改
 * 2, 修改变量命名方式为: uint8_t, uint16_t, uint32_t
 * 3, 修改usmart_reset_runtime为usmart_timx_reset_time
 * 4, 修改usmart_get_runtime为usmart_timx_get_time
 * 5, 修改usmart_scan函数实现方式,改成由usmart_get_input_string获取数据流
 * 6, 修改printf函数为USMART_PRINTF宏定义
 * 7, 修改定时扫描相关函数,改用宏定义方式,方便移植
 *
 * V3.5 20201220
 * 1,修改部分代码以支持AC6编译器
 *
 ****************************************************************************************************
 */

#include "usmart.h"
#include "usmart_str.h"
#include "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
#if 0
            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("------------------------USMART V3.5------------------------ \r\n\n");
            USMART_PRINTF("USMART There are 7 system commands (must be lowercase):\r\n\n");
            USMART_PRINTF("?:      Get help information\r\n\n");
            USMART_PRINTF("help:   Get help information\r\n\n");
            USMART_PRINTF("list:   List of available functions\r\n\n");
            USMART_PRINTF("id:     List of IDs for available functions\r\n\n");
            USMART_PRINTF("hex:    The parameter is displayed in hexadecimal format\r\n\n");
            USMART_PRINTF("dec:    Parameter displayed in decimal format\r\n\n");
            USMART_PRINTF("runtime:1/0,Enable/Disable function running timer\r\n\n");
            USMART_PRINTF("----------------------------END---------------------------- \r\n");
#endif
#else
            USMART_PRINTF("指令失效\r\n");
#endif
            break;

        case 2: /* 查询指令 */
            USMART_PRINTF("\r\n");
            USMART_PRINTF("-------------------------Function List--------------------------- \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("-------------------------Function 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%lX\r\n", res);    /* 转为16进制 */
                }
                else if (i != 4)return USMART_PARMERR;      /* 参数错误. */
                /* 参数显示设定功能 */
                else
                {
                    USMART_PRINTF("16 Binary parameter display!\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 Binary parameter display!\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%lX", 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%lX;\r\n", res); /* 输出执行结果(16进制参数显示) */
    }
    else USMART_PRINTF(";\r\n");                /* 不需要返回值,直接输出结束 */

    if (usmart_dev.runtimeflag)                 /* 需要显示函数执行时间 */
    {
        USMART_PRINTF("Function Run Time:%ld.%1ldms\r\n", usmart_dev.runtime / 10, usmart_dev.runtime % 10);    /* 打印函数执行时间 */
    }
}

/**
 * @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("Function error!\r\n");
                    break;

                case USMART_PARMERR:
                    USMART_PRINTF("Function error!\r\n");
                    break;

                case USMART_PARMOVER:
                    USMART_PRINTF("Too many parameters!\r\n");
                    break;

                case USMART_NOFUNCFIND:
                    USMART_PRINTF("No matching function found!\r\n");
                    break;
            }
        }
    } 
 
}

#if USMART_USE_WRFUNS == 1  /* 如果使能了读写操作 */

/**
 * @brief       读取指定地址的值
 * @param       无
 * @retval      无
 */ 
uint32_t read_addr(uint32_t addr)
{
    return *(uint32_t *)addr;
}

/**
 * @brief       在指定地址写入指定的值
 * @param       无
 * @retval      无
 */ 
void write_addr(uint32_t addr, uint32_t val)
{
    *(uint32_t *)addr = val;
}

#endif

4.usmart_str.h

c 复制代码
/**
 ****************************************************************************************************
 * @file        usmart_str.h
 * @author      正点原子团队(ALIENTEK)
 * @version     V3.5
 * @date        2020-12-20
 * @brief       USMART 串口调试组件
 *
 *              USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 它,你可以通过串口助手调用程
 *              序里面的任何函数,并执行.因此,你可以随意更改函数的输入参数(支持数字(10/16进制,支持负数)
 *              、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返 回值显示.
 *              V2.1版本以后新增hex和dec两个指令.他们可以用于设置函数参数的显示格式.也可以用于数据的进
 *              制转换,例如:
 *              输入"hex 100"  会在串口调试助手上看到 HEX 0X64.
 *              输入"dec 0X64" 会在串口调试助手上看到 DEC 100.
 *   @note
 *              USMART资源占用情况@MDK 3.80A@2.0版本:
 *              FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
 *              SRAM:72字节(最少的情况下)
 *              SRAM计算公式:   SRAM=PARM_LEN+72-4  其中PARM_LEN必须大于等于4.
 *              应该保证堆栈不小于100个字节.
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * 
 * V3.4之前版本详细修改说明见USMART文件夹下的:readme.txt
 * 
 * V3.4 20200324
 * 1, 新增usmart_port.c和usmart_port.h,用于管理USMART的移植,方便修改
 * 2, 修改变量命名方式为: uint8_t, uint16_t, uint32_t
 * 3, 修改usmart_reset_runtime为usmart_timx_reset_time
 * 4, 修改usmart_get_runtime为usmart_timx_get_time
 * 5, 修改usmart_scan函数实现方式,改成由usmart_get_input_string获取数据流
 * 6, 修改printf函数为USMART_PRINTF宏定义
 * 7, 修改定时扫描相关函数,改用宏定义方式,方便移植
 * V3.5 20201220
 * 1,修改部分代码以支持AC6编译器
 *
 ****************************************************************************************************
 */

#ifndef __USMART_STR_H
#define __USMART_STR_H

#include "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

5.usmart_str.c

c 复制代码
/**
 ****************************************************************************************************
 * @file        usmart_str.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V3.5
 * @date        2020-12-20
 * @brief       USMART 串口调试组件
 *
 *              USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 它,你可以通过串口助手调用程
 *              序里面的任何函数,并执行.因此,你可以随意更改函数的输入参数(支持数字(10/16进制,支持负数)
 *              、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返 回值显示.
 *              V2.1版本以后新增hex和dec两个指令.他们可以用于设置函数参数的显示格式.也可以用于数据的进
 *              制转换,例如:
 *              输入"hex 100"  会在串口调试助手上看到 HEX 0X64.
 *              输入"dec 0X64" 会在串口调试助手上看到 DEC 100.
 *   @note
 *              USMART资源占用情况@MDK 3.80A@2.0版本:
 *              FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
 *              SRAM:72字节(最少的情况下)
 *              SRAM计算公式:   SRAM=PARM_LEN+72-4  其中PARM_LEN必须大于等于4.
 *              应该保证堆栈不小于100个字节.
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * 
 * V3.4之前版本详细修改说明见USMART文件夹下的:readme.txt
 * 
 * V3.4 20200324
 * 1, 新增usmart_port.c和usmart_port.h,用于管理USMART的移植,方便修改
 * 2, 修改变量命名方式为: uint8_t, uint16_t, uint32_t
 * 3, 修改usmart_reset_runtime为usmart_timx_reset_time
 * 4, 修改usmart_get_runtime为usmart_timx_get_time
 * 5, 修改usmart_scan函数实现方式,改成由usmart_get_input_string获取数据流
 * 6, 修改printf函数为USMART_PRINTF宏定义
 * 7, 修改定时扫描相关函数,改用宏定义方式,方便移植
 *
 * V3.5 20201220
 * 1,修改部分代码以支持AC6编译器
 *
 ****************************************************************************************************
 */

#include "usmart.h"
#include "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;   /* 正确得到了参数 */
}

6.usmart_port.h

c 复制代码
/**
 ****************************************************************************************************
 * @file        usmart_port.h
 * @author      正点原子团队(ALIENTEK)
 * @version     V3.5
 * @date        2020-12-20
 * @brief       USMART 串口调试组件
 *
 *              USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 它,你可以通过串口助手调用程
 *              序里面的任何函数,并执行.因此,你可以随意更改函数的输入参数(支持数字(10/16进制,支持负数)
 *              、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返 回值显示.
 *              V2.1版本以后新增hex和dec两个指令.他们可以用于设置函数参数的显示格式.也可以用于数据的进
 *              制转换,例如:
 *              输入"hex 100"  会在串口调试助手上看到 HEX 0X64.
 *              输入"dec 0X64" 会在串口调试助手上看到 DEC 100.
 *   @note
 *              USMART资源占用情况@MDK 3.80A@2.0版本:
 *              FLASH:4K~K字节(通过USMART_USE_HELP和USMART_USE_WRFUNS设置)
 *              SRAM:72字节(最少的情况下)
 *              SRAM计算公式:   SRAM=PARM_LEN+72-4  其中PARM_LEN必须大于等于4.
 *              应该保证堆栈不小于100个字节.
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 * 
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * 
 * V3.4之前版本详细修改说明见USMART文件夹下的:readme.txt
 * 
 * V3.4 20200324
 * 1, 新增usmart_port.c和usmart_port.h,用于管理USMART的移植,方便修改
 * 2, 修改变量命名方式为: uint8_t, uint16_t, uint32_t
 * 3, 修改usmart_reset_runtime为usmart_timx_reset_time
 * 4, 修改usmart_get_runtime为usmart_timx_get_time
 * 5, 修改usmart_scan函数实现方式,改成由usmart_get_input_string获取数据流
 * 6, 修改printf函数为USMART_PRINTF宏定义
 * 7, 修改定时扫描相关函数,改用宏定义方式,方便移植
 *
 * V3.5 20201220
 * 1,修改部分代码以支持AC6编译器
 *
 ****************************************************************************************************
 */
 
#ifndef __USMART_PORT_H
#define __USMART_PORT_H

#include <stdio.h>
#include "stm32f4xx.h"

/*******************************************************************************************************************************/
/* 用户配置参数 */


#define MAX_FNAME_LEN           30      /* 函数名最大长度,应该设置为不小于最长函数名的长度。 */
#define MAX_PARM                10      /* 最大为10个参数 ,修改此参数,必须修改usmart_exe与之对应. */
#define PARM_LEN                200     /* 所有参数之和的长度不超过PARM_LEN个字节,注意串口接收部分要与之对应(不小于PARM_LEN) */


#define USMART_ENTIMX_SCAN      1       /* 使用TIM的定时中断来扫描SCAN函数,如果设置为0,需要自己实现隔一段时间扫描一次scan函数.
                                         * 注意:如果要用runtime统计功能,必须设置USMART_ENTIMX_SCAN为1!!!!
                                         */

#define USMART_USE_HELP         1       /* 使用帮助,该值设为0,可以节省近700个字节,但是将导致无法显示帮助信息。 */
#define USMART_USE_WRFUNS       1       /* 使用读写函数,使能这里,可以读取任何地址的值,还可以写寄存器的值. */

#define USMART_PRINTF           printf  /* 定义printf输出 */

/*******************************************************************************************************************************/
/* 引脚 和 串口 定义
 * 默认是针对USART1的.
 * 注意: 通过修改这12个宏定义,可以支持USART1~UART7任意一个串口.
 */

#define USART_TX_GPIO_PORT              GPIOA
#define USART_TX_GPIO_PIN               GPIO_PIN_9
#define USART_TX_GPIO_AF                GPIO_AF7_USART1
#define USART_TX_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* 发送引脚时钟使能 */

#define USART_RX_GPIO_PORT              GPIOA
#define USART_RX_GPIO_PIN               GPIO_PIN_10
#define USART_RX_GPIO_AF                GPIO_AF7_USART1
#define USART_RX_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* 接收引脚时钟使能 */

#define USART_UX                        USART1
#define USART_UX_IRQn                   USART1_IRQn
#define USART_UX_IRQHandler             USART1_IRQHandler
#define USART_UX_CLK_ENABLE()           do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0)  /* USART1 时钟使能 */

#define RXBUFFERSIZE            1       /* 缓存大小 */
#define USART_EN_RX             1       /* 使能(1)/禁止(0)串口1接收 */
#define USART_REC_LEN           200     /* 定义最大接收字节数 200 */

/*******************************************************************************************************************************/
/* USMART定时器 定义 */

# if USMART_ENTIMX_SCAN == 1    /* 开启了使能定时器扫描,则需要如下定义 */

/* TIMX 中断定义
 * 用于定时调用usmart.scan函数扫描串口数据,并执行相关操作
 * 注意: 通过修改这4个宏定义,可以支持TIM1~TIM17任意一个定时器.
 */
#define USMART_TIMX                     TIM4
#define USMART_TIMX_IRQn                TIM4_IRQn
#define USMART_TIMX_IRQHandler          TIM4_IRQHandler
#define USMART_TIMX_CLK_ENABLE()        do{ __HAL_RCC_TIM4_CLK_ENABLE(); }while(0)  /* TIMX 时钟使能 */

#endif

/*******************************************************************************************************************************/
/* 如果没有定义uint32_t,则定义 */
#define TYPE_EN             0
#if TYPE_EN
#ifndef uint32_t
typedef unsigned           char uint8_t;
typedef unsigned short     int  uint16_t;
typedef unsigned           int  uint32_t;
#endif
#endif


extern void usart_init(uint32_t baudrate);                     /* 串口初始化 */

extern char * usmart_get_input_string(void);                   /* 获取输入数据流 */
extern void usmart_timx_reset_time(void);                      /* 复位运行时间 */
extern uint32_t usmart_timx_get_time(void);                    /* 获取运行时间 */
extern void usmart_timx_init(uint16_t arr, uint16_t psc);      /* 初始化定时器 */

#endif

7.usmart_port.c

c 复制代码
/**
 ****************************************************************************************************
 * @file        usmart_port.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V3.5
 * @date        2020-12-20
 * @brief       USMART 移植文件
 *
 *              通过修改该文件,可以方便的将USMART移植到其他工程
 *              当:USMART_ENTIMX_SCAN == 0时,仅需要实现: usmart_get_input_string函数.
 *              当:USMART_ENTIMX_SCAN == 1时,需要多实现4个函数:
 *              usmart_timx_reset_time
 *              usmart_timx_get_time
 *              usmart_timx_init
 *              USMART_TIMX_IRQHandler
 *
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 *
 * V3.4之前版本详细修改说明见USMART文件夹下的:readme.txt
 *
 * V3.4 20200324
 * 1, 新增usmart_port.c和usmart_port.h,用于管理USMART的移植,方便修改
 * 2, 修改变量命名方式为: uint8_t, uint16_t, uint32_t
 * 3, 修改usmart_reset_runtime为usmart_timx_reset_time
 * 4, 修改usmart_get_runtime为usmart_timx_get_time
 * 5, 修改usmart_scan函数实现方式,改成由usmart_get_input_string获取数据流
 * 6, 修改printf函数为USMART_PRINTF宏定义
 * 7, 修改定时扫描相关函数,改用宏定义方式,方便移植
 *
 * V3.5 20201220
 * 1,修改部分代码以支持AC6编译器
 *
 ****************************************************************************************************
 */

#include "usmart.h"
#include "usmart_port.h"

/******************************************************************************************************/
/*  接收状态
 *  bit15,      接收完成标志
 *  bit14,      接收到0x0d
 *  bit13~0,    接收到的有效字节数目
 */
uint16_t g_usart_rx_sta = 0;
UART_HandleTypeDef g_uart1_handle;                  /* UART句柄 */
TIM_HandleTypeDef  g_timx_usmart_handle;            /* 定时器句柄 */

uint8_t g_rx_buffer[RXBUFFERSIZE];                  /* HAL库使用的串口接收缓冲 */
uint8_t g_usart_rx_buf[USART_REC_LEN];              /* 接收缓冲, 最大USART_REC_LEN个字节. */

/**
 * @brief       串口1中断服务函数
 * @param       无
 * @retval      无
 */
void USART_UX_IRQHandler(void)
{
#if SYS_SUPPORT_OS                              /* 使用OS */
    OSIntEnter();
#endif

    HAL_UART_IRQHandler(&g_uart1_handle);       /* 调用HAL库中断处理公用函数 */

#if SYS_SUPPORT_OS                              /* 使用OS */
    OSIntExit();
#endif
}

/**
 * @brief       Rx传输回调函数
 * @param       huart: UART句柄类型指针
 * @retval      无
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART_UX)             /* 如果是串口1 */
    {
        if((g_usart_rx_sta & 0x8000) == 0)      /* 接收未完成 */
        {
            if(g_usart_rx_sta & 0x4000)         /* 接收到了0x0d */
            {
                if(g_rx_buffer[0] != 0x0a)
                {
                    g_usart_rx_sta = 0;         /* 接收错误,重新开始 */
                }
                else
                {
                    g_usart_rx_sta |= 0x8000;   /* 接收完成了 */
                }
            }
            else                                /* 还没收到0X0D */
            {
                if(g_rx_buffer[0] == 0x0d)
                {
                    g_usart_rx_sta |= 0x4000;
                }
                else
                {
                    g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0] ;
                    g_usart_rx_sta++;
                    if(g_usart_rx_sta > (USART_REC_LEN - 1))
                    {
                        g_usart_rx_sta = 0;     /* 接收数据错误,重新开始接收 */
                    }
                }
            }
        }

        HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
    }
}

/**
 * @brief       UART底层初始化函数
 * @param       huart: UART句柄类型指针
 * @note        此函数会被HAL_UART_Init()调用
 *              完成时钟使能,引脚配置,中断配置
 * @retval      无
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;
    if(huart->Instance == USART_UX)                             /* 如果是串口1,进行串口1 MSP初始化 */
    {
        USART_UX_CLK_ENABLE();                                  /* USART1 时钟使能 */
        USART_TX_GPIO_CLK_ENABLE();                             /* 发送引脚时钟使能 */
        USART_RX_GPIO_CLK_ENABLE();                             /* 接收引脚时钟使能 */

        gpio_init_struct.Pin = USART_TX_GPIO_PIN;               /* TX引脚 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
        gpio_init_struct.Alternate = USART_TX_GPIO_AF;          /* 复用为USART1 */
        HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);   /* 初始化发送引脚 */

        gpio_init_struct.Pin = USART_RX_GPIO_PIN;               /* RX引脚 */
        gpio_init_struct.Alternate = USART_RX_GPIO_AF;          /* 复用为USART1 */
        HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   /* 初始化接收引脚 */

#if USART_EN_RX
        HAL_NVIC_EnableIRQ(USART_UX_IRQn);                      /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3);              /* 抢占优先级3,子优先级3 */
#endif
    }
}

/**
 * @brief       串口X初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @note        注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
 *              这里的USART的时钟源在sys_stm32_clock_init()函数中已经设置过了.
 * @retval      无
 */
void usart_init(uint32_t baudrate)
{
    g_uart1_handle.Instance = USART_UX;                         /* USART1 */
    g_uart1_handle.Init.BaudRate = baudrate;                    /* 波特率 */
    g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;        /* 字长为8位数据格式 */
    g_uart1_handle.Init.StopBits = UART_STOPBITS_1;             /* 一个停止位 */
    g_uart1_handle.Init.Parity = UART_PARITY_NONE;              /* 无奇偶校验位 */
    g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;        /* 无硬件流控 */
    g_uart1_handle.Init.Mode = UART_MODE_TX_RX;                 /* 收发模式 */
    HAL_UART_Init(&g_uart1_handle);                             /* HAL_UART_Init()会使能UART1 */

    /* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
    HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}

/**
 * @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

8.usmart_config.c

c 复制代码
#include "usmart.h"
#include "usmart_str.h"

/******************************************************************************************/
/* 用户配置区
 * 这下面要包含所用到的函数所申明的头文件(用户自己添加)
 */



/* 函数名列表初始化(用户自己添加)
 * 用户直接在这里输入要执行的函数名及其查找串
 */
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
};

/******************************************************************************************/

/* 函数控制管理器初始化
 * 得到各个受控函数的名字
 * 得到函数总数量
 */
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,字符串 */
    {},     /* 每个参数的长度暂存表,需要MAX_PARM个0初始化 */
    {},     /* 函数的参数,需要PARM_LEN个0初始化 */
    0,      /* runtimeflag */
    0       /* runtime */
};

五、USAMRT使用

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

1.输入函数名和参数:

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

2.系统命令:

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

3.示例:

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

六、总结


相关推荐
潜创微科技2 小时前
IT6520:USB‑C DP Alt Mode 到 MIPI 单芯片转换方案
嵌入式硬件·音视频
小慧10242 小时前
STM 32 TIM定时器(2)
stm32·单片机
无痕幽雨2 小时前
STM32实现MQTT及JSON包思路二
stm32·单片机·嵌入式硬件
風清掦2 小时前
【STM32学习笔记-14】WDG看门狗 - 14.2 WWDG窗口看门狗
笔记·stm32·单片机·嵌入式硬件·学习·fpga开发
czhaii3 小时前
单片机伺服电机加减速控制子程序
c语言·单片机
熠速3 小时前
基于PolarLabBox的永磁同步电机(PMSM)零速带载闭环+全速域无感:8小时从理论到落地
单片机·嵌入式硬件·硬件在环半实物仿真
深圳市晶科鑫实业有限公司3 小时前
5G与AIoT时代:如何选择晶振常用频率?
服务器·单片机·物联网·5g·智能路由器·健康医疗·信息与通信
JNX_SEMI4 小时前
EG2031L:220V半桥驱动,1.5A灌流,宽压5V供电
单片机·嵌入式硬件
m0_377108144 小时前
stm32-SPI
stm32·单片机·嵌入式硬件