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(可选)