当前位置: 首页 > 我的程序代码 > 正文

pelco协议及其实现的简单认识

在搞visca的同时顺便也搞了pelco。这里再做个笔记。

pelco,中文翻译为“派尔高”,在行文和写代码过程,写pelco比写“派尔高”快很多,所以一般就写pelco。这个协议在云台控制中用得比较多,比较出名的有PTZ,用rs232或rs485来通信。pelco有两种协议,D协议和P协议,两者命令封装不太一样,网上有协议的说明文档,也有实现的代码,我找到一个C#写的,某搜索引擎上找得到的介绍派尔高协议的,基本上都是直接把每个命令的数据一一写成数组来调用,个人认为这样不太方便。

本文所讲的是pelco D协议。

一、命令格式

文档中说pelco D协议的命令都是7个字节的(实际上,有些不是),命令格式如下图:


说明:

字节1:D协议同步头为0xFF。
字节2:相机编号(或称地址),范围为1~0xFF,0x01表示相机1。
字节3:CMND1,命令1。
字节4:CMND2,命令2。作为扩展命令,命令数值为奇数。
字节5:数据字节1,作为motion命令,其表示pan的速度。
字节6:数据字节2,作为motion命令,其表示tilt的速度。
字节7:检验码,计算公式:(字节2+字节3+字节4+字节5+字节6)% 0x100。注:检验码是除了同步头及本身(CKSM)字节外的所有字节的的和,再除以265,取余数(用“%”即可)。

命令共2大类,motion命令,和非motion命令(注:可以称为基本命令和扩展命令/高级命令)。其中motion命令,在CMND2中Bit0始终为0。反之,该比特为1表示非motion命令。
motion命令包含了水平移动、垂直移动、光圈、变焦、对焦等功能。这类命令是由命令1和命令2的各个位来表示的。如下图:

要实现某一项功能,只要在对应的位置设为1即可,有些命令是可以同时设置的,但有些是不行的,比如,可以同时实现左(右)转和向上(下)转动,但不能同时左转及右转(两者是互斥的)。

Bit7含义为“sense bit”(sense一词,在linux内核中使用比较多,我其实也搞不明白这个词中文该怎么表达),它决定了Bit4和Bit3的含义。如下图所示:

其实说白了,就是这3个比特的哪些组合可以控制相机上断电,自动扫描,哪些无效,等等。说到这里,想起来了公司某部门发的一个邮件,说某客户想接入云台,是用派尔高协议来控制的,然后还顺带一个中文的协议文档,这个文档翻译得不伦不类,应该是机器翻译的,把“sense bit”翻译成“感觉字节”,一开始看中文文档时,完全一头雾水,后面经过努力,上网上查了英文原版,才有点理解。所以,有时候,看英文原版获取的资源更通俗,更准确。

二、响应

相机返回的响应包有4种,0字节、4字节、7字节、18字节。除了第一种外,其它每种都和上面给出的命令格式相似,同步头+相机地址+信息体+检验码。文档中有每种响应包的具体格式的描述,如果格式来解析即可得到想要的数据。文档中说大部分命令是有返回值的——像上面的motion命令,也有响应包,但我实际测试的结果,串口是没有返回什么数据的,或者是我拿到的那个东西本身就是这样的。

三、协议文档备注

协议文档中每条命令都讲得比较细,不过,有部分命令是厂家自己实现的,比如,有些命令超过7个字节,这跟协议上讲的有冲突,但实际上却是可以的。这个主要是看厂家提供的协议文档了。说到这,不得不提现在我接手的这个任务,厂家提供的文档惜墨如金,不肯多写一点字,文档又不规范,东写一点,西写一点,给的文档也不全,有些命令还要问人家才知道。

四、实现

为了方便,专门为pelco通信定义了PELCOPacket_t结构体(其实它和上一篇文章的visca那个结构体是同一个东西),命令封装如下:

void _pelco_append_byte(PELCOPacket_t *packet, unsigned char byte)
{
    packet->bytes[packet->length]=byte;
    (packet->length)++;
}

void _pelco_init_packet(PELCOPacket_t *packet)
{
    // set it to null
    memset(packet->bytes, '\0', sizeof(packet->bytes));
    // we start writing at byte 2, the first byte and the second will be filled by the
    // packet sending function(_pelco_send_packet). This function will also append a cksm.
    packet->length = 2;
}

注意,初始化时,命令包的长度是2,因为pelco协议的真正数据是从第2个字节开始的,这让调用者关注的是真正的数据,而不用理会同步头、相机地址等信息。命令发送如下:

// All commands are 7 bytes long in D Protocol
int _pelco_send_packet(PELCOInterface_t *iface, PELCOPacket_t *packet)
{
    int cksm = 0;
    int i = 0;

    // check data:
    if (iface->address>0xff)
    {
        return PELCO_FAILURE;
    }

    // build header:
    packet->bytes[0] = 0xff;
    packet->bytes[1] = iface->address;

    // see spec, some cmd has 7 bytes, some has 9 bytes
    for (i = 0; i < packet->length; i++)
    {
        cksm += packet->bytes[1+i];
    }
    cksm  = cksm % 0x100;

    /*
    cksm = (packet->bytes[1] + packet->bytes[2] + packet->bytes[3] + 
            packet->bytes[4] + packet->bytes[5]) % 0x100;
    */
    // append footer(cksm)
    _pelco_append_byte(packet,cksm);

    return _pelco_write_packet_data(iface,packet);
    
    
}

因为有些命令不只有7个字节,所以这里用for循环计算每个需要计算的字节,而不是指定哪一些字节被计算。

命令的封装接口比较简单,基本上和visca一样,比如,像stop命令,就是所有的信息体都搞成0就行了。

int pelco_camera_stop(PELCOInterface_t *iface)
{
    PELCOPacket_t packet;

    _pelco_init_packet(&packet);
    _pelco_append_byte(&packet,0x00);
    _pelco_append_byte(&packet,0x00);
    _pelco_append_byte(&packet,0x00);
    _pelco_append_byte(&packet,0x00);

    return _pelco_send_packet_no_reply(iface, &packet);
}

参考资源:

1、一个C#实现的工程:http://www.codeproject.com/Articles/8034/Pelco-P-and-D-protocol-implementation-in-C

2、一个图文并茂的示例:http://www.commfront.com/RS232_Examples/CCTV/Pelco_D_Pelco_P_Examples_Tutorial.HTM

后记:本文出现很多次“搞”,近来的确是东搞西搞,人在公司,身不由己。本文仅对协议进行描述,不贴出实际的实现代码。

李迟记于2014年7月3日

本文固定链接: http://www.latelee.org/my-library/pelco-protocol-note.html

如无特别说明,迟思堂工作室文章均为原创,转载请注明: pelco协议及其实现的简单认识 | 迟思堂工作室

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter