10. 对象字典、数据类型和小端序
上一课只能从 Heartbeat 知道电机节点在线,以及它当前处于哪一种 NMT 通信状态。如果还想知道“这是什么类型的设备”,就需要读取电机提供的设备信息。
这个设备信息应该到哪里找?
CANopen 的共同通信规范 CiA 301 定义了一个标准参数:Device Type。它在对象字典中的地址是:
0x1000:00
其中 0x1000 和 00 都不是本教程或电机厂家临时选择的数字。0x1000 Device Type 是 CiA 301 定义的共同对象。本课会先查看资料和已有的原始通信记录,弄清这些结论各自来自哪里;第 11 课再由学员自己的 ESP32 发出读取请求。
现在又出现了新的问题:规范为什么用 0x1000:00 指向这个参数?冒号两边分别代表什么?一台电机内部还有故障状态、当前位置、目标速度和许多配置项,ESP32 又怎样准确说明“我要读其中哪一项”?
只发送“读取当前位置”这几个汉字肯定不行。不同国家、不同厂家和不同程序使用的名称可能不一样,CAN 报文也只携带数字和字节。
CANopen 的做法是给设备对外开放的每项数据安排一个数字地址。例如,前面 Device Type 的标准地址是 0x1000:00。ESP32 想读取设备类型时,会通过 CANH、CANL 发送一条带有这个地址的 CANopen 请求;电机看到地址后,找到对应数据并返回结果。
先不要急着背“对象字典”的定义。我们先追踪一次真实读取:地址从哪份资料查到、原始字节是谁发送的、程序又根据什么把字节解释成数值。走完这条过程后,再回头给对象字典下定义。
这一课不向电机发送报文。我们先学会回答四个问题:
0x1000:00 的两个数字分别是什么?
为什么一个参数地址还需要冒号和两部分数字?
同样四个字节为什么可能是正数,也可能是负数?
数值 0x12345678 为什么会排列成 78 56 34 12?
下一课会学习 CANopen 中专门访问这些参数的通信方式,它叫 SDO。先把上面的问题弄清楚,下一课才能真正读懂一组 SDO 请求和响应,而不是照着八个字节死记。
先弄清这些答案从哪里来
先直接回答最容易困惑的地方:
电机不会主动把“
0x1000叫 Device Type、它是UNSIGNED32”这些说明发给 ESP32。
上一课看到的 Heartbeat 是电机按照周期主动发送的。对象字典参数不是这样工作的。读取 Device Type 时,信息来自两个不同地方:
协议规范、厂家手册或 EDS 告诉我们地址、名称、类型和访问权限
电机的 CANopen 响应 告诉我们这台电机当前返回的实际字节
程序员先看资料,知道应该访问哪个地址、怎样解释结果;然后程序向电机发请求。电机返回的是字节,不会返回一行中文解释。
因此,“看懂一次对象读取”其实要分成四个问题:
| 要回答的问题 | 答案从哪里来 |
|---|---|
| 该读哪个地址 | CiA 规范、厂家手册或当前设备的 EDS |
| 这个地址的类型和权限是什么 | 同样查规范、手册或 EDS |
| 这台电机实际返回了什么 | 查看电机的 SDO 响应帧 |
| 返回字节最终表示多少 | 程序按数据类型和小端序解码 |
这不是靠经验看一眼字节就能猜出来的。如果没有对象定义资料,只看 92 01 04 00,最多只能说电机返回了四个字节;不能凭空断定它叫 Device Type,也不能断定它是有符号还是无符号数。
这条过程可以顺着下图从上向下看:

接下来不要求相信图中的结论。我们分别去看它们来自哪里。
0x1000 Device Type 从哪里查到
第 07 课已经使用过 CAN in Automation 发布的 CiA 301 CANopen 海报。当时查看的是默认 CAN-ID,这次看同一张海报左下方的对象字典表。
先只找表格第一行,不需要一次看完所有对象:

图片来源:CAN in Automation,CiA 301 CANopen poster(2024),第 1 页 General communication objects 区域。
第一行写着:
Index 1000h
Object VAR
Name Device type
海报中的 1000h 与 C 代码中的 0x1000 是同一个十六进制数。因此,我们可以从这行资料得到:
0x1000 这个地址的名称是 Device Type
它是 VAR,也就是只包含一个值的对象
这不是根据电机返回的 0x00040192 猜出来的,也不是从另一款电机代码抄来的。地址和名称先由 CANopen 共同规范给出。
同一张表中还能找到:
1001h Error register
1017h Producer heartbeat time
1018h Identity object
现在先认识这些行在哪里,不需要记住整张表。
数据类型和只读权限从哪里查到
海报适合快速查地址和名称,但没有展开每个条目的全部属性。实际开发还会查完整规范、当前设备的通信手册或 EDS。
EDS 是 Electronic Data Sheet,中文常称电子数据表。它是一种文本格式,用来描述 CANopen 设备实现的对象、数据类型、访问权限和默认值等信息。
一份标准示例 EDS 中,0x1000 条目写成:
[1000]
ParameterName=Device type
ObjectType=0x7
DataType=0x0007
AccessType=ro
DefaultValue=0x00000000
现在逐行读,不跳过来源:
| EDS 内容 | 怎样理解 |
|---|---|
[1000] | 这一段描述 Index 0x1000 |
ParameterName=Device type | 参数名称是 Device Type |
DataType=0x0007 | 数据类型编号 0x0007,即 UNSIGNED32 |
AccessType=ro | ro 是 read only,只允许读取 |
DefaultValue=0x00000000 | 这份示例文件给出的默认值 |
数据类型编号同样来自 CANopen 的类型定义。初学阶段先使用下面三个常见对应关系:
0x0005 UNSIGNED8
0x0006 UNSIGNED16
0x0007 UNSIGNED32
必须注意:上面是一份公开的标准示例 EDS,不是 IG35EC020 的 EDS。它可以帮助我们学习怎样查看 EDS,也能核对标准对象的写法,但不能把示例中的 DefaultValue 当成当前电机的实际值。
示例原文件:DS301_profile.eds
本教程现有的 IG35EC020 资料没有提供完整 EDS。因此,标准对象的地址和类型来自 CANopen 规范,当前电机是否实现、实际返回什么,还要通过真实通信确认;厂商私有对象则不能猜。
电机实际发送了什么
课程保留的最早完整原始记录,当时运行的是原始 TWAI 和手写 SDO 实验工程。
ESP32 先发送:
TX CAN-ID 0x601 DLC 8 DATA 40 00 10 00 00 00 00 00
这里的 0x1000:00 并没有消失,而是按 SDO 格式放进了 DATA 的固定位置。这个字节布局可以在 CiA 301 CANopen 海报第 1 页的 SDO services 表 中核对。先把 8 个字节标上序号:
| DATA 位置 | DATA[0] | DATA[1] | DATA[2] | DATA[3] | DATA[4]~DATA[7] |
|---|---|---|---|---|---|
| 本次内容 | 40 | 00 | 10 | 00 | 00 00 00 00 |
| SDO 中的用途 | 读取请求 | Index 低字节 | Index 高字节 | Sub-index | 读取请求中未使用 |
CANopen 在 SDO 帧中先放 Index 的低字节,再放高字节。因此 DATA[1] 和 DATA[2] 要按小端序组合:
Index = DATA[1] | (DATA[2] << 8)
= 0x00 | (0x10 << 8)
= 0x1000
DATA[3] 是 00,它就是 Sub-index 0x00。所以这帧 DATA 实际表达的是:
40 00 10 00 00 00 00 00
----- --
0x1000 00
请求读取对象 0x1000:00
注意不要把 CAN-ID 0x601 和对象地址 0x1000:00 混在一起:
CAN-ID 0x601 表示这是发给 Node-ID 0x01 的默认 SDO 请求
DATA 中的 00 10 00 表示这次要读的对象是 0x1000:00
前者说明“这帧发给哪个节点的哪种服务”,后者说明“这次具体读哪个参数”。
DATA[0] 为什么是 0x40,属于 SDO 命令字的完整定义,第 11 课再详细拆解。当前只需要知道:0x40 表示客户端想读取一个对象。
电机随后返回:
RX CAN-ID 0x581 DLC 8 DATA 43 00 10 00 92 01 04 00
响应中的 DATA[1]、DATA[2] 和 DATA[3] 仍然是 00 10 00。电机把这次访问的 Index 和 Sub-index 原样带回,ESP32 因此可以检查:这的确是对 0x1000:00 请求的回复,而不是其他对象的报文。
对照两帧可以看得更清楚:
| 报文 | DATA[0] 命令 | DATA[1..2] Index | DATA[3] Sub-index | DATA[4..7] |
|---|---|---|---|---|
| ESP32 请求 | 40 | 00 10 → 0x1000 | 00 | 未使用 |
| 电机响应 | 43 | 00 10 → 0x1000 | 00 | 92 01 04 00 |
0x601、0x581 和响应命令字 0x43 的完整定义留到第 11 课。当前继续看最后四个对象数据字节:
92 01 04 00
这四个字节才是电机针对这次请求返回的实际内容。在组合它们之前,先把两个判断依据落到可以打开核对的资料上。
依据一:0x1000 是 UNSIGNED32。
CANopenNode 的标准 DS301 示例 EDS 在 0x1000 条目中写着:
[1000]
ParameterName=Device type
DataType=0x0007
AccessType=ro
DataType=0x0007 还需要查数据类型表才能翻译。CANopenNode 的 CANopen 基本数据类型表 列出:
0x0007 = UNSIGNED32 = 32 位无符号整数
所以 0x1000:00 的值应当按 4 字节无符号整数解释。
依据二:CANopen 的多字节数值使用小端序。
CANopenNode 的基础定义文档 明确说明 CANopen itself is little endian。在它的对应源码注释 中也可以查到同样的说明。
因此,程序现在有了完整依据:数据长度是 4 字节,类型是无符号整数,最低有效字节放在前面。所以组合结果为:
92 01 04 00 -> 0x00040192
于是日志最终打印:
Device Type 0x1000:00 = 0x00040192
串口中的英文 Device Type 也不是电机发回来的。SDO 客户端程序中会先写好要读取的对象信息,形式类似:
// 0x1000:00 决定向电机读取哪个对象,SDO_VALUE_U32 决定按 4 字节无符号数解码。
// "Device Type" 只用于本地日志,电机响应中不会传回这段英文名称。
{0x1000, 0x00, SDO_VALUE_U32, "Device Type"}
这行代码的意思是:请求 Index 0x1000、Sub-index 0x00,按无符号 32 位数解码,打印时显示名称 Device Type。地址、类型和名称都是程序员查资料后写入程序的;电机的响应只补上这次的实际数据字节。
这也说明了为什么不能随便猜对象类型:如果程序表里一开始就写错,日志仍然可能打印出一个看似完整、实际解释错误的结果。
现在可以分清哪些内容是谁提供的:
0x1000 的名称和类型 来自 CANopen 资料
92 01 04 00 来自电机这次实际响应
0x00040192 程序按资料规定的类型和字节顺序组合出的结果
现在再理解什么是对象字典
一台 CANopen 设备会提供很多可访问的数据。对每一项数据,都需要说明:
Index 和 Sub-index 访问哪一项数据
Data type 占几个字节,是否允许表示负数
Access type 允许读取、写入,还是两者都允许
Value 当前设备保存或计算出来的实际数据
这些条目组织在一起,就叫 Object Dictionary,中文叫对象字典。可以先把它理解成设备对外提供的参数接口表:
| 对象地址 | 名称 | 数据类型 | 定义从哪里查 |
|---|---|---|---|
0x1000:00 | Device Type | UNSIGNED32 | CiA 301 通信对象定义 |
0x1001:00 | Error Register | UNSIGNED8 | CiA 301 通信对象定义 |
0x1018:02 | Product Code | UNSIGNED32 | CiA 301 Identity Object 定义 |
0x6064:00 | Position Actual Value | INTEGER32 | CiA 402 驱动器对象定义 |
表中的地址、名称和类型是查资料得到的,不是电机在 CAN 报文中发来的文字。电机只有在收到具体读取请求后,才会返回该地址的当前值,或者返回“对象不存在”等错误。
对一个只写过普通 C 程序的读者,可以这样理解它的用处。假设电机程序内部有一个保存当前位置的数值:
// 普通 C 变量只在本程序内可见;放入对象字典后才可通过 CANopen 地址访问。
int32_t position_actual;
这里只是为了讲解而写的假设,不是说 IG35EC020 内部一定有这个 C 变量。ESP32 不能知道另一块芯片里的变量名,更不能直接读取它的内存。CiA 402 因此规定了一个对外通信地址 0x6064:00。只要某台电机宣称并实现了这个对象,控制器就可以用这个数字地址请求当前位置,而不需要知道电机内部代码怎样写。
对象字典不是电机上电后自动发出的一张完整表,也不是互联网数据库。它是设备实现的一组 CANopen 参数接口。ESP32 使用 SDO 等 CANopen 通信方式访问某个具体地址,电机再返回该地址对应的数据或错误响应。
对象字典也不一定对应芯片中一块连续内存。有些条目对应普通变量,有些值由设备实时计算,还有些条目在读写时会执行专门处理。对通信双方来说,重要的是地址、类型、权限和行为符合资料定义。
一个完整对象地址由两部分组成
对象地址通常写成:
0x1018:02
冒号左边是 Index,中文叫主索引;冒号右边是 Sub-index,中文叫子索引。
0x1018 : 02
Index Sub-index
Index 是 16 位数字,常写成四位十六进制;Sub-index 是 8 位数字,常写成两位十六进制。
看下面这张图时,先看上半部分怎样拆地址,再看橙色一行怎样从 0x1018 中选出第 02 项:

所以 0x1018:02 不是一个带小数点的数,也不是 CAN-ID。它表示:
先找到 Index 0x1018:Identity Object
再找到 Sub-index 0x02:Product Code
完整书写时建议保留四位 Index 和两位 Sub-index:
0x1000:00
0x1018:02
0x6041:00
这样一眼就能看出冒号两边各自是什么。厂家资料有时写成 1018h, sub 02h,其中后缀 h 同样表示十六进制;它与 0x1018:02 表达的是同一组地址。
为什么还需要 Sub-index
有些对象只保存一个值。例如:
0x1001:00 Error Register
这时通常使用 Sub-index 00 访问它。
另一些对象需要把一组相关数据放在同一个 Index 下。0x1018 Identity Object 就是很好的例子:
| 完整地址 | 内容 | 类型 |
|---|---|---|
0x1018:00 | 支持的最高子索引 | UNSIGNED8 |
0x1018:01 | Vendor ID | UNSIGNED32 |
0x1018:02 | Product Code | UNSIGNED32 |
0x1018:03 | Revision Number | UNSIGNED32 |
0x1018:04 | Serial Number | UNSIGNED32 |
只说“读取 0x1018”还不完整,因为设备不知道你要的是厂家编号、产品编号还是序列号。必须同时给出 Sub-index。
0x1018:00 的值通常是这个记录支持的最高子索引。这里若读到 04,说明可以继续查看 01 到 04。不要把这句话扩展成“所有对象的 Sub-index 00 永远都是数量”;不同对象的定义仍要查对应规范或设备资料。
在正式术语中:
- 只有单个值的对象常称为
VAR; - 多个相同类型条目组成的对象常称为
ARRAY; - 多个不同含义或不同类型条目组成的对象常称为
RECORD。
0x1018 是 RECORD。当前阶段先会根据 Index 和 Sub-index 找到具体条目即可,不需要背下所有对象类型编码。
Index 的范围能告诉我们什么
对象字典不是从 0x0000 开始随意编号。不同范围留给不同用途。
初学阶段先认识三个最常遇到的区域:
| Index 范围 | 主要用途 | 本教程例子 |
|---|---|---|
0x1000~0x1FFF | CANopen 通信参数 | 0x1000、0x1017、0x1018 |
0x2000~0x5FFF | 厂商自定义参数 | 必须查当前厂家的资料 |
0x6000~0x9FFF | 标准设备规范相关参数 | CiA 402 的 0x6041、0x6064 |
第 08 课讲过 CiA 301 和 CiA 402 的分工。现在可以看到这种分工怎样落到对象字典里:
0x1017 Producer Heartbeat Time 属于共同通信参数
0x6041 Statusword 属于驱动与运动控制参数
0x6064 Position Actual Value 属于驱动与运动控制参数
上面三行的名称不是从电机返回字节中识别出来的。0x1017 要查 CiA 301,0x6041 和 0x6064 要查 CiA 402。再向具体电机发起读取,才能确认它是否实现了该对象,以及它当前的值。
对象落在某个标准区域,只说明应该去哪里查它的定义,不代表每台设备一定实现这个区域中的全部可选对象。
例如,某个地址在 CiA 402 中有标准定义,当前电机仍可能因为不支持对应功能而没有实现它。收到“对象不存在”的明确响应时,应记录实际结果,不能换一个相似地址继续猜。
0x2000~0x5FFF 更不能跨厂家照搬。两家设备都使用 0x2001,其含义也可能完全不同。
数据类型决定怎样解释 DATA
找到对象地址,只解决了“读哪一项”。还必须知道它的数据类型,才能决定:
有效数据有几个字节?
它能不能表示负数?
这些字节组合后应该得到什么数?
本教程最常见的六种整数类型如下:
| CANopen 名称 | C 中常用类型 | 字节数 | 数值范围 |
|---|---|---|---|
UNSIGNED8 | uint8_t | 1 | 0~255 |
INTEGER8 | int8_t | 1 | -128~127 |
UNSIGNED16 | uint16_t | 2 | 0~65535 |
INTEGER16 | int16_t | 2 | -32768~32767 |
UNSIGNED32 | uint32_t | 4 | 0~4294967295 |
INTEGER32 | int32_t | 4 | -2147483648~2147483647 |
UNSIGNED 表示无符号,只表示零和正数;INTEGER 在这里表示有符号整数,可以表示负数。
例如,下面三个对象的名称和数据类型分别由 CiA 301 和 CiA 402 定义:
0x1001:00 Error Register UNSIGNED8
0x6041:00 Statusword UNSIGNED16
0x6064:00 Position Actual Value INTEGER32
当前位置可能位于人为定义零点的正方向或负方向,所以 0x6064:00 使用有符号 32 位数。若错误地按 UNSIGNED32 打印,负位置可能变成一个非常大的正数。
相同字节可以有不同结果
假设四个有效数据字节都是 FF,组合后的 32 位比特是:
0xFFFFFFFF
按不同类型解释:
UNSIGNED32 4294967295
INTEGER32 -1
原始比特没有改变,改变的是解释方式。因此不能只看“响应有 4 字节”就决定使用 uint32_t,必须先查对象定义。
小端序解决多字节排列问题
一字节数值没有排列问题。两字节或四字节数值需要约定:先放哪一个字节?
CANopen 的多字节数值使用小端序,英文是 little-endian。可以先记成一句话:
最低有效字节放在前面。
以 32 位数值 0x12345678 为例,把它从右向左每两位分成一个字节:
12 34 56 78
^^
最低字节
放入 CANopen 数据区时,最低字节 78 先进入 DATA[0]:

最终排列是:
DATA[0] DATA[1] DATA[2] DATA[3]
78 56 34 12
不要把“低字节在前”理解成把每个字节内部的二进制位也倒过来。78 这个字节本身仍然是 0x78;变化的是四个字节在数据区中的先后位置。
接收端怎样组合 16 位数值
假设收到两个有效字节:
37 06
低字节是 0x37,高字节是 0x06。组合时把高字节左移 8 位:
value = 0x37 + (0x06 << 8)
= 0x37 + 0x0600
= 0x0637
对应的 C 函数可以写成:
// 调用前要先确认响应至少有 2 字节;本函数只负责按 CANopen 小端序组合数据。
// data[0] 保持在低 8 位,data[1] 左移到高 8 位,按位或后得到完整 uint16_t。
static uint16_t read_le_u16(const uint8_t *data)
{
return (uint16_t)data[0]
| ((uint16_t)data[1] << 8);
}
data[0] 直接放在最低 8 位;data[1] << 8 把第二个字节移动到第 8~15 位;按位或 | 再把两部分合到一个 uint16_t 中。
强制转换 (uint16_t) 写在移位之前,明确告诉编译器先把字节扩展成 16 位数,再进行左移。
接收端怎样组合 32 位数值
四字节版本按同样规律继续左移 16 位和 24 位:
// 调用前要先确认响应至少有 4 字节,不能拿短响应直接进入这个函数。
// 四个字节分别放入结果的第 0、8、16、24 位,再用按位或合并。
static uint32_t read_le_u32(const uint8_t *data)
{
return (uint32_t)data[0]
| ((uint32_t)data[1] << 8)
| ((uint32_t)data[2] << 16)
| ((uint32_t)data[3] << 24);
}
例如 78 56 34 12:
data[0] 0x00000078
data[1] << 8 0x00005600
data[2] << 16 0x00340000
data[3] << 24 0x12000000
按位或后 0x12345678
下一课分析 SDO 时,还会看到 Index 本身也按低字节在前放入数据区。例如 Index 0x6041 会出现为:
41 60
Sub-index 只有一个字节,因此 0x00 直接放入对应位置,不需要再讨论字节先后。
有符号数应该在什么时候转换
接收 INTEGER32 时,可以先按相同的小端规则组合出 32 位比特,再把它解释成 int32_t:
// 字节顺序与 UNSIGNED32 完全相同;区别只发生在最后一步的类型解释。
// 转为 int32_t 后,最高位为 1 的结果会按补码解释为负数。
static int32_t read_le_i32(const uint8_t *data)
{
return (int32_t)read_le_u32(data);
}
例如收到:
FF FF FF FF
read_le_u32() 先得到 0xFFFFFFFF,最后转换为 int32_t 后得到 -1。
这里仍然要先确认对象类型确实是 INTEGER32。如果对象定义为 UNSIGNED32,同样的比特就应该保留为 4294967295。
访问权限决定能不能读写
对象字典条目通常还带有访问权限:
| 常见标记 | 英文 | 含义 |
|---|---|---|
ro | read only | 只能读取 |
wo | write only | 只能写入 |
rw | read/write | 可以读取和写入 |
const | constant | 固定常量 |
访问权限不是给文档看的装饰。设备收到请求后会检查它。
例如 0x1018:02 Product Code 是只读身份信息。控制器可以请求读取,但不应该尝试写入。下一课会看到,访问不存在的对象、写只读对象或使用错误数据长度时,设备会通过 SDO Abort 明确拒绝请求。
“可以写”也不表示“现在适合写”。电机控制对象即使标为 rw,仍可能受到 NMT 状态、CiA 402 状态、数值范围和安全条件限制。
用已有硬件记录练习
本课没有增加新的 ESP32 固件,也不需要再次烧录。因为此时还没有学习 SDO 请求格式,提前发送参数访问报文只会变成照抄字节。下面使用 2026 年 8 月 5 日保存的实际通信记录;第 11 课再从空工程写出只读 SDO 客户端,由学员自己重新完成请求和验证。
不要先看程序打印的参数名和最终数值。我们直接看保存下来的原始帧:
读取 0x1018:00
TX ID=0x601 DATA=[40 18 10 00 00 00 00 00]
RX ID=0x581 DATA=[4F 18 10 00 04 00 00 00]
读取 0x6041:00
TX ID=0x601 DATA=[40 41 60 00 00 00 00 00]
RX ID=0x581 DATA=[4B 41 60 00 31 02 00 00]
读取 0x6064:00
TX ID=0x601 DATA=[40 64 60 00 00 00 00 00]
RX ID=0x581 DATA=[43 64 60 00 AA 16 00 00]
完整节选记录保存在 IG35EC020 实际 SDO 读取记录。
现在仍然不解释每帧前四个字节,它们属于第 11 课的 SDO 格式。本课只使用已经查到的对象类型,练习解码响应帧后面的有效数据。
0x1018:00 为什么只需要一字节
对象类型是 UNSIGNED8,数值是 0x04:
有效数据长度 1 字节
数据字节 04
含义 最高子索引为 04
0x6041:00 怎样得到 0x0231
CiA 402 定义 Statusword 的类型是 UNSIGNED16,所以响应中的有效数据是 2 字节。这次电机实际返回:
31 02
手算:
0x31 | (0x02 << 8) = 0x0231
当前只练习字节组合。0x0231 中每一位对应什么驱动状态,要到 CiA 402 状态机课程再查定义和解释。
0x6064:00 怎样排列成四字节
这次电机的响应帧实际带回四个数据字节:
AA 16 00 00
按小端序组合:
AA | (16 << 8) | (00 << 16) | (00 << 24)
= 0x000016AA
= 5802
CiA 402 定义 0x6064:00 的类型是 INTEGER32。当前组合结果的最高位为 0,所以十进制结果是正数 5802。将来读到最高位为 1 的位置值时,需要按 int32_t 解释负数。
自己完成四道拆解题
先不要看后面的答案,拿纸写出每一步。
题目一:找对象
地址:0x1018:03
写出它的 Index、Sub-index、名称和数据类型。
题目二:组合两字节
一个 UNSIGNED16 对象返回:
34 12
组合后的十六进制数值是多少?
题目三:组合四字节
一个 UNSIGNED32 对象返回:
92 01 04 00
组合后的十六进制数值是多少?
题目四:同一组比特按两种类型解释
数据字节是:
FF FF FF FF
分别按 UNSIGNED32 和 INTEGER32 解释。
对照答案
题目一:
Index 0x1018
Sub-index 0x03
名称 Revision Number
数据类型 UNSIGNED32
题目二:
0x34 | (0x12 << 8) = 0x1234
题目三:
92 01 04 00 -> 0x00040192
这个结果与课程开发阶段从 IG35EC020 读取到的 Device Type 数值一致,但题目只训练小端序,不在本课解释 Device Type 各个位的含义。第 11 课完成 SDO 实验后,学员会从自己的电机获得并核对实际结果。
题目四:
UNSIGNED32 4294967295
INTEGER32 -1
如果这四题能独立完成,下一课看到下面这种 SDO 数据时,就已经能认出 Index 的低高字节和返回值的低高字节:
4B 41 60 00 37 06 00 00
现在不需要猜 4B。它是 SDO 协议自己的命令字,下一课会从请求和响应过程开始解释。
回到开头的 0x1000:00
现在可以把它完整读成:
0x1000 Index,找到 Device Type 对象
00 Sub-index,选择这个对象的第 00 项
UNSIGNED32 数据类型,结果占 4 字节且不表示负数
ro 访问权限,只读取
对象字典解决“访问哪个参数”,数据类型解决“怎样解释参数”,小端序解决“多字节怎样排列”。这三件事不能互相替代:
只有地址,没有类型 不知道数据长度和正负
只有类型,没有地址 不知道正在访问哪项数据
忽略小端序 会把字节组合成错误数值
下一课会把这些知识放进真实 CAN 帧:ESP32 作为 SDO Client,请求电机读取 0x1000:00、0x1001:00 和 0x1018,再检查电机返回的数据或 Abort Code。
资料链接
CAN in Automation:CANopen:
该页面介绍对象字典在 CANopen 通信和应用程序之间的作用,以及通信参数所在的 Index 区域。
CAN in Automation:CiA 306 Electronic Device Description:
该页面说明 EDS 和 DCF 的用途及区别。
CANopenNode:CANopen 基本数据类型和字节序:
该页面列出 CANopen 数据类型编号,并明确说明 CANopen 使用小端序。
CiA 301 CANopen 海报:
海报集中列出了对象字典、常用通信对象、数据类型和 SDO 报文格式,可用于核对本课中的标准对象地址。