本文的重点是对CANopen协议的理解,不是编程实现
参考链接
canopen快速入门 1cia301协议介绍_哔哩哔哩_bilibili
CANopen是什么? CANopen通讯基础(上)_哔哩哔哩_bilibili
CANopen概述
图1. CAN报文标准帧的格式
CAN的报文可简单的被分为帧ID和数据部分,由于其灵活性,在CAN通讯刚出来时,各个厂家一般根据自己的喜好进行编写协议,这最终导致了很混乱的局面。因此诞生CANopen协议,CANopen是由CIA组织提出的CAN的应用层协议,包含CIA301、CIA401、CIA402…,其中CIA301是CANopen中最基础的协议,规定基于帧ID和报文的分类用法,相当于定义了一个CAN报文框架。CIA401、CIA402…则是基于CIA301基础协议,针对不同类型的通讯器件,所制定的更具体的协议,比如CIA402就是驱动器协议,全球通用,很多驱动器厂商都会支持CIA402这种协议。
注意:CAN通信有标准帧和扩展帧的区别,本文仅考虑标准帧的情况。
CIA301基于帧ID的功能划分
CAN报文主要分为帧ID和数据两部分,CIA301协议,对ID部分进行了划分,把从低到高的各个ID划分成了不同区域,每个区域实现不同功能,根据ID的不同,决定CAN报文的类型,每种类型有不同的格式,所以后面的数据会根据格式进行拆解。
图2. CIA301协议基于帧ID的功能划分
在CANopen协议中,定义了一个概念,通讯标识符 Communication Object Indentifier,后续简写为COB_ID,COB_ID是CAN报文的唯一标识符,COB_ID本质上就是CAN报文中的帧ID。根据图1可知,标准帧的帧ID共有11位,也就是COB_ID有11位。CANopen协议将COB_ID分成了功能码(FUNCTION)和NodeID两个部分。其中功能码占4位,NodeID占7位,如图3所示,(图2的第2列的功能码ID位搞错了,应该是bits10~7)
图3. COB_ID格式
功能码的作用就是字面意思,不同的码值代表不同的功能,而NodeID代表的是CAN网络中的各个网络节点的编号,范围是1~127。这个NodeID号是设备自己定义的,比如我是一个驱动器,我可以给我自己定一个NodeID号,比如1。甚至我可以提供上位机的配置接口,去配置我的NodeID号。根据我的理解,同一个CAN网络中所有设备,NodeID号应不要重复。比如我是主控平台(Master),我要通过CAN网络分别去控制三个轴系的电机驱动器。那么这三个电机驱动器的NodeID不能一样,否则主控平台根据NodeID发送的指令不知道最终发给了谁。NodeID是用来辨识从设备的。
下面根据功能码,以从小到大的顺序,对CANopen的报文做一个分类。
图4. 基于功能码的CANopen报文分类
基于图4的内容,CANopen协议主要需要学习的报文为下表中的8种。
序号 | 报文分类 | COD_ID | 学习难度 | 是否重点学习 |
1 | NMT | 000H | 简单 | ☆ |
2 | SYNC | 080H | 正常 | ☆ |
3 | EMCY | 080H+NodeID | 简单 | - |
4 | PDO | 从节点发送 TxPDO1~TxPDO4 180H+NodeID 280H+NodeID 380H+NodeID 480H+NodeID 从节点接收 RxPDO1~RxPDO4 200H+NodeID 300H+NodeID 400H+NodeID 500H+NodeID | 地狱 | ☆ |
5 | SDO | 主节点发送 600H+NodeID 主接点接收 580H+NodeID | 正常 | ☆ |
6 | 节点保护报文 | 700H+NodeID | 简单 | 有的资料也把这3种节点状态监控相关的报文统一在NMT这类报文中,本文也会 |
7 | 心跳报文 | 700H+NodeID | 简单 | |
8 | Boot-UP启动报文 | 700H+NodeID | 简单 |
CANopen协议报文详解
图5. CANopen协议报文的基本格式
图5是CANopen协议报文的基本格式,主要包含COB_ID和最多8个字节的数据部分。下面根据前文的分类对每一种报文进行详解。 在CAN网络中,存在主节点和从节点之分。在本文中,我们以单片机通过CAN连接驱动器控制电机为例。单片机为主节点,驱动器为从节点。
0 OD对象字典
在正式介绍CANopen协议报文之前,我们需要了解一个CANopen中用到的极其重要的一个概念,不得不吐槽网上很多资料对此部分不够重视。Object Dictionary,对象字典本质上就是一个表格,表格里面的内容都有其独特的含义和作用。在CANopen编程中,我们大部分的时间都是在编写配置这个对象字典,来达到我们的目的。
以单片机通过CANopen和驱动器通信来控制电机为例,单片机是CAN网络的主节点,它有它的对象字典。驱动器是CAN网络的从节点,它也有它的对象字典。这两个对象字典都是需要编写配置的。
- 单片机的对象字典,一般是使用开源的对象字典工具软件(objdictedit)生成,我们需要学习该软件的使用,同时还须学习移植CanFestival开源库到单片机,CanFestival和objdictedit要配合起来使用。
- 驱动器的对象字典,一般驱动器厂商会默认配置好。然后局部的内容,可以通过单片机通过SDO通信进行配置 或者 使用驱动器的上位机进行配置。
如果我们要读/写表格里面的内容,是不是需要知道它在表格的第几行第几列啊。 对象字典里面是用索引index和子索引sub-index来唯一确定这个表格每个数据的地址的。 有时候你还会发现对象字典里面某个索引/子索引对应的数据,竟然存放的是另外一个数据的索引/子索引。用C语言的话来说,它存的是指针,那存这个指针数据它本身的索引/子索引是什么?指针的指针。指针、地址、索引/子索引含义是一样的。怎么说?有没有被我成功的绕进去。 但对于对象字典的理解,本就是这么绕的,提前做好心理准备。
图6. 对象字典举例
图6所示是一个对象字典的局部内容。这里面有索引/子索引,有名称,有含义,还有位宽和读写属性。这里所展示的对象字典的局部,其实是CIA402协议里面所规定的内容,也就是说,所有使用CANopen协议的驱动器,它的对象字典里面,都是这样定义的。
图7. CANopen对象字典通用结构
图7所示为CANopen对象字典的通用结构,其中特别填充的部分是我们用的最多的。
- 1000h-1FFFh这个索引段,是用来配置通讯的。
- 6000h-9FFFh这个索引段是标准设备字协议区。解释一下,比如CIA401、CIA402…这些协议都是针对的是不同类型的设备。CIA402是针对驱动器设备的,CIA406是针对编码器设备的。 什么叫做标准设备子协议区,这个标准说明是意思就是固定。前面对图6的解释也讲过,所有支持CIA402的驱动器的对象字典索引为6040h、6060 h、607A h都固定是这个含义。
- 2000h-5FFFh这个索引段是制造商特定子协议区, 从两个层面去理解:
- 如果我是一个驱动器(从节点),这里面的内容由我驱动器的厂商自己去定义什么意思,自己去定义怎么用。我们在做单片机开发的时候,基本上不用管驱动器这部分的协议。
- 如果我是一个单片机(主节点),这里面的内容也是由我单片机自己去定义。
这么说可能有点抽象,比如我在配置PDO的时候,我可以配置2000h-5FFFh这里面的某个地址里面存储的内容是目标速度。然后在配置PDO通讯参数的时候,我把这个地址放在通讯参数里面,这里就是刚刚提到的指针的指针了,PDO后配置好之后就不用管了,往从机发PDO的时候,CANopen就会自动找到这个地址来把目标速度发送给驱动器。同时呢,我单片机的主程序,只需要修改这个地址里面的数据就可以,就等价于修改了目标速度。 这部分内容会在后文中进一步详细讲,如果没有理解透,通读一遍全文后再来读这里。
1 NMT网络管理报文
在开始正式介绍NMT网络管理报文之前,需要先介绍一个概念,CANopen协议的状态机。
图8. CANopen协议的状态机
如图8所示,CAN网络中的每一个从设备,都有会有一个状态,在不同状态下,可以执行不同的操作,有些操作比如e就只能在Operational这个状态才能执行。 下面我们来看abcdef分别代表什么。
a | NMT | 网络管理 |
b | Node Guard | 节点保护 |
c | SDO | 服务数据报文 |
d | Emergency | 紧急报文 |
e | PDO | 过程数据报文 |
f | Boot-up | 启动报文 |
另外,图8中有1,2,3,4,5,6,6种状态转移
1: Start Remote node (0x01)
2:Stop Remote Node (0x02)
3:Enter Pre-Operational State (0x80)
4:Reset Node(0x81)
5:Reset Communication (0x82)
6:Boot up
1.1 NMT状态转移报文 [ COB_ID: 000H ]
其中1~5的NMT报文只能从主节点(单片机)发送,所有从节点(驱动器)必须支持 NMT 服务。NMT报文不需要应答。NMT 报文格式如下:
图9 .NMT报文格式
其中CS是命令字,不同命令字代表不同的含义,Node-ID表示从节点的ID。 当Node-ID = 0时,则代表该指令是针对所有的从节点。 如果Node-ID≠0,则该命令仅针对该Node-ID。CS的取值及含义如下:
图10. NMT报文CS命令字的含义
1.2 NMT BOOT-UP报文 [ COB_ID: 700H + NodeID ]
图8中的“6”状态转移,是Boot-UP, 是由从节点(驱动)发布Boot-UP报文通知主节点(单片机)它已经从initialising状态进入pre-operational状态。
图11. NMT Boot-UP报文
这个报文我们只需要做个了解,在实际编程的过程中,应该用不到。
1.3 NMT节点保护报文 [ COB_ID: 700H + NodeID ]
NMT节点保护报文的机制是,主节点(单片机)可以监视每个从节点(驱动器)的当前状态。主节点周期性的发送报文去询问从节点的状态,从节点收到主节点的问询后将自己的状态回复给主节点,在设定的时间内如果主节点没有收到从节点的信息或信息错误都会判断从节点通讯故障。下面给出主节点的询问报文和从节点的回复报文。
图12. NMT 节点保护报文
从图12中我们可以看出,主节点的询问报文只有COB_ID没有数据, 而从节点的回复报文是有一个字节的数据的。该字节又分成了Bit7 和 Bits6-0两个部分。 Bit7为触发位,在每次节点保护应答中交替“0”和“1”,在第一次节点保护时置0。下面给出Bit0 - Bit6状态表:
图13. 节点保护报文,从节点回复报文,Bit0-Bit6的状态表
图13中,有两个标记,第一个0x0被划掉了,因为这个状态0x0不会在NMT节点保护报文中出现,从节点一般是自己初始化后,发送Boot-UP报文到主节点,它自己呢,就会进入Pre-operational状态。 而不是我们通过NMT节点保护报文去询问得到。 第二个红色方框框住的是扩展帧才有的状态,我们不必去关注。 最后只剩下0x4 0x5 0x7F,即 Stoped / Operational / Pre-operational三个状态,是我们需要关注的。 不过节点保护报文是一问一答,效率不高,下面介绍心跳报文。
1.4 NMT心跳报文 [ COB_ID: 700H + NodeID ]
从节点还可被配置为产生周期性的被称作心跳报文(Heartbeat)的报文,从节点周期将该报文为发送给主节点,主节点可以通过报文中的状态值得知从节点的状态。
图14. 心跳报文的格式
其状态如下,与NMT节点保护报文是一样的。
图15. 心跳报文的状态表
简单说就是主节点你不用发询问报文了,我从节点直接周期性的发送状态过来,显然,心跳报文的效率更高,但从节点应该需要做一个配置,在后文中,我们会讲SDO通信,SDO通信主要就是用来配置从节点的。
2 SYNC同步报文 [ COB_ID: 080H ]
根据我的理解, SYNC同步报文应该是由主节点(单片机)发给从节点(驱动器)的,是广播式的,所有从节点都能收到同步报文,且不需要应答。SYNC是由主节点周期性地发送,每发送一个SYNC相当于一拍,所以可以实现整个从节点网络一拍一拍的进行,同步各个从节点的时钟。
图16. SYNC同步报文
如图16所示,同步报文只有个COB_ID,没有数据。
那么主节点(单片机)的同步报文是以什么样的周期发送给从节点(驱动器)呢? 这个就需要对主节点(单片机)的对象字典进行配置了。
对象字典中,有三个和同步报文相关的索引1005H、1006H、1007H
- 1005H索引规定了SYNC报文的COB-ID,这个一般不需要更改。
- 1006H索引规定了同步帧的循环周期,注意:单位是us
- 1007H索引规定了约束了同步帧发送后,从节点发送PDO的时效,即在这个时间窗口内发送的PDO才有效,超过时间的PDO将被丢弃。注意:单位也是us
3 EMCY紧急报文 [ COB_ID: 080H + NodeID ]
紧急报文一般来讲,也是CAN网络中从节点发给主节点的,比如从节点中驱动器,有编码器,当这些设备内部出现的致命错误的时候就会触发紧急报文的发送,以最高优先级发送到主节点。适用于中断类型的错误报警信号。一个紧急报文包含8个字节,报文格式如下:
图17.EMCY紧急报文格式
紧急报文和同步报文的功能码都是080H,但是他们的区别是很多的,很容易区分。
- 同步报文一般是主节点发给从节点的,紧急报文是从设备发给主节点的。
- 同步报文没有NodeID,而紧急报文有NodeID,这个NodeID可以指示是哪一个从设备发生了故障。
- 同步报文是不带数据的,但是紧急报文是带8个字节的数据的,8个字节拉满。其目的是通过这8个字节的信息让主节点能够分析出从设备到底发生了什么故障。
如图17所示,紧急报文的8个字节数据被分为了3个部分
第一个部分包含两个字节,Byte0-1,表示应急错误代码。错误代码表格如下图所示。
图18.EMCY紧急报文的错误代码及含义
图18所示的错误代码及含义应该是由CANopen的基础协议CIA301所定义,也就是是固定不变的。 另外,错误代码中“XX”部分是由相应的标准设备子协议规定。 什么意思呢? 就是如果我是一个驱动器,那么“XX”部分是由CANopen中的CIA402协议去规定。如果我是个编码器呢,那么“XX”部分是由CANopen中的CIA406协议去规定。总的来说呢,就是这个错误代码及含义是固定不变的,通过查询CIA301和CIA402/CIA406/其他设备子协议,就可以查到错误代码的含义。
第二个部分包含1字节,Byte2,表示错误寄存器的数据。前面我们提到了OD对象字典。也提到过无论是主节点(单片机),还是从节点(驱动器/编码器/其他类型的设备),都有一个属于它们自己的对象字典。 那么在CIA301协议中我们规定,索引1001H,就是错误寄存器,位宽为8位。 假设我们CAN网络中有个驱动器出问题了, 那么驱动器它本身(如果驱动器它自己实现了错误寄存器这部分协议的话,有些驱动器不会实现完整的协议的),它会自己判断到底是什么错误,然后呢,把这个错误信息,写到它自己的对象字典的1001H索引里面。随后发送EMCY紧急报文到CAN网络中,并且紧急报文中的第3个字节Byte2就会自动去拷贝对象字典里面索引为1001H里面的数据。当我们主节点收到这个报文的时候,就可以做更细致的错误分析。如下图所示,为错误寄存器1001H的8个位的数据每一位的含义。
图19.EMCY紧急报文的错误寄存器每个位的含义
第三个部分,共5个字节,Byte3-7,制造商特定的错误区域,这部分就不是标准的CIA301和CIA402/CIA406/其他设备子协议所规定的内容啦。 所以假设有两个驱动器的制造商,那么这两个驱动器的制造商在Byte3-7的定义很大概率是不同的。这部分内容有驱动器制造商自己去定义错误区域的含义。这部分的定义呢,应该出现在该驱动器的规格书里面,以便于用户去查询/定位错误的具体信息。
在以下这个参考链接中,有一个EMCY紧急报文的举例说明,可以看看。
canopen快速入门10emcy_哔哩哔哩_bilibili
4 SDO服务数据报文 [COB_ID: 580H+NodeID (RxSDO) | 600H+NodeID (TxSDO)]
原本想着根据COB_ID的大小,以从小到大的顺序来安排本文的内容,然而我还是发现,SDO的内容还是放在PDO的内容之前好一些,PDO更难理解是一方面,另外,对于PDO通讯参数和映射参数的配置,是需要使用SDO这部分内容的前置知识的,所以我们先讲SDO,再讲PDO。
如标题所示,SDO报文有两种COB_ID,一个发送一个接收。 SDO通讯是一种一问一答的方式,一般是由主节点发起一个对从节点的对象字典的某个索引/子索引的读或写的操作,然后从节点反馈主节点,你的写操作是否成功或你要读的数据。这个RxSDO和TxSDO是我本人为了便于理解,自己提的说法,都是针对主节点而言。
我们来看SDO的COB_ID具体的定义规则:
图20. SDO COB_ID的具体定义
图20的SDO COB_ID具体定义这里就不细讲了,一目了然。 下面我们将SDO分为读操作和写操作分别展开具体的通讯协议。(再次强调,我们是主节点去读/写操作从节点对象字典,主语是主节点)
读操作
图21. SDO读操作主节点发送报文
由主节点发送给从节点
图22.SDO读操作从节点回应报文
由从节点发送给主节点
下面做一个举例说明
图23. 驱动器设备CIA402协议的对象字典的局部内容
图23中的数值都是十六进制,注意一下08,10,20,分别表示8位,16位,32位。
图24.SDO读操作示例
如图24所示,示例中是主节点现在先要读取驱动器1号(NodeID=1)的伺服实际位置6063 00(索引 子索引,图23最后一行)。CANopen是低字节在前高字节在后的小端格式,所以图24中的6360,实际上6063。
从节点回应的报文,43代表我回的是4个字节(图22有写),63 60/00表示6063索引/00子索引。最后13 D1 FF FF就是具体的实际位置的数据啦。由于CANopen是小端格式,所以反过来是FF FF FD 13,转换成10进制为-12013。
写操作
图25. SDO写操作主节点发送报文
由主节点发送给从节点
图26.SDO写操作从节点回应报文
由从节点发送给主节点
同样的,下面也给出一个SDO写操作的示例。
图27. SDO写操作示例(写成功)
如图27所示,我们先看发送报文,23H表示写4个字节,7A 60 00表示607A索引/00子索引, 回顾刚才的图23,607A/00表示的是目标位置。 A0 86 01 00 是 00 01 86 A0的小端格式 = 100000d。 再看回应报文,60表示写操作成功了,后面索引和数据回来的是一样的。说明我们把目标位置100000写到NodeID = 1的驱动器的对象字典里面去啦。 如果驱动器正常工作的话, 就会把电机转到100000这个位置去啦。
那么有成功也会有失败,失败的报文是怎样的呢?
图28. SDO通讯失败(读/写操作)
如图28所示,无论的读还是写,如果SDO通讯失败,返回的命令字都是80。数据中最后4个字节表示错误代码。其定义如下:
图29.SDO通讯失败错误代码
如图29所示,红色框住的部分是比较常见的SDO通讯错误。第一个可能是索引搞错了,大小端没注意。第二个可能是位宽搞错了,比如往8位宽的字典里面写32位宽的数据。
5 PDO过程数据报文 [ COB_ID : 180H+NodeID ~500H+NodeID]
终于到达了地狱级难度的PDO啦,哈哈,其实也没有那么难,如果前文的所有内容你都理解了,那么PDO也不过是水到渠成的事情,只不过就是步骤多一些,多绕几个弯子。
PDO相比于SDO而言,它的优势在于它不需要应答信号,效率高,因此用于传输实时性要去高的数据。但缺点也比较明显,就是在运行PDO之前,需要进行一些地狱级难度的配置工作。大家不必担心和心急,请耐心的看后文,我们一层一层的破开云雾见月明。遇到有疑惑的地方,带着疑惑往下看,你得疑惑应该都会对应的解答。
知识点1
下面讲第一个我认为在学习PDO的时候比较重要的事情,我们在网上所看到的资料,所说的TxPDO和RxPDO基本是都是针对从节点而言的。所谓的传输Tx,是说从节点传输PDO报文到主节点。 所谓的接收Rx,是说从节点接收主节点发过来的PDO报文。 每一个PDO都有两组参数要配置,通讯参数和映射参数(后面细讲),说的也是去配置从节点的对象字典。SDO的读写操作是指主节点去读写操作从节点。 PDO的Tx和Rx指的是从节点的传输和接收。网上的资料很少去强调这个,导致很多时候理解起来有点困难。
知识点2
第二个比较重要事情也需要进一步提前说明, 之前我们也提到了,无论主节点还是从节点,都是存在一个对象字典的。PDO的通讯是需要提前进行一些配置工作的, 网上很多资料的重点是去讲 如何利用SDO通讯,去配置从节点的PDO的通讯参数和映射参数。但是忽略了主节点也需要配置PDO相关的对象字典,我们主节点对于PDO参数的配置,主要是用开源的对象字典工具软件(objdictedit)生成。但这也是一个重要的事情,不能忽视。 可能这时候有点绕了, 我们从节点是不是存在TxPDO和RxPDO。对于从节点的TxPDO和RxPDO,都需要对他们的通讯参数和映射参数进行配置。 那么从节点的TxPDO配置完了是不是要发送报文给主节点。 那么主节点的RxPDO是不是也需要做一个对于的配置。有点像串口通信,从节点的TxPDO对于主节点的RxPDO。从节点的RxPDO对于主节点的TxPDO。 总结来讲,就是从节点的TxPDO和RxPDO,主节点的TxPDO和RxPDO都需要进行配置。 从节点的PDO配置主要通过主节点发送SDO通讯去配置从节点PDO的通讯参数和映射参数的对应的对象字典。 主节点的PDO配置主要通过开源的objdictedit软件直接生成。
知识点3
是不是感觉上面两个比较重要的事情有点绕了, 哈哈,下面我讲第三个重要的事情,前面我们提到无论是TxPDO和RxPDO,每一个PDO都有两组参数要配置,这两组参数分别叫做通讯参数和映射参数。注意一我的用词,是两组参数。为什么用组,而不用个。因为通讯参数和映射参数中包含的配置项都不止一项。举个例子,从节点的第一个TPDO1,它所对应的通讯参数和映射参数在其对象字典中的索引分别是1800H和1A00H。 好,这还没完,TPDO1的通讯参数,包含7个配置项。其具体的实现方式,就是子索引。之前大家可能对子索引还比较疑惑,为什么索引下面还有个子索引呢,其实就是有时候索引是不够用的,因此同一功能的索引下面还包含很多个子索引。比如TPDO1的通讯参数索引是1800H,它的子索引分别是00H 01H 02H…06H。每个子索引代表的含义,位宽,值的含义如图30所示。
图30. 从节点 – TxPDO1 – 通讯参数- 索引/子索引,以及每个子索引的含义,位宽,值的含义
同理,TPDO1的映射参数的1A00H对应的子索引也不止一个,如图31所示。
图31.从节点-TPDO1-映射参数-索引/子索引,以及每个子索引的含义,位宽,值的含义
映射参数的子索引还不是固定的个数,这主要取决于第一个子索引00H,它代表的映射参数数目。如图31所示,当它的值为2的时候,后面就还有01 02两个子索引。以此类推,如果它的值是4呢,后面可以有01 02 03 04四个子索引。 但是这里有一个约束,就是你所有的映射参数的位宽,加起来不能超过64位。可以这么说,就是你后面可以最多8个子索引,每个子索引的位宽都是8位,8*8=64位。
知识点4
怎么样? 信息量是不是上来了?所以叫地狱难度。接下来,我们说第四个重要的事情。一般标准的CANopen协议里面,无论是从节点还是主节点。 它的TxPDO和RxPDO都是4个,即TxPDO1、TxPDO2、TxPDO3、TxPDO4、RxPDO1、RxPDO2、RxPDO3、RxPDO4,总共8个PDO。每个PDO都会有对应的通讯参数和映射参数。那就是16个索引,不知道多少个子索引了。 那有的人就会觉得,啊?我配置从节点的一个TPDO1都费劲了,我还要配从节点8个PDO,主节点8个PDO,那不是得废老大劲啦? 这里给一个回答,就是真实的使用场景,这些PDO可能未必会全部使用。大家按照需要去配置。 下面给出从节点所有PDO的COB_ID和通讯参数/映射参数的索引,(子索引定义都一样的,如图30和31所示)
图32. 从节点 TPDO/RPDO的COB_ID以及通讯参数/映射参数索引
前面我们第二个重要的事情提到,主节点是需要配置TPDO/RPDO的,那主节点的COB_ID以及通讯/映射参数的索引是怎样的呢?请看图33。
图33. 主/从节点的PDO COB_ID以及通讯/映射参数索引
注意到图33中主机的COB_ID是个大的范围,是因为一个CAN网络中可以允许有127个从Node。这些Node的ID是不一样的。在一个有多个从节点的CAN网络中,主节点的TPDO和RPDO的使用率应该是较大的,甚至有可能8个都用完。而从节点的TPDO和RPDO有可能的只使用一组。从图33还有另外的关键信息,即主机的TPDO/RPDO的COB_ID与从机相反
- 主机的TPDO和从机的RPDO的COB_ID号是一样的;
- 主机的RPDO和从机的TPDO的COB_ID号是一样的。
知识点5
紧接上文,我们讲第5个重要的问题, 想象一个场景,如果CAN网络中,有3个驱动器, 我主机需要通过TPDO分别发送3个目标速度给3个驱动器。 由于驱动器的NodeID号不一样,所以主机需要用3个TPDO,配置3个不同的COB_ID。但在配置映射参数的时候,问题来啦,我这3个目标速度应该放在哪个索引里面。我主机的TPDO应该去哪些索引里面去取3个目标速度呢? 有的熟悉CIA402协议的同学可以立马反应过来了,马上举手站起来就回答。目标速度的索引/子索引是60 FF/00。 记忆力很好哈,可惜,这个不是老师要的答案哈,秀儿你坐下。 对于驱动器而言,它遵循CIA402协议,目标速度的索引确实是在60 FF/00这个位置。 但!对于CAN网络中的主机(单片机)而言,它是不遵循CIA402协议,这一点很重要。 如果目标速度是在索引60 FF/00这个地址,那岂不是我主机发给3个驱动器的目标速度,都是一样的值啦。 正确的做法应该是把的3个电机的目标速度的索引放在 2000H~5FFFH这个区域,如图7所示,这个索引区域是什么?是制造商特定子协议区。换成大白话就是,这部分索引,我想怎么定义就怎么定义。这部分的配置,还是在objdictedit工具里面配置。
请仔细阅读上述文字后,再继续阅读下文。
通讯参数和映射参数的配置以及报文格式
随着信息量越来越大,所产生的疑惑可能会越来越多,主要的疑惑可能是我的通讯参数和映射参数具体应该怎么配置,参数里面各个子索引分别的什么含义?我们RxPDO报文和TxPDO报文它的格式到底是怎么样的? 下面以配置主节点的TPDO和配置从节点的RPDO为例进行进一步的讲解。
例子:假设现在我们要用主节点的TxPDO1给从节点驱动器发送目标速度,从节点用RxPDO1来进行接收。
网上的资料经常会忽略或不重视,主节点的TxPDO1的配置,因为这步的操作是在objdictedit工具里面完成的。下面我们就先来讲主节点TxPDO1的配置。
主节点TxPDO1的配置
TxPDO1的需要配置三个东西
1、在制造商特定子协议区自己定义一个目标速度的索引
2、TxPDO1的通讯参数配置
3、TxPDO1的映射参数配置
我们先看第1点如何实现。
面我们来看第2点,TPDO1的通讯参数配置
0x00 Highest Sublndex Supported
最大子索引支持个数,只读的,我们不需要配置。6就代表,后面0x00子索引后面还有0x01、0x02、0x03、0x04、0x05、0x06 6个子索引。
0x01 COB_ID used by PDO
指的是主节点在发送TPDO报文的时候的COB_ID。 这里由0x180 + NodeID组成。比如我主节点想通过TPDO1发送目标速度个驱动器,驱动器的NodeID是1,那么这里的COB_ID就要写0x180+1 = 0x181。
0x02 Transmission Type
TPDO的传输类型,这个子索引对于的数据位宽的8位, 不同的值代表着TPDO不同的传输方式。如下图所示:
当TPDO传输类型的值为0时,代表同步/非循环模式,如果映射数据发生改变,且接收到一个同步帧,则发送该TPDO。可是同步帧一般情况下都是由主节点发送给从节点的。所以在主节点用TPDO发送数据给从节点的时候,我感觉一般不用这个类型。
当TPDO传输类型的值为1~240时,代表同步/循环模式,比如值为2,那么该TPDO报文将在接收到两个同步帧之后,但还是同样一个问题,同步帧一般情况下都是由主节点发送给从节点的。主节点发送同步帧的时候,不知道主节点会对同步帧计数吗?
241~253这一段值,保留,暂时不管
当TPDO传输类型的值为为254、255时,代表异步/制造商特定事件和设备子协议特定事件。 网上很多资料说当映射数据改变或事件计时器到,就会发送PDO。但有人测试过,254或者255的情况下,如果映射数据改变的时候,不会发送PDO,只有事件计时器到才会发送PDO。 我的理解是254代表映射数据改变的触发TPDO,这个不靠谱。所以,这里可能靠谱的做法是选255,然后配合0x05 Event Timer做事件定时触发。
0x03 Inhibit Time
禁止时间,这一项对异步传输类型的TPDO才有用,用来规定TPDO发送的最小时间间隔。这其实就是流量控制,防止TPDO狂发,占用大量CAN总线带宽。CIA301协议规定,禁止时间的分辨率是100us,精度是2毫秒。比如你要禁止1毫秒,你这个数值得写10d。你要禁止2毫秒,这个数值得写20。注意,有些设备可能把这个分辨率改成1ms什么的,使用的时候注意。
0x04 Compatibility Entry,这个参数不管
0x05 Event Timer
事件定时器,这个应该是和传输类型为 254/255的异步传输类型配合使用。 这个参数名很容易让人误解,这里要这么去理解这个参数,这个参数代表的是TPDO定时周期,在移植CANopen协议开源库到单片机的时候,它是需要实现带定时器功能,也就是说CANopen协议内部是自带定时器的。 这就可以解释这个参数了。 这个参数的单位的1ms。 比如你填入一个1,那么CANopen应该就会每隔1ms触发一次TPDO。 配合0x03禁止时间使用。
0x06 SYNC start value
TPDO同步帧的起始值,这个容易理解,这个是配合传输类型为1~240使用,比如传输类型值是2。 同步帧起始值这里我配置的是1,那么我第一次出发TPDO报文,应该是只需要收到1个同步帧就可以了。 后续第2帧开始以后的TPDO报文,我猜测应该还是要接到2同步帧才能触发发送。目前来讲,没什么卵用。
那么总结一下,主机发送TPDO的方式,无非就两种
- 一种是传输类型为1~240,当收到从节点的n个同步帧之后触发TPDO。
- 一种是传输类型为255,结果0x05的Event Timer,主机不需要接收从节点的同步帧,而是采用自己内部的定时器进行周期性的触发。
具体选择哪一种呢? 需要做一个验证,我有一个设想,如果主节点往从节点发送的同步帧,也算是主机自己接到的同步帧的话,也就是说主节点在发送同步的是同时,也会触发自己的TPDO,这样就美滋滋了。 这就果断选同步循环方式。 否则,就选择第二种,定时器触发方式。本文中,稳妥起见,我暂时认为我的设想是错误的,即如果我配置主机的TPDO是同步循环模式,则从节点必须发送同步帧到主机,才能触发主机的TPDO。 所以我选择定时器触发。
那么我们通讯参数配置则如下
下面来看第3点 ,TxPDO1的映射参数配置
映射参数我们也简单来理解一下,这个比通讯参数简单多了。
这里我认为这个软件少显示了一些信息,这里只简化显示了变量名,没有显示 子索引 和 位宽。不过生成对象字典的时候应该会自动生成,标准的映射参数配置,我们来看下图。
你看【值】的部分, 是存在 7100H(索引) / 01H(子索引) / 10H(16位宽)3个部分的。
从节点RxPDO1的配置
前面提到了,从节点,比如说驱动器,它的对象字典,可能有两种配置方式,一种是通过驱动器本身的上位机软件进行配置,一种是通过主节点给从节点发送SDO指令来配置。 上位机配置的方式和上面主节点用objdictedit配置方式估计大同小异,而有些驱动器是不支持上位机配置的方式的。所以关于从节点RxPDO1的配置,我们用SDO指令的方式讲解,而且里面还有一些其他的东西需要注意。
假设我们使用RPDO1来接收主机发过来的信息。那么我们需要配置RPDO1的通讯参数和映射参数。即:1400H和1600H两个索引。RPDO1的通讯参数和映射参数的索引是1400H和1600H。 由于它一个索引号,不止要配置一个参数。所以还有子索引。每个子索引呢,代表不同的含义。
上面这两张图是有点不严谨的,不必在意,可以看到是抄了以前的图,然后稍微改了改。
使用SDO的配置方式,是由固定流程的。
SDO指令如果搞忘了可以倒回去看第4小节。
0x22是通用的写,驱动器未必都支持这种协议,建议还是使用下面这种方式。
2f:写入1个;
2b:写入2个;
27:写入3个;
23:写入4个
下面对上述指令进行一一解析。
第一条指令
第二条指令
第三条指令
注意一下,这里追加映射参数的时候,所有参数的总长度不能超过8个字节。
比如我01子索引映射参数长度20H=32d位
02 子索引映射参数长度20H=32d位
前面两个参数已经64位 = 8字节了, 后面03 04就不能再加了。
第四条指令
第5条指令。
前面5条配置完,RPDO1的配置就结束了,但是它并不代表现在PDO就能运行了。 因为CANopen协议规定,PDO服务必须只能在operational操作状态下在能运行。 另外驱动器本身还需要做一些设置,也是通过SDO服务。
第6~9条命令
这时候我们可以用SDO发送速度,也可以用PDO发送目标速度了。前面第1~5条指令,就为了配置驱动器的RPDO1的。就是为了让主机通过PDO把目标速度发过去。 如果你不用PDO,前面的配置反而没意义了。而且PDO发送效率更高,不需要应答。
第10条指令
PDO的配置结束时候,还需要发送应NMT网络管理指令。将从机驱动器NODE-ID假设=0x01的CANopen状态机的状态,切换成operational操作状态。
第11~∞条
大家有没有发现我们在配置从节点RxPDO的时候,COB_ID配置的是201,但是前面主节点TxPDO的配置却是配置的181。 这是objedit个坑,它会提示你180+NodeID,我在截图的时候没注意,就搞成181了。 前面知识点4提到过的。主节点的RxPDO和TxPDO的COB_ID和 从节点的COB_ID是反的。所以应该是这样:
总结:学习一个新东西,最好还是要做一个笔记整理,整理的过程中你会发现以前以为明白的地方,其实并没有理解透彻,这个时候能继续查漏补缺进一步完善。 下集预告,CANopen开源库移植,欢迎关注/阅订。