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?- 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;
- }
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?- 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);
- }
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?- 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;
- }
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?- /* 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
/* 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日