3. SDO协议基础:客户端/服务器模型、CCS与SCS编码规则、SDO请求与应答帧格式

SDO,全称是Service Data Object。说白了,它就是CANopen协议里用来访问对象字典的“万能钥匙”。你想读参数、写配置、做诊断,都得靠它。我刚开始接触CANopen时,总觉得SDO很复杂,后来发现只要搞懂了它的通信模型和帧结构,其实就那么回事。

3.1 客户端/服务器模型

SDO采用典型的客户端/服务器模型。这个模型怎么理解呢?我打个比方:

  • 客户端:主动发起请求的一方。通常是CANopen主站,比如你的PC或者PLC。
  • 服务器:被动响应请求的一方。就是我们的STM32从站设备。

客户端发一个请求帧,服务器回一个应答帧。一来一回,完成一次SDO传输。嗯,这里要注意:SDO通信是确认型服务,也就是说,客户端发了请求,必须等到服务器的应答,才能进行下一次操作。

关键点:SDO的客户端/服务器模型是一对一的。每个SDO通道只能服务一个对象。如果你想同时访问多个对象,要么开多个SDO通道,要么用块传输模式(后面章节会讲)。

我在项目中遇到过一个问题:主站连续发了两个SDO请求,没有等应答。结果从站直接懵了,总线报错。后来我养成了一个习惯——发一个请求,等一个应答,再发下一个。这个顺序千万别乱。

3.2 CCS与SCS编码规则

SDO帧里有个关键字段,叫命令码。客户端发的叫CCS(Client Command Specifier),服务器回的叫SCS(Server Command Specifier)。它们俩的编码规则,决定了这次SDO操作的类型。

我整理了一张表,把常用的CCS/SCS编码列出来:

命令码(bit 7-5) 缩写 含义 方向
001 Download 写请求(客户端→服务器) CCS
010 Upload 读请求(客户端→服务器) CCS
011 Abort 中止传输 CCS/SCS
100 Download应答 写应答(服务器→客户端) SCS
101 Upload应答 读应答(服务器→客户端) SCS

你看,CCS和SCS的编码是对称的。客户端发Download(001),服务器就回Download应答(100)。客户端发Upload(010),服务器就回Upload应答(101)。

我的经验:调试SDO时,我习惯先看CAN报文的前3个bit。如果CCS=001,说明主站要写数据;如果CCS=010,说明主站要读数据。这个判断比看整个帧快得多。

3.3 SDO请求与应答帧格式

SDO的CAN数据帧固定为8字节。这8个字节怎么分配?我画个图你就明白了。

SDO 8字节帧结构 字节0 命令码 字节1-2 索引 字节3 子索引 字节4-7 数据(4字节) CCS(bit7-5) 对象字典索引 子索引 最多4字节数据 字节0详解 bit7-5: CCS/SCS命令码 | bit4: 保留 | bit3-0: 传输指示 例如:0x23 = 001 0 0011 → Download请求,标准传输

你看,8个字节分成了4个部分:

  1. 字节0:命令码。包含CCS/SCS(高3位)和传输指示(低4位)。
  2. 字节1-2:对象字典的索引。比如0x1017就是心跳生产者时间。
  3. 字节3:子索引。用于访问数组或记录类型对象。
  4. 字节4-7:数据。最多4字节,不够的补0。

注意:SDO的索引和子索引是小端模式(Little-Endian)。比如索引0x1017,在CAN报文里是0x17, 0x10。我刚开始写代码时,因为大小端搞反,折腾了一整天。你写代码时一定要确认好字节序。

3.4 举个例子:读一个对象

假设我们要读对象字典0x1017(心跳生产者时间)的子索引0x00。整个过程是这样的:

客户端发请求:

CAN ID: 0x600 + 节点ID(假设节点ID=1,则CAN ID=0x601)
数据: 0x40 0x17 0x10 0x00 0x00 0x00 0x00 0x00

这里0x40 = 010 0 0000,CCS=010表示Upload请求。

服务器回应答:

CAN ID: 0x580 + 节点ID(节点ID=1,则CAN ID=0x581)
数据: 0x43 0x17 0x10 0x00 0x64 0x00 0x00 0x00

这里0x43 = 100 0 0011,SCS=100表示Upload应答。数据0x64=100,说明心跳时间设置为100ms。

避坑指南:我曾经在调试时发现,服务器返回的应答帧里,数据长度可能小于4字节。比如读一个8位参数,数据只有1个字节有效。这时候别把后面3个0当成有效数据。我一般会先检查命令码里的传输指示位,确认实际数据长度。

3.5 再举个例子:写一个对象

现在我们要写对象0x1017的子索引0x00,把心跳时间改成200ms(0xC8)。

客户端发请求:

CAN ID: 0x600 + 节点ID(节点ID=1,CAN ID=0x601)
数据: 0x23 0x17 0x10 0x00 0xC8 0x00 0x00 0x00

0x23 = 001 0 0011,CCS=001表示Download请求。数据0xC8=200。

服务器回应答:

CAN ID: 0x580 + 节点ID(节点ID=1,CAN ID=0x581)
数据: 0x60 0x17 0x10 0x00 0x00 0x00 0x00 0x00

0x60 = 110 0 0000,SCS=110表示Download应答。注意,应答帧的数据段通常为0,表示写入成功。

总结一下:SDO通信就是客户端发命令,服务器回状态。命令码决定了操作类型,索引和子索引定位对象字典,数据段承载参数。搞懂这8个字节,你就掌握了SDO的核心。