一、串口SHELL模式配置分析解读

编程入门 行业动态 更新时间:2024-10-23 19:20:47

一、<a href=https://www.elefans.com/category/jswz/34/1769224.html style=串口SHELL模式配置分析解读"/>

一、串口SHELL模式配置分析解读

一、串口SHELL模式:
配置串口shell的原理:该配置是通过定时器中断进行计数操作,同时在定时器计数中对串口的中断收到的数据进行检查。

串口头文件配置:

#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收extern uint8_t  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern uint16_t USART_RX_STA;         		//接收状态标记	用户配置参数	  
#define MAX_FNAME_LEN 		30	//函数名最大长度,应该设置为不小于最长函数名的长度。											   
#define MAX_PARM 			10	//最大为10个参数 ,修改此参数,必须修改usmart_exe与之对应.
#define PARM_LEN 			200	//所有参数之和的长度不超过PARM_LEN个字节,注意串口接收部分要与之对应(不小于PARM_LEN)#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进制参数显示#define USMART_USE_HELP		1	//使用帮助,该值设为0,可以节省近700个字节,但是将导致无法显示帮助信息。
#define USMART_USE_WRFUNS	1	//使用读写函数,使能这里,可以读取任何地址的值,还可以写寄存器的值.
#define USMART_ENTIMX_SCAN 	1	//使用TIM的定时中断来扫描SCAN函数,如果设置为0,需要自己实现隔一段时间扫描一次scan函数.//函数名列表	 
struct _m_usmart_nametab
{void* func;			//函数指针const uint8_t* name;		//函数名(查找串)	 
};
//usmart控制管理器
struct _m_usmart_dev
{struct _m_usmart_nametab *funs;	//函数名指针void (*init)(uint8_t);				//初始化uint8_t (*cmd_rec)(uint8_t*str);			//识别函数名及参数void (*exe)(void); 				//执行 void (*scan)(void);             //扫描uint8_t fnum; 				  		//函数数量uint8_t pnum;                        //参数数量uint8_t id;							//函数iduint8_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[];	
extern struct _m_usmart_dev usmart_dev;	uint8_t usmart_get_parmpos(uint8_t num);						//得到某个参数在参数列里面的起始位置
uint8_t usmart_strcmp(uint8_t*str1,uint8_t *str2);					//对比两个字符串是否相等
uint32_t usmart_pow(uint8_t m,uint8_t n);							//M^N次方
uint8_t usmart_str2num(uint8_t*str,uint32_t *res);					//字符串转为数字
uint8_t usmart_get_cmdname(uint8_t*str,uint8_t*cmdname,uint8_t *nlen,uint8_t maxlen);//从str中得到指令名,并返回指令长度
uint8_t usmart_get_fname(uint8_t*str,uint8_t*fname,uint8_t *pnum,uint8_t *rval);		//从str中得到函数名
uint8_t usmart_get_aparm(uint8_t *str,uint8_t *fparm,uint8_t *ptype); 	//从str中得到一个函数参数
uint8_t usmart_get_fparam(uint8_t*str,uint8_t *parn);  			//得到str中所有的函数参数.	void usmart_init(uint8_t sysclk);//初始化
uint8_t usmart_cmd_rec(uint8_t*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);//在指定地址写入指定的值
uint32_t usmart_get_runtime(void);	//获取运行时间
void usmart_reset_runtime(void);//复位运行时间

串口文件配置:

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*)ti_i,"void ti_i(uint8_t on)",(void*)ti_o,"void ti_o(uint8_t on)",				
};//函数控制管理器初始化
//得到各个受控函数的名字
//得到函数总数量
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初始化
};   uint8_t *sys_cmd_tab[]=
{"?","help","list","id","hex","dec","runtime",	   
};	uint8_t USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
uint16_t USART_RX_STA=0;       //接收状态标记	
uint8_t aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲int fputc(int ch, FILE *f)
{ 	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   USART1->DR = (uint8_t) ch;      return ch;
}//串口初始化中添加HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量//初始化串口调试配置
void usmart_init(uint8_t sysclk)
{
#if USMART_ENTIMX_SCAN==1Timer4_Init(1000,(uint32_t)sysclk*100-1);//分频,时钟为10K ,100ms中断一次,注意,计数频率必须为10Khz,以和runtime单位(0.1ms)同步.
#endif	usmart_dev.sptype=1;	//十六进制显示参数
}	void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance==USART1)//如果是串口1{if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始else USART_RX_STA|=0x8000;	//接收完成了 }else //还没收到0X0D{	if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  }		 }}}
}//对比字符串str1和str2
//*str1:字符串1指针
//*str2:字符串2指针
//返回值:0,相等;1,不相等;
uint8_t usmart_strcmp(uint8_t *str1,uint8_t *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(uint8_t*str1,uint8_t *str2)
{while(1){										   *str2=*str1;	//拷贝if(*str1=='\0')break;//拷贝完成了.str1++;str2++;}
}
//得到字符串的长度(字节)
//*str:字符串指针
//返回值:字符串的长度		   
uint8_t usmart_strlen(uint8_t*str)
{uint8_t len=0;while(1){							 if(*str=='\0')break;//拷贝完成了.len++;str++;}return len;
}
//m^n函数
//返回值:m^n次方
uint32_t usmart_pow(uint8_t m,uint8_t n)
{uint32_t result=1;	 while(n--)result*=m;    return result;
}	    
//把字符串转为数字
//支持16进制转换,但是16进制字母必须是大写的,且格式为以0X开头的.
//支持负数 
//*str:数字字符串指针
//*res:转换完的结果存放地址.
//返回值:0,成功转换完成.其他,错误代码.
//1,数据格式错误.2,16进制位数为0.3,起始格式错误.4,十进制位数为0.
uint8_t usmart_str2num(uint8_t*str,uint32_t *res)
{uint32_t t;int tnum;uint8_t bnum=0;		//数字的位数uint8_t *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;//成功转换
}
//得到指令名
//*str:源字符串
//*cmdname:指令名
//*nlen:指令名长度		
//maxlen:最大长度(做限制,指令不可能太长的)	
//返回值:0,成功;其他,失败.	  
uint8_t usmart_get_cmdname(uint8_t*str,uint8_t*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;//正常返回
}
//获取下一个字符(当中间有很多空格的时候,此函数直接忽略空格,找到空格之后的第一个字符)
//str:字符串指针	
//返回值:下一个字符
uint8_t usmart_search_nextc(uint8_t* str)
{		   	 	str++;while(*str==' '&&str!='\0')str++;return *str;
} 
//从str中得到函数名
//*str:源字符串指针
//*fname:获取到的函数名字指针
//*pnum:函数的参数个数
//*rval:是否需要显示返回值(0,不需要;1,需要)
//返回值:0,成功;其他,错误代码.
uint8_t usmart_get_fname(uint8_t*str,uint8_t*fname,uint8_t *pnum,uint8_t *rval)
{uint8_t res;uint8_t fover=0;	  //括号深度uint8_t *strtemp;uint8_t offset=0;  uint8_t parmnum=0;uint8_t temp=1;uint8_t 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;		//返回执行结果
}//从str中得到一个函数的参数
//*str:源字符串指针
//*fparm:参数字符串指针
//*ptype:参数类型 0,数字;1,字符串;0XFF,参数错误
//返回值:0,已经无参数了;其他,下一个参数的偏移量.
uint8_t usmart_get_aparm(uint8_t *str,uint8_t *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++;			//偏移到转义符后面的字符,不管什么字符,直接COPYi++;}					*fparm=*str;		//小写或者数字保持不变fparm++;}	}i++;//偏移量增加str++;}*fparm='\0';	//加入结束符*ptype=type;	//返回参数类型return i;		//返回参数长度
}
//得到指定参数的起始地址
//num:第num个参数,范围0~9.
//返回值:该参数的起始地址
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;
}
//从str中得到函数参数
//str:源字符串;
//parn:参数的多少.0表示无参数 void类型
//返回值:0,成功;其他,错误代码.
uint8_t usmart_get_fparam(uint8_t*str,uint8_t *parn)
{	uint8_t i,type;  uint32_t res;uint8_t n=0;uint8_t len;uint8_t 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,&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;//正确得到了参数
}//处理系统指令
//0,成功处理;其他,错误代码;
uint8_t usmart_sys_cmd_exe(uint8_t *str)
{uint8_t i;uint8_t 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://帮助指令printf("\r\n");
#if USMART_USE_HELP printf("------------------------USMART V3.3------------------------ \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\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((uint8_t*)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;
}//从str中获取函数名,id,及参数信息
//*str:字符串指针.
//返回值:0,识别成功;其他,错误代码.
uint8_t usmart_cmd_rec(uint8_t*str) 
{uint8_t sta,i,rval;//状态	 uint8_t rpnum,spnum;uint8_t rfname[MAX_FNAME_LEN];//暂存空间,用于存放接收到的函数名  uint8_t 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((uint8_t*)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)
{uint8_t id,i;uint32_t res;		   uint32_t temp[MAX_PARM];//参数转换,使之支持了字符串 uint8_t sfname[MAX_FNAME_LEN];//存放本地函数名uint8_t pnum,rval;id=usmart_dev.id;if(id>=usmart_dev.fnum)return;//不执行.usmart_get_fname((uint8_t*)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]=(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)printf("%ld",temp[i]);//10进制参数显示else printf("0X%X",temp[i]);//16进制参数显示 	   }if(i!=pnum-1)printf(",");}printf(")");
#if USMART_ENTIMX_SCAN==1usmart_reset_runtime();	//计时器清零,开始计时
#endifswitch(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==1usmart_get_runtime();//获取函数执行时间
#endifif(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)
{uint8_t 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 	//如果使能了读写操作
//读取指定地址的值		 
uint32_t read_addr(uint32_t addr)
{return *(uint32_t*)addr;//	
}
//在指定地址写入指定的值		 
void write_addr(uint32_t addr,uint32_t val)
{*(uint32_t*)addr=val; 	
}
#endifvoid USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 */uint32_t timeout=0;/* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 */while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)//等待就绪{timeout++;超时处理if(timeout>HAL_MAX_DELAY) break;		}timeout=0;while(HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1{timeout++; //超时处理if(timeout>HAL_MAX_DELAY) break;	}/* USER CODE END USART1_IRQn 1 */
}

对定时器配置:

void Timer4_Init(uint16_t arr,uint16_t psc);
uint32_t usmart_get_runtime(void);void Timer4_Init(uint16_t arr,uint16_t psc)
{ htim4.Instance=TIM4;                          //通用定时器4htim4.Init.Prescaler=psc;                     //分频htim4.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器htim4.Init.Period=arr;                        //自动装载值htim4.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;HAL_TIM_Base_Init(&htim4);HAL_TIM_Base_Start_IT(&htim4); //使能定时器4和定时器4中断 					 
}void usmart_reset_runtime(void)
{__HAL_TIM_CLEAR_FLAG(&htim4,TIM_FLAG_UPDATE);//清除中断标志位 __HAL_TIM_SET_AUTORELOAD(&htim4,0XFFFF);     //将重装载值设置到最大__HAL_TIM_SET_COUNTER(&htim4,0);             //清空定时器的CNTusmart_dev.runtime=0;	
}uint32_t usmart_get_runtime(void)
{if(__HAL_TIM_GET_FLAG(&htim4,TIM_FLAG_UPDATE)==SET)//在运行期间,产生了定时器溢出{usmart_dev.runtime+=0XFFFF;}usmart_dev.runtime+=__HAL_TIM_GET_COUNTER(&htim4);return usmart_dev.runtime;		//返回计数值
}  void TIM4_IRQHandler(void)
{/* USER CODE BEGIN TIM4_IRQn 0 */if(__HAL_TIM_GET_IT_SOURCE(&htim4,TIM_IT_UPDATE)==SET)//溢出中断{usmart_dev.scan();	//执行usmart扫描__HAL_TIM_SET_COUNTER(&htim4,0);;    //清空定时器的CNT__HAL_TIM_SET_AUTORELOAD(&htim4,100);//恢复原来的设置}__HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);//清除中断标志位/* USER CODE END TIM4_IRQn 0 *///HAL_TIM_IRQHandler(&htim4);/* USER CODE BEGIN TIM4_IRQn 1 *//* USER CODE END TIM4_IRQn 1 */
}

更多推荐

一、串口SHELL模式配置分析解读

本文发布于:2024-02-25 09:17:17,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1698491.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:串口   模式   SHELL

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!