3、对象字典数据类型:基本数据类型(布尔、整数、浮点)、复合数据类型(数组、记录)、域数据类型
好,咱们今天聊聊对象字典里的数据类型。说实话,这块内容看起来有点枯燥,但它是整个CANopen协议的基石。你想想看,如果连数据怎么存、怎么读都搞不清楚,后面那些复杂的PDO、SDO配置根本玩不转。
我刚开始接触CANopen时,就被这些数据类型绕晕过。后来踩了几个坑,才慢慢摸清楚门道。今天我把这些经验梳理出来,希望能帮你少走弯路。
3.1 基本数据类型
基本数据类型,说白了就是最基础的数据单元。CANopenNode里支持的类型,跟C语言的标准类型基本一一对应。
3.1.1 布尔型(BOOLEAN)
布尔型最简单,就两个值:TRUE(1)或FALSE(0)。占用1个字节。我个人习惯,在配置设备参数时,凡是只有两种状态的,比如使能/禁止、启动/停止,都用布尔型。
3.1.2 整数型
整数型分有符号和无符号两大类。具体看下表:
| 数据类型 | 长度(字节) | 取值范围 | CANopenNode中的宏定义 |
|---|---|---|---|
| UNSIGNED8 | 1 | 0 ~ 255 | ODT_UNSIGNED8 |
| UNSIGNED16 | 2 | 0 ~ 65535 | ODT_UNSIGNED16 |
| UNSIGNED32 | 4 | 0 ~ 4294967295 | ODT_UNSIGNED32 |
| INTEGER8 | 1 | -128 ~ 127 | ODT_INTEGER8 |
| INTEGER16 | 2 | -32768 ~ 32767 | ODT_INTEGER16 |
| INTEGER32 | 4 | -2147483648 ~ 2147483647 | ODT_INTEGER32 |
嗯,这里要注意。CANopen标准里还有UNSIGNED64和INTEGER64,但CANopenNode默认不支持。我遇到过有人硬要往里塞64位数据,结果协议栈直接报错。如果你真需要,得自己扩展协议栈,但说实话,大部分工业现场用32位就够了。
3.1.3 浮点型
浮点型就两种:REAL32(单精度)和REAL64(双精度)。
- REAL32:4字节,对应C语言的float。精度约7位有效数字。
- REAL64:8字节,对应C语言的double。精度约15位有效数字。
我在项目中遇到过一个问题:用REAL32传温度值,结果发现小数点后第三位开始抖动。后来查了半天,发现是传感器精度本身只有0.1℃,但上位机非要显示到0.001℃。其实这种情况,用INTEGER16加个比例因子更靠谱。
3.2 复合数据类型
复合数据类型,就是把基本数据类型组合起来用。CANopen里主要有两种:数组(ARRAY)和记录(RECORD)。
3.2.1 数组(ARRAY)
数组就是同一类型数据的集合。每个元素通过子索引(SubIndex)访问。子索引0通常存储数组长度,子索引1开始存实际数据。
举个例子,一个长度为5的UNSIGNED16数组:
// 对象字典定义示例
OD_ENTRY_HWP_ARRAY16(0x2001, 5, myArray)
// 访问方式
// 子索引0:读取数组长度(返回5)
// 子索引1:读取第1个元素
// 子索引2:读取第2个元素
// ...以此类推
我个人习惯,用数组来存多通道的采集数据。比如8通道的模拟量输入,每个通道一个UNSIGNED16,数组长度就是8。这样上位机通过SDO读子索引1~8,就能拿到所有通道的值。
3.2.2 记录(RECORD)
记录就灵活多了。它允许不同数据类型的成员组合在一起。每个成员也有自己的子索引。
典型的例子是设备配置参数:
| 子索引 | 参数名称 | 数据类型 | 说明 |
|---|---|---|---|
| 0 | 记录长度 | UNSIGNED8 | 固定为3 |
| 1 | 设备ID | UNSIGNED16 | 1~255 |
| 2 | 采样周期 | UNSIGNED32 | 单位:微秒 |
| 3 | 使能标志 | BOOLEAN | TRUE/FALSE |
你看,记录里可以混搭不同类型。这在配置复杂设备时特别有用。我做过一个伺服驱动器项目,对象字典里一个记录包含了位置环PID参数、速度环PID参数、电流限幅值……总共十几个成员,类型五花八门。用记录封装起来,管理起来清爽多了。
3.3 域数据类型
域(DOMAIN)数据类型,是CANopen里比较特殊的存在。它不限定具体的数据类型,可以存放任意长度的数据。说白了,就是一个「万能口袋」。
域类型的特点:
- 长度可变:不像基本类型那样固定字节数。
- 类型灵活:可以存字符串、二进制数据、甚至自定义结构体。
- 访问受限:域类型不支持PDO映射,只能通过SDO分段传输。
我举个例子。有一次客户要求通过CANopen升级固件。固件文件大小可能从几十KB到几百KB不等。用数组?不行,长度不确定。用记录?也不行,类型太复杂。最后就是用域类型,把固件数据包一股脑塞进去,上位机通过SDO分段读写,完美解决。
域类型在CANopenNode中的定义方式:
// 对象字典中定义域类型
OD_ENTRY_HWP_DOMAIN(0x1F50, firmwareData)
// 访问时,需要通过SDO分段传输
// 每次传输最多7字节(CAN标准帧数据场)
3.4 实际应用中的选择建议
说了这么多,到底该怎么选?我总结几条经验:
- 状态开关类:用布尔型。简单直接,别整花活。
- 数值测量类:优先用整数型。精度不够就加比例因子,比浮点型靠谱。
- 高精度计算类:用REAL32或REAL64。但记得确认字节序。
- 多通道同类数据:用数组。子索引访问方便。
- 混合配置参数:用记录。结构清晰,扩展性好。
- 大块不定长数据:用域类型。但别滥用,小心总线负载。
嗯,数据类型这块就聊到这儿。说白了,选对数据类型,你的对象字典就成功了一半。下一章咱们聊聊对象字典的索引分配和子索引设计,那才是真正考验架构能力的地方。