STM32 USMART源码理解


本文是基于正点原子USMART源码理解。

该组件由 ALIENTEK 开发提供,功能类似 linux 的 shell(RTT 的 finsh 也属于此类)。USMART 最主要 的功能就是通过串口调用单片机里面的函数,并执行,对我们调试代码是很有帮助的。

USMART 是由 ALIENTEK 开发的一个灵巧的串口调试互交组件,通过它你可以通过串口 助手调用程序里面的任何函数,并执行。因此,你可以随意更改函数的输入参数(支持数字(10/16 进制)、字符串、函数入口地址等作为参数),单个函数最多支持 10 个输入参数,并支持函数返 回值显示,目前最新版本为 V3.1。

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

有了 USMART,你可以轻易的修改函数参数、查看函数运行结果,从而快速解决问题。比 如你调试一个摄像头模块,需要修改其中的几个参数来得到最佳的效果;普通的做法:写函数 ->修改参数->下载->看结果->不满意->修改参数->下载->看结果->不满意….不停的循环,直到满 意为止。这样做很麻烦不说,单片机也是有寿命的啊,老这样不停的刷,很折寿的。而利用 USMART,则只需要在串口调试助手里面输入函数及参数,然后直接串口发送给单片机,就执 行了一次参数调整,不满意的话,你在串口调试助手修改参数在发送就可以了,直到你满意为 止。这样,修改参数十分方便,不需要编译、不需要下载、不会让单片机折寿。



结构体

//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;							//函数id
	u8 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
};
//函数控制管理器初始化
//得到各个受控函数的名字
//得到函数总数量
struct _m_usmart_dev usmart_dev=
{
	usmart_nametab,
	usmart_init,
	usmart_cmd_rec,
	usmart_exe,
	usmart_scan,
	sizeof(usmart_nametab)/sizeof(struct _m_usmart_nametab),//函数数量
	0,	  	//参数数量
	0,	 	//函数ID
	1,		//参数显示类型,0,10进制;1,16进制
	0,		//参数类型.bitx:,0,数字;1,字符串	    
	0,	  	//每个参数的长度暂存表,需要MAX_PARM个0初始化
	0,		//函数的参数,需要PARM_LEN个0初始化
};
 //函数名列表	 
struct _m_usmart_nametab
{
	void* func;			//函数指针
	const u8* name;		//函数名(查找串)	 
};

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*)led_set,"void led_set(u8 sta)",
	(void*)test_fun,"void test_fun(void(*ledset)(u8),u8 sta)",				  	    
	(void*)LCD_ReadPoint,"u16 LCD_ReadPoint(u16 x,u16 y)",
};	

usmart_scan

本函数,通过对串口接收的数据进行判断,如果有数据,那么执行函数cmd_rec,是一个结构体,成员是函数指针,那么也就是相当于运行函数usmart_cmd_rec,该函数获取函数名,id,参数个数,然后执行函数
usmart_exe,这个函数的功能是执行一些函数,比如执行延时函数delay_ms,最多允许10个参数的传递。

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);	//成功返回0
			if(len!=USMART_FUNCERR)sta=len;				//sta = 0
			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;//状态寄存器清空	    
	}
}

usmart_cmd_rec

本函数能够解析串口传入的字符串,比如delay_ms(u16 nms),这样,然后将函数名,参数分离。首先,通过调用函数usmart_get_fname得到函数名和参数个数,然后从本地函数中遍历,如果函数名和参数一致,那么记录该函数在本地函数中位置,并返回退出。其中usmart_get_fparam是得到函数参数的个数。

//从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);//得到接收到的数据的函数名及参数个数
	//读取成功,sta返回0
	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_fparam

本函数是通过对串口传入的参数进行解析,一开始先偏移到( 后的第一个字节进行解析判断,首先,调用函数usmart_get_aparm,得到第一个参数,之后再调用,肯定是第二个,第三个参数这样;比如第一个参数是delay_ms(1000),也就是延时1000ms。那么把1000保存下来即可。usmart_get_parmpos得到指定参数的起始地址,参数表示第几个参数,返回该参数的起始地址。plentbl保存参数的长度,这样,在一块连续的地址中,存的都是参数。

//从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)
	{
		//1000,2000,str函数名,delay_ms
		i=usmart_get_aparm(str,tstr,&type);	//得到第一个参数  
		str+=i;								//偏移
		switch(type)
		{
			case 0:	//数字
				if(tstr[0]!='\0')				//接收到的参数有效
				{					    
					i=usmart_str2num(tstr,&res);	//记录该参数,字符串转int 
					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;//正确得到了参数
}

usmart_get_fname

本函数,也是分析str,首先分析的是函数的返回值类型,一般有char,string,int,void等不超过5个长度的返回值类型,因此,若函数是void型,则标记不返回值;接着,找到函数名真正起始的位置,fname保存函数名,fover的作用是括号匹配,fpname应该只保存第一个参数,其它的没有保存;而且传入的参数也有可能是void,因此也进行了判断。

//从str中得到函数名
//*str:源字符串指针
//*fname:获取到的函数名字指针
//*pnum:函数的参数个数
//*rval:是否需要显示返回值(0,不需要;1,需要)
//返回值:0,成功;其他,错误代码.
//usmart_get_fname((u8*)usmart_dev.funs[id].name,sfname,&pnum,&rval);//得到本地函数名,及参数个数
//funs[id] = 具体哪一个函数,funs[id].name = 函数的名字,sfname = 一个u8类型的数组
//作用,记录函数名,参数个数和返回值
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;
	//判断函数是否有返回值
	//str = "void delay_ms(u16 nms)"
	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;	//0x85 = 1000 0101,高位标记,那么就是5
		strtemp++; 
	} 
	//返回类型,void,string,int,char等等,这里判断有没有返回值
	if(pcnt)//接收完了
	{
		fpname[pcnt&0x7f]='\0';//加入结束符
		if(usmart_strcmp(fpname,"void")==0)*rval=0;//不需要返回值
		else *rval=1;							   //需要返回值
		pcnt=0;
	} 
	res=0;
	strtemp=str;
	//str = "void delay_ms(1000)"
	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)
	{
		//str = "void delay_ms(1000)"
		/* 	*fname = delay_ms
		 * 	遇到(,fover++ = 1,这里fover的作用应该是括号匹配
		 * 	
		 */
		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;		//返回执行结果
}

usmart_get_aparm

本函数是获取一个参数。可能是字符型,也有可能是string类型的。

//从str中得到一个函数的参数
//*str:源字符串指针
//*fparm:参数字符串指针
//*ptype:参数类型 0,数字;1,字符串;0XFF,参数错误
//返回值:0,已经无参数了;其他,下一个参数的偏移量.
//1000,2000,str函数名,delay_ms,但是应该是1000,2000,因为已经移位到第一个字节了
u8 usmart_get_aparm(u8 *str,u8 *fparm,u8 *ptype)
{
	
	/*		void delay_ms(1500)
	 * 		delay_ms(0X5DC);
	 * 		Function Run Time:1500.0ms
	 * 		
	 * */
	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++;			//偏移到转义符后面的字符,不管什么字符,直接COPY
					i++;
				}					
				*fparm=*str;		//小写或者数字保持不变
				fparm++;
			}	
		}
		i++;//偏移量增加
		str++;
	}
	*fparm='\0';	//加入结束符
	*ptype=type;	//返回参数类型
	return i;		//返回参数长度
}

usmart_get_runtime

若产生定时器溢出,则运行时间加0xffff。

//获得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_exe

//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_str2num

//把字符串转为数字
//支持16进制转换,但是16进制字母必须是大写的,且格式为以0X开头的.
//不支持负数 
//*str:数字字符串指针
//*res:转换完的结果存放地址.
//返回值:0,成功转换完成.其他,错误代码.
//1,数据格式错误.2,16进制位数为0.3,起始格式错误.4,十进制位数为0.
u8 usmart_str2num(u8*str,u32 *res)
{
	//比如str = 12
	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)
	{
		//bnum = 2,bnum = 1,t = 1.res+=1*10=10,res+=10+2=12
		if(bnum)bnum--;
		if(*p<='9'&&*p>='0')t=*p-'0';	//得到数字的值
		else t=*p-'A'+10;				//得到A~F对应的值	    
		*res+=t*usmart_pow(hexdec,bnum);//返回值:m^n次方	   
		p++;
		if(*p=='\0')break;//数据都查完了.	
	}
	return 0;//成功转换
}

由于刚学USMART,以后肯定会经常需要用到USMART,到时候加上一点案例,进行更新。

猜你喜欢

转载自blog.csdn.net/qq_40318498/article/details/100163203
今日推荐