微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 搞定单片机多字节串口接收

搞定单片机多字节串口接收

时间:11-19 来源:互联网 点击:

unsigned char boardAddr;//板选地址,通过检测几个io引脚,具体怎么得到的就不写了,很简单的unsigned char g_DatRev [10]={0};//接收缓存bit retFlag=0;//为1代表串口接收到了一帧数据

串口初始化函数,晶振22.1184

void init_uart(){SCON = 0x50;                 //串口方式1允许接收TMOD = 0x21;                //定时器1,方式2,8位自动重载,同时配置定时器0,工作方式1PCON = 0x80;                // 波特率加倍TH1 = 0xfa;TL1 = 0xfa;               //写入串口定时器初值TH0=(65536-2)/256;    //写入定时器0初值,串口传输一个字节时间为(1/19200)*10,计算得0.52msTL0=(65536-2)%256;   //定时器0定时大约1ms多EA=1;ET0=1;                  //波特率:19200    22.1184M  初值:250(0xfa)IE = 0x90;           TR1 = 1;                   }

串口中断函数

void UART_INT(void) interrupt 4{ static unsigned char count;//串口接收计数的变量RI = 0;g_DatRev[count] = SBUF;if(g_DatRev[count]==0xaa&&count==0)             //帧头{count=1;                                                 }else if(count==1&&g_DatRev[count]==0x55) {  count=2;          }else if (count==2&&g_DatRev[2] == boardAddr){ CK = g_DatRev[count];count=3;        }else if(count>=3&&count<9){                   CK += g_DatRev[count];count ++;}else if(count == 9&&CK==g_DatRev[9]){     ES = 0; retFlag = 1;count=0;            }            else{count=0;} resettimer();}

//判断count不为0的话就启动定时器void resettimer(){TR0=0;TH0=(65536-2)/256;TL0=(65536-2)%256;if(count!=0){TR0=1;}}定时器中断函数void T0_time()interrupt 1{     TR0=0;TH0=(65536-2)/256;TL0=(65536-2)%256;count=0;}

这种方法的确是本人自己想出来的,别人可能也这样做过,但我这个绝对不是抄袭或者模仿来的。这样写的确可以避免前面提到过的bug,不过代价是多用了一个定时器的资源,而且中断函数里的内容更多了,占用了更多的时间。

要是能把第一种方法改进一下就好了,主要是那个校验不能为aa的那个bug,因为毕竟传输到一半突然断了的可能性是非常小的。后来我想第一个判断if(count==0&&receive[count]==0xaa)好像有点太严格了,考虑到第二字节的帧头,跟板选地址不可能为aa,于是把这个改写为if(count>=0&&count<=2&& receive[count]==0xaa),这样就把bug出现的几率降到了非常小,也只是在前一帧结尾数据恰好为 aa 55 板选 的时候才出现,几率是多少大家自己算一下吧,。这样我自己觉得,昨天写的那种方法改进到这个程度,应该算可以啦,反正我是很满意了。

实际上我还想过其他的方法,比如缓存的数组采用移位寄存的方式。拿前面的4个字节的协议为例。

void ser()interrupt 4

{

unsigned char i;

RI=0;

for(i=0;i<3;i++)

{

receive[i]=receive[i+1];

}

receive[3]=SBUF;

if(reveive[0]==0xaa&&receive[1]==0x55&&receive[2]==receive[3])

{

ret_flag=1;

ES = 0;

}

}

这段代码看上去可是简单明了,这样判断可是不错啊,同时判断帧头跟校验不会产生前面提到的bug。说实话当时我刚想出这种方法并写出来的时候,马上就被我给否了。那个for循环可真是很占时间的啊,延时函数都是这样写的。每次都循环一下,这延时太长,通信速度太快的话就不能接收到下一字节数据了。最要命的是这个时间的长度是随着通信协议帧的字节数增加而增加的,如果一次要接收几十个字节,肯定就玩完了。这种方法我一次都没用过。

不过我居然又想出来了这种方法的改良措施,是前两天刚想出来的,,还没有实践过呢。

下面代码的协议就按第二段程序(定时器清零的那个协议,一共10字节)

全局变量

bit ret_flag;

unsigned char receive[256]={0};

unsigned char boardaddress;

中断函数

void ser()interrupt 4

{

static unsigned char i=0;

static unsigned char total=0;

RI=0;

receive[i]=SBUF;

total=total-receive[i-7]+receive[i-1];

if(receive[i-9]==0xaa&&receive[i-8]==0x55&& receive[i-7]==boardaddress&&receive[i]==total)

{

ret_flag=1;

ES = 0;

}

i++;

}

之所以要定义256个长度的数组,就是为了能够让数组“首尾相接”。因为0 -1 = 255 , 255+1 = 0。而且我在计算校验的时候也改进了算法,不会因为数据长度的增加而增加计算校验值的时间。这种方法也是我不久前才想出来的,所以还没有经过实际的验证。上面的代码可能会有逻辑上的错误,如果真有错误,有网友看出来的话,请在下面留言告诉我。这个方法也是我原创的哦,别人也肯能会想到,不过我这个绝对不是抄袭别人的。

上面的代码最大的缺点就是变量定义的太多了,太占ram资源了,编译的时候可能会出现错误,毕竟51单片机才128字节的ram(有的资源也很丰富的,比如c8051系列的),这一下子就是256字节的变量。不过对于资源多一些的单片机,这样写还是可以的

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top