您现在的位置: 爱51代码网 >> 范文 >> 文章正文
[组图]单片机通信过程中的控制命令扩展方案

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

在制作东西的过程中用通信来进行数据和控制命令传输是经常的事,即使需要传输的内容全部是数据,我们也希望能有一个起始信号标志一帧数据的开始。
但在实际实际使用时经常会遇到控制命令不好选择的情况,就拿传输数据的起始信号来说,假设这里用的是串口传输,传输的单位是字节。
如果传输的数据有范围限制,我们就可以从数据不可能涉及到的范围中选取控制信号。比如之前做过的一个机器人(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; } /********************************************************************************/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
单字节传输数据时,如果数据可能会占用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;
}
/********************************************************************************/


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

扩展控制命令的方法有很多,这里介绍的只是我用的方法,希望能够抛砖引玉

发送端拓扑图画错了,dat<MinCtrl? 改为 dat>=MinCtrl? 尽量都在用Modbus的协议,协议开放,资料好找,而且串口,以太网都有涉及,还是国家推荐的一个工业标准通讯协议。
做数据采集的时候各厂家的通讯协议都不一样,每个设备都要单独写接口,有时候给的资料不全,或者是错的,在现场还要去改掉,痛苦得很。
有一个标准的协议至少你也有理由可以要求别的厂家来按你的要求去做,测试时候也方便,不用专门针对通讯协议写模拟器。

通讯我觉得发送好做接收难,一般接收,遇到的通讯定长,固定头这两种比较多。

定长,每次通讯数据包长度固定,数着收到一定的字节数之后解析他内容就可以了,粘包的时候比较麻烦些,一般定时发送比较多,超过多少时间没有收到就认为一个包结束了。

固定头,数据包长度有些固定,有些不固定,不过有一个可以识别的头,也有些有尾。

定长的数据包相比更好处理些,固定头的数据有时候为了防止头重码,有些是用多字节来当数据头的。

我电脑上的处理一般都采取同一办法,超时检查,超过一定的时间没有数据进来,我就认为通讯结束了,不论是定长的数据,还是有头尾的数据,接收不完整的时候你程序只能在那里等,这样处理时间上浪费了一些,但是通用性好一些,而且一般采集上也没时间要求太高的场合。

关于通讯校验,有些通讯是加校验,防止传送过程中出错,有些通讯就不加校验了,全靠拼速度,速度够快下一包的数据就把前一次的错误盖过去了,不同地方可以采集不同办法处理。

  • 上一篇文章:

  • 下一篇文章: 没有了
  • 最新文章 热点文章 相关文章
    lotus数据列表文档个数如何实时统
    lotus代理中LS如何将字符串保存到
    在lotus BS系统里怎样方便实现统
    undefined reference timer_crea
    linux文件/usr/lib破坏了,还原后
    linux上运行system函数时,print
    Failed to open eth0
    android手机无法与eclipse或电脑
    C/C++洗牌算法源代码
    servlet技术实现用户名唯一的验证
    undefined reference timer_crea
    Failed to open eth0
    C/C++洗牌算法源代码
    ZOJ 3700 Ever Dream 文章中单词
    TortoiseGit和msysGit安装及使用
    sharepoint 2010 获取用户信息Us
    设计包含max函数的队列
    mysql主从同步延迟方案解决的学习
    生日旅行总结
    中小板生日快乐随感
    STM32的AHB和APB有什么关系?
    wince5.0模拟器下用GDI实心画
    操作系统的执行中断程序后的
    vs2005开发WINCE6.0应用程序
    VS2005的MFC做WINCE5.0的人机
    嵌入式音频播放器通过软件把
    UCOS-II S3C2440 串口的中断
    wince5.0如何将xml文件发送到
    not syncing: Attempted to 
    打印一个单片机的开发板大概
     



    设为首页 | 加入收藏 | 网站地图 | 友情链接 |