3. SDO通信原理:协议格式、请求响应与传输模式
SDO,全称是Service Data Object。说白了,它就是CANopen网络里的“配置通道”。PDO负责跑实时数据,SDO负责干“重活”——读写对象字典、改参数、查状态。我刚开始接触CANopen时,总觉得SDO又慢又复杂,后来才发现,没有SDO,你连设备都配不起来。
3.1 SDO协议格式——报文里到底装了啥?
每个SDO报文固定8字节。前4字节是“命令+索引+子索引”,后4字节是数据。我习惯把前4字节叫“头”,后4字节叫“身子”。
| 字节偏移 | 内容 | 说明 |
|---|---|---|
| Byte 0 | 命令字(CCS/SCS) | 高5位:客户端命令/服务器命令 |
| Byte 1-2 | 索引(Index) | 对象字典索引,小端格式 |
| Byte 3 | 子索引(SubIndex) | 对象字典子索引 |
| Byte 4-7 | 数据(Data) | 最多4字节,小端格式 |
命令字是关键。它决定了这次SDO是“读”还是“写”,是“成功”还是“失败”。
命令字速查表(客户端→服务器)
- 0x22:写请求(下载,4字节以内)
- 0x40:读请求(上传)
- 0x21:写请求(分段传输,第一段)
- 0x60:读响应(上传,数据就绪)
举个例子。我想把对象字典索引0x6000、子索引0x01的值改成0x12345678。报文长这样:
发送:22 00 60 01 78 56 34 12
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
│ │ │ │ └─── 数据(小端)
│ │ │ └────── 子索引
│ └──┴───────── 索引(小端)
└────────────── 命令字 0x22(写请求)
收到响应:
接收:60 00 60 01 00 00 00 00
↑
└── 命令字 0x60(写成功)
嗯,这里要注意:索引和子索引在报文里是小端格式。0x6000在报文里是00 60,不是60 00。我见过有人栽在这个坑里,调了半天发现索引写反了。
3.2 SDO请求与响应——一问一答的规矩
SDO是典型的“客户端-服务器”模型。主站是客户端,从站是服务器。客户端发请求,服务器回响应。没有请求,就没有响应。
我画了一张流程图,帮你理清这个交互过程。
响应里的命令字,我列个表方便你对照:
| 命令字 | 含义 | 说明 |
|---|---|---|
| 0x60 | 写成功 | 下载完成,数据已写入对象字典 |
| 0x42 | 读成功 | 上传完成,数据在报文后4字节 |
| 0x80 | 异常响应 | 操作失败,后4字节包含错误码 |
我曾经踩过的坑:有一次从站一直不回响应,我以为是硬件坏了。查了半天,发现是主站发的索引写错了,从站根本找不到那个对象。从站不会主动报错,它只是沉默。所以,SDO超时一定要加处理逻辑,别傻等。
3.3 快速传输与分段传输——什么时候用哪个?
SDO有两种传输模式:快速传输和分段传输。区别很简单——数据能不能塞进4个字节。
快速传输(Expedited Transfer)
数据长度 ≤ 4字节时,用快速传输。一次请求,一次响应,完事。我90%的SDO通信都用快速传输,因为配置参数大多不超过4字节。
快速传输的报文格式:
写请求:22 [索引低] [索引高] [子索引] [数据0] [数据1] [数据2] [数据3]
读请求:40 [索引低] [索引高] [子索引] 00 00 00 00
读响应:42 [索引低] [索引高] [子索引] [数据0] [数据1] [数据2] [数据3]
命令字的bit0表示“是否有数据”,bit1表示“数据大小”。0x22 = 0010 0010,表示写请求、快速传输、数据长度4字节。
分段传输(Segmented Transfer)
数据长度 > 4字节时,必须用分段传输。比如读写一个64字节的配置块,或者一个字符串名称。
分段传输分三步走:
- 初始化段:客户端发0x21,告诉服务器“我要发大包了,总长度是XX字节”。
- 数据段:客户端一包一包发,每包最多7字节数据。服务器每包都要回响应。
- 结束段:最后一包数据不足7字节时,用toggle位和结束标志通知服务器。
我画了个分段传输的时序图:
我的建议:能用快速传输就别用分段传输。分段传输慢,而且容易出错。我一般只在读写设备名称、固件版本这类长字符串时,才用分段传输。如果你自己写从站代码,记得把SDO缓冲区设大一点,至少64字节,免得分段传输时溢出。
3.4 异常响应与错误码——出事了怎么办?
SDO响应中,命令字0x80表示异常。后4字节是错误码,告诉你具体出了什么问题。
常见的错误码:
| 错误码 | 含义 | 常见原因 |
|---|---|---|
| 0x05030000 | Toggle位错误 | 分段传输时toggle位不匹配 |
| 0x05040000 | SDO超时 | 服务器没在规定时间内收到数据 |
| 0x06010000 | 不支持的访问 | 试图写一个只读对象 |
| 0x06020000 | 对象不存在 | 索引/子索引在对象字典中找不到 |
| 0x06070000 | 数据类型不匹配 | 写入的数据长度或类型不对 |
| 0x06090000 | 子索引不存在 | 子索引超出范围 |
| 0x08000000 | 一般错误 | 其他未分类错误 |
我曾经遇到过一个诡异的问题:写0x6000时一直返回0x06010000,说不支持的访问。我查了对象字典,明明是可读写的。后来发现,是设备处于“预操作”状态,还没进入“操作”状态,某些对象在预操作状态下是只读的。所以,SDO通信前,先确认设备状态。
3.5 实战建议——写代码时要注意什么?
如果你在用CANopenNode写从站,SDO处理逻辑框架已经帮你搭好了。你只需要关注两件事:
- 对象字典注册:确保你的对象字典条目正确注册,索引不冲突。
- 回调函数:有些对象在读写时需要触发动作(比如保存参数到EEPROM),在回调里实现。
快速传输和分段传输的选择,我总结成一句话:
4字节以内用快速,超过4字节用分段。能快速就别分段,省时省力省带宽。
嗯,SDO的原理就这些。说白了就是“发命令、读索引、写数据、等响应”。你只要把报文格式记熟,把错误码查清楚,SDO通信就不会出大问题。