CiA402
為什么不放到一個PDO中。而是分成幾個PDO?
簡短答案:裝不下 + 解耦時序。
- PDO負載上限:經典CAN的每個PDO只有8字節。TargetPosition(607A:00=32bit) + ProfileVelocity(60FF:00=32bit) + ModesOfOperation(6060:00=8bit) 共9字節,單個PDO放不下,必須拆分。常見做法是把兩個32位參數放同一PDO,8位的放另一個PDO。
- 時序與觸發解耦:不同PDO可設不同的Transmission Type、事件/抑制時間(Event/Inhibit)和SYNC策略。把快變、實時性高的量放一個PDO(高頻/同步),慢變量放另一個PDO(低頻/事件),降低總線負載、保證控制環路延遲。
- 優先級與仲裁:不同COB?ID帶來不同仲裁優先級,關鍵控制量可用更高優先級PDO。
兼容性與標準建議:CiA 402常見/推薦映射本就將控制字、模式、目標值等分布到多個PDO,便于互操作和廠商預置配置。 - 例外:若用 CANopen FD(大于8字節),可合并更多對象;但經典CAN的8字節限制仍適用。
IO PDO
RPDO 參數
/* index 0x1400 : b'Receive PDO 1 Parameter'. */UNS8 masterdic_highestSubIndex_obj1400 = 6; /* number of subindex - 1*/UNS32 masterdic_obj1400_COB_ID_used_by_PDO = 0x181; /* 385 */UNS8 masterdic_obj1400_Transmission_Type = 0xFF; /* 255 */UNS16 masterdic_obj1400_Inhibit_Time = 0x64; /* 100 */UNS8 masterdic_obj1400_Compatibility_Entry = 0x0; /* 0 */UNS16 masterdic_obj1400_Event_Timer = 0x1; /* 1 */UNS8 masterdic_obj1400_SYNC_start_value = 0x0; /* 0 */const CONSTSTORE subindex masterdic_Index1400[] = {{ RO, uint8, sizeof (UNS8), .pObject=&masterdic_highestSubIndex_obj1400 },{ RW, uint32, sizeof (UNS32), .pObject=&masterdic_obj1400_COB_ID_used_by_PDO },{ RW, uint8, sizeof (UNS8), .pObject=&masterdic_obj1400_Transmission_Type },{ RW, uint16, sizeof (UNS16), .pObject=&masterdic_obj1400_Inhibit_Time },{ RW, uint8, sizeof (UNS8), .pObject=&masterdic_obj1400_Compatibility_Entry },{ RW, uint16, sizeof (UNS16), .pObject=&masterdic_obj1400_Event_Timer },{ RW, uint8, sizeof (UNS8), .pObject=&masterdic_obj1400_SYNC_start_value }};
這里定義的是 CANopen 對象字典條目 0x1400,即“接收 PDO1 通信參數”(RPDO1)。代碼為每個子索引分配了內存變量,并通過常量子索引表 masterdic_Index1400 將它們暴露給對象字典。子索引 0(highestSubIndex)為 6,表示實現了子索引 1 到 6。
子索引 1(COB?ID)設為 0x181,這與節點 1 的 TPDO1 標準 COB?ID(0x180 + 0x01)相匹配,因此本 RPDO 會接收該從站的 TPDO1。COB?ID 的最高位(bit31)為 0,表示該 PDO 處于使能狀態。子索引 2(傳輸類型)為 0xFF,表示異步接收(不依賴 SYNC)。其余字段——Inhibit Time(3)、Compatibility Entry(4)、Event Timer(5)和 SYNC start value(6)——在規范中主要對 TPDO 側有意義,RPDO 側通常被忽略,但很多棧會保留相同字段以保持結構一致。
RPDO
/* index 0x1600 : b'Receive PDO 1 Mapping'. */UNS8 masterdic_highestSubIndex_obj1600 = 1; /* number of subindex - 1*/UNS32 masterdic_obj1600[] = {0x20100110 /* 537919760 */};ODCallback_t masterdic_Index1600_callbacks[] = {NULL,NULL,};const CONSTSTORE subindex masterdic_Index1600[] = {{ RW, uint8, sizeof (UNS8), .pObject=&masterdic_highestSubIndex_obj1600 },{ RW, uint32, sizeof (UNS32), .pObject=&masterdic_obj1600[0] }};
這段代碼定義了 CANopen 對象字典條目 0x1600,即“接收 PDO1 映射參數”(RPDO1 Mapping)。子索引 0(masterdic_highestSubIndex_obj1600)設為 1,表示當前這路 RPDO 只映射了 1 個對象。具體的映射項以 32 位整型保存于 masterdic_obj1600 數組中。
唯一的映射值 0x20100110 按照“Index(16 位) | SubIndex(8 位) | LengthInBits(8 位)”編碼:
Index = 0x2010
SubIndex = 0x01
Length = 0x10(16 位) 含義是:這路 RPDO1 攜帶的前 16 位數據將寫入本地對象 0x2010:01(即 Application_data_Input_16bit[0])。映射總長度為 2 字節,符合經典 CAN 幀 8 字節上限。
masterdic_Index1600 將 0x1600:00(映射條目數)和 0x1600:01(第一條映射詞)導出到對象字典,均為可寫(RW),對應的回調為 NULL,修改這些條目不會觸發回調。
映射變量
/* index 0x2010 : Mapped variable b'Application data Input 16bit' */UNS8 masterdic_highestSubIndex_obj2010 = 8; /* number of subindex - 1*/const CONSTSTORE subindex masterdic_Index2010[] = {{ RO, uint8, sizeof (UNS8), .pObject=&masterdic_highestSubIndex_obj2010 },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[0] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[1] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[2] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[3] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[4] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[5] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[6] },{ RO, uint16, sizeof (UNS16), .pObject=&Application_data_Input_16bit[7] }};
這段代碼定義了對象字典條目 0x2010,名稱為“Application data Input 16bit”。子索引 0 的值為 8(highestSubIndex=8),表示該對象包含 8 個有效的子索引,即 0x2010:01 到 0x2010:08。
masterdic_Index2010 將每個子索引發布到對象字典:子索引 0 是只讀的數量字段,子索引 1~8 分別指向數組 Application_data_Input_16bit 的 8 個 16 位元素(均為只讀)。這意味著外部通過 SDO 只能讀取這些值,不能寫入;它們通常由應用邏輯或通過 RPDO 接收過程來更新。
實務上,每個 0x2010:n 都是一個 16 位輸入量,可被映射到 RPDO 中。例如,你的 RPDO1 映射 0x20100110(索引 0x2010、子索引 0x01、長度 16 位)就對應這里的第一個元素。為了避免并發訪問問題,如果這些值可能在中斷或通信回調中更新,可考慮在應用側使用合適的同步或將數據聲明為 volatile。
Other
心跳
心跳生產者(使能/周期=200ms)
- 對應:0x1017 Producer Heartbeat Time
- 本文件變量:masterdic_obj1017
- 設置:0x1017 = 200 即每200ms發送心跳;設置為0關閉
- 心跳COB-ID固定為 0x700 + NodeID(無需單獨配置)
緊急消息(EMCY)
- 對應:0x1014 Emergency COB-ID
- 本文件變量:masterdic_obj1014(注意:當前未加入 objdict 表,運行時不可通過SDO改)
- 設置:0x1014 = 0x80 + NodeID;將 bit31 置1可禁用(0x80000000 | COB-ID)
- 相關只讀信息:0x1001 Error Register,0x1003 Pre-defined Error Field(錯誤日志)
節點保護(Guarding) 使能、保護時間、生命周期因子
- 對應:0x100C Guard Time(ms)、0x100D Life Time Factor
- 本文件變量:masterdic_obj100C、masterdic_obj100D(同樣未加入 objdict,SDO不可改)
- 使能:兩者都>0;禁用:任一為0
- 提醒:節點保護與心跳機制互斥,按CiA 301應二選一(推薦使用心跳)
使能同步產生
-
0x1005 COB-ID SYNC
低 11 位為 CAN-ID(通常 0x80)
置 bit30=1 啟用“本設備產生 SYNC”;bit30=0 表示僅消費 SYNC -
0x1006 Communication cycle period
0 則按該周期(μs)發送 SYNC;=0 則不周期發送 -
0x1007 Sync window length(可選)
-
0x1019 Synchronous counter overflow value(可選)