通信过程中的控制命令扩展方案
在制作东西的过程中用通信来进行数据和控制命令传输是经常的事,即使需要传输的内容全部是数据,我们也希望能有一个起始信号标志一帧数据的开始。
但在实际实际使用时经常会遇到控制命令不好选择的情况,就拿传输数据的起始信号来说,假设这里用的是串口传输,传输的单位是字节。
如果传输的数据有范围限制,我们就可以从数据不可能涉及到的范围中选取控制信号。比如之前做过的一个机器人(http://v.youku.com/v_show/id_XMzA2NjUyNDU2.html 两年前做的,大家瞧瞧哈)需要通过串口传输各个舵机的脉宽值,脉宽值相当于数据,它的范围只能能在50~250之间,在0~49 : 251~255都可以作为控制信号。实际使用正是用0xff标志机器人全身17个脉宽信号的到来。
这是简单的情况,如果说数据可能涉及到的范围覆盖了0~255,那就不能再从中选取控制信号了,因为接收方无法判断到底是数据信号还是控制信号。
今天的内容就是讨论如何在数据信号覆盖到的范围内扩展出控制信号。
1、能想到的最简单的方法就是加线。添加一根新的线来标志传输的开始,或者用加出来的线的高低电平区分控制信号和数据信号。
2、还有一种常用的方法就是接收端通过计时判断一帧数据的开始。因为发送端在连续发送数据时,数据之间的时间间隔是非常小的,接收端每次接收到数据都开始计时,如果下一个数据来临比较快则认为是紧接着上个数据连续传输的,而如果隔一段时间没有数据传输则从头开始计数。
3、如果对数据精度要求不高,可以腾出一个数据值作为控制信号。比如把0xff腾出来作为控制信号,而数据中如果出现0xff一律换成0xfe。
以上几种方法都用过,各有优缺点,最近又想出一种新方法——添加上层协议,规定一个通用的传输协议,只要在原来传输的基础上套用一下这个协议就可以从数据范围内扩展出控制信号。
这种方法貌似比上面3种都好,不需要像1那样扩展硬件,也不需要像2那样不能连续发送太快,也不会降低数据精度。
下面就介绍一下我用的方法:
假设数据信号可能是0~255任意一个数值,现在我要扩展出若干个控制信号。
首先,我选取一段控制信号范围,定义一个常量MinCtrl,选取MinCtrl ~ 255都可作为控制信号,0 ~ MinCtrl-1 作为数据信号。
这样扩展出了255-MinCtrl+1个控制信号(实际可以使用的控制信号有255-MinCtrl个,后面再介绍原因),但也带来一个问题:如果数据信号在MinCtrl ~ 255 之间改怎么传输?
我采用的方法是这样的:发送端检测到数据dat在MinCtrl ~ 255之间就会先发送一个MinCtrl,再发送一个dat – MinCtrl。接收端接收到MinCtrl之后认为下一个字节加上MinCtrl才是一个完整的数据。
发送端的拓扑图如下:
接收端的拓扑图如下:
说明:
1、MinCtrl作为协议自己使用的控制信号,用户实际可使用的控制信号是MinCtrl+1 ~ 255。
2、为了保证“数据和信号值不可能相同”的原则,MinCtrl最小值是128(0x80),也就是说最多可扩展出127个可使用的控制信号。
2、如果数据在0 ~ MinCtrl-1 范围内,只需发送一个字节就可传输数据;如果数据在MinCtrl ~ 255 范围内,需要发送两个字节传输数据。
所以,MinCtrl取得越小,需要2个字节传输数据的概率就越大,所以根据实际需要选取MinCtrl的值,不要选得过小,这样不利于快速传输。
下面是协议驱动程序:
/*
单字节传输数据时,如果数据可能会占用0~255所有的值,则起始信号判断是个普遍问题。
本文件是发送端和接收端的数据处理程序,在原有单字节传输的基础上套用本协议,可以从单字节传输中扩展出最多127个控制信号。
*/
/*最小控制信号MinCtrl
采用的方法是这样的:从 0~MinCtrl-1 仍然作为数据,这个范围内的数据一个字节便可传输;
从MinCtrl到0xff腾出来作为控制信号,其中MinCtrl是特殊的控制信号,它标志着
下一个字节的数据要加上MinCtrl作为真正接收到的数据。在发送端,当要发送的数据
dat>=MinCtrl时,将分为两个字节发送,第一个字节发送MinCtrl,第二个字节发送
dat-MinCtrl。
在 ( MinCtrl, 0xff ] 范围内的数可以单独作为控制信号。
注:
MiniCtrl取得越小,可以容纳的控制信号越多,但是数据需要发送两个字节的概率
就越大。所以根据具体传输的需求,让MinCtrl尽可能大,这样需要两个字节传输数据的
概率就小,会提高传输速度;
MinCtrl最小值是0x80,如果MinCtrl小于0x80,则下一字节的数据会大于MinCtrl,
为了不让数据与控制信号出现同样的值,规定 MinCtrl>=0x80;
发送端和接收端的 MinCtrl 必须定义相同的值。*/
//发送端:
#define MinCtrl 0xfe//( MinCtrl, 0xff ] 可用作控制信号,发送端和接收端这个定义应当相同
/********************扩展处控制信号的数据发送函数***************************/
void ExpendedSend(unsigned char dat)
{
if(dat<MinCtrl)//可以直接发送
{
//下面是原来发送一个字节的函数
SCI_sendB(dat);
}
else//说明在控制信号范围内,要分两次发送
{
//下面是原来发送一个字节的函数
SCI_sendB(MinCtrl);
delayForSend();//这是连续发送两字节的延时,根据接收端的响应速度调整
SCI_sendB(dat-MinCtrl);
}
}
/***************************************************************************/
/*************************扩展出控制信号的数据接收函数*********************
把接收到的数据传进此函数,在此函数中判断是控制信号还是数据,以及根据约定好的协议
得出数据的值。
/***************************************************************************/
void ExpendedRec(unsigned char get)
{
static unsigned char FlagMinCtrl=0;//标志本次接收到的数据是否需要加上MinCtrl,0-不要,1-要
unsigned char dat;//这是经过协议计算后的真正接收到的数据
if(get==MinCtrl)//说明下一个字节加上MinCtrl就是一个数据
{
FlagMinCtrl=1;
goto RecEnd;
}
else if(get>MinCtrl)//说明在控制信号范围内
goto DealCommand;
else//get在数据范围内,肯定标志着接收到了一个数据,但还要判断是否需要加上MinCtrl
{
if(FlagMinCtrl==1)//要
{
FlagMinCtrl=0;
dat=get+MinCtrl;
goto DealData;
}
else if(FlagMinCtrl==0)//不要,本次数据就是一个小于MinCtrl的数据
{
dat=get;
goto DealData;
}
}
DealCommand:
//当前的get就是命令,下面是对命令的处理
return;
DealData:
//当前dat的值就是接收到的数据,下面对其处理
return;
RecEnd://结束
return;
}
/********************************************************************************/