关于正点原子USMART的一些理解

功能简介:
有点类似shell指令一样,Linux下的shell是通过输入指令,然后shell脚本去按path路径去寻找相应指令,运行最先找到的那个指令文件。这里实现的机制则是通过串口输入指令,单片串口接受并分析指令,然后匹配对应的指令然后运行并反馈。到这里,这个程序倒是开拓了我的思路,以前没想过使用串口来控制单片机的,最多就是收发一些字符串。由于原子哥的视频长度有限,也可能是他们不想在语法上花费过多时间,所以讲解的就比较的简略,下面我将根据其运行的过程以及一些语法上做出一些解释。
流程分析
1.定时器中断的扫描函数

void TIM4_IRQHandler(void)
{ 		    		  			    
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)//溢出中断
	{
		usmart_dev.scan();	//执行usmart扫描	
		TIM_SetCounter(TIM4,0);		//清空定时器的CNT
		TIM_SetAutoreload(TIM4,100);//恢复原来的设置		    				   				     	    	
	}				   
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  //清除中断标志位    
}

原子哥在视频中说过这个的实现过程是通过定时器中断执行这个usmart的扫描函数,下面再看看扫描函数中的具体逻辑,如果在一个工程里面难以寻找到一个中断函数,可以在启动文件中寻找。

2.扫描函数

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;//状态寄存器清空	    
	}
}

USART_RX_STA是设定一个标志字节,bit7,6是判断是否接受完成并且接受数据是否正确,bit0~5则是接受的字符串长度,可以看出扫描函数的逻辑很简单就是接受数据然后判断数据,然后执行,接下来就是如何对命令进行处理以及运行命令
2.1 usmart处理串口数据

u8 usmart_cmd_rec(u8*str) 
{
	u8 sta,i,rval;//状态	 
	u8 rpnum,spnum;
	u8 rfname[MAX_FNAME_LEN];//暂存空间,用于存放接收到的函数名 ,
	//#define MAX_FNAME_LEN 		30 
	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;
}

这里有函数usmart_get_fname(str,rfname,&rpnum,&rval);//得到接收到的数据的函数名及参数个数,其原形是
u8 usmart_get_fname(u8str,u8fname,u8 *pnum,u8 *rval)
这里可以是对指针的一种经典用法,传入两个字符串的地址,然后进行操作,常见一些字符串操作函数,比如大家常用strpcy()之类的,传入参数,修改参数的用法还有更多更复杂的用法,但是其核心原理就是通过指针去修改指针所指向的内存区域,指针指向谁就修改谁,n级指针修改n-1级指针的内存区域,所以遇到指针多画内存的四区模型以后可能会更新,这里挖一个小坑。下面我们先介绍一下这个函数的具体逻辑,

//从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;
	//if(((pcnt&0x7f)==4)&&(*strtemp!='*'))break;//最后一个字符,必须是*
	//个人感觉这句话应该使用 || 
	//if(((pcnt&0x7f)==4)||(*strtemp ='*'))break;
	//这样当读完五个字符或者最后一个读是‘*’ 表示指针才算是把参数类型读完整了
	
	/*****************************/
	//判断函数是否有返回值
	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;
	} 
	/*****************************/
	//这一块就很简单了,就是跳过空格,不要被循环条件所迷惑了,主要就是跳过所有空格和'*' 主要看offset的值,他会是函数名的前一个位置
	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 
			{
				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;		//返回执行结果
}

重点先记住几个参数的意义,

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值