对象字典英文叫Object Dictionary, 简称OD,是CANopen中的核心概念。每个CANopen设备都会有个OD。
OD主要用于设备的配置和通信。不同的设备会有不同的OD,通过读取它们的OD,用户可以知道这些设备的信息、如何与设备进行通信以及配置设备。
这和去饭馆吃饭类似,饭馆的菜单就是OD,客人阅读菜单,就知道饭馆能做哪些菜,然后就能做出正确的点菜操作。
一 概念理解
对象字典的结构和python中的dict类似,下面是设备OD内容的部分展示,
乍一看有点懵,这里使用python中的字典来描述上图,就比较好懂了,
myOD = {
}
myOD[0x1000] = {
0x00:0x12345678} # Device Type, UNSIGNED32
myOD[0x1001] = {
0x00: 0x12} # Error Register, UNSIGNED8
myOD[0x1008] = {
0x00: "hello"} # Device Name, VIS_STRING
myOD[0x1017] = {
0x00: 0x1234} # Heartbeat Prod. Time, UNSIGNED16
myOD[0x1018] = {
0x00: 0x04, # number of subindexes, UNSIGNED8
0x01: 0x12345678, # Vendor ID, UNSIGNED32
0x02: 0x12345678, # Product Code, UNSIGNED32
0x03: 0x12345678, # Revision Number, UNSIGNED32
0x04: 0x12345678 # Serial Number, UNSIGNED32
}
OD中存放很多对象,每个对象都有唯一的Index与之对应,占2个字节,如果想寻址对象内部元素,就需要使用SubIndex,占1个字节,所以Index和SubIndex的组合总共占3个字节。
如0x1018对应的对象,含有4个元素,分别是Vendor ID,Product Code,Revision Number和Serial Number,SubIndex分别是1,2,3,4。SubIndex 0上存放的值表示对象里包含的元素个数。如果对象中只有一个元素,那么元素值就会直接存放到SubIndex 0对应的元素上。
CANopen规定的OD结构如下图,
这里再简化一点,如下表,
Index | 对象 |
---|---|
0x0000 | 未使用 |
0x0001 ~ 0x009F | 数据类型区,存放常规数据类型和用户自定义数据类型 |
0x00A0 ~ 0x0FFF | 保留 |
0x1000 ~ 0x0FFF | 通信简表区,包含设备类型,错误寄存器和通信相关对象 |
0x2000 ~ 0x5FFF | 用户自定义区,存放用户自定义的对象 |
0x6000 ~ 0x9FFF | 标准设备简表区 ,存放标准行规中定义的对象 |
0xA000 ~ 0xFFFF | 保留 |
实际使用时,并不会用完所有的Index,都是根据需要选择一些Index来使用。例如标准设备简表区,用户开发的产品可能是用于某一行业的,那么就只需要该行业的标准行规定义的对象,如运动控制对应的DS-402
二 OD的文件格式
OD一般保存为EDS文件或DCF文件,在上一篇文章中我们已经使用了一个EDS文件,即CANopenSocket.eds
DCF文件格式与EDS基本一样,只是DCF可以保存设备的ID信息,并且可以只保存部分OD,意义在哪呢?有时我们想更新设备中的OD,但是只更新一部分,这样就可以使用DCF了。
EDS和DCF文件的具体格式可以参考CANopen的官方文件,即DS-306
三 代码实战
下面通过pythonCANopen来体验一下OD,从Client(即去饭馆吃饭的客人)的角度来看看OD中的内容,
首先要创建虚拟CAN设备vcan0,
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
然后执行下面python代码,
import time
import canopen
# 创建一个网络用来表示CAN总线
network = canopen.Network()
# 添加slave节点,其id是6,对象字典为CANopenSocket.eds
node = canopen.RemoteNode(6, 'CANopenSocket.eds')
network.add_node(node)
# 连接到CAN总线
network.connect(bustype='socketcan', channel='vcan0')
for obj in node.object_dictionary.values():
# 打印对象的Index和名称
print('0x{:X}: {}'.format(obj.index, obj.name))
'''
如果对象类型是Record,即内部包含多个元素的对象,
就把其内部元素的SubIndex和名称打印出来
'''
if isinstance(obj, canopen.objectdictionary.Record):
for subobj in obj.values():
print(' {}: {}'.format(subobj.subindex, subobj.name))
这样就可以看到OD里的对象信息了。
如果想访问单个OD对象,可以使用ParameterName或者Index/SubIndex组合,例如想访问CANopenSocket.eds中Index为0x2301、SubIndex为01对应的值,
代码如下,
obj_element = node.object_dictionary[0x2301][1]
print(obj_element.default)
obj_element = node.object_dictionary['Trace config']['Size']
print(obj_element.default)
可以打印其默认值
四 总结
本文主要简单讲述了CANopen中的对象字典,这是CANopen中很重要的概念,通过python代码可以很好的理解其意义。
如果有写的不对的地方,希望能留言指正,谢谢阅读。