51单片机串口任意长度数据帧接收
在单片机的串口通信中,经常会遇到不定长数据帧的接收问题,处理这个问题一般采用超时判断机制,即在一定的时长之后若还未接收到数据,则认为该帧数据接收完毕。在目前较新型的单片机中,都会在配置串口时提供相关的寄存器,其中就包含有超时机制。但在一些较老的单片机(特别是8位机)中,都没有提供这类配置寄存器,所以只能依靠软件来实现超时判断,下面就以51单片机为例,讨论一下如何实现串口任意长度数据帧的接收。
在51单片机中,串口通信若使用可变波特率方式则要占用定时器T1,所以使用超时定时的时候,就只能选择定时器T0了。接下来讨论一下如何确定超时时间的长度,下图是51单片机中串口的单帧数据结构。
在51中数据长度有8位和9位两种形式,这里就以最长的9位形式为例。假设波特率是9600,即第秒能产生9600个位,那么可以算出,以上数据帧共11位所需要的时长约为1.2ms左右。考虑到中断时延等因素,这里取2ms。该时长不要取得太长,否则可能引起两个数据帧之间的交联。
本例接收来自串口的ASCII码数据,然后把整帧数据显示到LCD1602屏上,单帧数据最长32字节。代码如下。
#include <reg51.h>
#include "lcd1602.h"
unsigned char gotData[32],i=0x00,j=0x00;
bit ready=0;
void main(void)
{
SCON |= 0x50; //串口方式1、接收
TMOD |= 0x21; //T1方式2、T0方式1
TH1 = 0xfd;
TL1 = 0xfd; //波特率9600
IE |= 0x92; //开启中断
TR1 = 1; //开启T1
LcdInit(DOUBLE, INC, NOSHIFT, OPEN, NOSHOW, BLINK);//LCD初始化
while(1)
{
if(ready) //完整数据帧接收完毕
{
LcdWriteCommand(0x01, 1); //清屏
ePutstr(0,0,gotData); //显示接收到的内容
do
{
gotData[j] = 0;
}while(j--); //接收到的内容清零
ready = 0; //清零接收完成标志
}
}
}
void uart(void) interrupt 4
{
if(RI) //是接收中断
{
RI = 0; //清零接收中断标志位
TH0 = 0xf8;
TL0 = 0x2f; //2ms定时
TR0 = 1; //开启T0
gotData[i++] = SBUF; //读取收到的字节
}
}
void timer0(void) interrupt 1
{
TR0 = 0; //关闭T0
j = i; //暂存接收到的数据帧长度
i = 0x00; //长度清零
ready = 1; //置位接收完成标志
}
上述代码中的LCD1602部分可参见以前的博文。通过试验,实现了从1个字符到32个字符的接收显示,接收非常稳定,并未出现数据丢失的情况。