最近在用STM8S003这个片子做项目,在做串口通信的时候,发现以前写的协议太简单了,项目中用不适合。

//协议 : 0XDD xx xx  xx xx xx xx  0XAA @far @interrupt void UART1_Receive(void){
unsigned char res;
res=UART1_DR;
if(res==0xDD)       //头
{
Rec_statu=1;     //标志开始接收
Rec_Cnt=0;
Rec_End=0;
return;
}
if(res==0xAA)       //尾
{
Rec_statu=0;
Rec_End=1;        //标志接收完成
SendData();       //接收完一组数据后 在发送一组数据出去
return;
}
if(Rec_statu==1)           //如果处于数据接收中
{
Rec_Buf[Rec_Cnt++]=res;
}
return;
}

这是测试用的一个简单协议,用0XDD作为数据头,0XAA作为数据尾。不限制数据长度。能进行简单的通信功能,但是实际项目中应用时,如果传输的数据中有0XDD或者0XAA,这个协议在解析是就可能出错。

于是决定修改协议,将协议的头改为两位数据。

// A5 5A(头) 45(数据类型) 04(数量) 0x00 0x00 0x00 0x00 0xFF(校验)
// A5 5A 45 04 08 A0 09 B0 5E
@far @interrupt void UART1_Receive(void)
{
unsigned char res;
unsigned int check=0x00;   //  校验
res=UART1_DR;
UART1_SR&=~(1<<5);      //RXNE 清零
if((res==0xA5)&&(Rec_head==0))               //头
{
Rec_head=1;             //标志开始接收
Rec_statu=0;
Rec_Cnt=0;
Rec_End=0;
return;
}
if((res==0x5A)&&(Rec_head==1))
{
Rec_head=1;             //成功接收到了数据头 正式开始接收数据
Rec_statu=1;
Rec_Cnt=0;
Rec_End=0;
return;
}
if(Rec_Cnt==6)              //尾
{
check=(0x5a+0x5a+Rec_Buf[0]+Rec_Buf[1]+Rec_Buf[2]+Rec_Buf[3]+Rec_Buf[4]+Rec_Buf[5]);
if(res==(check&0xff))          //数据正确 存储接收到的温度值
{
//在这块处理数据时 串口中断会进不来
//env_tem=Rec_Buf[4]*256+Rec_Buf[5];    //环境温度值 2480
//obj_tem=Rec_Buf[2]*256+Rec_Buf[3];    //目标温度值 2208
Rec_End=1;            //标志接收完成
}
else
Rec_End=0;            //接收失败
Rec_head=0;
Rec_statu=0;
Rec_Cnt=0;
return;
}
if(Rec_statu==1)           //如果处于数据接收中
{
Rec_Buf[Rec_Cnt]=res;
Rec_Cnt++;
return;
}
return;}

数据头变成了两位,数据长度为固定值,增加了校验位。看起来貌似没有什么问题,但是仔细分析,发现还是有很多漏洞。

当接收到0XA5时,准备开始接收数据,当收到0X5A时,正式开始接收数据,然后统计数据的个数,当收到规定个数数据时,结束接收数据。 如果发送的数据是 A5 5A 45 04 08 A0 09 B0 5E时,可以正常接收到数据。但是如果发送的数据是 A5 00 01 02 03 5A 45 04 08 A0 09 B0 5E 时,遇见0XA5标记数据准备接收,然后又接着收到了00 01 02 03这几个数据,此时由于接收数据没有正式开始,所以程序不会有任何动作,当接收到下一个数据0x5A时,程序开始正式接收数据。但是这笔数据是非法数据,被程序误判为正常数据。从而接收到了一组非法数据。

那么能不能用下标来控制数据头的识别,比如如果遇到了0xA5,数据准备接收,同时开始计数,如果下一个数据是0x5A,那么标记开始正式接收数据。这样看起来好像能够避免上面发送 这笔数据 A5 00 01 02 03 5A 45 04 08 A0 09 B0 5E 的情况,但是如果发送的数据是 A5 A5 5A 45 04 08 A0 09 B0 5E 呢?我们来分析一下,如果遇到A5程序准备接收数据,同时开始计数,第二个数据是A5而不是设定的5A,那么判定为非法数据,计数器清零,准备重新接收,当第三个数据5A进来时,发现不是数据头A5,抛弃本次数据,重新等待数据头A5。显而易见这笔数据里面包含有正常数据,不过第一个数据是A5刚好和数据头重复了,导致本次数据里面的正常数据仍然被丢弃了。

那么如果避免这种情况,可以考虑在接收的数据判断收到的头A5,并且后面紧跟的数据是5A,那么就说明接收到了正确的数据头。但是有个问题,本次接收的数据如果是A5,下次的数据还不知道是不是5A,那么本次的数据是要丢弃还是要存储起来,如果要存储起来,那么后面连续好多个5A的话,如果区分哪个5A是哪一次接收的。这样判断起来程序就会显得极为复杂,那么有没有简单有效的方法呢?可以换个思维,能不能判断如果这次接收到了数据尾5A,判断上次接收的数据是A5,那么说明接收到了数据头。这样的话如果有连续多个A5的话,只要后面不是5A,那么就不会开始接收数据。新修改的协议如下:

//接收数据标志位 各个位含义
//bit7:接收到尾    0xA5 0x5A  0 未收到  1 收到
//bit6:接收到头    0x55 0xAA  0 未收到  1 收到
//bit0-5:接收到数据个数      最多64个数据
unsigned  char rec_flag = 0;            //接收数据标志位
unsigned  char rec_buf[20] = {0}; //接收数据存储
//串口接收数据格式为:  头1(0xA5) 头2(0x5A)  数据N位  尾1(0x55) 尾2(0xAA)
// 0xA5 0x5A xx xx xx xx xx xx xx xx xx xx xx  0x55 0xAA//接收中断函数 中断号18
#pragma vector  =  20                   // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void UART1_Handle( void )
{unsigned char res = 0;if( rec_flag & 0x80 )                               //如果接收数据结束返回 数据被读取后才清0 标志位{return;}res = UART1_DR;UART1_SR &= ~( 1 << 5 );                                //RXNE 清零rec_flag++;                                         //接收数据计数//这种方式判断,可以有效识别类似 A5 A5 A5 5A XX XX 这样的数据开头//如果只判断第一位是A5,第二位是5A的话,上面的这种数据就判定为非法数据,不能有效识别if( ( res == 0xA5 ) && ( ( rec_flag & 0x40 ) == 0 ) )       //如果没有开始接收数据,并且当前的数据是0xA5 将计数清零{rec_flag = 0;rec_buf[0] = res;return;}if( ( rec_flag & 0x3F ) == 0x01 )                   //第二位数据{if( res == 0x5A )                               //如果第二位数据是0x5A 说明已经正确收到了数据头{rec_buf[rec_flag & 0x3F] = res;rec_flag |= ( 1 << 6 );                         // 接收到头第2位   0x5A     置标志位}else{rec_flag = 0;                                   //接收错误,重新开始}return;}if( ( ( rec_flag & 0x40 ) == 0x40 ) && ( ( rec_flag & 0x80 ) == 0 ) ) //已经成功接收到了头并且没有收到尾{//本次接收的是0xAA 上一笔数据是0x55 说明数据传输结束//这种方式可以有效识别 数据和结束符一样的情况如 XX XX XX 55 55 55 AA//如果接收到55就开始判断的话,上述的情况就会识别为非法数据,不能有效判断if( res == 0xAA )                               //接收到尾 第2位{rec_buf[rec_flag & 0x3F] = res;if( rec_buf[( rec_flag & 0x3F ) - 1] == 0x55 ) //如果上次接收的数据是 尾第1位{rec_flag |= ( 1 << 7 );                     //接收数据结束}}else{rec_buf[rec_flag & 0x3F] = res;                 //存储接收到的数据}}
}

这次协议改为了,数据头为两位 A5 5A ,数据尾也为两位 55 AA。数据长度可以为任意值,校验位暂时没加进去。

这次判断的流程为,如果接收到的数据是A5那么就把计数值清0,如果不是A5,接收数据,并把计数值加1,然后在判断第一位数据是不是5A,如果不是5A,那么重新清0,重新接收。 这样的话 如果发送的数据是 A5 00 00 5A xx xx 时,第一位数据5A,计数器清0,然后接收的第一位数据是00,不等于5A,那么计时器清0,重新开始接收。如果发送的数据是 A5 A5 A5 5A xx xx时,接收的第一个数据是5A,计时器清0,接收的第二个数据还是5A,那么计时器的值还是0,当第三个5A来时,计数器的值还为0,当第四个数据来时,不为A5,那么接收数据,计数器加1,此时计数器的值为1。然后判断计数器的值为1时,收到的数据是5A,开始正式接收数据。这样就可以有效的判断到数据头,不会误判和漏判。同样数据结束时也用两位判断 55 AA,当本次接收的数据是AA,同时判断上一次接收的数据是55时,说明数据接收结束。由于判断数据尾时,前面接收的数据都在数组中存储着,所以判断本次接收的数据和前一次接收的数据可以同时进行。比判断数据头简单。

修改后的协议可以接收 A5 5A xx xx xx xx 55 AA这样的标准数据,同时也能有效的判断到 类似 A5 A5 A5 A5 5A xx xx xx xx 55 55 55 55 AA这种带有干扰数据,但是又是合法数据的数据。如果需要数据校验,可以在结束符之前添加校验位。这次修改后的协议目前看来还是比较理想的,可以考虑在实际项目中应用。

STM8S003单片机串口通信通信协议分析相关推荐

  1. c语言ecit,组态王与单片机串口通信通信协议    ECIT

    一. 通讯参数: 通讯参数包括数据位,停止位,波特率.校验方式. 数据位.停止位.波特率由单片机决定.组态王中的设定和单片机一致即可.校验方式参照"数据传输格式"中相关部分. 二. ...

  2. 基于PC与单片机串口通信的温度监控系统程序设计

    基于PC与单片机串口通信的温度监控系统程序设计 1.系统介绍 1)本系统主要讲解基于PC与单片机串口通信的温度监控系统程序设计(如图1),上位机采用常用的PC机,下位机使用的是STC89C52单片机, ...

  3. 51单片机串口通信模板_深入理解51单片机串口通信及通信实例

    串口通信的原理 串口通信(SerialCommunications)的概念非常简单,串口按位(bit)发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一 ...

  4. 蓝桥杯单片机串口通信学习提升笔记

    今日得以继续蓝桥杯国赛备赛之旅: 有道是 "不知何事萦怀抱,醒也无聊,醉也无聊,梦也何曾到谢桥." 那我们该如何 让这位诗人纳兰 "再听乐府曲 ,畅解相思苦"呢 ...

  5. 51单片机串口通信【收集学习向】

    本篇整理了学习51串口通信过程中看到的一些比较好的文章,有很多的超链接.并根据自己遇到的情况分析了串口传输乱码的一些原因. 目录 1.原理知识 2.测试程序和虚拟仿真 3.电路设计与公母口连线 电路设 ...

  6. Android串口通信实例分析【附源码】

    Android 串口通信实例分析,用的时开源的android-serialport-api 这个是用android ndk实现的串口通信,我把他做了一个简化,适合于一般的程序的串口通信移植,欢迎拍砖- ...

  7. 51单片机-串口通信

    目录 1.什么是通信 1.1.通信的概念 1.2.传送方式 1.3.同步方式 1.4.传送方向 1.5.校验方式 2.单片机串口介绍 2.1.硬件电路 2.2.电平标准 2.3.常见接口 2.4.内部 ...

  8. 普中单片机--串口通信(2)---通过串口助手发送数据点LED

    普中单片机–串口通信(2) 通过串口助手发送数据 点亮LED 软件部分 #include <reg52.h> #define jingzhen 12000000UL /*使用12.0M晶体 ...

  9. 51单片机串口通信原理、相关寄存器配置与简单串口收发程序代码

    目录 1. 串口通信原理 2. 51单片机串口通信 2.1 串口简要模式图 2.2 相关寄存器 (1)PCON.SCON.SBUF (2)IE.IPH.IP (3)配置T1定时器 2.3 波特率和系统 ...

最新文章

  1. dijkstra算法matlab程序_编程习题课 | 用最短路算法为你的小地图导航
  2. java 类参数_Java的数据类型和参数传递(详解)
  3. 【放洋屁了】知识焦虑-几点感慨
  4. jmeter服务器测试项目,JMeter-项目测试
  5. struts 的 MVC ,自己堆栈跟踪(可以跟着做一遍)
  6. 敬业福和花花卡算啥?这次不来,你亏了
  7. Linux学习笔记-配置SAMBA服务
  8. Wireshark网络封包分析软件——过滤表达式
  9. Linux网络编程必学的TCP/IP协议——图解分层(通俗易懂)【建议新手收藏】
  10. 安卓版Qinmei 追番必备神器 缓冲快
  11. JAVA 通过Excel导出pdf_教你用Java 将Excel转为PDF
  12. Centos 7 设置静态IP地址
  13. C语言炫酷的文件操作
  14. vue 自动播放语音
  15. python数列求和_python练习--数列求和
  16. 自定义等高的cell(storyboard)
  17. aui移动端UI框架
  18. matlab 边缘检测 抠图,ps如何调整边缘的流程:边缘检测、调整边缘、输出(调整边缘抠图...
  19. c语言程序 计算离高考天数,用c++程序计算一个孩子从出生到高考需要多少天
  20. php不建议用织梦cms,你不得不知的织梦cms安全性设置常识 - DeDecms

热门文章

  1. win8.1 安装.NET Framework3.5
  2. ACCESS模糊查询出现的变态问题,不知道该问题的希望注意,知道内幕的高手还望给小弟一个解释 Thanks...
  3. 关于C#中timer类
  4. echarts 折线图阴影颜色渐变
  5. Hive SemanticException
  6. ELK 6.2版本部署
  7. log4j日志输出性能优化
  8. 设计模式之单例模式8种实现方式,其五:懒汉式(线程不安全,同步代码块)
  9. linux系统反应优化,细说Linux系统优化-实践篇【转载】
  10. linux en_us支持什么格式的中文,更改Linux操作系统下的显示默认支持语言