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个字符的接收显示,接收非常稳定,并未出现数据丢失的情况。 

posted @ 2022-05-14 16:09  fxzq  阅读(2944)  评论(0)    收藏  举报