3、CANopen协议基础:对象字典、PDO与SDO通信、NMT状态机

好,咱们进入CANopen的核心部分。说实话,很多工程师一上来就被CANopen的协议栈吓住了,觉得太复杂。其实你把它拆开看,就三个东西:对象字典PDO与SDONMT状态机。搞懂这三个,CANopen就算入门了。

我在做第一个CANopen项目时,就是被对象字典搞得晕头转向。后来发现,它就是个“通讯地址本”,没那么玄乎。

3.1 对象字典(Object Dictionary)

对象字典,简称OD。说白了,就是CANopen设备里的一张“大表”。这张表里,每个条目都有一个16位的索引(Index)和8位的子索引(Subindex)。

举个例子:

  • 索引0x1000:设备类型
  • 索引0x1001:错误寄存器
  • 索引0x6000:数字量输入(具体哪个通道,看子索引)

我习惯把对象字典想象成酒店的“房间号”。索引是楼层,子索引是房间号。你要找什么东西,直接报房间号就行。

对象字典的三大区域:

  • 通信对象区(0x1000-0x1FFF):存放通讯参数,比如波特率、节点ID、PDO映射等。
  • 制造商特定区(0x2000-0x5FFF):厂家自己定义的功能,比如我做的驱动器,会把电流环参数放这里。
  • 标准设备区(0x6000-0x9FFF):符合CiA 4xx规范的设备,比如数字量I/O、模拟量输入等。

嗯,这里要注意:对象字典不是随便写的。每个条目都有固定的数据类型,比如UNSIGNED8、UNSIGNED16、INTEGER32等。你写错了,通讯就会出问题。

我的小技巧:在调试时,先用CAN分析仪读一下0x1000和0x1001,确认设备活着。这就像去医院先量体温一样,基础检查。

3.2 PDO与SDO通信机制

对象字典建好了,怎么读写它?这就引出了两种通讯方式:PDOSDO

3.4.1 SDO(服务数据对象)

SDO,全称Service Data Object。它是一问一答的模式,像打电话:你问一句,我答一句。

SDO的特点:

  • 可靠:有确认机制,发出去的数据,对方必须回复。
  • :一次只能读写一个对象字典条目,效率低。
  • 用于配置:上电初始化时,用SDO设置参数。

SDO的报文结构是这样的:

COB-ID: 0x600 + NodeID(请求)
数据: [命令] [索引低] [索引高] [子索引] [数据...]

COB-ID: 0x580 + NodeID(响应)
数据: [命令] [索引低] [索引高] [子索引] [数据...]

举个例子,我想读节点1的0x1000(设备类型):

发送:0x601  40 00 10 00 00 00 00 00
接收:0x581  43 00 10 00 01 00 00 00

你看,0x40是“读请求”,0x43是“读响应”。数据部分就是设备类型值0x0001。

我曾经踩过的坑:SDO的响应超时时间,默认是1秒。如果你的设备处理慢,或者总线负载高,超时就会报错。我建议把超时设到3-5秒,尤其是带电机驱动的设备。

3.4.2 PDO(过程数据对象)

PDO,全称Process Data Object。它是广播模式,像微信群发消息:我说了,你们自己听。

PDO的特点:

  • 快速:一次最多传8字节数据,适合实时控制。
  • 无确认:发出去就不管了,丢了就丢了。
  • 用于实时数据:比如速度、位置、电流等。

PDO分两种:

  • TPDO(发送PDO):设备主动往外发数据。
  • RPDO(接收PDO):设备接收外部数据。

PDO的触发方式有四种:

触发方式 说明 适用场景
事件触发 数据变化时自动发送 数字量输入、状态变化
定时触发 固定周期发送 模拟量采样、位置反馈
远程请求 主站发请求,从站回复 诊断、同步读取
同步触发 收到SYNC报文后发送 多轴同步控制

我个人习惯用同步触发做多轴控制。你想想看,所有轴在同一个SYNC信号下同时更新数据,这样就不会有相位差。

3.4.3 PDO映射

PDO能传什么数据?这就涉及到PDO映射。说白了,就是把对象字典里的条目,映射到PDO的数据区。

举个例子,我想让TPDO1发送“实际位置”和“实际速度”:

对象字典0x1A00(TPDO1映射参数):
子索引0:映射数量 = 2
子索引1:0x60640020(实际位置,32位)
子索引2:0x606C0010(实际速度,16位)

这样,TPDO1每次发送6字节数据:4字节位置 + 2字节速度。

我的经验:PDO映射最好在设备初始化时一次性配好。运行时改映射,容易出问题。我曾经在产线上改映射,结果设备直接宕机了。

3.3 NMT状态机

NMT,全称Network Management。它管理着CANopen设备的“生命周期”。

NMT状态机有四个状态:

  • 初始化(Initialisation):上电后自动进入,设备自检。
  • 预操作(Pre-Operational):只能SDO通讯,不能PDO。用于配置参数。
  • 操作(Operational):SDO和PDO都能用。正常工作状态。
  • 停止(Stopped):只能接收NMT命令,不能通讯。用于紧急停止。

状态切换靠NMT报文:

COB-ID: 0x000
数据: [命令] [节点ID]

常用命令:
0x01:启动节点(进入操作状态)
0x02:停止节点(进入停止状态)
0x80:进入预操作状态
0x81:复位节点
0x82:复位通讯

举个例子,启动节点1:

发送:0x000  01 01

嗯,这里要注意:NMT报文是广播的,所有节点都能收到。如果你只想控制某个节点,在数据字节里填上节点ID。填0表示控制所有节点。

我曾经犯过的错:在预操作状态下发了PDO,结果设备没反应。后来查手册才知道,预操作状态只允许SDO。所以,配置完参数后,一定要发NMT启动命令,让设备进入操作状态。

3.4 心跳与节点守护

CANopen设备怎么知道对方还活着?靠心跳(Heartbeat)节点守护(Node Guarding)

心跳:设备周期性发送0x700 + NodeID的报文,告诉主站“我还活着”。

节点守护:主站主动发请求,从站回复。

我建议用心跳,因为它简单可靠。节点守护已经逐渐被淘汰了。

心跳时间在对象字典0x1017里设置,单位毫秒。比如设成100,就是每100ms发一次心跳。

我的习惯:心跳时间设成100-500ms。太短了浪费带宽,太长了故障响应慢。对于伺服驱动器,我一般设200ms。

3.5 总结

好了,CANopen的基础就这些。你记住:

  • 对象字典是数据仓库,SDO和PDO是搬运工。
  • SDO搬得慢但稳,适合配置。
  • PDO搬得快但糙,适合实时控制。
  • NMT管着设备的死活状态。

下一章,我会讲怎么用这些知识,做一个Modbus转CANopen的网关。到时候,咱们把Modbus的寄存器,映射到CANopen的对象字典里。你想想看,是不是很有意思?