基于C#实现工业级16进制串口通信
一、核心通信类设计usingSystem;usingSystem.IO.Ports;usingSystem.Text;usingSystem.Timers;publicclassHexSerialPort{privateSerialPort_serialPort;privatereadonlyobject_locknew();privateTimer_receiveTimernewTimer(50);// 数据完整性定时器publiceventEventHandlerbyte[]DataReceived;publiceventEventHandlerstringErrorOccurred;publicHexSerialPort(){_serialPortnewSerialPort();_receiveTimer.ElapsedOnReceiveTimerElapsed;}// 初始化配置publicvoidInitialize(stringportName,intbaudRate,ParityparityParity.None,StopBitsstopBitsStopBits.One,intdataBits8){_serialPort.PortNameportName;_serialPort.BaudRatebaudRate;_serialPort.Parityparity;_serialPort.StopBitsstopBits;_serialPort.DataBitsdataBits;_serialPort.DataReceivedOnDataReceived;}// 打开串口publicboolOpen(){try{if(!_serialPort.IsOpen){_serialPort.Open();_receiveTimer.Start();returntrue;}returnfalse;}catch(Exceptionex){HandleError($打开串口失败:{ex.Message});returnfalse;}}// 关闭串口publicvoidClose(){_receiveTimer.Stop();if(_serialPort.IsOpen){_serialPort.Close();}}// 发送16进制数据publicvoidSend(byte[]data){try{byte[]crcCalculateCRC(data);byte[]framenewbyte[data.Length2];Array.Copy(data,0,frame,0,data.Length);Array.Copy(crc,0,frame,data.Length,2);_serialPort.Write(frame,0,frame.Length);OnDataSent(frame);}catch(Exceptionex){HandleError($发送数据失败:{ex.Message});}}// 数据接收处理privatevoidOnDataReceived(objectsender,SerialDataReceivedEventArgse){try{intbufferSize_serialPort.BytesToRead;byte[]buffernewbyte[bufferSize];_serialPort.Read(buffer,0,bufferSize);Array.Copy(buffer,0,ReceiveBuffer,ReceiveBuffer.Length,bufferSize);_receiveTimer.Stop();_receiveTimer.Start();}catch(TimeoutException){ProcessBuffer();}catch(Exceptionex){HandleError($接收数据异常:{ex.Message});}}privatevoidOnReceiveTimerElapsed(objectsender,ElapsedEventArgse){ProcessBuffer();}privatevoidProcessBuffer(){lock(_lock){if(ReceiveBuffer.Length5)return;// 最小有效帧长度// 解析数据帧intheaderIndexArray.IndexOf(ReceiveBuffer,0xAA);if(headerIndex!0)return;// 起始符校验intfooterIndexArray.IndexOf(ReceiveBuffer,0x55);if(footerIndex-1)return;byte[]framenewbyte[footerIndex1];Array.Copy(ReceiveBuffer,0,frame,0,footerIndex1);// 校验CRCbyte[]crcnewbyte[2]{frame[frame.Length-2],frame[frame.Length-1]};if(!VerifyCRC(frame,crc)){HandleError(CRC校验失败);return;}// 触发数据事件byte[]payloadnewbyte[frame.Length-4];Array.Copy(frame,2,payload,0,payload.Length);DataReceived?.Invoke(this,payload);// 清空缓冲区Array.Clear(ReceiveBuffer,0,ReceiveBuffer.Length);}}// CRC16-Modbus校验privatebyte[]CalculateCRC(byte[]data){ushortcrc0xFFFF;for(inti0;idata.Length;i){crc^(ushort)(data[i]8);for(intj0;j8;j){if((crc0x8000)!0){crc(ushort)((crc1)^0x1021);}else{crc1;}}}returnnewbyte[]{(byte)(crc0xFF),(byte)(crc8)};}privateboolVerifyCRC(byte[]frame,byte[]crc){byte[]calculatedCalculateCRC(frame,0,frame.Length-2);returncalculated[0]crc[0]calculated[1]crc[1];}// 数据缓冲区privatebyte[]ReceiveBuffernewbyte[1024];// 事件处理protectedvirtualvoidOnDataSent(byte[]data){// 实现发送日志记录}privatevoidHandleError(stringmessage){ErrorOccurred?.Invoke(this,message);}}二、数据帧格式定义// 工业标准数据帧结构publicclassIndustrialDataFrame{publicconstbyteStartMarker0xAA;publicconstbyteEndMarker0x55;publicbyteAddress{get;set;}// 设备地址publicbyteFunctionCode{get;set;}// 功能码publicbyte[]Payload{get;set;}// 有效载荷publicushortCrc{get;set;}// CRC校验码// 构建数据帧publicstaticbyte[]BuildFrame(byteaddress,bytefunctionCode,byte[]payload){byte[]framenewbyte[211payload.Length2];frame[0]StartMarker;frame[1]address;frame[2]functionCode;Array.Copy(payload,0,frame,3,payload.Length);byte[]crcCalculateCRC(frame,0,frame.Length-2);frame[frame.Length-2]crc[0];frame[frame.Length-1]crc[1];frame[frame.Length-0]EndMarker;returnframe;}}三、使用示例// 初始化串口varserialnewHexSerialPort();serial.Initialize(COM3,9600,Parity.None,StopBits.One,8);serial.DataReceived(s,data){// 处理接收到的数据varframeParseDataFrame(data);Console.WriteLine($收到数据:{BitConverter.ToString(frame.Payload)});};serial.Open();// 发送数据byte[]payloadnewbyte[]{0x01,0x03,0x00,0x00};byte[]frameIndustrialDataFrame.BuildFrame(0x01,0x03,payload);serial.Send(frame);四、关键功能实现1. CRC16查表法优化privatestaticreadonlyushort[]Crctablenewushort[256]{0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,// ... 完整查表数据};privateushortCalculateCRC(byte[]data,intoffset,intlength){ushortcrc0xFFFF;for(intioffset;ioffsetlength;i){crc(ushort)((crc8)^Crctable[(crc^data[i])0xFF]);}returncrc;}2. 异常处理机制privatevoidHandleSerialError(Exceptionex){// 自动重连逻辑if(exisTimeoutException||exisIOException){Task.Delay(1000).ContinueWith(_Open());}// 日志记录File.AppendAllText(error.log,${DateTime.Now}:{ex.Message}\n);}3. 数据解析器publicIndustrialDataFrameParseDataFrame(byte[]data){if(data.Length5)thrownewArgumentException(无效数据帧);returnnewIndustrialDataFrame{Addressdata[1],FunctionCodedata[2],Payloadnewbyte[data.Length-4],Crc(ushort)(data[data.Length-2]|(data[data.Length-1]8))};}五、工程实践建议1. 配置管理{SerialPort:{PortName:COM3,BaudRate:9600,Parity:None,StopBits:One,DataBits:8,FlowControl:None},Timeout:5000,RetryCount:3}2. 性能优化双缓冲区技术使用ArrayPoolbyte减少内存分配异步接收采用BeginRead/EndRead实现非阻塞IO批量写入合并多个小数据包为单个发送操作3. 调试工具publicstaticclassDebugHelper{publicstaticvoidLogFrame(byte[]data){stringhexBitConverter.ToString(data).Replace(-,);File.AppendAllText(debug.log,${DateTime.Now}:{hex}\n);}}六、扩展功能实现1. 流量统计publicclassTrafficMonitor{privatelong_sentBytes;privatelong_receivedBytes;publicvoidUpdateSent(longbytes)Interlocked.Add(ref_sentBytes,bytes);publicvoidUpdateReceived(longbytes)Interlocked.Add(ref_receivedBytes,bytes);}2. 固件升级publicasyncTaskFirmwareUpdateAsync(StreamfirmwareStream){constintbufferSize1024;byte[]buffernewbyte[bufferSize];while(awaitfirmwareStream.ReadAsync(buffer,0,bufferSize)0){awaitSendWithAck(buffer,bufferSize);}}privateasyncTaskSendWithAck(byte[]data,intlength){intretry0;while(retry3){Send(data,0,length);if(awaitWaitForAck(3000))return;retry;}thrownewTimeoutException(固件升级超时);}参考代码 c# 串口通讯实现工业上用的16HEX方式的报文通讯www.youwenfan.com/contentcsv/93393.html七、测试方案1. 单元测试[TestFixture]publicclassSerialPortTests{[Test]publicvoidTestCRCCalculation(){byte[]testData{0x01,0x03,0x00,0x00};ushortcrcHexSerialPort.CalculateCRC(testData);Assert.AreEqual(0x0645,crc);}}2. 压力测试publicvoidStressTest(){vartasksnewTask[100];for(inti0;i100;i){tasks[i]Task.Run((){for(intj0;j1000;j){SendTestPacket();}});}Task.WaitAll(tasks);}