在最近做的一个LED显示的项目中,所有的控制,数据和测试命令全部是由上位机用通讯方式下发的.由于之前对通讯问题没有很好重视,所以在这个项目中,认真的解决了这个问题.通讯报文的处理方式很多.状态机的方式有他的优点.一个是很迅速,一个是占用的内存很少,效率很高.在这里给出个例子.希望对大家有一点参考价值.
首先,设置通讯中断
void Timer2() interrupt 5 using 2
{ TF2=0; // 清定时器2溢出标志
LedScan(); //LED扫描中断服务程序
if (RI) // 判有无通信
ReadSbuf(); //接收处理
}
/*通讯初始化*/
void serial_init (void);//用户自编
/*以报文格式如下为例*/
#define RECID1 0 // 包头状态1
#define RECID2 1 // 包头状态2
#define RECADDR 2 // 地址
#define RECCMD 3 // 命令
#define RECBH 4 // 编号
#define RECLEN 5 // 长度
#define RECBUF 6 // 数据
#define RECEND 7 // 结束符
#define RECSUM 8 // 校验
/******************************************
ReadSbuf() 分析一帧条通信数据
******************************************/
void ReadSbuf(void)
{
com_rt=0; //485通讯方式,控制硬件处于接收状态
RI = 0;
switch(rState)
{ case RECID1: if(SBUF==2) // 启始字符1,一字节
rState = RECID2;
break;
case RECID2: if(SBUF==2) // 启始字符2,一字节
rState="RECADDR";
else
rState="RECID1";
break;
case RECADDR:rState = RECCMD; // 地址,一字节(暂没用)
len=SBUF;
break;
case RECCMD: if (SBUF==0x35)
{ APLD="0xeb";break; } // 给出再线编程标志
else
rState = RECBH; // 命令,一字节(暂没用)
dsp[rn].cn= 0;
break;
case RECBH: dsp[rn].kn=SBUF-0x30; // 编号,一字节
rState = RECLEN;
rk=0;len=0;
break;
case RECLEN: len <<=4; // 长度,四字节
len+=SBUF-0x30;
if(++rk>=4)
{ rk="0";len -= 7;
rState = RECBUF;
} break;
case RECBUF: dsp[rn].p[rk]=SBUF; // 接收字符 len-7个
if(++rk>=len)
rState = RECEND;
break;
case RECEND: if(SBUF==3) // 结束符
{ rState="RECSUM";
dsp[rn].p[rk]=0;rk=0;
sm |=com_tab[rn]; // 置对应显示位
if(++rn>7) rn="0";
}
else
rState="RECID1";
break;
case RECSUM: len=SBUF; // 读掉2字节校验和
if(++rk>=2)
rState="RECID1";
break;
default: len=SBUF;
rState=RECID1;
}
/*从上面的例子可以看出,每个中断接收一个字节后,都要判断该字节是否正确,如果正确,通过给rState赋值,使状态机向后跳转.也就是每次进入CASE语句时,只执行一种情况.如果报文中途出错,则回到原始状态*/