一种解析串行通信帧格式的方案(C语言)
方案介绍当只使用一个缓存区解析帧格式时会有抗干扰能力弱的问题。本方案是采用多个缓存区解析并缓存数据当接收到第一个帧头时会启用一个空闲状态缓冲区缓存收到第二个帧头时会再启用另一个空闲状态缓冲区缓存如此……示意图如下B4AC为帧头。根据帧头确定帧长度再根据帧长度确定校验和帧尾的位置验证数据帧的正确性如果数据帧不正确或者正确且使用完了数据清空该数据帧所在的缓冲区状态变为空闲等待后续再使用。帧格式程序.C#includeFrameFormat.hTargetFramePara FramePara{FRAME1HEAD1,FRAME1HEAD2,FRAME1TAIL1,FRAME1TAIL2,MAXDATALEN};//目标帧的帧头、帧尾和帧长定义FrameAnalysis Frame1;//解析帧格式的结构体//FrameFormatStu SendFrame {0xB4,0xAC,0,0,0,0xf1,0xf2};//发送的帧格式/*************************************************************************************** *函数名称 GetDataAndAnalysis *函数的功能和目的 拿到串口中断函数接收的数据并分析符不符合帧格式 *输入参数 FrameAnalysis *Framex : 存放帧格式及在解析过程中必要的参数 TargetFramePara FrameParax 存放了目标帧的帧头、帧尾和帧长 uint8_t InputData 输入数据 *输出参数 void *返回值 void ***************************************************************************************/voidGetFrameAnalysisResult(FrameAnalysis*Framex,TargetFramePara FrameParax,uint8_tInputData){uint8_ti;uint8_tHeadVerify0;uint8_t*p_Data;uint16_tusLengthC0;uint8_tucTemp0;for(i0;iPIPELINENUM;i){p_Data(Framex-FrameArray[i].ucHead1);//Framex-FrameArray[i].ucHead1if(p_Data[0]FrameParax.ucHead1p_Data[1]FrameParax.ucHead2)//已经匹配上了帧头{p_Data[Framex-usRxNum[i]]InputData;//缓存新数据if((Framex-usRxNum[i])3(Framex-usRxNum[i](Framex-FrameArray[i].usLength-3)))//判断在计算校验的范围{Framex-ucCheckout[i]^InputData;//计算校验}else{//;not deal with}if(Framex-usRxNum[i]3)//刚接收完帧长度{usLengthCFramex-FrameArray[i].usLength;if(usLengthCFrameParax.usMaxLength||usLengthC12)//接收的数据长度大于存储区存在数组越界行为或者小于最短帧长度{memset(p_Data,0x00,FrameParax.usMaxLength);//清空缓存区//Framex-usRxNum[i] 0; //清除计数continue;}else{Framex-ucCheckout[i]p_Data[0]^p_Data[1]^p_Data[2]^p_Data[3];//在这定义检验的初始值}}elseif(Framex-usRxNum[i]Framex-FrameArray[i].usLength-1)//刚接收完最后一个字节{Framex-ucRecFlag|0x80;//接收完成标志Framex-ucIndexi;Framex-ucDataLengthFramex-FrameArray[i].usLength-12;ucTempFramex-usRxNum[i]-2;if((Framex-ucCheckout[i]^0xff)p_Data[ucTemp])//校验正确{Framex-ucRecFlag|0x40;//校验成功标志}else{//;not deal withmemset(p_Data,0x00,FrameParax.usMaxLength);//清空缓存区}if((FrameParax.ucTail1p_Data[ucTemp1])(FrameParax.ucTail2p_Data[ucTemp2]))//校验正确{Framex-ucRecFlag|0x20;//帧头成功标志}else{//;not deal withmemset(p_Data,0x00,FrameParax.usMaxLength);//清空缓存区}}else{//;not deal with}Framex-usRxNum[i]1;}elseif(InputDataFrameParax.ucHead1)//输入数据与帧头第一个字节相同{if(HeadVerify0)//且没有被确认过{p_Data[0]InputData;Framex-usRxNum[i]1;HeadVerify0x01;//确认过帧头第一个字节}else//已被确认过{p_Data[0]0;//帧头清零 即使不处理也没问题因为肯定是索引小的先匹配上帧头后边的帧头为0Framex-usRxNum[i]0;}}elseif(InputDataFrameParax.ucHead2)//输入数据与帧头第二个字节相同{if(p_Data[0]FrameParax.ucHead1)//帧头的第一个数据已经匹配过{p_Data[1]InputData;Framex-usRxNum[i]2;}else//帧头的第一个数据未匹配过不处理{// ;not deal with}}}}/*************************************************************************************** *函数名称 ExecuteCommand *函数的功能和目的 执行具体的命令操作 *输入参数 FrameAnalysis *Framex : 存放帧格式及在解析过程中必要的参数 *输出参数 void *返回值 void ***************************************************************************************/voidExecuteCommand(FrameAnalysis*Framex){uint8_tucIndex;uint16_tTemp;FrameFormatStu*p_Data;//结构体类型指针if((Framex-ucRecFlag0xe0)0xe0)//数据帧完整的标志{ucIndexFramex-ucIndex;p_DataFramex-FrameArray;switch(p_Data[ucIndex].ucCommand){case0x40:LED0!LED0;break;case0x41:break;case0x42:break;case0x43:break;case0x44:break;default:break;}Framex-ucRecFlag0;//清除解析的标志memset(p_Data[ucIndex],0x00,p_Data[ucIndex].usLength);//清空接收完成的缓存数据}else{//;not deal with}}.h#includesys.h#includestring.h#ifndef__FRAMEFORMAT_H#define__FRAMEFORMAT_H/********************************************************宏定义*******************************************************************/#defineFRAMEDATALEN201//支持的最长数据段长度#definePIPELINENUM5//支持最多同时缓存帧数据的数量#defineFRAME1HEAD10xB4//帧头首个字节#defineFRAME1HEAD20xAC//帧头第2个字节#defineFRAME1TAIL10xB2//帧尾首个字节#defineFRAME1TAIL20xB0//帧尾第2个字节#defineMAXDATALEN(FRAMEDATALEN12)//支持最大的帧长度/*********************************************************************************************************************************//********************************************************定义结构体***************************************************************/typedefstruct{uint8_tucHead1;//帧头uint8_tucHead2;//帧头uint16_tusLength;//帧长uint8_tucSeq;//帧号uint8_tucRetrySeq;//重发序号预留uint8_tucLocalAddr;//本机地址uint8_tucTargetAddr;//目标地址uint8_tucCommand;//命令字uint8_tucData[FRAMEDATALEN3];//最后3个字节为校验和帧尾}FrameFormatStu;//帧格式typedefstruct{FrameFormatStu FrameArray[PIPELINENUM];uint8_tucCheckout[PIPELINENUM];//校验uint16_tusRxNum[PIPELINENUM];//接收数据的数量uint8_tucRecFlag;//接收标志uint8_tucIndex;//成功的索引值uint8_tucDataLength;//数据段长度}FrameAnalysis;//缓存的帧数据、在解析过程中的校验值、接收数量、接收标志、成功的索引和数据段长度typedefstruct{uint8_tucHead1;//帧头uint8_tucHead2;//帧头uint8_tucTail1;//帧尾uint8_tucTail2;//帧尾uint16_tusMaxLength;//最大帧长度}TargetFramePara;//目标帧格式的帧头、帧尾和最大帧长/*********************************************************************************************************************************/voidGetFrameAnalysisResult(FrameAnalysis*Framex,TargetFramePara FrameParax,uint8_tInputData);voidExecuteCommand(FrameAnalysis*Framex);externTargetFramePara FramePara;externFrameAnalysis Frame1;//extern TargetFramePara FramePara;externFrameFormatStu SendFrame;#endif