USMART串口调试

article/2025/8/30 1:34:47

目录

  • 一、USMART简介
  • 二、USMART的移植
    • 2.1 usmart_str.h头文件
    • 2.2 usmart.h头文件
    • 2.3 usmat_str.c源文件
    • 2.4 usmart.c源文件
    • 2.5 usmart_config.c源文件
  • 三、USMART的实现
    • 3.1 USMART的实现流程
    • 3.2 USMART的调用


一、USMART简介

  USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过它,你可以通过串口助手调用程序里面的任何函数,并执行。因此,你可以随意更改函数的输入参数(支持数字(10/16进制)、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返回值显示。V2.1新增 hex和dec两个指令。他们可以用于设置函数参数的显示格式。也可以用于数据的进制转换。
  有了 USMART,你可以轻易的修改函数参数、查看函数运行结果,从而快速解决问题。而且单片机也是有寿命的,经常刷程序会折寿的。而利用USMART,则只需要在串口调试助手里面输入函数及参数,然后直接串口发送给单片机,就执行了一次参数调整,不满意的话,你在串口调试助手修改参数在发送就可以了,直到你满意为止。这样,修改参数十分方便,不需要编译、不需要下载、不会让单片机折寿。

例如:

输入"hex 100" 会在串口调试助手上看到 HEX 0X64.
输入"dec 0X64" 会在串口调试助手上看到 DEC 100.

USMART 的特点如下:

1, 可以调用绝大部分用户直接编写的函数。
2, 资源占用极少(最少情况:FLASH:4K;SRAM:72B)。
3,支持参数类型多(数字(包含 10/16 进制,支持负数)、字符串、函数指针等)。
4, 支持函数返回值显示。
5,支持参数及返回值格式设置。
6, 支持函数执行时间计算(V3.1 版本新特性)。
7, 使用方便。

二、USMART的移植

  USMART组件的移植大概5个文件,usmart.c 负责与外部互交等。usmat_str.c 主要负责命令和参数解析。usmart_config.c 主要由用户添加需要由 usmart 管理的函数。usmart.h 和 usmart_str.h 是两个头文件,其中 usmart.h 里面含有几个用户配置宏定义,可以用来配置 usmart 的功能及总参数长度(直接和 SRAM 占用挂钩)、是否使能定时器扫描、是否使用读写函数等。

2.1 usmart_str.h头文件

主要是声明usmat_str.c里的函数

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

2.2 usmart.h头文件

#ifndef __USMART_H
#define __USMART_H	  		  
#include "usmart_str.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	//使用读写函数,使能这里,可以读取任何地址的值,还可以写寄存器的值.
///END///#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 u8* name;		//函数名(查找串)	 
};
//usmart控制管理器
struct _m_usmart_dev
{struct _m_usmart_nametab *funs;	//函数名指针void (*init)(u8);				//初始化u8 (*cmd_rec)(u8*str);			//识别函数名及参数void (*exe)(void); 				//执行 void (*scan)(void);             //扫描u8 fnum; 				  		//函数数量u8 pnum;                        //参数数量u8 id;							//函数idu8 sptype;						//参数显示类型(非字符串参数):0,10进制;1,16进制;u16 parmtype;					//参数的类型u8  plentbl[MAX_PARM];  		//每个参数的长度暂存表u8  parm[PARM_LEN];  			//函数的参数u8 runtimeflag;					//0,不统计函数执行时间;1,统计函数执行时间,注意:此功能必须在USMART_ENTIMX_SCAN使能的时候,才有用u32 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(u8 sysclk);//初始化
u8 usmart_cmd_rec(u8*str);	//识别
void usmart_exe(void);		//执行
void usmart_scan(void);     //扫描
u32 read_addr(u32 addr);	//读取指定地址的值
void write_addr(u32 addr,u32 val);//在指定地址写入指定的值
u32 usmart_get_runtime(void);	//获取运行时间
void usmart_reset_runtime(void);//复位运行时间#endif

2.3 usmat_str.c源文件

#include "usmart_str.h"
#include "usmart.h"		   //对比字符串str1和str2
//*str1:字符串1指针
//*str2:字符串2指针
//返回值:0,相等;1,不相等;
u8 usmart_strcmp(u8 *str1,u8 *str2)
{while(1){if(*str1!=*str2)return 1;//不相等if(*str1=='\0')break;//对比完成了.str1++;str2++;}return 0;//两个字符串相等
}
//把str1的内容copy到str2
//*str1:字符串1指针
//*str2:字符串2指针			   
void usmart_strcopy(u8*str1,u8 *str2)
{while(1){										   *str2=*str1;	//拷贝if(*str1=='\0')break;//拷贝完成了.str1++;str2++;}
}
//得到字符串的长度(字节)
//*str:字符串指针
//返回值:字符串的长度		   
u8 usmart_strlen(u8*str)
{u8 len=0;while(1){							 if(*str=='\0')break;//拷贝完成了.len++;str++;}return len;
}
//m^n函数
//返回值:m^n次方
u32 usmart_pow(u8 m,u8 n)
{u32 result=1;	 while(n--)result*=m;    return result;
}	    
//把字符串转为数字
//支持16进制转换,但是16进制字母必须是大写的,且格式为以0X开头的.
//不支持负数 
//*str:数字字符串指针
//*res:转换完的结果存放地址.
//返回值:0,成功转换完成.其他,错误代码.
//1,数据格式错误.2,16进制位数为0.3,起始格式错误.4,十进制位数为0.
u8 usmart_str2num(u8*str,u32 *res)
{u32 t;u8 bnum=0;	//数字的位数u8 *p;		  u8 hexdec=10;//默认为十进制数据p=str;*res=0;//清零.while(1){if((*p<='9'&&*p>='0')||(*p<='F'&&*p>='A')||(*p=='X'&&bnum==1))//参数合法{if(*p>='A')hexdec=16;	//字符串中存在字母,为16进制格式.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;//数据都查完了.	}return 0;//成功转换
}
//得到指令名
//*str:源字符串
//*cmdname:指令名
//*nlen:指令名长度		
//maxlen:最大长度(做限制,指令不可能太长的)	
//返回值:0,成功;其他,失败.	  
u8 usmart_get_cmdname(u8*str,u8*cmdname,u8 *nlen,u8 maxlen)
{*nlen=0;while(*str!=' '&&*str!='\0') //找到空格或者结束符则认为结束了{*cmdname=*str;str++;cmdname++;(*nlen)++;//统计命令长度if(*nlen>=maxlen)return 1;//错误的指令}*cmdname='\0';//加入结束符return 0;//正常返回
}
//获取下一个字符(当中间有很多空格的时候,此函数直接忽略空格,找到空格之后的第一个字符)
//str:字符串指针	
//返回值:下一个字符
u8 usmart_search_nextc(u8* str)
{		   	 	str++;while(*str==' '&&str!='\0')str++;return *str;
} 
//从str中得到函数名
//*str:源字符串指针
//*fname:获取到的函数名字指针
//*pnum:函数的参数个数
//*rval:是否需要显示返回值(0,不需要;1,需要)
//返回值:0,成功;其他,错误代码.
u8 usmart_get_fname(u8*str,u8*fname,u8 *pnum,u8 *rval)
{u8 res;u8 fover=0;	  //括号深度u8 *strtemp;u8 offset=0;  u8 parmnum=0;u8 temp=1;u8 fpname[6];//void+X+'/0'u8 fplcnt=0; //第一个参数的长度计数器u8 pcnt=0;	 //参数计数器u8 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;		//返回执行结果
}//从str中得到一个函数的参数
//*str:源字符串指针
//*fparm:参数字符串指针
//*ptype:参数类型 0,数字;1,字符串;0XFF,参数错误
//返回值:0,已经无参数了;其他,下一个参数的偏移量.
u8 usmart_get_aparm(u8 *str,u8 *fparm,u8 *ptype)
{u8 i=0;u8 enout=0;u8 type=0;//默认是数字u8 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>='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++;			//偏移到转义符后面的字符,不管什么字符,直接COPYi++;}					*fparm=*str;		//小写或者数字保持不变fparm++;}	}i++;//偏移量增加str++;}*fparm='\0';	//加入结束符*ptype=type;	//返回参数类型return i;		//返回参数长度
}
//得到指定参数的起始地址
//num:第num个参数,范围0~9.
//返回值:该参数的起始地址
u8 usmart_get_parmpos(u8 num)
{u8 temp=0;u8 i;for(i=0;i<num;i++)temp+=usmart_dev.plentbl[i];return temp;
}
//从str中得到函数参数
//str:源字符串;
//parn:参数的多少.0表示无参数 void类型
//返回值:0,成功;其他,错误代码.
u8 usmart_get_fparam(u8*str,u8 *parn)
{	u8 i,type;  u32 res;u8 n=0;u8 len;u8 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;		//参数错误.*(u32*)(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,&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;//正确得到了参数
}

2.4 usmart.c源文件

#include "usmart.h"
#include "usart.h"
//#include "sys.h" //系统命令
u8 *sys_cmd_tab[]=
{"?","help","list","id","hex","dec","runtime",	   
};	    
//处理系统指令
//0,成功处理;其他,错误代码;
u8 usmart_sys_cmd_exe(u8 *str)
{u8 i;u8 sfname[MAX_FNAME_LEN];//存放本地函数名u8 pnum;u8 rval;u32 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://帮助指令printf("\r\n");
#if USMART_USE_HELPprintf("------------------------USMART V3.1------------------------ \r\n");printf("    USMART是由ALIENTEK开发的一个灵巧的串口调试互交组件,通过 \r\n");printf("它,你可以通过串口助手调用程序里面的任何函数,并执行.因此,你可\r\n");printf("以随意更改函数的输入参数(支持数字(10/16进制)、字符串、函数入\r\n");	  printf("口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返 \r\n");printf("回值显示.新增参数显示进制设置功能,新增进制转换功能.\r\n");printf("技术支持:www.openedv.com\r\n");printf("USMART有7个系统命令:\r\n");printf("?:      获取帮助信息\r\n");printf("help:   获取帮助信息\r\n");printf("list:   可用的函数列表\r\n\n");printf("id:     可用函数的ID列表\r\n\n");printf("hex:    参数16进制显示,后跟空格+数字即执行进制转换\r\n\n");printf("dec:    参数10进制显示,后跟空格+数字即执行进制转换\r\n\n");printf("runtime:1,开启函数运行计时;0,关闭函数运行计时;\r\n\n");printf("请按照程序编写格式输入函数名及参数并以回车键结束.\r\n");    printf("--------------------------ALIENTEK------------------------- \r\n");
#elseprintf("指令失效\r\n");
#endifbreak;case 2://查询指令printf("\r\n");printf("-------------------------函数清单--------------------------- \r\n");for(i=0;i<usmart_dev.fnum;i++)printf("%s\r\n",usmart_dev.funs[i].name);printf("\r\n");break;	 case 3://查询IDprintf("\r\n");printf("-------------------------函数 ID --------------------------- \r\n");for(i=0;i<usmart_dev.fnum;i++){usmart_get_fname((u8*)usmart_dev.funs[i].name,sfname,&pnum,&rval);//得到本地函数名 printf("%s id is:\r\n0X%08X\r\n",sfname,usmart_dev.funs[i].func); //显示ID}printf("\r\n");break;case 4://hex指令printf("\r\n");usmart_get_aparm(str,sfname,&i);if(i==0)//参数正常{i=usmart_str2num(sfname,&res);	   	//记录该参数	if(i==0)						  	//进制转换功能{printf("HEX:0X%X\r\n",res);	   	//转为16进制}else if(i!=4)return USMART_PARMERR;//参数错误.else 				   				//参数显示设定功能{printf("16进制参数显示!\r\n");usmart_dev.sptype=SP_TYPE_HEX;  }}else return USMART_PARMERR;			//参数错误.printf("\r\n"); break;case 5://dec指令printf("\r\n");usmart_get_aparm(str,sfname,&i);if(i==0)//参数正常{i=usmart_str2num(sfname,&res);	   	//记录该参数	if(i==0)						   	//进制转换功能{printf("DEC:%lu\r\n",res);	   	//转为10进制}else if(i!=4)return USMART_PARMERR;//参数错误.else 				   				//参数显示设定功能{printf("10进制参数显示!\r\n");usmart_dev.sptype=SP_TYPE_DEC;  }}else return USMART_PARMERR;			//参数错误. printf("\r\n"); break;	 case 6://runtime指令,设置是否显示函数执行时间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)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)printf("Run Time Calculation ON\r\n");else printf("Run Time Calculation OFF\r\n"); }}else return USMART_PARMERR;   			//未带参数,或者参数错误	 }else return USMART_PARMERR;				//参数错误. printf("\r\n"); break;	    default://非法指令return USMART_FUNCERR;}return 0;
}

//移植注意:本例是以stm32为例,如果要移植到其他mcu,请做相应修改.
//usmart_reset_runtime,清除函数运行时间,连同定时器的计数寄存器以及标志位一起清零.并设置重装载值为最大,以最大限度的延长计时时间.
//usmart_get_runtime,获取函数运行时间,通过读取CNT值获取,由于usmart是通过中断调用的函数,所以定时器中断不再有效,此时最大限度
//只能统计2次CNT的值,也就是清零后+溢出一次,当溢出超过2次,没法处理,所以最大延时,控制在:2*计数器CNT*0.1ms.对STM32来说,是:13.1s左右
//其他的:TIM4_IRQHandler和Timer4_Init,需要根据MCU特点自行修改.确保计数器计数频率为:10Khz即可.另外,定时器不要开启自动重装载功能!!#if USMART_ENTIMX_SCAN==1
//复位runtime
//需要根据所移植到的MCU的定时器参数进行修改
void usmart_reset_runtime(void)
{TIM_ClearFlag(TIM4,TIM_FLAG_Update);//清除中断标志位 TIM_SetAutoreload(TIM4,0XFFFF);//将重装载值设置到最大TIM_SetCounter(TIM4,0);		//清空定时器的CNTusmart_dev.runtime=0;	
}
//获得runtime时间
//返回值:执行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms
//需要根据所移植到的MCU的定时器参数进行修改
u32 usmart_get_runtime(void)
{if(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update)==SET)//在运行期间,产生了定时器溢出{usmart_dev.runtime+=0XFFFF;}usmart_dev.runtime+=TIM_GetCounter(TIM4);return usmart_dev.runtime;		//返回计数值
}  
//下面这两个函数,非USMART函数,放到这里,仅仅方便移植. 
//定时器4中断服务程序	 
void TIM4_IRQHandler(void)
{ 		    		  			    if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)//溢出中断{usmart_dev.scan();	//执行usmart扫描	TIM_SetCounter(TIM4,0);		//清空定时器的CNTTIM_SetAutoreload(TIM4,100);//恢复原来的设置		    				   				     	    	}				   TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  //清除中断标志位    
}
//使能定时器4,使能中断.
void Timer4_Init(u16 arr,u16 psc)
{NVIC_InitTypeDef   NVIC_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);  ///使能TIM4时钟TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInitStructure.TIM_Period=arr;   //自动重装载值TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//初始化定时器4TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //允许定时器4更新中断TIM_Cmd(TIM4,ENABLE); //使能定时器4NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//外部中断4NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;//子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);//配置NVIC}
#endif

//初始化串口控制器
//sysclk:系统时钟(Mhz)
void usmart_init(u8 sysclk)
{
#if USMART_ENTIMX_SCAN==1Timer4_Init(1000,(u32)sysclk*100-1);//分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步.
#endifusmart_dev.sptype=1;	//十六进制显示参数
}		
//从str中获取函数名,id,及参数信息
//*str:字符串指针.
//返回值:0,识别成功;其他,错误代码.
u8 usmart_cmd_rec(u8*str) 
{u8 sta,i,rval;//状态	 u8 rpnum,spnum;u8 rfname[MAX_FNAME_LEN];//暂存空间,用于存放接收到的函数名  u8 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((u8*)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;
}
//usamrt执行函数
//该函数用于最终执行从串口收到的有效函数.
//最多支持10个参数的函数,更多的参数支持也很容易实现.不过用的很少.一般5个左右的参数的函数已经很少见了.
//该函数会在串口打印执行情况.以:"函数名(参数1,参数2...参数N)=返回值".的形式打印.
//当所执行的函数没有返回值的时候,所打印的返回值是一个无意义的数据.
void usmart_exe(void)
{u8 id,i;u32 res;		   u32 temp[MAX_PARM];//参数转换,使之支持了字符串 u8 sfname[MAX_FNAME_LEN];//存放本地函数名u8 pnum,rval;id=usmart_dev.id;if(id>=usmart_dev.fnum)return;//不执行.usmart_get_fname((u8*)usmart_dev.funs[id].name,sfname,&pnum,&rval);//得到本地函数名,及参数个数 printf("\r\n%s(",sfname);//输出正要执行的函数名for(i=0;i<pnum;i++)//输出参数{if(usmart_dev.parmtype&(1<<i))//参数是字符串{printf("%c",'"');			 printf("%s",usmart_dev.parm+usmart_get_parmpos(i));printf("%c",'"');temp[i]=(u32)&(usmart_dev.parm[usmart_get_parmpos(i)]);}else						  //参数是数字{temp[i]=*(u32*)(usmart_dev.parm+usmart_get_parmpos(i));if(usmart_dev.sptype==SP_TYPE_DEC)printf("%lu",temp[i]);//10进制参数显示else printf("0X%X",temp[i]);//16进制参数显示 	   }if(i!=pnum-1)printf(",");}printf(")");usmart_reset_runtime();	//计时器清零,开始计时switch(usmart_dev.pnum){case 0://无参数(void类型)											  res=(*(u32(*)())usmart_dev.funs[id].func)();break;case 1://有1个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0]);break;case 2://有2个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1]);break;case 3://有3个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2]);break;case 4://有4个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3]);break;case 5://有5个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4]);break;case 6://有6个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\temp[5]);break;case 7://有7个参数res=(*(u32(*)())usmart_dev.funs[id].func)(temp[0],temp[1],temp[2],temp[3],temp[4],\temp[5],temp[6]);break;case 8://有8个参数res=(*(u32(*)())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=(*(u32(*)())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=(*(u32(*)())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;}usmart_get_runtime();//获取函数执行时间if(rval==1)//需要返回值.{if(usmart_dev.sptype==SP_TYPE_DEC)printf("=%lu;\r\n",res);//输出执行结果(10进制参数显示)else printf("=0X%X;\r\n",res);//输出执行结果(16进制参数显示)	   }else printf(";\r\n");		//不需要返回值,直接输出结束if(usmart_dev.runtimeflag)	//需要显示函数执行时间{ printf("Function Run Time:%d.%1dms\r\n",usmart_dev.runtime/10,usmart_dev.runtime%10);//打印函数执行时间 }	
}
//usmart扫描函数
//通过调用该函数,实现usmart的各个控制.该函数需要每隔一定时间被调用一次
//以及时执行从串口发过来的各个函数.
//本函数可以在中断里面调用,从而实现自动管理.
//如果非ALIENTEK用户,则USART_RX_STA和USART_RX_BUF[]需要用户自己实现
void usmart_scan(void)
{u8 sta,len;  if(USART_RX_STA&0x8000)//串口接收完成?{					   len=USART_RX_STA&0x3fff;	//得到此次接收到的数据长度USART_RX_BUF[len]='\0';	//在末尾加入结束符. sta=usmart_dev.cmd_rec(USART_RX_BUF);//得到函数各个信息if(sta==0)usmart_dev.exe();	//执行函数 else {  len=usmart_sys_cmd_exe(USART_RX_BUF);if(len!=USMART_FUNCERR)sta=len;if(sta){switch(sta){case USMART_FUNCERR:printf("函数错误!\r\n");   			break;	case USMART_PARMERR:printf("参数错误!\r\n");   			break;				case USMART_PARMOVER:printf("参数太多!\r\n");   			break;		case USMART_NOFUNCFIND:printf("未找到匹配的函数!\r\n");   			break;		}}}USART_RX_STA=0;//状态寄存器清空	    }
}#if USMART_USE_WRFUNS==1 	//如果使能了读写操作
//读取指定地址的值		 
u32 read_addr(u32 addr)
{return *(u32*)addr;//	
}
//在指定地址写入指定的值		 
void write_addr(u32 addr,u32 val)
{*(u32*)addr=val; 	
}
#endif

2.5 usmart_config.c源文件

配置具体需要实现功能的函数

#include "usmart.h"
#include "usmart_str.h"
用户配置区///
//这下面要包含所用到的函数所申明的头文件(用户自己添加) 
#include "delay.h"		
#include "sys.h"
#include "lcd.h"extern void led_set(u8 sta);
extern void test_fun(void(*ledset)(u8),u8 sta);
//函数名列表初始化(用户自己添加)
//用户直接在这里输入要执行的函数名及其查找串
struct _m_usmart_nametab usmart_nametab[]=
{
#if USMART_USE_WRFUNS==1 	//如果使能了读写操作(void*)read_addr,"u32 read_addr(u32 addr)",(void*)write_addr,"void write_addr(u32 addr,u32 val)",	 
#endif		   (void*)delay_ms,"void delay_ms(u16 nms)",(void*)delay_us,"void delay_us(u32 nus)",	 (void*)LCD_Clear,"void LCD_Clear(u16 Color)",(void*)LCD_Fill,"void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)",(void*)LCD_DrawLine,"void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)",(void*)LCD_DrawRectangle,"void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)",(void*)LCD_Draw_Circle,"void Draw_Circle(u16 x0,u16 y0,u8 r)",(void*)LCD_ShowNum,"void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size)",(void*)LCD_ShowString,"void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)",(void*)LCD_Fast_DrawPoint,"void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color)",(void*)LCD_ReadPoint,"u16 LCD_ReadPoint(u16 x,u16 y)",							 (void*)LCD_Display_Dir,"void LCD_Display_Dir(u8 dir)",(void*)LCD_ShowxNum,"void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)",(void*)led_set,"void led_set(u8 sta)",(void*)test_fun,"void test_fun(void(*ledset)(u8),u8 sta)",				  	    
};						  
///END///
/
//函数控制管理器初始化
//得到各个受控函数的名字
//得到函数总数量
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,	 	//函数ID1,		//参数显示类型,0,10进制;1,16进制0,		//参数类型.bitx:,0,数字;1,字符串	    0,	  	//每个参数的长度暂存表,需要MAX_PARM个0初始化0,		//函数的参数,需要PARM_LEN个0初始化
};   

三、USMART的实现

3.1 USMART的实现流程

①第一步,添加需要调用的函数(在 usmart_config.c 里面的 usmart_nametab 数组里面添加),如:

(void*)led_set,"void led_set(u8 sta)",

其中led_set函数定义如下

//LED状态设置函数
void led_set(u8 sta)
{LED1=sta;
} 

②第二步,主函数初始化串口;
③第三步,初始化 USMART(通过usmart_init 函数实现),如下

usmart_dev.init(84); 	//初始化USMART	

  此初始化主要配置usmart.c源文件中的定时器函数Timer4_Init,然后在定时器中断里不停的调用usmart_scan扫描函数

④第四步,轮询 usmart_scan 函数,处理串口数据。

3.2 USMART的调用

通过串口助手与单片机不停的收发数据
在这里插入图片描述
上图中 list、id、?、help、hex、dec 和 runtime 都属于 usmart 自带的系统命令

命令作用
list用于打印所有 usmart 可调用函数
id用于获取各个函数的入口地址
help(或者‘ ?’)打印 usmart 使用的帮助信息
hex 和 dec当不带参数的时候,hex 和 dec 分别用于设置串口显示数据格式为 16 进制/10 进制。当带参数的时候,hex 和 dec 就执行进制转换
runtime用于函数执行时间统计功能的开启和关闭

http://chatgpt.dhexx.cn/article/FLf88QD7.shtml

相关文章

USMART

USMART是正点原子团队为其STM32开发平台开发的一种类似linux的shell的调试工具。具体工作过程是通过串口发送命令给单片机,然后单片机收到命令之后调用单片机里面对应的相关函数,并执行,同时支持返回结果。 普通的做法&#xff1a;写函数 ->修改参数->下载->看结果-&…

USMART调试组件

什么是USMART? USMART是正点原子团队为其STM32开发平台开发的一种类似linux的shell的调试工具。具体工作过程是通过串口发送命令给单片机,然后单片机收到命令之后调用单片机里面对应的相关函数,并执行,同时支持返回结果。 USMART调试过程:&#xff1a; USMART应用场景&#x…

2021-07-13 Charles破解

Charles破解码&#xff1a;https://play.golang.org/p/Qtt2CmHbTzU 使用方法&#xff1a;浏览器打开运行代码&#xff0c;复制name和key即可 注&#xff1a;如有侵权请联系删除&#xff01;

charles破解历程

题记 看文章看到javassist可以直接修改java字节码&#xff0c;之前没有尝试过&#xff0c;因为charles是用java写的跨平台抓包工具&#xff0c;之前我也用过&#xff0c;所以拿来进行测试&#xff01; 简介 Javassist是一个开源的分析、编辑和创建Java字节码的类库。 Javas…

Charles4.62破解版本下载

第一种方式&#xff1a; 这种方式下载后&#xff0c;还要自己输入许可证 链接 提取码&#xff1a;2vb0 添用激活码计算器生成激活码 点击这个&#xff0c;把刚才创建的名字和许可证输入进去即可 然后关闭软件&#xff0c;重新打开即可 第二种方式&#xff1a; https://ww…

charles破解 mac

下载charles并安装 https://www.charlesproxy.com/latest-release/download.do 下载charles破解包charles.jar https://www.zzzmode.com/mytools/charles/ 然后用下载的charles.jar替换原先的charles即可 文件路径/contents/java/charles.jar

charles破解https请求

当你的app包从http升级到https的时候&#xff0c;是不是忽然间发现你的请求抓不到了呢&#xff1f;别担心&#xff0c;只是因为你们的app加密升级了&#xff0c;但是我们还是可以正常破解的。接下来直接谈破解步骤啦&#xff1a; 1&#xff1a;首先打开charles,在其主tab栏点击…

Charles破解方法

// Charles Proxy License // 适用于Charles任意版本的注册码&#xff0c;谁还会想要使用破解版呢。 // Charles 4.2目前是最新版&#xff0c;可用。 Registered Name: https://zhile.io License Key: 48891cf209c6d32bf4 本方法通杀charlse系列激活问题。 破解方式&#…

mac抓包工具charles破解版安装及简单使用

在windows上一般使用fiddler进行抓包&#xff0c;但是在mac上就显得很僵硬&#xff0c;所以通过查阅资料&#xff0c;总算安装并且配置好了同样强大的charles&#xff0c;遂在此总结。 首先附上破解版的下载地址 链接:https://pan.baidu.com/s/1yHH-958uLiuXvQbdvGegOQ 密码:y…

Charles破解三十分钟重启

使用charles的用户应该都知道&#xff0c;这个charles是一款收费软件&#xff0c;启动时得等待十秒&#xff0c;然后每过半个小时还会重启&#xff0c;真难受 下面时解决的方法&#xff0c;这个方法不是原厂&#xff0c;指示本人了解到的&#xff0c;一切与本人无关哦&#xf…

MAC安装Charles破解版简易教程

Charles&#xff1a;青花瓷抓包工具。 官网下载&#xff1a;https://www.charlesproxy.com/download/&#xff08;可惜限制了30天的使用期限&#xff09; 破解版下载&#xff1a;https://download.csdn.net/download/fifteen718/10774499 破解版安装方式&#xff1a; 通过上…

MAC Charles 抓包工具安装以及使用方法(带破解)

1.Charles客户端下载&#xff1a; 官网地址&#xff1a;https://www.charlesproxy.com/download/ 选择适合自己的系统版本下载 2.下载安装完成后激活 激活网站地址&#xff1a;https://www.zzzmode.com/mytools/charles/ 打开安装好的Charles&#xff0c;菜单栏 Help->Re…

抓包工具Charles —— 破解、抓包入门

2019独角兽企业重金招聘Python工程师标准>>> Charles工具官网&#xff1a;https://www.charlesproxy.com/ Charles工具下载地址&#xff1a;https://www.charlesproxy.com/download/ Charles破解包下载地址&#xff1a;http://pan.baidu.com/s/1boV4w2r 密码:kqtj 一…

一招破解抓包工具Charles,并进行移动APP抓包分析

一、简介 Charles是目前最强大最流行的http抓包调试工具&#xff0c;Mac、Unix、Windows各个平台都支持。特别是做APP开发&#xff0c;调试与服务端的通信&#xff0c;Charles是必备工具。 目前Charles是收费的&#xff0c;不过可以破解。 1.Charles下载地址&#xff1a; ht…

charles 抓包软件 安装、使用

记录一下charles的简单使用 安装 charles 官网下载&#xff0c;点击跳转到官网下载地址 我使用的是v4.6.3 windows版 破解 链接: 点击跳转到破解页面 在charles里面点击 HELP —>REGISTER 输入名称和生成的密钥即可 安装证书&#xff08;用于抓取https请求&#xff0…

Charles的破解以及Charles乱码问题处理

一. 简介及安装 一、charles的使用 1.1 charles的说明 Charles其实是一款代理服务器&#xff0c;通过过将自己设置成系统&#xff08;电脑或者浏览器&#xff09;的网络访问代理服务器&#xff0c;然后截取请求和请求结果达到分析抓包的目的。该软件是用Java写的&#xff0…

串级调节系统参数整定方法(串级调节器参数整定)

串级调节系统参数整定方法&#xff08;串级调节器参数整定&#xff09; 两步法整定串级调节系统PID参数一步整定法整定串级调节系统PID参数 串级控制系统由单回路PID调节器(作为主调节器)和外给定调节器(作为副调节器)彼此串接组成双回路调节系统&#xff0c;主调节器的控制输出…

基于Simulink的Ziegler-Nichols PID参数经验整定法

Ziegler-Nichols整定法适用对象为带纯延迟的一阶惯性环节&#xff0c;即G(s)K*e^(-τs)/(Ts1) 其中&#xff0c;K为比例系数&#xff1b;T为惯性时间常数&#xff1b;τ为纯延迟时间常数。 当被控对象的单位阶跃响应曲线看起来近似一条S形曲线时&#xff0c;可用Ziegler-Nicho…

PID控制参数整定口诀

0. PID控制系统 1. PID调参口诀 参数整定找最佳&#xff0c;从小到大顺序查 先是比例后积分&#xff0c;最后再把微分加。 P-I-D 曲线振荡很频繁&#xff0c;比例度盘要放大 曲线漂浮绕大湾&#xff0c;比例度盘往小扳 曲线偏离回复慢&#xff0c;积分时间往下降 曲线波…