通信过程中的控制命令扩展方案

nicekwell 2013-07-23 09:28:26
加精
在制作东西的过程中用通信来进行数据和控制命令传输是经常的事,即使需要传输的内容全部是数据,我们也希望能有一个起始信号标志一帧数据的开始。
但在实际实际使用时经常会遇到控制命令不好选择的情况,就拿传输数据的起始信号来说,假设这里用的是串口传输,传输的单位是字节。
如果传输的数据有范围限制,我们就可以从数据不可能涉及到的范围中选取控制信号。比如之前做过的一个机器人(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;
}
/********************************************************************************/

使用方法:
发送端只要将需要发送的数据传给这里的发送函数,这里的发送函数会根据协议调用原来的发送函数发送数据。
接收端只要将接收到的原始数据传递给这里的接收函数,在接收函数的指定位置加入相应的对数据和命令的响应代码就行了。

扩展控制命令的方法有很多,这里介绍的只是我用的方法,希望能够抛砖引玉,听听大家的方法。
...全文
3609 60 打赏 收藏 转发到动态 举报
写回复
用AI写文章
60 条回复
切换为时间正序
请发表友善的回复…
发表回复
迷茫的行者 2013-12-02
  • 打赏
  • 举报
回复
学习了。需要好好理解下。
帝国疯子 2013-08-08
  • 打赏
  • 举报
回复
怎么做到的啊。、实在是不容易啊、
帝国疯子 2013-08-08
  • 打赏
  • 举报
回复
不错学习了、、、、、
Marco_oracle 2013-08-05
  • 打赏
  • 举报
回复
学习学习学习学习
mbugaifc 2013-08-04
  • 打赏
  • 举报
回复
pzpss 2013-08-03
  • 打赏
  • 举报
回复
laoyue888 2013-08-02
  • 打赏
  • 举报
回复
真的不错,回去好好学学哈!
nicekwell 2013-08-02
  • 打赏
  • 举报
回复
引用 44 楼 zhouzhipen 的回复:
如果你的设备不需要第三方接入控制,完全没有必要用MODBUS之类的标准协议,这些协议虽然有完整的资料,但是作为通用协议,对于某些单片机来说,处理起来比较困难。 类似于你提到的问题,使用定长包头+数据的方式很容易解决,比如: 数据包头可以使用3个字节,第一个字节表示控制码,第二三个字节表示后续数据长度。 在接收时,只要先接收包头,解析出长度,再按长度接收数据,一个数据帧就接收完成了。 其实在包头可以增加其它任何东西,比如校验码,PACKET ID,等。 总之适合就好,不要追求什么“标准”。
我现在已经习惯于使用数据头+数据的格式了,至于数据头如何标志习惯于使用转义符。 在看了楼上们的回答后我发现我的方法也是转义符,不过转义的是数据而不是控制命令。 一般所说的转义符是通过一个特殊符号来标志下个字节的特殊含义,一般这个字节作为控制码,大部分数据吗则以普通非转义方式传输; 而我说的方法是通过一个特殊符号(MinCtrl)来标志下一个字节是一个特殊段(>=MinCtrl)的数据码。 从结构上来说我的方法确实较复杂,不过它的好处是能直接用一个字节传输控制码,不像转义需要两个字节。 但是这种需要是很少的,除非控制码的发送频率高于数据,所以现在一般情况我都是用转义符来传输控制码(当然也包括数据头)。 但也如你所说带来了另一个问题,如果其他用户想要和我通信的话也必须按照转义符的格式收发数据。 不管这么多了,Modbus协议暂时也没研究,等到实在跟用户无法沟通时再使用通用协议吧~~~
vssvss 2013-08-01
  • 打赏
  • 举报
回复
引用 30 楼 PALLEE 的回复:
向高手学习!
母校
弈轩 2013-08-01
  • 打赏
  • 举报
回复
窘相 2013-08-01
  • 打赏
  • 举报
回复
向高手学习!
zhouzhipen 2013-07-31
  • 打赏
  • 举报
回复
如果你的设备不需要第三方接入控制,完全没有必要用MODBUS之类的标准协议,这些协议虽然有完整的资料,但是作为通用协议,对于某些单片机来说,处理起来比较困难。 类似于你提到的问题,使用定长包头+数据的方式很容易解决,比如: 数据包头可以使用3个字节,第一个字节表示控制码,第二三个字节表示后续数据长度。 在接收时,只要先接收包头,解析出长度,再按长度接收数据,一个数据帧就接收完成了。 其实在包头可以增加其它任何东西,比如校验码,PACKET ID,等。 总之适合就好,不要追求什么“标准”。
bzzsky 2013-07-28
  • 打赏
  • 举报
回复
Thinks!
wyld 2013-07-28
  • 打赏
  • 举报
回复
谢谢分享!!!!!!!
BAOo00 2013-07-27
  • 打赏
  • 举报
回复
正在学习中,谢谢你的分享。。。
落痕 2013-07-27
  • 打赏
  • 举报
回复
Modbus 去看看。。
lxj808 2013-07-26
  • 打赏
  • 举报
回复
通信就是要一套好的协议来做规范,我收藏有时间看看。
ybj9456 2013-07-26
  • 打赏
  • 举报
回复
正在学习中,谢谢你的分享。。。
cn在此 2013-07-25
  • 打赏
  • 举报
回复
学习了,加油
liuyuanrs 2013-07-25
  • 打赏
  • 举报
回复
好东西,收藏了
加载更多回复(28)

27,373

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧