c#串口通信(二)--通信协议的发送及数据解析

在简单的认识及使用串口通信之后,在实际应用中应该是配合硬件,和负责硬件通信的同事沟通好通信协议,来进行设备的控制及数据获取。
常用的串行通信协议有TTL电平(普通MCU芯片输出的串口电平)、RS232(工业上常用的串口标准)及RS485(串行总线标准,常用语通信距离为几十米到上千米时)等。
本文采用的是RS485,为半双工工作方式。下面是一个简单的获取设备信息的例子。
通信协议如下:

数据包格式:

包头(2byte) 命令码(1byte) 包长(2byte) 数据/状态(Nbyte) 异或校验(1byte)

发送数据/状态格式(24 bytes/每包,不足补0):

数据 说明
设备编号 8byte
系统时间 4byte

接收设备返回的格式:

数据 说明
设备编号 8byte
设备电压温度 8byte,浮点数
增益 1byte
4G状态 1byte
存储容量 1byte
时间戳 4byte 单位:秒
计数器 4byte
控制类型 1byte 0x00-停止测量,0x01-开始测量,0x02-测量类型1,0x03-测量类型2,0x04-测量类型3

发送获取数据的命令代码:

byte[] package = new byte[24];
int index=0;
package[index++] = 0x77;//帧头
package[index++] = 0xCC;
package[index++] = cmd;//命令码    
package[index++] = 0;//数据长度高字节
package[index++] = 18;//数据长度低字节
//数据
var idbytes = Encoding.ASCII.GetBytes(id);
 for (int i = 0; i < idbytes.Length; i++)
 {
     
     package[index++] = idbytes[i];//填充设备id
 }
 DateTime Now_Time = DateTime.Now;

 int Time_Sec = GetTimeStamp(Now_Time);

 package[index++] = (byte)(Time_Sec >> 24);
 package[index++] = (byte)(Time_Sec >> 16);
 package[index++] = (byte)(Time_Sec >> 8);
 package[index++] = (byte)(Time_Sec);
//校验码
byte tmp = 0;
var length = package.Length - 1;
for (byte i = 0; i < length; i++)
    tmp ^= package[i];
package[23] =tmp ;
serialPort.Write(package, 0, package.Length);

解析回复的数据(DataReceived事件):

Thread.Sleep(400);//延时,等待接收完数据 接收100个字节数据时间约为6ms
byte[] buffle = new byte[serialPort.BytesToRead];
serialPort.Read(buffle, 0, buffle.Length);//buffle为接收的数据
if (e.EventType != SerialData.Chars||serialPort == null)
{
     
    return;
}
if (!serialPort.IsOpen)//串口在关闭时不接收数据 为了防止关闭串口时卡死的问题
{
     
    serialPort.DiscardInBuffer();//丢弃接收缓冲区数据
    return;
}
bool isAll = false;

if (DataBuffer[0] == 0x77 && DataBuffer[1] == 0xCC)//上次未接收完
{
     
    var Length = DataBuffer.Length;
    DataBuffer =RS485Helper. AddBytes(DataBuffer, buffle);
    isAll = (Length == buffle.Length + bufferReadLength);
}
else//接收开始
{
     
    if (buffle[0] == 0x77 && buffle[1] == 0xCC)
    {
     
        bufferReadLength = buffle.Length;
        var length = (ushort)(buffle[3] << 8)+ (ushort)buffle[4]+6;//包长加包头包尾才是总长度
        DataBuffer = new byte[length];
        DataBuffer = RS485Helper.AddBytes(DataBuffer, buffle,false);//这个函数是将新增的byte[]添加到原有buffle后面
        isAll = (length == buffle.Length);
    }
}
if (isAll)
{
     
    Analyse(DataBuffer.Clone() as byte[]);
    DataBuffer = new byte[1000];
    isAccept = false;
}
private void Analyse(byte[] buffle)
{
     
    var gucCommand = buffle[2];
    var devId = Encoding.Default.GetString(buffle.Skip(3).Take(8).ToArray());
    object data=null;
    switch (gucCommand)
    {
     
    	case RS485Helper.DEV_INQUIRE_CMD:
            Deal_DevInfo(buffle);
            break;	//更新连接标志
        default: break;
    }
    if (OnComReceiveDataHandler != null)
    {
     
       OnComReceiveDataHandler(data, ComPortName, devId);//这里使用委托让UI界面去更新,注意现在是在子线程里面,使用
    }
}
private void Deal_DevInfo(byte[] buffle)
        {
     
			ushort Data_Lenght = (ushort)((buffle[3] << 8) + buffle[4]);//接收字节的长度

            string id= BitConverter.ToString(buffle, 5, 8);//仪器编号
            var power = BitConverter.ToSingle(buffle, 13).ToString("f2");//电量
            var Tem = BitConverter.ToSingle(buffle, 17).ToString("f2");//温度
            float Gain_Value = 0;
            switch (buffle[21])//增益
            {
     
                case 0: Gain_Value = 0.25f; break;
                case 1: Gain_Value = 0.5f; break;
            }        
            var Wireless_State = String.Format("{0:X}", buffle[22]);//4G状态  
            var Space = buffle[23].ToString();//剩余空间

            int Time_Value = (buffle[24] << 24) + (buffle[25] << 16) + (buffle[26] << 8) + buffle[27];
            var Col_Time = GetDateTime(Time_Value);//时间

            //计数器
            var Data_Count = (uint)((buffle[28] << 24) + (buffle[29] << 16) + (buffle[30] << 8) + buffle[31]);

            byte Work_Mode = buffle[32];//工作模式
            var workMode = "";
            switch (Work_Mode)
            {
     
                case 0: workMode = "停止"; break;
                case 1: workMode = "采集"; break;
                case 2: workMode = "模式1"; break;
                case 3: workMode = "模式2"; break;
                case 4: workMode = "模式3"; break;
                default: workMode = "停止"; break;
            }
            ...
}

你可能感兴趣的:(#,c#,非控件常用操作,c#,c#,串口通信,SerialPort,半双工通信,RS485)