4. SDO通信机制:读懂驱动器的“内心世界”

大家好,我是你们的老朋友。今天我们来聊聊CANopen里一个非常核心的机制——SDO。说实话,我刚开始接触CANopen时,觉得PDO和SDO都差不多,都是发数据嘛。直到有一次调试伺服驱动器,参数怎么都写不进去,我才真正意识到:SDO和PDO,完全是两码事

SDO的全称是Service Data Object,服务数据对象。它的作用很简单:访问对象字典。你可以把它想象成一条专用通道,专门用来读写设备里的参数。比如驱动器的电流环增益、位置环比例系数、电子齿轮比……这些参数,都得靠SDO来读写。

核心要点:SDO是“配置通道”,PDO是“数据通道”。一个用来设参数,一个用来跑数据。千万别搞混。

4.1 SDO的概念与作用

SDO基于客户端/服务器模型。说白了,就是一问一答。你发一个请求,设备回一个响应。这种机制保证了可靠性——每个SDO报文都有确认,不像PDO那样“发了就不管了”。

我个人习惯把SDO比作挂号信。你寄出去,对方必须签收,签收单还会寄回来。而PDO呢,就像发微信——发了就发了,对方看不看得到,你并不确定。

SDO的主要作用有这几个:

  • 读写对象字典条目:比如索引0x6098(位置模式下的加速度值)
  • 配置设备参数:驱动器上电后,通过SDO把参数写进去
  • 读取设备状态:比如读取驱动器的故障码、当前温度等
  • 固件升级:有些设备支持通过SDO分段传输来更新固件

我在项目中遇到过一个问题:一台伺服驱动器每次断电后参数就丢失。查了半天,发现是SDO写参数后没有执行保存到EEPROM的指令。嗯,这个坑我后面会细说。

4.2 SDO请求/响应协议

SDO的报文结构其实很规整。一个标准的SDO请求包含:

  • COB-ID:0x600 + 节点ID(请求),0x580 + 节点ID(响应)
  • 数据域(8字节):包含命令字、索引、子索引、数据

来看一个具体的例子。假设我们要读取节点ID为1的驱动器的设备类型(索引0x1000,子索引0x00):

// 请求报文(主站 → 从站)
COB-ID: 0x601
数据: 40 00 10 00 00 00 00 00
      ↑  ↑  ↑  ↑
      |  |  |  └─ 子索引 (0x00)
      |  |  └──── 索引低字节 (0x10)
      |  └─────── 索引高字节 (0x00)
      └────────── 命令字 (0x40 = 读取请求)

// 响应报文(从站 → 主站)
COB-ID: 0x581
数据: 42 00 10 00 01 00 00 00
      ↑  ↑  ↑  ↑  ↑
      |  |  |  |  └─ 数据 (0x00000001 = 驱动器类型)
      |  |  |  └──── 子索引 (0x00)
      |  |  └─────── 索引低字节 (0x10)
      |  └────────── 索引高字节 (0x00)
      └───────────── 命令字 (0x42 = 读取响应,成功)

你想想看,这个协议其实很优雅。8个字节就把“我要读什么”和“读到了什么”都表达清楚了。命令字0x40表示读请求,0x42表示读响应成功。如果读失败,会返回0x80开头的错误码。

小技巧:我习惯把常用的SDO命令字记在便签上:0x40=读,0x22=写(4字节),0x2B=写(2字节),0x2F=写(1字节)。调试时看一眼就知道报文对不对。

4.3 SDO分段传输与快速传输

SDO有两种传输方式:快速传输分段传输。为什么需要两种?因为对象字典里的数据大小不一样。

快速传输:数据长度 ≤ 4字节时使用。一个SDO报文搞定,效率最高。比如读写一个32位的速度值,用快速传输最合适。

分段传输:数据长度 > 4字节时使用。比如读写一个字符串类型的设备名称,或者下载固件,就需要分段传输。分段传输会把数据切成多个SDO报文,每个报文带一个序列号,接收方收到后要发确认。

我画了一张流程图,帮你理解这两种方式的区别:

SDO传输方式选择流程 开始SDO传输 数据长度 ≤ 4字节? 快速传输 分段传输 特点: 1个SDO报文搞定 数据在数据域中 效率最高 特点: 多个SDO报文 带序列号确认 适合大数据传输 传输完成

分段传输的协议稍微复杂一点。每个分段报文里有个toggle位,用来交替标识是第几个分段。接收方每收到一个分段,都要回复一个确认报文。如果某个分段丢了,发送方会重传。

注意:分段传输时,如果网络负载很重,可能会出现分段丢失或乱序。我建议在配置阶段使用快速传输,只有在读写大块数据时才用分段传输。

4.4 SDO超时与错误处理

SDO是可靠传输,但并不意味着不会出错。常见的错误有:

  • 超时错误:主站发了请求,从站没响应。通常是因为从站忙、总线冲突或节点掉线。
  • 协议错误:比如命令字不对、索引不存在、数据长度不匹配。
  • 资源错误:比如试图写入一个只读参数,或者写入的值超出范围。

我曾经遇到过一个典型的超时问题:主站读取驱动器的温度参数,偶尔会超时。后来发现是驱动器的温度采样周期是100ms,而主站的SDO超时设成了50ms。你想想看,主站50ms就等不及了,驱动器还没采样完呢。把超时改成200ms后,问题就解决了。

SDO错误响应报文的格式是这样的:

// 错误响应报文
COB-ID: 0x581
数据: 80 00 10 00 00 00 00 00
      ↑
      └── 命令字 (0x80 = 错误响应)

// 如果错误码是0x06020000(索引不存在)
数据: 80 00 10 00 00 00 02 06
                           ↑  ↑
                           │  └── 错误码低字节 (0x06)
                           └───── 错误码高字节 (0x02)

常见的SDO错误码:

错误码 含义 我的建议
0x05030000 Toggle位错误 检查分段传输的序列号
0x06010000 不支持该访问类型 确认参数是否可读/可写
0x06020000 索引/子索引不存在 查对象字典,确认索引号
0x06070010 数据长度不匹配 检查写入的数据字节数
0x06090030 参数值超出范围 确认参数允许的范围
0x08000020 数据无法传输或存储 可能是硬件故障

避坑指南:我曾经在调试时遇到0x06070010错误,查了半天发现是写入了一个32位参数,但只发了2个字节。记住:写入前一定要确认参数的数据类型。UINT32是4字节,INT16是2字节,别搞错了。

4.5 实战:使用SDO读写驱动器参数

好了,理论讲完了,我们来点实际的。假设我们要配置一个伺服驱动器的电子齿轮比。这个参数通常在索引0x6093(位置模式下的齿轮比分子)和0x6094(分母)。

先读取当前的齿轮比分子:

// 步骤1:发送读取请求
// 节点ID = 2,索引0x6093,子索引0x00
发送: 0x602 40 93 60 00 00 00 00 00

// 步骤2:接收响应
接收: 0x582 42 93 60 00 00 00 01 00
// 解析:数据 = 0x00000100 = 256

// 当前分子是256,我们想改成512
// 步骤3:发送写入请求(4字节数据)
发送: 0x602 22 93 60 00 00 02 00 00
// 数据 = 0x00000200 = 512

// 步骤4:接收写入响应
接收: 0x582 60 93 60 00 00 00 00 00
// 0x60 = 写入成功

嗯,这里要注意:写入成功后,参数只是写到了RAM里。如果断电,参数就丢了。要保存到EEPROM,需要执行保存参数命令。不同厂家的驱动器保存方式不同,有的用SDO写索引0x1010,有的用特殊命令。

我以常见的CiA 402标准为例,保存参数的方法:

// 保存所有参数到非易失性存储器
// 索引0x1010,子索引0x01,写入"save"字符串
发送: 0x602 23 10 10 01 65 76 61 73
// 数据 = "save" = 0x65766173

// 响应
接收: 0x582 60 10 10 01 00 00 00 00

实战要点总结:

  1. 先确认参数索引和子索引,查对象字典
  2. 确认参数的数据类型(1/2/4字节)
  3. 发送请求后,等待响应(设好超时)
  4. 检查响应命令字,确认是否成功
  5. 如果需要保存,执行保存命令

最后说一句:SDO调试时,我建议先用CAN分析仪抓一下报文,看看实际发出去的是什么。很多时候,你以为你发了正确的报文,实际上索引高低字节搞反了,或者数据长度不对。抓包一看,问题就清楚了。

好了,SDO的内容就讲到这里。记住:SDO是配置的基石,搞懂了SDO,你就掌握了和驱动器对话的基本能力。


专注资料整理