C#界面程序设计——05串口接收不连续数据处理
在处理数据交互时,无论单任务还是多任务,交互流程可概述为以下两种情况:
情况A:
![情况A](https://img-blog.csdnimg.cn/20210119104852369.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDAxMjAzMw==,size_16,color_FFFFFF,t_70)
情况B:
![情况B](https://img-blog.csdnimg.cn/20210119104936994.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDAxMjAzMw==,size_16,color_FFFFFF,t_70)
注:情况B是指电路板收到A并校验正确后才可接收B。
但是在数据交互过程中,无论是通过RS422/485还是通过RS232通信,经常会遇到断包的状态。
一般来说,每包的数据都是单独校验,使用CRC校验或是简单的和校验。当遇到断包时,会出现
数据包A被分开几次接收,而且可能会出现数据包A的结尾会和B一起被接收,这些情况我们在考虑到可用性和安全性的情况下,不能当做无效数据包处理。
本文针对这种情况,基于C#的串口接收触发函数给出一种解决方案。
在提出解决方案前,需要计算机与电路板的通信协议中包含如下元素作为前提:
1.每个数据包有标志位,可以通过某位或某字节的值的值该数据包的长度、类型等信息。
2.如果数据包长度不固定,则必须有足够长度的头尾标识。
3.上述数据包交互过程中不会在交互过程中被插入其他数据。如在一对多交互过程中数据包被掺杂。
首先,建立长度判断函数,本文案例是以数据包的第二个字节作为标识,即Data[1]。
/*
* 函数:Zhen_type_Length
* 参数:zhentype 帧类型 如0XA2
* 功能:根据帧类型计算出各帧的数据长度
* 返回值:各帧的数据长度
* */
public int Zhen_type_Length(byte zhentype)
{
int Zhen_type_L = 0;
switch (zhentype)
{
case 0XA0:
Zhen_type_L = 138;
break;
case 0X90:
Zhen_type_L = 20;
break;
case 0X91:
Zhen_type_L = 25;
break;
case 0XA4:
Zhen_type_L = 244;
break;
case 0XA3:
Zhen_type_L = 30;
break;
case 0XA1:
Zhen_type_L = 22;
break;
case 0XA2:
Zhen_type_L = 22;
break;
case 0X80:
Zhen_type_L = 546;
break;
default:
Zhen_type_L = 0;
break;
}
return Zhen_type_L;
}
然后设计接收部分,主要设计思路为根据接收到的第一条信息开始建立数据缓冲区,每次接收触发时判断长度是否足够,如此几次之后数据长度大于等于预期长度之后再处理,处理之后将缓冲区中有效数据与有效数据之前的数据清空,如果有效数据之后仍有数据则前移至缓冲区开头,前移之后需要再次判断剩余数据是否为有效数据。
/*
* 函数:FaSPcom_DataReceived
* 功能:串口接收信息处理,兼有接收触发、发送下一条帧数据、存入循环缓冲区并校验的功能
* 循环缓冲区预分配
* */
int ZhenLength_Record = 0;//纪录当前缓冲区帧数据有效数据长度
byte[] ZhenDataHuanChong = new byte[1000];//缓冲区
private void FFSPcom_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] Data_FaSPcom = new byte[FaSPcom.BytesToRead];
FaSPcom.Read(Data_FaSPcom, 0, Data_FaSPcom.Length);//接收数据,存入Data_FaSPcom
if (Data_FaSPcom.Length == 0 ) { return; }//防止空包
// if (Data_FaSPcom[0] == 0 && Data_FaSPcom.Length == 1) { return; }//防止零包
Array.Copy(Data_FaSPcom, 0, ZhenDataHuanChong, ZhenLength_Record, Data_FaSPcom.Length);
ZhenLength_Record = ZhenLength_Record + Data_FaSPcom.Length;
int ZhenLength = Zhen_type_Length(ZhenDataHuanChong[1]);//根据标识字节判断该包数据长度,注意判断依据是缓冲区数据,而不一定是刚刚接收到的数据包
if (ZhenLength_Record < ZhenLength) { return; }//如果数据长度不足,返回,等待下一次接收数据
bool Deal_Double = false;
if (1<ZhenLength_Record - ZhenLength)//判断后面是否还跟了一包完整数据
{
if (Zhen_type_Length(ZhenDataHuanChong[ZhenLength + 1]) + ZhenLength < ZhenLength_Record)
{
Deal_Double = true;
}
}
byte[] Data = new byte[ZhenLength];
Array.Copy(ZhenDataHuanChong, 0, Data, 0, ZhenLength);//将第一包有效数据提取
//此处写校验与处理部分,处理参数为Data
//无论校验通过与否,删除已处理部分,由于长度记录的变量、缓冲区均为全局,此处使用函数处理
Clear_Data_HuanChong(ZhenLength);
ZhenLength_Record = ZhenLength_Record - ZhenLength;
if (true == Deal_Double)
{
ZhenLength = Zhen_type_Length(ZhenDataHuanChong[ZhenLength + 1]);
//此处再提取缓冲区中的后一包数据,并校验、处理
}
Clear_Data_HuanChong(ZhenLength);
ZhenLength_Record = ZhenLength_Record - ZhenLength;
}
public void Clear_Data_HuanChong(int ClearLength)//将缓冲区的前ClearLength个数据覆盖
{
byte[] BeiFen = new byte[ClearLength];
for (int i = 0; i < ZhenDataHuanChong.Length - ClearLength; i++)
{
ZhenDataHuanChong[i] = ZhenDataHuanChong[ClearLength+i];
}
}