玩转单片机之三--串口通信,接收数据

发布者:快乐的小鸟最新更新时间:2016-09-12 来源: eefocus关键字:单片机  串口通信  接收数据 手机看文章 扫描二维码
随时随地手机看文章
#include

#define uchar unsigned char    //byte

#define uint unsigned int    //word

 

sbit led1=P0^0;     

sbit fir=P2^4; //fir=0;工作

sbit sec=P2^5; //sec=0;工作

sbit thi=P2^6; //thi=0;工作

sbit fot=P2^7; //fot=0;工作

 

 

uchar table[]={0x28,0xeb,0x32,0xa2,0xe1,0xa4,0x24,0xea,0x20,0xa0};

//P1=table[i];/* 0    1    2   3    4    5    6    7    8    9 */

static uchar dispbuf[5];

 

//动态显示数字的函数

void scandisp(void)

{

       unsigned int i;

       fir=0;

       P1=table[ dispbuf[0] ];

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

fir=1;

       sec=0;

       P1=table[ dispbuf[1] ];

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

sec=1;

       thi=0;

       P1=table[ dispbuf[2] ];

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

thi=1;

       fot=0;

       P1=table[ dispbuf[3] ];

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

fot=1;

}    

 

//十六进制转十进制存储

void HEX_TO_BCD(unsigned int n)

{

 

       dispbuf[3]=n/1000;

       dispbuf[2]=(n/100)%10;

       dispbuf[1]=(n/10)%10;

       dispbuf[0]=n%10; 

}

 

void main(void)

{    

     uchar a;

       uint mydata;

       mydata=0x00;

       TMOD=0x20;

       PCON=0x00;

       SCON=0x50;

       TL1=0xfd;

       TH1=0xfd;

       TR1=1;

       while(1)  //动态现实是接收的数据

       {     //如果没有接收到数据,RI=0,一直循环显示原值

              //如果有接收到数据,RI=1,跳出循环重新计算并再次进入循环

              HEX_TO_BCD(mydata);

              while(RI==0)  scandisp();

              RI=0;     //重新置0

              a=SBUF;   //从缓冲区获取数据

           mydata=a;

                    

              //HEX_TO_BCD(mydata);

              //scandisp();

              //SBUF=a;

              //while(TI==0)

              //TI=0;

       }

}

 

1. 自定义数据通信协议
    这里所说的数据协议是建立在物理层之上的通信数据包格式。所谓通信的物理层就是指我们通常所用到的RS232、RS485、红外、光纤、无线等等通信方式。在这个层面上,底层软件提供两个基本的操作函数:发送一个字节数据、接收一个字节数据。所有的数据协议全部建立在这两个操作方法之上。
通信中的数据往往以数据包的形式进行传送的,我们把这样的一个数据包称作为一帧数据。类似于网络通信中的TCPIP协议一般,比较可靠的通信协议往往包含有以下几个组成部分:帧头、地址信息、数据类型、数据长度、数据块、校验码、帧尾。
    帧头和帧尾用于数据包完整性的判别,通常选择一定长度的固定字节组成,要求是在整个数据链中判别数据包的误码率越低越好。减小固定字节数据的匹配机会,也就是说使帧头和帧尾的特征字节在整个数据链中能够匹配的机会最小。通常有两种做法,一、减小特征字节的匹配几率。二、增加特征字节的长度。通常选取第一种方法的情况是整个数据链路中的数据不具有随即性,数据可预测,可以通过人为选择帧头和帧尾的特征字来避开,从而减小特征字节的匹配几率。使用第二种方法的情况更加通用,适合于数据随即的场合。通过增加特征字节的长度减小匹配几率,虽然不能够完全的避免匹配的情况,但可以使匹配几率大大减小,如果碰到匹配的情况也可以由校验码来进行检测,因此这种情况在绝大多说情况下比较可靠。
    地址信息主要用于多机通信中,通过地址信息的不同来识别不同的通信终端。在一对多的通信系统中,可以只包含目的地址信息。同时包含源地址和目的地址则适用于多对多的通信系统。
    数据类型、数据长度和数据块是主要的数据部分。数据类型可以标识后面紧接着的是命令还是数据。数据长度用于指示有效数据的个数。
    校验码则用来检验数据的完整性和正确性。通常对数据类型、数据长度和数据块三个部分进行相关的运算得到。最简单的做法可是对数据段作累加和,复杂的也可以对数据进行CRC运算等等,可以根据运算速度、容错度等要求来选取。

2. 上位机和下位机中的数据发送
    物理通信层中提供了两个基本的操作函数,发送一个字节数据则为数据发送的基础。数据包的发送即把数据包中的左右字节按照顺序一个一个的发送数据而已。当然发送的方法也有不同。
    在单片机系统中,比较常用的方法是直接调用串口发送单个字节数据的函数。这种方法的缺点是需要处理器在发送过程中全程参与,优点是所要发送的数据能够立即的出现在通信线路上,能够立即被接收端接收到。另外一种方法是采用中断发送的方式,所有需要发送的数据被送入一个缓冲区,利用发送中断将缓冲区中的数据发送出去。这种方法的优点是占用处理器资源小,但是可能出现需要发送的数据不能立即被发送的情况,不过这种时延相当的小。对于51系列单片机,比较倾向于采用直接发送的方式,采用中断发送的方式比较占用RAM资源,而且对比直接发送来说也没有太多的优点。以下是51系列单片机中发送单个字节的函数。

void SendByte(unsigned char ch)
{
     SBUF = ch;
     while(TI == 0);
     TI = 0;
}
    上位机中关于串口通信的方式也有多种,这种方式不是指数据有没有缓冲的问题,而是操作串口的方式不同,因为PC上数据发送基本上都会被缓冲后再发送。对于编程来说操作串口有三种方式,一、使用windows系统中自带的串口通信控件,这种方式使用起来比较简单,需要注意的是接收时的阻塞处理和线程机制。二、使用系统的API直接进行串口数据的读取,在windows和linux系统中,设备被虚拟为文件,只需要利用系统提供的API函数即可进行串口数据的发送和读取。三、使用串口类进行串口操作。在此只介绍windows环境下利用串口类编程的方式。
CSerialPort是比较好用的串口类。它提供如下的串口操作方法:
void WriteToPort(char* string, int len);
    串口初始化成功后,调用此函数即可向串口发送数据。为了避免串口缓冲所带来的延时,可以开启串口的冲刷机制。

3. 下位机中的数据接收和协议解析
    下位机接收数据也有两种方式,一、等待接收,处理器一直查询串口状态,来判断是否接收到数据。二、中断接收。两种方法的优缺点在此前的一篇关于串口通信的文章中详细讨论过。得出的结论是采用中断接收的方法比较好。
    数据包的解析过程可以设置到不同的位置。如果协议比较简单,整个系统只是处理一些简单的命令,那么可以直接把数据包的解析过程放入到中断处理函数中,当收到正确的数据包的时候,置位相应的标志,在主程序中再对命令进行处理。如果协议稍微复杂,比较好的方式是将接收的数据存放于缓冲区中,主程序读取数据后进行解析。也有两种方式交叉使用的,比如一对多的系统中,首先在接收中断中解析“连接”命令,连接命令接收到后主程序进入设置状态,采用查询的方式来解析其余的协议。
    以下给出具体的实例。在这个系统中,串口的命令非常简单。所有的协议全部在串口中断中进行。数据包的格式如下:
0x55, 0xAA, 0x7E, 0x12, 0xF0, 0x02, 0x23, 0x45, SUM, XOR, 0x0D
    其中0x55, 0xAA, 0x7E为数据帧的帧头,0x0D为帧尾,0x12为设备的目的地址,0xF0为源地址,0x02为数据长度,后面接着两个数据0x23, 0x45,从目的地址开始结算累加、异或校验和,到数据的最后一位结束。
    协议解析的目的,首先判断数据包的完整性,正确性,然后提取数据类型,数据等数据,存放起来用于主程序处理。代码如下:
if(state_machine == 0)       // 协议解析状态机
{
      if(rcvdat == 0x55)       // 接收到帧头第一个数据
          state_machine = 1;
      else
          state_machine = 0;      // 状态机复位
}
else if(state_machine == 1)
{
      if(rcvdat == 0xAA)       // 接收到帧头第二个数据
          state_machine = 2;
      else
          state_machine = 0;      // 状态机复位
}
else if(state_machine == 2)
{
      if(rcvdat == 0x7E)       // 接收到帧头第三个数据
          state_machine = 3;
     else
          state_machine = 0;      // 状态机复位
}
else if(state_machine == 3)
{
      sumchkm = rcvdat;       // 开始计算累加、异或校验和
      xorchkm = rcvdat;
      if(rcvdat == m_SrcAdr)      // 判断目的地址是否正确
          state_machine = 4;
      else
          state_machine = 0;
}
else if(state_machine == 4)
{
      sumchkm += rcvdat;
      xorchkm ^= rcvdat;
      if(rcvdat == m_DstAdr)      // 判断源地址是否正确
          state_machine = 5;
      else
          state_machine = 0;
   }
else if(state_machine == 5)
{
      lencnt = 0;            // 接收数据计数器
      rcvcount = rcvdat;        // 接收数据长度
      sumchkm += rcvdat;
      xorchkm ^= rcvdat;
      state_machine = 6;
}
else if(state _machine == 6 || state _machine == 7)
{
      m_ucData[lencnt++] = rcvdat;     // 数据保存
      sumchkm += rcvdat;
      xorchkm ^= rcvdat;
      if(lencnt == rcvcount)      // 判断数据是否接收完毕
          state_machine = 8;
      else
          state_machine = 7;
}
else if(state_machine == 8)
{
      if(sumchkm == rcvdat)      // 判断累加和是否相等
          state_machine = 9;
      else
          state_machine = 0;
}
else if(state_machine == 9)
{
      if(xorchkm == rcvdat)      // 判断异或校验和是否相等
          state_machine = 10;
      else
          state_machine = 0;
}
else if(state_machine == 10)
{
      if(0x0D == rcvdat)       // 判断是否接收到帧尾结束符
      {
          retval = 0xaa;    // 置标志,表示一个数据包接收到
      }
      state_machine = 0;     // 复位状态机
}

    此过程中,使用了一个变量state_machine作为协议状态机的转换状态,用于确定当前字节处于一帧数据中的那个部位,同时在接收过程中自动对接收数据进行校验和处理,在数据包接收完的同时也进行了校验的比较。因此当帧尾结束符接收到的时候,则表示一帧数据已经接收完毕,并且通过了校验,关键数据也保存到了缓冲去中。主程序即可通过retval的标志位来进行协议的解析处理。
    接收过程中,只要哪一步收到的数据不是预期值,则直接将状态机复位,用于下一帧数据的判断,因此系统出现状态死锁的情况非常少,系统比较稳定,如果出现丢失数据包的情况也可由上位机进行命令的补发,不过这种情况笔者还没有碰到。
    对于主程序中进行协议处理的过程与此类似,主程序循环中不断的读取串口缓冲区的数据,此数据即参与到主循环中的协议处理过程中,代码与上面所述完全一样。

关键字:单片机  串口通信  接收数据 引用地址:玩转单片机之三--串口通信,接收数据

上一篇:玩转单片机之四-点亮4个数码管
下一篇:玩转单片机之二--USB转串口

推荐阅读最新更新时间:2024-03-16 15:09

学好单片机四步走
如何学习单片机?学习单片机的步骤是什么?经常有单片机爱好者问我们,我们初略总结了一下,单片机学习的过程基本上可分四个阶段: 第一阶段:是先浏览教科书里的硬件部分,大至了解单片机的硬件结构。如ROM、RAM、地址、I/O口等,以及看一些厂家的MCU资料(Data Sheet),来加强MCU所提供各项资源的印象。 第二阶段:就是了解二进位数字、十六进位数和软件方面的内容。尽管有很多高级语言可用于单片机的编程,但我觉得初学还是以汇编语言为好,更有利于和硬件结合,掌握硬件结构。知道汇编语言、机器语言、指令、 程序等概念后,从MOV指令开始,学习汇编语言和编程,在此如51的MCU汇编语言系统有11条指令,简单又好理解它们怎样
[单片机]
改善8051系统用电效率的微控制器
摘要:一种改进架构的高性能8051设计、外围功能集成、选用合适的时钟源以降低功耗;并介绍节省电能的软件技术及采用待机模式降低功耗的技巧。 关键词:停机模式 空闲模式 功率管理模式 便携式产品的功能和性能日新月异。 消费者对产品性能的要求也越来越高,需要更强大的运算能力支持;另一方面,希望产品具有更低的功耗。 尽管已经出现了很多功耗处理器,但它们的性能通常很有限。Dallas公司的系列高速微控制器在性能和功耗之间取得了一个很好的折衷,采用了8051架构——世界上最流行的微控制器之一。简单易用、丰富的I/O资源使这种微控制器深受设计者的喜爱,并被广泛接受。它的流行势头已蔓延到了便携式领域,在很多应用中都有其用武之地。 本文
[应用]
PIC单片机实现护理机智能控制的设计
引言 随着我国老龄化进程的加剧,当今社会中存在着一种因失去生活自理能力而“长期卧床的弱势群体”,特别是那些几乎无意识的弱势群体,他们需要被人长期照顾,特别是他们的大小便的清洁处理。然而由于该弱势群体数量大、护理人员紧缺和护理费用高等问题,导致这些弱势群体的家庭护理矛盾日益凸显。目前市场上的长期卧床病人大小便清洁护理机在“智能护理”方面己比较完善,已经具有大小便自动识别与回收、温水清洗与自动烘干等功能,达到了“人性化”护理的要求,但在使用过程中仍然需要专门的“陪护人员”,对于绝大多数现代家庭而言,无论从人力还是财力,这都将是一个沉重的负担,同时也限制了大小便清洁护理机在家庭中的推广使用,因此,社会迫切需要一种在现有护理功能的基础上能
[单片机]
PIC<font color='red'>单片机</font>实现护理机智能控制的设计
avr单片机+RFID的门禁系统
设计文档: 单片机源程序如下: /****************************************************************************************************************** 程序名: 射频卡门禁系统 功能概述: 1、 点阵LCD(128*64)显示。在正常、设置情况下LCD显示系统各个时期的菜单以供选择。 2、 射频卡开锁,开锁时用射频卡放在读卡器上(距离 15CM)即可开锁,如果用非法卡连续刷写次数超过3 次则报警(报警功能没有添加),这时系统将关闭刷
[单片机]
avr<font color='red'>单片机</font>+RFID的门禁系统
TJA1020+51单片机实现的LIN总线通讯源程序(master+slave)
LIN mater的51单片机代码 /**************master*****************/ #include REGX52.H #include intrins.h sbit p31=P3^1; sbit k1=P1^0; sbit k2=P1^1; sbit p20=P2^0; unsigned char t; unsigned char sendflag, flag; void delay(unsigned int i) { while(i--); } void send() { // 同步 ID data1 data2
[单片机]
TJA1020+51<font color='red'>单片机</font>实现的LIN总线通讯源程序(master+slave)
大联大友尚集团推出基于TI无线MCU的智能照明控制解决方案
2015年4月7日,致力于亚太地区市场的领先电子元器件分销商---大联大控股宣布,其旗下友尚推出基于TI的SimpleLink 蓝牙低能耗CC2540T的智能照明控制解决方案。大联大友尚代理的这款高度可靠的低功耗无线微控制器(MCU)提供了-40℃至125℃的宽泛温度范围和适合工业应用的USB连接。凭借TI的BLE-Stack 软件以及支持内部更新的无线下载等示例应用,大联大友尚更为开发者简化了基于CC2540T的完整的解决方案。 蓝牙智能技术为工业应用带来了许多全新的特性与优势,例如通过智能手机或平板计算机更轻松地获取信息并控制系统,同时还能避免更多手动和常见的复杂系统。通过这种真正的集成式单芯片蓝牙低能耗解决方
[物联网]
大联大友尚集团推出基于TI无线<font color='red'>MCU</font>的智能照明控制解决方案
51单片机的定时器与计数器
1.0 1.0.1定时器 (1)51单片机的定时器是一个内部外设。 (2)定时器相当于CPU的一个“闹钟”。 (3)定时器是用计数器来实现的。 1.0.2计数器 (1)计数器可以计数外部脉冲的个数. (2)脉冲:(个人理解)单片机中一个低电平跳变成高电平在回到低电平的这么一个过程就称为一个脉冲。 1.0.3定时器是如何工作的 (1)第一步:先设置好定时器的时钟源(AT89C51单片机的时钟源只有一个不需要设置) (2)第二步:初始化时钟相关寄存器 (3)第三步:设置定时时间(计数个数) (4)第四步:设置中断处理程序(定时器总是与中断相互配合使用) (5)第五步:打开定时器 (6)第六步:定时器计数到后产生中断,然后执行中
[单片机]
单片机按键学习总结
基本的按键程序结构分析: 1 void key_scan(void) 2 { 3 if (!key) //检测按键是否按下 4 { 5 delay_ms(20); //延时去抖,一般20ms 6 if(!key) 7 { 8 ...... 9 } 10 while (!key); //等待按键释放 11 } 12 } 注意:以上基本按键程序中,在按键执行之后必须要加上等待按键释放,否则程序会出现一些奇怪的问题,比如说按键累加时按键一次,却累加了多次。 可
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved