一、对象字典——CANopen设备的灵魂

做CANopen开发这么多年,我越来越觉得,搞懂对象字典(Object Dictionary,简称OD),就等于掌握了CANopen的命门。你想想看,一个CANopen设备,不管是伺服驱动器、IO模块还是编码器,它里面到底有什么参数?怎么配置?怎么读取状态?答案全在对象字典里。

说白了,对象字典就是CANopen设备的“户口本”。每个设备都有一本,里面按编号记录了所有能访问的数据。我刚开始接触时,觉得这东西就是个参数表,后来踩过坑才明白——它是整个CANopen通信的基石。

核心定义:对象字典是一个有序的、标准化的数据集合,每个条目通过16位索引(Index)和8位子索引(Subindex)唯一标识。它描述了设备的所有功能、参数和状态。

二、对象字典的结构——索引与子索引

2.1 为什么需要两层索引?

这个问题我当年也困惑过。后来在做一个多轴控制器项目时,发现一个对象下面可能挂着一组数据,比如“电机参数”这个对象,下面有额定电流、额定转速、极对数等多个参数。如果只用一层索引,要么编号不够用,要么结构混乱。

CANopen的设计者很聪明,用了两层结构:

  • 索引(Index):16位,范围0x0000~0xFFFF,用来定位“哪一类数据”
  • 子索引(Subindex):8位,范围0x00~0xFF,用来定位“该类数据中的具体哪一个”

举个例子,索引0x6000是“第一轴的位置实际值”,子索引0x01是“高位字”,子索引0x02是“低位字”。这样设计,一个索引最多可以挂256个子项,够用了吧?

2.2 对象字典的地址空间划分

CANopen协议把整个对象字典空间划分成了几个区域,每个区域有特定用途。我整理了一张表,你一看就明白:

索引范围 用途 我常打交道的对象
0x1000~0x1FFF 通信对象(COB) 设备类型、错误寄存器、SDO参数、PDO映射
0x2000~0x5FFF 制造商特定对象 各家驱动器的私有参数,比如增益、滤波
0x6000~0x9FFF 标准化设备子协议 DS402运动控制对象,比如控制字、状态字
0xA000~0xFFFF 保留/网络变量 很少用到,但有些网关会占用

嗯,这里要注意:0x1000~0x1FFF是强制实现的,每个CANopen设备都必须有。我见过有人偷懒没实现0x1001(错误寄存器),结果主站一读就报错,排查了半天。

三、关键对象字典条目详解(1000h~1FFFh)

这部分是通信相关的核心对象,我挑几个最常用的讲讲。每个我都亲手配置过,踩过的坑也一并告诉你。

3.1 0x1000:设备类型

这个对象告诉主站:你是什么设备?比如0x00020192表示“DS402伺服驱动器”。我习惯在设备上电后先读这个,确认从站类型对不对。

3.2 0x1001:错误寄存器

8位数据,每一位代表一种错误类型。比如bit0是“通用错误”,bit4是“通信错误”。有一次现场设备报错,我读0x1001发现bit4置位,立刻知道是CAN总线出了问题,而不是电机烧了。

3.3 0x1005:COB-ID SYNC

同步报文的ID,默认是0x80。多轴同步时,所有轴都监听这个ID。我建议不要轻易改它,除非你的总线有冲突。

3.4 0x1010:保存参数

写入“save”关键字(0x65766173)到子索引0x01,就能把当前参数保存到EEPROM。注意:写入后设备会卡顿几十毫秒,别在运动过程中做这个操作。

我曾经踩过的坑:有一次在产线上,程序每10秒写一次0x1010保存参数,结果EEPROM一个月就写坏了。后来才知道,工业级EEPROM擦写寿命也就10万次。所以,只在参数改变时才保存,不要循环写

3.5 0x1011:恢复出厂设置

写入“load”关键字(0x64616F6C)到子索引0x01,设备会恢复默认参数。这个操作很暴力,慎用。我一般只在调试阶段用,现场不敢乱动。

3.6 0x1016:心跳消费者超时时间

设置主站多久没收到从站心跳就报错。单位是毫秒,我一般设500ms。太短容易误报,太长又反应迟钝。

3.7 0x1017:心跳生产者时间

从站每隔多少毫秒发一次心跳。我习惯设100ms,既不会占用太多总线带宽,又能及时检测到设备掉线。

3.8 0x1400~0x1BFF:接收PDO和发送PDO参数

这部分是PDO通信的核心,定义了PDO的COB-ID、传输类型、禁止时间等。我后面会专门讲PDO,这里先提一句:传输类型(子索引0x02)很关键,1表示“同步周期传输”,255表示“异步事件触发”。

四、如何通过SDO读写对象字典

4.1 SDO是什么?

SDO(Service Data Object)是CANopen的“快递员”,专门用来读写对象字典。它基于“请求-应答”模式,你发一个请求帧,设备回一个应答帧。虽然速度比PDO慢,但胜在可靠,每个字节都有确认。

4.2 SDO报文格式

一个SDO报文占8个字节,结构如下:

字节0 字节1~2 字节3 字节4~7
命令字 索引(低字节在前) 子索引 数据(最多4字节)

命令字决定了操作类型:

  • 0x40:读请求(上传)
  • 0x42:读应答(设备返回数据)
  • 0x23:写请求(下载)
  • 0x60:写应答(设备确认收到)
  • 0x80:异常应答(出错了)

4.3 实战:用SDO读取设备类型

假设我们要读索引0x1000(设备类型),子索引0x00。主站发送:

CAN ID: 0x600 + 节点ID(比如节点1就是0x601)
数据: 40 00 10 00 00 00 00 00
      ↑  ↑  ↑  ↑
      |  |  |  子索引=0x00
      |  索引=0x1000(低字节在前)
      命令字=0x40(读请求)

设备应答:

CAN ID: 0x580 + 节点ID(0x581)
数据: 42 00 10 00 92 01 02 00
      ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑
      |  |  |  |  设备类型=0x00020192
      |  |  |  子索引=0x00
      |  索引=0x1000
      命令字=0x42(读应答,数据长度4字节)

4.4 实战:用SDO写入控制字

DS402协议中,控制字在索引0x6040,子索引0x00。我们要写入0x0006(使能操作):

CAN ID: 0x600 + 节点ID
数据: 23 40 60 00 06 00 00 00
      ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑
      |  |  |  |  数据=0x00000006
      |  |  |  子索引=0x00
      |  索引=0x6040
      命令字=0x23(写请求,4字节数据)

设备应答:

CAN ID: 0x580 + 节点ID
数据: 60 40 60 00 00 00 00 00
      ↑
      命令字=0x60(写应答,成功)

我的调试小技巧:如果设备返回0x80(异常),别慌。读一下字节4~7,里面包含了错误码。比如0x06020000表示“对象不存在”,0x06010002表示“数据类型不匹配”。这些错误码在CiA301标准里都有定义,查一下就知道问题在哪。

五、对象字典的SVG结构图

下面这张图是我自己画的,把对象字典的层次结构和SDO读写流程串起来了。你看完应该能有个整体印象。

CANopen对象字典结构图 主站(Master) 发送SDO请求 接收SDO应答 从站(Slave) 接收SDO请求 返回SDO应答 SDO请求帧(0x600+NodeID) SDO应答帧(0x580+NodeID) 对象字典(OD) 0x1000 设备类型 0x1001 错误寄存器 0x1017 心跳生产者时间 0x6040 控制字 0x6060 操作模式 索引与子索引 索引(Index):16位,定位对象 子索引(Subindex):8位,定位子项 例如:0x6040:0x00 SDO通过CAN总线读写从站的对象字典,每个对象由索引+子索引唯一确定

六、总结一下

对象字典这东西,说复杂也复杂,说简单也简单。你只要记住三点:

  1. 它是设备的“参数仓库”,所有能读能写的数据都在里面
  2. 用索引+子索引定位,就像门牌号一样精确
  3. SDO是访问它的工具,虽然慢但可靠,适合配置和诊断

我个人习惯,在调试新设备时,第一件事就是读0x1000确认设备类型,然后读0x1001看看有没有历史错误。这两个对象能帮你快速判断设备是否正常。嗯,今天就先聊到这儿,后面我们会深入PDO和同步控制,那才是多轴同步的重头戏。

一句话记住:对象字典是CANopen的“内存映射”,SDO是读写这个映射的“API”。搞懂这两样,CANopen你就入门了。


公众号:蓝海资料掘金营,微信deep3321