2、SDO对象字典基础:对象字典的结构、索引与子索引的概念、常用对象字典条目解析

好,咱们进入正题。上一章聊了SDO的报文格式,但SDO到底在读写什么?说白了,就是对象字典。对象字典是CANopen设备的灵魂,没有它,SDO就是个空壳子。

我刚开始接触CANopen时,觉得对象字典这名字挺唬人。后来发现,它其实就是一张大表格,把设备里所有能访问的参数都列了出来。每个参数都有个门牌号,SDO就是快递员,按门牌号送货取货。

2.1 对象字典的结构

对象字典在CANopen标准里叫Object Dictionary,缩写OD。它本质上是一个线性数组,每个元素叫一个对象。每个对象用16位的索引来定位,范围从0x0000到0xFFFF。

但别被这个范围吓到。实际用到的索引,主要集中在几个区域:

索引范围 用途 我常用的
0x0000 保留,不用 —
0x0001 – 0x001F 静态数据类型定义 基本不碰
0x0020 – 0x003F 复杂数据类型定义 偶尔看
0x1000 – 0x1FFF 通信对象 天天打交道
0x2000 – 0x5FFF 制造商特定对象 自己定义参数放这里
0x6000 – 0x9FFF 标准化设备子协议 看设备类型
0xA000 – 0xFFFF 保留或网络专用 几乎不用

你看,真正干活的就是0x1000到0x5FFF这块。通信参数、设备信息、你自己的应用数据,全塞在这里面。

核心要点:对象字典不是硬盘,不是存文件的。它是运行时的参数映射表,每个条目对应一个变量或一组变量。SDO读写对象字典,本质上就是在读写这些变量。

2.2 索引与子索引的概念

索引是16位的,够用吗?对于简单设备,一个索引对应一个参数,没问题。但有些参数是数组或结构体,比如一个电机驱动器的电流环PID参数,有P、I、D三个值。如果每个值占一个索引,太浪费了。

所以CANopen引入了子索引。子索引是8位的,范围0x00到0xFF。索引+子索引,就能精确定位到对象字典里的任何一个条目。

举个例子,我调试过一个伺服驱动器,它的位置环参数放在索引0x2000下:

  • 子索引0x00:表示这个对象有多少个子条目(这里是3个)
  • 子索引0x01:位置环比例增益Kp
  • 子索引0x02:位置环积分时间Ti
  • 子索引0x03:位置环微分时间Td

SDO读0x2000子索引0x01,拿到的就是Kp值。读0x2000子索引0x00,拿到的是3,告诉你下面有三个参数。

我的习惯:设计对象字典时,子索引0x00一定要填对。很多从站调试工具会先读子索引0x00来校验参数个数。填错了,工具直接报错,排查起来很头疼。

子索引的编号规则,标准里其实有建议:

  • 0x00:强制存在,表示子条目数量(对于数组或记录)
  • 0x01 – 0xFE:实际数据条目
  • 0xFF:扩展或特殊用途,很少用

对于单个变量(比如一个32位整数),子索引通常只有0x00,而且它的值就是变量本身。但标准规定,单变量对象的子索引0x00必须返回0x01,表示只有一个子条目。嗯,这里要注意,有些初学者会忽略这个细节。

2.3 常用对象字典条目解析

咱们做从站调试,有几个索引是绕不开的。我把它们列出来,你调试时心里有个数。

2.3.1 设备信息区(0x1000 – 0x1029)

这一块是CANopen标准强制或推荐实现的,用来描述设备本身。

索引 名称 说明
0x1000 设备类型 32位,高16位是设备子协议号,低16位是附加信息
0x1001 错误寄存器 8位,每一位代表一类错误,调试时经常看
0x1005 COB-ID同步报文 定义SYNC报文的ID,默认0x80
0x1017 心跳生产者时间 单位毫秒,0表示不发送心跳
0x1018 设备标识 包含厂商ID、产品代码、版本号等,子索引0x01-0x04

我记得有一次,现场设备死活连不上主站。我读0x1001一看,错误寄存器是0x10,查手册发现是通信错误。再读0x1005,发现COB-ID被改成了0x00。原来是有人误操作改了SYNC ID。这种问题,不看对象字典根本找不到原因。

2.3.2 通信参数区(0x1200 – 0x13FF)

SDO通信相关的参数,主要在这里。

  • 0x1200:服务器SDO参数(从站用)
  • 0x1280 – 0x12FF:额外的服务器SDO(如果你需要多个SDO通道)
  • 0x1300 – 0x13FF:客户端SDO参数(主站用,从站一般不需要)

0x1200的子索引结构是这样的:

子索引 名称 说明
0x00 最大子索引 固定为0x03
0x01 COB-ID客户端→服务器 默认0x600 + 节点ID
0x02 COB-ID服务器→客户端 默认0x580 + 节点ID
0x03 节点ID 可选,有些实现不填

你想想看,如果SDO通信不通,第一件事就是读0x1200,确认COB-ID对不对。我曾经遇到一个从站,0x1201子索引0x01的值被写成了0x700,结果SDO请求发过去石沉大海。查了半天,才发现是COB-ID被改了。

2.3.3 制造商特定区(0x2000 – 0x5FFF)

这一块是留给咱们自己发挥的。你的传感器、执行器、控制参数,全放这里。

设计时我建议:

  • 0x2000 – 0x2FFF:放配置参数(上电后一般不常改)
  • 0x3000 – 0x3FFF:放运行参数(运行时频繁读写)
  • 0x4000 – 0x4FFF:放诊断信息(只读)

举个例子,一个温度传感器从站,我可能会这样设计:

索引 子索引 名称 类型 访问
0x2000 0x01 温度报警上限 INT16 读写
0x2000 0x02 温度报警下限 INT16 读写
0x3000 0x01 当前温度值 INT16 只读
0x4000 0x01 最大温度记录 INT16 只读
0x4000 0x02 最小温度记录 INT16 只读

避坑指南:我曾经在0x2000里放了一个32位浮点数,但忘了对齐。结果主站用SDO读回来,数值总是错的。后来发现,对象字典里的数据对齐很重要。CANopen标准建议4字节数据按4字节对齐,2字节按2字节对齐。别偷懒,对齐做好,少很多麻烦。

2.4 对象字典的访问属性

每个对象字典条目,除了索引、子索引、数据类型,还有访问属性。这个属性决定了SDO能不能读写它。

  • ro:只读。比如设备版本号,只能读不能写。
  • wo:只写。很少见,但有些命令参数是只写的。
  • rw:读写。大部分配置参数都是这个。
  • const:常量。读出来固定不变,比如0x1018子索引0x01的厂商ID。

调试时,如果你写一个参数写不进去,先看看它的访问属性是不是ro。我见过有人对着只读参数写了一天,还以为是SDO协议有问题。

2.5 对象字典与SDO的关系

到这里,你应该明白了:SDO就是对象字典的读写工具。SDO请求里带的索引和子索引,就是对象字典的门牌号。主站发一个SDO写请求,把数据送到指定索引子索引的位置;从站收到后,更新对应的变量,再回复确认。

反过来,主站发SDO读请求,从站从对象字典里取出数据,打包成SDO响应发回去。

所以,调试SDO通信,本质上就是调试对象字典的配置。对象字典配对了,SDO通信就成功了一半。

一句话总结:对象字典是CANopen设备的“记忆体”,SDO是访问这个记忆体的“手”。你把手伸对了地方,才能拿到想要的东西。

对象字典与SDO的关系 CANopen主站 SDO请求 SDO响应 CANopen从站 对象字典 0x1000 设备类型 0x1018 设备标识 0x1200 服务器SDO参数 0x2000 制造商特定参数 SDO请求携带索引+子索引 从站根据索引子索引访问对象字典

这张图把关系画得很清楚。主站发SDO请求,从站收到后去对象字典里找对应的索引子索引,然后返回数据。整个过程,对象字典就是那个“数据库”。

调试小技巧:如果你手头有CAN分析仪,抓一下SDO报文,然后对照对象字典表,一条一条对。你会发现,SDO报文里的索引和子索引,就是对象字典里的门牌号。对上了,通信就通了。