1. 程式人生 > >SONY VISCA協議及其簡單認識

SONY VISCA協議及其簡單認識

最近在搞visca協議,在這裡寫寫,算是個記錄。

visca是索尼公司搞出來的,用來控制相機的協議,一般通過rs232來通訊(看了些資料,也有用rs485的)。

一、命令格式

命令通訊的基本單元稱為包(Packet)。一個包的長度為3到16位元組,由頭部、訊息體和結束符三部分組成。命令包的第一個位元組稱為命令頭(Header)。高半位元組由1 (最高位,固定為1)和傳送方(控制者)地址(地址一般為0)組成,低半位元組由0和裝置(相機)地址(或稱“編號”)組成,從組成格式看,可以外接的相機最多有7臺,如向1號相機發送命令,則命令頭為0x81。命令包最後一個位元組為終結符號,固定為0xff。中間部分位元組稱為訊息體。協議說明文件中將命令頭寫成“8x”,其中x表示相機地址。

命令共2類:普通命令(Command)及查詢命令(Inquiry)。前者是直接傳送命令到相機,後者是從相機獲取到資料。
具體的命令包格式如下:
8X QQ RR … FF
其中QQ為命令分類,01表示普通命令,09表示查詢命令。RR為類別碼(Category code)。X表示相機地址。範圍1~7。

二、響應

每個命令均有響應包,格式如下:
X0 … … FF
其中X範圍為9~F,數值為相機編號+8。以FF結束。傳送普通命令時,相機會返回ACK響應,但查詢命令不會返回ACK。

ACK響應包格式:X0 41 FF
普通命令響應包格式:X0 51 FF
查詢命令響應包格式:X0 51 ... FF
其中,X範圍為9~F,是相機地址值+8。查詢命令的響應包中帶有資料,每種資料均不相同,可以詢查協議文件。

錯誤資訊格式如下:
語法錯誤:X0 61 02 FF
命令取消:X0 61 04 FF
沒有socket:X0 61 05 FF
命令沒有執行:X0 61 41 FF
其中X的值和上面的一樣。“socket”的範圍暫時還不太瞭解。這些值就是程式碼做出判斷的依據。

三、協議文件備註
對於協議文件中qprs這類的描述方式,直接將其放到16位的十六進位制資料的各項(十六進位制格式為0xAAAA)中即可。比如一個命令的響應包格式為“y0  50  0p 0q  0r  0s  FF”,則實際得到的資料是0xpqrs。如“01 02 03 04”,對應資料為0x1234。反之亦然。在程式碼中用移位來實現即可。下面看幾個經典的命令格式。

1、不帶引數的命令
相機上電CAM_Power命令格式: 8x  01  04  00  02  FF
“8x”中的“x”表示相機編號。此類命令,直接按命令欄位來組裝即可。

2、帶引數的命令
變焦CAM_Zoom命令格式為:8x  01  04  47  0p 0q  0r  0s  FF。
“0p 0q 0r 0s”中的pqrs組成focus position引數。組裝命令時,要將這個引數依次移位到對應的欄位。假設引數值為0x1234,則對應的欄位為“01 02 03 04”。
CAM_AFMode命令可以設定Active/Interval Time兩個值,格式為:8x  01  04  27  0p 0q  0r  0s  FF
“0p 0q”對應於movement time,“0r 0s”對應於Interval,組裝命令時,要分別進行組裝。方式見上。

3、查詢類命令,不帶引數
像CAM_PowerInq查詢命令,傳送8x  09  04  00  FF,直接返回y0  50  02  FF或y0  50  03  FF
其中“y0  50  02  FF”是返回的資料,y值為相機編號+8。對於此類命令,直接讀取第3個位元組即可得到對應的狀態。

4、查詢類命令,帶引數
像CAM_ZoomPosInq命令,傳送8x  09  04  47  FF,返回y0  50  0p 0q  0r  0s  FF
在查詢命令中,有大部分的命令是帶有可變資料的,“y0  50  0p 0q  0r  0s  FF”中的“0p 0q 0r 0s”需要移位後才能知道確切的值,對應的值為0xpqrs。

四、實現

很慶幸,關於visca,已經有libvisca開源專案了(見參考資源)。下面參考這個專案,做了一些小修改,寫一下關鍵的實現程式碼。

關於串列埠的開啟、讀寫、關閉,在此不再多說。下面說一下命令的封裝:

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. void _visca_append_byte(VISCAPacket_t *packet, unsigned char byte)  
  2. {  
  3.     packet->bytes[packet->length]=byte;  
  4.     (packet->length)++;  
  5. }  
  6. void _visca_init_packet(VISCAPacket_t *packet)  
  7. {  
  8.     // set it to null
  9.     memset(packet->bytes, '\0'sizeof(packet->bytes));  
  10.     // we start writing at byte 1, the first byte will be filled by the
  11.     // packet sending function(_visca_send_packet). This function will also append a terminator.
  12.     packet->length=1;  
  13. }  
save_snippets.png
void _visca_append_byte(VISCAPacket_t *packet, unsigned char byte)
{
    packet->bytes[packet->length]=byte;
    (packet->length)++;
}

void _visca_init_packet(VISCAPacket_t *packet)
{
    // set it to null
    memset(packet->bytes, '\0', sizeof(packet->bytes));
    // we start writing at byte 1, the first byte will be filled by the
    // packet sending function(_visca_send_packet). This function will also append a terminator.
    packet->length=1;
}

這兩個函式是命令的填充,每次呼叫_visca_append_byte就填充一個字元,在未填充前,要呼叫_visca_init_packet來初始化包的長度。當然,實現上也可以直接用陣列形式把每個命令合到一起傳送出去。_visca_send_packet是填充頭部和尾部資料,無須呼叫者進行考慮,呼叫者只需關注實際的命令資料即可。這也是使用了VISCAInterface_t的好處(後面寫pelco實現的將會看到)。

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. int32_t _visca_send_packet(VISCAInterface_t *iface, VISCACamera_t *camera, VISCAPacket_t *packet)  
  2. {  
  3.     // check data:
  4.     if ((iface->address>7)||(camera->address>7)||(iface->broadcast>1))  
  5.     {  
  6.         com_print("(%s): Invalid header parameters\n",__FILE__);  
  7.         com_print("addr: %d %d broadcast: %d(0x%x)\n",iface->address,camera->address,  
  8.                     iface->broadcast,iface->broadcast);  
  9.         return VISCA_FAILURE;  
  10.     }  
  11.     // build header:
  12.     packet->bytes[0]=0x80;  
  13.     packet->bytes[0]|=(iface->address << 4);  
  14.     if (iface->broadcast>0)  
  15.     {  
  16.         packet->bytes[0]|=(iface->broadcast << 3);  
  17.         packet->bytes[0]&=0xF8;  
  18.     }  
  19.     else
  20.     {  
  21.         packet->bytes[0]|=camera->address;  
  22.     }  
  23.     // append footer(0xff)
  24.     _visca_append_byte(packet,VISCA_TERMINATOR);  
  25.     return _visca_write_packet_data(iface,packet);  
  26. }  
save_snippets.png
int32_t _visca_send_packet(VISCAInterface_t *iface, VISCACamera_t *camera, VISCAPacket_t *packet)
{
    // check data:
    if ((iface->address>7)||(camera->address>7)||(iface->broadcast>1))
    {
        com_print("(%s): Invalid header parameters\n",__FILE__);
        com_print("addr: %d %d broadcast: %d(0x%x)\n",iface->address,camera->address,
                    iface->broadcast,iface->broadcast);
        return VISCA_FAILURE;
    }

    // build header:
    packet->bytes[0]=0x80;
    packet->bytes[0]|=(iface->address << 4);
    if (iface->broadcast>0)
    {
        packet->bytes[0]|=(iface->broadcast << 3);
        packet->bytes[0]&=0xF8;
    }
    else
    {
        packet->bytes[0]|=camera->address;
    }

    // append footer(0xff)
    _visca_append_byte(packet,VISCA_TERMINATOR);

    return _visca_write_packet_data(iface,packet);
}

命令響應包函式如下:

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. int32_t _visca_get_reply(VISCAInterface_t *iface, VISCACamera_t *camera)  
  2. {  
  3.     // first message: -------------------
  4.     if (_visca_get_packet(iface)!= VISCA_SUCCESS)  
  5.         return VISCA_FAILURE;  
  6.     iface->type=iface->ibuf[1]&0xF0;  
  7.     // skip ack messages
  8.     while (iface->type==VISCA_RESPONSE_ACK)  
  9.     {  
  10.         if (_visca_get_packet(iface)!=VISCA_SUCCESS)  
  11.             return VISCA_FAILURE;  
  12.         iface->type=iface->ibuf[1]&0xF0;  
  13.     }  
  14.     switch (iface->type)  
  15.     {  
  16.     case VISCA_RESPONSE_CLEAR:  
  17.         return VISCA_SUCCESS;  
  18.         break;  
  19.     case VISCA_RESPONSE_ADDRESS:  
  20.         return VISCA_SUCCESS;  
  21.         break;  
  22.     case VISCA_RESPONSE_COMPLETED:  
  23.         return VISCA_SUCCESS;  
  24.         break;  
  25.     case VISCA_RESPONSE_ERROR:  
  26.         return VISCA_CMDERROR;  
  27.         break;  
  28.     }  
  29.     return VISCA_FAILURE;  
  30. }  
save_snippets.png
int32_t _visca_get_reply(VISCAInterface_t *iface, VISCACamera_t *camera)
{
    // first message: -------------------
    if (_visca_get_packet(iface)!= VISCA_SUCCESS)
        return VISCA_FAILURE;

    iface->type=iface->ibuf[1]&0xF0;

    // skip ack messages
    while (iface->type==VISCA_RESPONSE_ACK)
    {
        if (_visca_get_packet(iface)!=VISCA_SUCCESS)
            return VISCA_FAILURE;
        iface->type=iface->ibuf[1]&0xF0;
    }

    switch (iface->type)
    {
    case VISCA_RESPONSE_CLEAR:
        return VISCA_SUCCESS;
        break;
    case VISCA_RESPONSE_ADDRESS:
        return VISCA_SUCCESS;
        break;
    case VISCA_RESPONSE_COMPLETED:
        return VISCA_SUCCESS;
        break;
    case VISCA_RESPONSE_ERROR:
        return VISCA_CMDERROR;
        break;
    }

    return VISCA_FAILURE;
}

裡面一些巨集定義如下:

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. /* response types */
  2. #define VISCA_RESPONSE_CLEAR             0x40
  3. #define VISCA_RESPONSE_ADDRESS           0x30
  4. #define VISCA_RESPONSE_ACK               0x40
  5. #define VISCA_RESPONSE_COMPLETED         0x50
  6. #define VISCA_RESPONSE_ERROR             0x60
save_snippets.png
/* response types */
#define VISCA_RESPONSE_CLEAR             0x40
#define VISCA_RESPONSE_ADDRESS           0x30
#define VISCA_RESPONSE_ACK               0x40
#define VISCA_RESPONSE_COMPLETED         0x50
#define VISCA_RESPONSE_ERROR             0x60

其實判斷也十分簡單,就是根據協議給出的錯誤碼來一一判斷。
其它的程式碼,直接參考libvisca即可,不在這裡列出。

參考資源:

後記:截止本文編寫時,手上還沒有得到硬體資源,文章是根據libvisca和visca協議文件中的描述來寫的,應該不具有實踐價值。

李遲記於2014年6月30日