串口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模式配置分析解读
发布评论