这段时间在一个项目中负责程序与下位机USB设备的通讯。将接收到的USB数据做帧处理之后做成一个完整的帧送入队列中等待上层应用对帧数据进行解析。
相信很多人在做与下位机通讯的项目的时候,都会为帧处理烦恼。因为上位机在接收数据的时候,由于收到操作系统调度的影响,有时候收到的是半帧,有时候收到的是一帧半数据。如果不做帧处理的话,就会严重丢包。
在项目中我写了一个帧处理代码,经过测试验证很稳定。拿出来分享一下,也请大家多多指教。
我们项目中的协议是<<.................>>这样的。第5、6字节是帧长度。帧处理采用状态机的概念。
代码如下:
1、状态枚举
1 public enum FrameCheckState 2 { 3 HandleStart, 4 OneMark, 5 TwoMark, 6 CmdOne, 7 CmdTwo, 8 LenghtOne, 9 LenghtTwo 10 }
2、使用到的全部变量
1 //USB读取缓存区 2 static byte[] readBuffer; 3 //帧缓存区 4 static byte[] frameBuffer; 5 //帧缓存区中等待处理标记 6 static int _getIndex; 7 static int GetIndex 8 { 9 get { return _getIndex; } 10 set 11 { 12 if (value > 0) 13 _getIndex = (value >= frameBuffer.Length) ? (value - frameBuffer.Length) : value; 14 else 15 _getIndex = 0; 16 } 17 } 18 //帧缓存区中已经接收到的标记 19 static int _nowIndex; 20 static int NowIndex 21 { 22 get { return _nowIndex; } 23 set 24 { 25 if (value > 0) 26 _nowIndex = (value >= frameBuffer.Length) ? (value - frameBuffer.Length) : value; 27 else 28 _nowIndex = 0; 29 } 30 } 31 static int indexTmp; 32 //帧处理当前状态 33 static myEnums.FrameCheckState FrameState = myEnums.FrameCheckState.HandleStart; 34 //帧处理需要用的全局变量 35 static int getLength = 0, getCmd = 0; 36 static byte[] bgetLength = new byte[2], bgetCmd = new byte[2];
3、线程循环读USB
1 /// <summary> 2 /// 线程循环读取USB缓冲区 3 /// </summary> 4 static void ReadCmd() 5 { 6 byte[]buffer=new byte[usbReader.ReadBufferSize]; 7 int transferlength=0; 8 try 9 { 10 while(true) 11 { 12 usbReader.Read(buffer, Consts.ReadTimeOut, out transferlength); 13 14 if(transferlength!=0) 15 { 16 for(int i=0;i<transferlength;i++) 17 { 18 frameBuffer[GetIndex] = buffer[i]; 19 GetIndex++; 20 21 //Console.Write(buffer[i].ToString("X2") + " "); 22 } 23 //Console.WriteLine(); 24 25 GetFrameHandle(); 26 } 27 Thread.Sleep(Consts.ReadSleepTime); 28 } 29 } 30 catch 31 { 32 Console.WriteLine("读取USB错误"); 33 } 34 }
4、帧处理
1 /// <summary> 2 /// 帧处理方法 3 /// </summary> 4 private static void GetFrameHandle() 5 { 6 while (true) 7 { 8 if (NowIndex == GetIndex) 9 return; 10 11 switch (FrameState) 12 { 13 case myEnums.FrameCheckState.HandleStart: 14 if (frameBuffer[NowIndex] == ‘<‘) 15 FrameState = myEnums.FrameCheckState.OneMark; 16 17 NowIndex++; 18 break; 19 20 case myEnums.FrameCheckState.OneMark: 21 if (frameBuffer[NowIndex] == ‘<‘) 22 { 23 indexTmp = NowIndex; 24 FrameState = myEnums.FrameCheckState.TwoMark; 25 } 26 else 27 FrameState = myEnums.FrameCheckState.HandleStart; 28 29 NowIndex++; 30 break; 31 32 case myEnums.FrameCheckState.TwoMark: 33 FrameState = myEnums.FrameCheckState.CmdOne; 34 bgetCmd[0] = frameBuffer[NowIndex]; 35 NowIndex++; 36 break; 37 38 case myEnums.FrameCheckState.CmdOne: 39 FrameState = myEnums.FrameCheckState.CmdTwo; 40 bgetCmd[1] = frameBuffer[NowIndex]; 41 getCmd = BitConverter.ToInt16(bgetCmd, 0); 42 NowIndex++; 43 break; 44 45 case myEnums.FrameCheckState.CmdTwo: 46 FrameState = myEnums.FrameCheckState.LenghtOne; 47 bgetLength[0] = frameBuffer[NowIndex]; 48 NowIndex++; 49 break; 50 51 case myEnums.FrameCheckState.LenghtOne: 52 FrameState = myEnums.FrameCheckState.LenghtTwo; 53 bgetLength[1] = frameBuffer[NowIndex]; 54 getLength = BitConverter.ToInt16(bgetLength, 0); 55 NowIndex++; 56 break; 57 58 case myEnums.FrameCheckState.LenghtTwo: 59 //获取到的帧长度超过缓冲池的长度,或者比12小,说明接收到的数据是错误的 60 //12是因为能接收到的信息,最短的长度为12. 61 if (getLength > frameBuffer.Length || getLength < 12) 62 { 63 FrameState = myEnums.FrameCheckState.HandleStart; 64 return; 65 } 66 67 //6是因为 ‘<<‘+cmd+length=6,NowIndex此时指在length的下一位。 68 if (NowIndex > GetIndex) 69 { 70 if (getLength > (GetIndex + frameBuffer.Length - NowIndex + 6)) 71 return; 72 } 73 else 74 { 75 if (getLength > GetIndex - NowIndex + 6) 76 return; 77 } 78 79 DataBeans.Frame myFrame = new DataBeans.Frame(); 80 byte[] myData = new byte[getLength]; 81 myFrame.cmd = getCmd; 82 myFrame.length = getLength; 83 myFrame.data = myData; 84 85 myFrame.data[0] = 0x3C; 86 myFrame.data[1] = 0x3C; 87 myFrame.data[2] = bgetCmd[0]; 88 myFrame.data[3] = bgetCmd[1]; 89 myFrame.data[4] = bgetLength[0]; 90 myFrame.data[5] = bgetLength[1]; 91 for (int i = 6; i < getLength; i++) 92 { 93 myFrame.data[i] = frameBuffer[NowIndex]; 94 NowIndex++; 95 } 96 97 //Console.Write("\t\t"); 98 //for (int i = 0; i < myFrame.data.Length;i++ ) 99 //{ 100 // Console.Write(myFrame.data[i].ToString("X2") + " "); 101 //} 102 //Console.WriteLine(); 103 104 //帧的最后的确是结束标记‘>>‘,视为完整的一帧,送入队列 105 if (myFrame.data[getLength - 1] == ‘>‘ && myFrame.data[getLength - 2] == ‘>‘) 106 DataBeans.DataLists.setFrame(myFrame); 107 //否则的话,不是一个正确的帧,回退到起始标记的下一位处重新做帧扫描 108 else 109 NowIndex = indexTmp; 110 111 FrameState = myEnums.FrameCheckState.HandleStart; 112 break; 113 114 default: 115 FrameState = myEnums.FrameCheckState.HandleStart; 116 break; 117 } 118 } 119 }
呵呵,0x3C=‘<‘ 0x3E=‘>‘
自己写的一个帧处理代码,请多多指教
时间: 2024-11-04 12:22:23