3. 通信对象与SDO:SDO的通信机制与实战配置

大家好,我是老张。今天咱们聊聊SDO——服务数据对象。说实话,在CANopen的各类通信对象里,SDO是我用得最多的一个。为什么?因为你要配置从站参数、读写设备数据,基本都靠它。我刚开始接触CANopen那会儿,被SDO的请求响应格式搞得晕头转向,后来踩了几个坑才算真正搞明白。

3.1 SDO的通信机制

SDO说白了就是「一问一答」的模式。主站发一个请求,从站回一个响应。这种机制保证了数据传输的可靠性——你想想看,如果发出去没回应,你根本不知道设备收到没有。

我个人习惯把SDO理解成「CANopen世界的Modbus RTU」。它基于客户端/服务器模型:

  • 客户端:通常是主站(比如PLC、工控机),发起请求
  • 服务器:是从站设备(比如驱动器、I/O模块),处理请求并回复

每个从站设备默认有两组SDO通道:

SDO通道 发送COB-ID 接收COB-ID 用途
SDO 1 0x600 + 节点ID 0x580 + 节点ID 默认参数配置
SDO 2 0x640 + 节点ID 0x5C0 + 节点ID 扩展配置

举个例子,节点ID为3的从站,主站发请求用0x603,从站回复用0x583。这个对应关系我建议你记牢了,调试的时候经常要用到。

核心要点:SDO是确认型服务,主站发一个请求,从站必须回一个响应。如果超时没收到响应,主站会重发或报错。

3.2 SDO的请求与响应格式

SDO报文一共8个字节。我刚开始看协议文档的时候,觉得这格式挺复杂。后来自己画了个图,一下就清楚了。

SDO请求报文结构(8字节) 命令字 Byte 0 索引 Byte 1-2 子索引 Byte 3 数据(4字节) Byte 4-7 命令字: 0x22=写, 0x40=读, 0x60=写回复 索引: 对象字典的16位地址 子索引: 对象字典的8位子地址 数据: 要写入或读取的实际数据

命令字这块,我常用的就几个:

  • 0x22:写请求(下载)——我要往从站写数据
  • 0x40:读请求(上传)——我要从从站读数据
  • 0x60:写响应——从站告诉我写成功了
  • 0x41:读响应——从站把数据发回来了
  • 0x80:异常响应——出错了,里面带错误码
实战技巧:调试的时候,我习惯用CAN分析仪抓报文。看到0x80开头的响应,基本就是配置出问题了。这时候别慌,看后面4个字节的错误码,对照协议文档就能定位问题。

3.3 SDO的快速传输与分段传输

这里有个重要的概念——快速传输和分段传输。说白了就是:

  • 快速传输:数据≤4字节,一个报文搞定
  • 分段传输:数据>4字节,拆成多个报文发

我在项目中遇到过这样的情况:配置一个伺服驱动器的电子齿轮比,需要写8个字节的数据。一开始我用快速传输,结果从站没反应。后来才意识到,超过4字节必须用分段传输。

快速传输示例

假设我要把从站节点ID=3的设备,对象字典索引0x2000子索引0x01的值设为0x12345678:

主站请求:22 00 20 01 78 56 34 12
          ↑  ↑   ↑  ↑  ↑  ↑  ↑  ↑
         命令 索引  子索引   数据(小端)
         
从站响应:60 00 20 01 00 00 00 00
          ↑
         写成功确认

注意数据是小端格式!我刚开始就栽在这个坑里——0x12345678在报文里是78 56 34 12,不是12 34 56 78。这个顺序搞反了,写进去的数据全是错的。

分段传输示例

如果要写8个字节的数据0x1122334455667788到索引0x2001子索引0x00:

第一段(主站发):21 01 20 00 11 22 33 44
                  ↑
                 命令字0x21表示分段写,还有数据

第二段(主站发):00 55 66 77 88 00 00 00
                  ↑
                 命令字0x00表示最后一段

从站响应:60 01 20 00 00 00 00 00
         写成功
注意:分段传输时,每段之间不能间隔太久。我遇到过一些从站设备,如果两段间隔超过100ms,它会认为传输超时,直接丢弃数据。所以分段传输时,建议连续发送,中间别干别的事。

3.4 使用SDO配置从站参数

好了,理论说完了,咱们来点实战的。我个人习惯用这样的步骤来配置从站:

  1. 先读后写:配置前先读一下当前值,确认对象字典地址对不对
  2. 写前备份:把原始值记下来,万一配错了还能恢复
  3. 写后回读:写完再读一遍,确认写入成功

举个例子,配置一个变频器的加速时间(假设对象字典为0x2002子索引0x01,数据类型为32位无符号整数):

// 第一步:读取当前值
主站请求:40 02 20 01 00 00 00 00
从站响应:41 02 20 01 0A 00 00 00  // 当前值为10(单位ms)

// 第二步:写入新值(设为500ms)
主站请求:22 02 20 01 F4 01 00 00  // 500 = 0x01F4
从站响应:60 02 20 01 00 00 00 00  // 写入成功

// 第三步:回读确认
主站请求:40 02 20 01 00 00 00 00
从站响应:41 02 20 01 F4 01 00 00  // 确认已改为500
避坑指南:我曾经遇到过一个问题——写参数时从站返回0x80,错误码是0x06020000(对象字典不存在)。查了半天,发现是索引写错了。所以写代码的时候,建议把对象字典地址定义成宏或者常量,别手写数字,容易出错。

另外,有些从站设备在写入某些参数后需要重新上电才能生效。比如修改节点ID、波特率这些。我一般会在写入后加一个延时,然后发送复位命令(NMT的0x81)。

嗯,SDO这块内容就这些。说白了就是记住三点:报文格式、快速/分段的选择、写后回读的习惯。掌握了这些,配置从站参数基本不会出大问题。

我的小习惯:调试SDO的时候,我总会在CAN分析仪上设置一个过滤器,只显示0x600和0x580相关的报文。这样看起来清爽多了,不会被其他报文干扰。

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