0、提醒
????????CANOpen使用時,需要清楚什么是大端和小端,這對于CANOpen數據發送及解析時,有很大的幫助。且學習開發CANOpen時,需要具備一定的CAN基礎。
1、CANOpen協議介紹
①、什么是CANOpen協議
????????CANOpen協議是一種架構在控制局域網絡(Controller Area Network, CAN)上的高層通信協議,它廣泛應用于工業自動化、機械工程和汽車電子等領域。CANOpen協議通過對象字典、服務數據對象(SDO)、過程數據對象(PDO)等機制,為機器人、運動控制、過程控制、樓宇自動化、交通運輸等行業提供了一種標準化的通信解決方案。
????????CANOpen協議的標準由非營利組織CiA(CAN in Automation)進行起草、審核及維護工作。
????????CANOpen協議本身是一個單一的標準,它定義了設備間的通信協議和接口規范。
????????CANOpen協議支持從10kbps到1Mbps的波特率,具體包括10k、20k、50k、125k、250k、500k、800k和1Mbps。
????????簡單來說,CANOpen協議是一個基于CAN協議的的一個應用層協議。
②、CANOpen協議的特性
????????開放性:CANOpen是一個公開的標準,任何公司都可以使用它來開發產品,無需支付許可費用。
????????網絡管理:CANOpen協議定義了網絡管理功能,如節點自診斷、網絡監控等,使得網絡維護更加方便。
????????對象字典:CANOpen使用對象字典來描述設備的功能和狀態,使得設備之間的通信更加標準化。
????????服務:CANOpen定義了一系列的服務,如NMT(Network Management)、TPDO(Time-Triggered Protocol Data Object)、RPDO(Real-Time Protocol Data Object)等,以滿足不同的應用需求。
????????PDO(Process Data Object):PDO是CANOpen中用于快速傳輸過程數據的數據對象,可以實時地傳輸控制和狀態信息。
????????SDO(Service Data Object):SDO用于訪問設備的對象字典,可以讀取或寫入設備的狀態和配置信息。
????????緊急消息:CANOpen還定義了緊急消息,用于在發生嚴重故障時立即通知網絡中的其他設備。
????????心跳消息:心跳消息用于監測網絡中設備的運行狀態,如果一段時間內沒有收到某個設備的心跳消息,網絡管理系統會認為該設備已經離線。
????????CANOpen常用縮寫
③、CANOpen協議應用場景
????????在CANOpen協議中,對于不同類型的CANopen 設備和應用場景分別有不同的子協議,如CiA301、CiA401、CiA402、CiA421、CiA423、CiA424、CiA426、CiA430、CiA433等眾多子協議構成了CANopen協議。
2、CANOpen字典
????????CANOpen字典(Object Dictionary, OD)是CANopen協議的核心組成部分,它是一個有序的對象組,用于描述CANopen節點的所有參數,包括通訊數據的存放位置。
????????CANOpen字典的結構由索引和子索引組成:
????????索引:每個對象采用一個16位的索引值來尋址,范圍在0x0000到0xFFFF之間。
????????子索引:在某些索引下,為了訪問數據結構中的單個元素,定義了一個8位的子索引,范圍是0x00到0xFF之間。
????????CANOpen的SDO模式下的數據傳輸時,也是按如下格式進行發送和接收。其中字典中的部分索引可能沒有子索引。
3、CANOpen通信
????????CANOpen通信有兩種模式,分別是SDO通信、和PDO通信,如下圖所示為CANOpen通信的設備模型圖。
①、SDO通信
????????SDO 全稱為服務數據對象,其通過對象索引和子索引與CANOpen字典建立聯系,通過SDO可以讀取對象數據,在允許的情況下,也可以修改寫入對象數據。
????????SDO 傳輸方式遵循客戶端—服務器模式(CS),即一應一答方式。由 CAN 總線網絡中的 SDO 客戶端發起,SDO 服務器作出應答。
????????SDO 傳輸報文由COB-ID和數據段組成,數據段采用小端模式,即低位在前,高位在后排列。所有的 SDO 報文數據段都必須是 8 個字節。
????????SDO發送數據的COB-ID都是0x600+節點ID
????????SDO接收數據的COB-ID都是0x580+節點ID
SDO讀數據報文格式
SDO寫數據報文格式
②、PDO通信
????????PDO 全稱為 過程數據對象, 其用來傳輸實時的數據,是 CANopen 中最主要的數據傳輸方式。通過SDO配置PDO后,可以實現設備端的數據周期性自動上傳到服務器,也可以實現控制發送的數據小于8字節,由于PDO發送的數據無返回,因此可以極大的提升數據傳輸速度。
????????按照接收與發送的不同,PDO又分為TPDO和RPDO,其中RPDO用于服務器給設備端發送控制數據,TPDO為設備端周期性上傳數據到服務器。
????????PDO 的傳輸遵循的是生產者消費者模型,即 CAN 總線網絡中生產者產生的 TPDO 可根據COB-ID 由網絡上一個或者多個消費者 RPDO 接收。每個PDO由通信參數和映射參數共同決定最終傳輸的方式及內容。
????????如下是所使用的電機控制器中實現 PDO 的傳輸的4 個 RPDO 和4 個 TPDO。注意,不同控制器的PDO數量可能不一致,需要根據實際產品進行手冊參考。
????????PDO使用時,需要進行映射配置操作,如下所示為PDO映射操作的流程圖。
????????因PDO映射內容較多,因此將在下一篇博客中詳細介紹PDO映射操作。
3、CANOpen應用
①、設置CiA402模式
????????發送:601 2B 02 20 01 00 00 00 00 (設置為 CiA402 模式)
//2B 02 20 01 00 00 00 00 (CiA402 模式)
#define WRITE_2_BYTE 0x2B
#define WRITE_60_CTRL_PARAM 0x2002
#define CTRL_MODE_SELECT 0x01
#define CIA402_MODE 0x00//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = WRITE_2_BYTE;
data[1] = WRITE_60_CTRL_PARAM & 0xFF;
data[2] = WRITE_60_CTRL_PARAM >> 8 & 0xFF;
data[3] = CTRL_MODE_SELECT;
data[4] = CIA402_MODE;serve_motor_send_data(0x601, data);
//成功會返回數據:60 02 20 01 00 00 00 00
②、循環同步位置模式
????????發送:601 2F 60 60 00 08 00 00 00 (循環同步位置模式)
//2F 60 60 00 08 00 00 00 (循環同步位置模式)
#define WRITE_1_BYTE 0x2F
#define WRITE_60_RUN_MODE 0x6060
#define CYCLIC_SYNC_POS_MODE 0x08//將數據按規定填充到CAN的8字節數據區
char data[8] = {0};
data[0] = WRITE_1_BYTE;
data[1] = WRITE_60_RUN_MODE & 0xFF;
data[2] = WRITE_60_RUN_MODE >> 8 & 0xFF;
data[3] = 0x00;
data[4] = CYCLIC_SYNC_POS_MODE;serve_motor_send_data(0x601, data);
//成功會返回數據: 60 60 60 00 00 00 00 00
③、電機準備
????????發送: 601 2B 40 60 00 06 00 00 00 (設置 6040h為 0x6, 使電機準備)
//2B 40 60 00 06 00 00 00
#define WRITE_2_BYTE 0x2B
#define WRITE_60_CTRL_STATE 0x6040
#define MOTOR_READY 0x06//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = WRITE_2_BYTE;
data[1] = WRITE_60_CTRL_STATE & 0xFF;
data[2] = WRITE_60_CTRL_STATE >> 8 & 0xFF;
data[3] = 0x00;
data[4] = MOTOR_READY;servo_motor_send_data(0x601, data);
//正常會返回數據:60 40 60 00 00 00 00 00
③、電機鎖軸使能
????????發送: 601 2B 40 60 00 0F 00 00 00 (設置 6040h為 0xF, 使電機使能)
//2B 40 60 00 0F 00 00 00
#define WRITE_2_BYTE 0x2B
#define WRITE_60_CTRL_STATE 0x6040
#define MOTOR_ENABLE 0x7F//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = WRITE_2_BYTE;
data[1] = WRITE_60_CTRL_STATE & 0xFF;
data[2] = WRITE_60_CTRL_STATE >> 8 & 0xFF;
data[3] = 0x00;
data[4] = MOTOR_ENABLE;servo_motor_send_data(0x601, data);
//正常會返回數據:60 40 60 00 00 00 00 00
④、電機松軸失能
????????發送: 601 2B 40 60 00 07 00 00 00 (設置 6040h為 0x7, 使電機失能)
//2B 40 60 00 07 00 00 00
#define WRITE_2_BYTE 0x2B
#define WRITE_60_CTRL_STATE 0x6040
#define MOTOR_DISABLE 0x07//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = WRITE_2_BYTE;
data[1] = WRITE_60_CTRL_STATE & 0xFF;
data[2] = WRITE_60_CTRL_STATE >> 8 & 0xFF;
data[3] = 0x00;
data[4] = MOTOR_DISABLE;servo_motor_send_data(0x601, data);
//正常會返回數據:60 40 60 00 00 00 00 00
⑤、設置電機目標位置
????????發送: 601 23 7A 60 00 10 27 00 00 (設置目標位置 607Ah為 10000)
//目標位置 : 23 7A 60 00 10 27 00 00 (設置 607Ah為 10000)
#define WRITE_4_BYTE 0x23
#define WRITE_60_POSITION 0x607Aint pos = 90;//90度
int pos_pulse = (int)(pos * 10000.0 / 360);//數值單位轉換(角度值數據轉換為脈沖)
char data[8] = { 0 };//將數據按規定填充到CAN的8字節數據區
data[0] = WRITE_4_BYTE;
data[1] = WRITE_60_POSITION & 0xFF;
data[2] = WRITE_60_POSITION >> 8 & 0xFF;
data[3] = 0x00;
data[4] = pos_pulse & 0xFF;
data[5] = pos_pulse >> 8 & 0xFF;
data[6] = pos_pulse >> 16 & 0xFF;
data[7] = pos_pulse >> 24 & 0xFF;servo_motor_send_data(0x601, data);
//正常會返回到數據
//例如:60 7A 60 00 00 00 00 00
⑥、讀取電機實際位置
????????發送:601 40 63 60 00 00 00 00 00
#define READ_60_CMD 0x40
#define READ_60_POSITION 0x6063//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = READ_60_CMD;
data[1] = READ_60_POSITION & 0xFF;
data[2] = READ_60_POSITION >> 8 & 0xFF;servo_motor_send_data(0x601, data);
//正常會返回到讀取到的位置數據
//例如:43 63 60 00 F1 2D C0 01
⑦、讀取電機實際速度
????????發送:601 40 6C 60 00 00 00 00 00
//40 6C 60 00 00 00 00 00
#define READ_60_CMD 0x40
#define READ_60_SPEED 0x606C//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = READ_60_CMD;
data[1] = READ_60_SPEED & 0xFF;
data[2] = READ_60_SPEED >> 8 & 0xFF;servo_motor_send_data(0x601, data);
//正常會返回到讀取到的速度數據
//例如:43 6C 60 00 7C F6 FF FF
⑧、讀取電機實際電流
????????發送:601 40 78 60 00 00 00 00 00
//40 78 60 00 00 00 00 00
#define READ_60_CMD 0x40
#define READ_60_CURRENT 0x6078//將數據按規定填充到CAN的8字節數據區
char data[8] = { 0 };
data[0] = READ_60_CMD;
data[1] = READ_60_CURRENT & 0xFF;
data[2] = READ_60_CURRENT >> 8 & 0xFF;servo_motor_send_data(0x601, data);
//正常會返回到讀取到的電流數據
//例如:4B 78 60 00 FC FF 00 00