一、通信协议的制订
1、帧头。
这个是用来识别帧的,不同的帧有不同的帧头,就好像两个人有两个不同的名字,这样才能将他们区别开来。
2、帧数据。
我们所要传输的信息都应该包含在“帧数据”这部分中。在此部分中,要确定以下几点。
①帧长度:这个帧一共包含多少字节的数据。
②目标地址:目前的这帧数据是要发给谁。
③自身ID:这帧数据是由我发送出去的,那么我就要告诉接收方我的名字,混个眼熟嘛,哈哈。
④用户指令:这帧消息发出去,是要求接收方去完成什么。
⑤指令参数:这个也可以说是用户指令的延伸,假如说目标地址是某个工作小组的地址,然后我发指令给他们,要求小组去完成某件事,但有可能只需要小组中的某一个成员去做就行了,所以我就要在后面说明是要哪个成员去做事,当然,这个延伸还可以继续下去,比如说,我指定了某个人,还可以指定是哪件事,或者我还可以指定做的时间、地点、用什么方式,等等。
⑥校验码:发送出去的数据很有可能在中途发生事故,比如说有个邮递员去送信,途中被人拉去喝酒了,结果喝着喝着把信件给弄丢了,或是他在迷迷糊糊中,把给张三的信给了李四,而把给李四的信给了王五,那王五的信呢?哦,对了,我就说刚刚掉了什么东西呢。经过这一事件,用户向邮局投诉,于是邮局就派了个人来监督邮递员,以免他再去喝酒,当然,在实际中这个很不划算,因为这就等于两个人送信了嘛,而且很有可能邮递员会把监督的人拉去一起喝酒,哈哈哈。这个监督的人就相当于协议中的校验码,当然,它是不喝酒的。
上述几点就是这个通信协议的全部内容了,应该很简单吧!另外,我们还要确定这些数据发送的顺序,帧头当然是第一个,然后是帧长度,为什么呢?因为这样设计使得数据在接收时校对比较方便,即第一个数据是帧头,校对结果是对的,然后我们校对第二个数据——帧长度,如果长度对了,那说明接收到的数据个数没有差,然后就可以进行数据内容的校验了。为了后面命令解析时更加方便,所以我们的顺序就这么来:帧头、帧长度、目标地址、自身ID、用户命令、指令参数、校验码。
二、多字节协议的接收处理
下面我们来看看如何对协议数据进行接收处理(以单片机串口通信为例)。
1、接收
首先,我们要在串口中断里判断接收到的第一个字节即帧头是否正确,如果帧头正确我们就继续接收数据,并进行计数,记录每一次连续接收是多少个字节,然后再与帧长度进行核对。这里要特别说明一下:如何判断我们接收到的数据是需要的数据呢?首先,要用一个数组来存放接收到的数据,最先进行帧头的校对,因为单片机串口每次中断只能发送且只能接收一个字节(8bit数据),而每帧数据是连续发送的,所以我们每接收到一个字节都要判断本次连续接收到的第一个字节是不是帧头,如果是则加1,如果不是,则计数清0。而每接收完一个字节都要判断计数变量的值是否与帧长度相等,相等则清0计数变量,并置接收完毕标志。然后在主程序里,我们判断接收完毕标志是否置位,如果置位则开始进行命令解析。
串口接收多字节协议参考代码如下:
Buffer_Tx[tt]=Getchar();//保存一个字节
//接收缓冲器SBUF一次只能接收一个字节,所以是每接收一个字节中断一次。
if(Buffer_Tx[0]!=0xfd)
tt=0;//如果不是帧头,则置0重新接收
else
tt++;
if(tt==Buffer_Tx[1])//如果收到的数据帧的长度与数据帧第二个字节相等,则表明正确接收
{
/*(协议规定数据帧的第二个字节为帧长度)*/
tt=0;
flag_RecTure=1;//正确接收标志置1
}
2、命令解析
1、判断帧头及帧长度是否正确。
2、在1基础上,判断校验码是否正确。校验码正确则表示数据完全正确。
3、在2的基础上,将目标地址与对象属性相匹配。
4、在3的基础上,对单片机ID进行匹配,选择具体的执行器件。
5、在4的基础上,进行命令参数匹配,并执行相应的命令。
参考代码如下:
voidCheck_Com(void)
{
if(RV->Frame_Head==Fr_Head&&RV->Frame_Length==Fr_LengthCom)//协议第1、2字节的核对
{
if(RV->CRC_Dat==CRC_Check(Buffer_Tx,14))//CRC校验,正确则表明数据接收完全正确
{
if(RV->TargetS_Address==0xff)//目标短地址核匹配
{
if(Check_SCM_Add())//单片机ID匹配
{
switch(RV->User_Com)//用户命令
{
case0x01://联网指令
Send_TureCom();//发送握手正确包
break;
}
}
}
}
}
}
其他说明
1、使用结构体可以在很大程度上增加程序的可读性。我们可以定义一个与协议帧数据相对应的结构体指针,然后指向进行数据接收的数组的地址,这样我们就可以通过操作结构体成员来操作数组成员了,非常的直观。
2、switch...case语句的可读性要远远好于if...else语句。