跳转到主要内容

CANOpen 协议简要说明

judy 提交于

CANopen是一种架构在控制局域网路(Controller Area Network, CAN)上的高层通讯协定,包括通讯子协定及设备子协定常在嵌入式系统中使用,也是工业控制常用到的一种现场总线。CANopen 实现了OSI模型中的网络层以上(包括网络层)的协定。CANopen 标准包括寻址方案、数个小的通讯子协定及由设备子协定所定义的应用层。 CANopen 支援网络管理、设备监控及节点间的通讯,其中包括一个简易的传输层,可处理资料的分段传送及其组合。一般而言数据链结层及物理层会用CAN来实作。除了 CANopen 外,也有其他的通讯协定(如EtherCAT)实作 CANopen 的设备子协定。CANopen由非营利组织CiA(CAN in Automaion)进行标准的起草及审核工作,基本的 CANopen 设备及通讯子协定定义在 CAN in Automation (CiA) draft standard 301中。针对个别设备的子协定以 CiA 301 为基础再进行扩充。如针对 I/O 模组的 CiA401 及针对运动控制的 CiA402。

本说明是CAN上层协议CANOpen协议的嵌入式简化,较原始CANOpen版本简单易用,且更适应嵌入式模块通讯的需要,并且兼容CANOpen协议。

下面按照协议支持的功能分别进行说明:

NMT网络管理服务

网络管理状态表:

终端主节点→从节点
<div id="mytable">
<body>
<table width="291" height="159" border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0-字节</p></td>
<td><p>1-字节</p></td>
</tr>
<tr>
<td valign="middle"><p>0</p></td>
<td valign="middle"><p>命令说明符</p></td>
<td valign="middle"><p>节点ID</p></td>
</tr>
<tr>
<td><p>(16#000)</p></td>
<td><p>(CS)</p></td>
<td><p>(1)</p></td>
</tr>
</tbody>
</table>
</body>
</div>

说明:一般终端主节点是指上位机,如上位用于网络管理和监控的PC机,从节点是指被管理的各模块。

如果节点ID是0表示命令会被广播至所有从节点,从节点必须执行相应的命令。

命令说明符功能见下表:
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td valign="middle"><p>命令说明符(CS)</p></td>
<td valign="middle"><p>意义</p></td>
</tr>
<tr>
<td valign="middle"><p>1(16#01) </p></td>
<td valign="middle"><p>启动节点进入可操作状态</p></td>
</tr>
<tr>
<td valign="middle"><p>2(16#02) </p></td>
<td valign="middle"><p>停止远程节点</p></td>
</tr>
<tr>
<td valign="middle"><p>128(16#80) </p></td>
<td valign="middle"><p>使节点进入预操作状态</p></td>
</tr>
<tr>
<td valign="middle"><p>129(16#81) </p></td>
<td valign="middle"><p>复位节点</p></td>
</tr>
<tr>
<td valign="middle"><p>130(16#82) </p></td>
<td valign="middle"><p>复位通讯</p></td>
</tr>
</tbody>
</table>
</body>
</div>

例:启动节点1进入可操作状态,
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>16#000</p></td>
<td><br /></td>
<td><p>16#80</p></td>
<td><p>16#04</p></td>
</tr>
</tbody>
</table>
</body>
</div>

网络管理状态表:
<center><img src="http://xilinx.eetrend.com/files-eetrend-xilinx/article/201812/13966-411…; alt=""></center>

<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>状态转换</p></td>
<td><p>说明</p></td>
</tr>
<tr>
<td><p>(1) </p></td>
<td><p>通电后自动初始化</p></td>
</tr>
<tr>
<td><p>(2) </p></td>
<td><p>完成初始化自动进入预操作状态</p></td>
</tr>
<tr>
<td><p>(3),(6) </p></td>
<td><p>启动远程节点</p></td>
</tr>
<tr>
<td><p>(4),(7) </p></td>
<td><p>进入预操作状态</p></td>
</tr>
<tr>
<td><p>(5)(8) </p></td>
<td><p>停止远程节点</p></td>
</tr>
<tr>
<td><p>(9),(10),(11) </p></td>
<td><p>复位节点</p></td>
</tr>
<tr>
<td><p>(12),(13),(14) </p></td>
<td><p>复位通讯</p></td>
</tr>
</tbody>
</table>
</body>
</div>

根据节点的状态提供以下服务:
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr></tr>
<tr>
<td><p><br />
初始化</p></td>
<td><p>预操作</p></td>
<td><p>可操作</p></td>
<td><p>停机</p></td>
</tr>
<tr>
<td><p>PDO </p></td>
<td><br /></td>
<td><br /></td>
<td><p>√</p></td>
<td><br /></td>
</tr>
<tr>
<td><p>SDO </p></td>
<td><br /></td>
<td><p>√</p></td>
<td><p>√</p></td>
<td><br /></td>
</tr>
<tr>
<td><p>启动引导</p></td>
<td><p>√</p></td>
<td><br /></td>
<td><p>√</p></td>
<td><br /></td>
</tr>
<tr>
<td><p>网络管理</p></td>
<td><br /></td>
<td><p>√</p></td>
<td><p>√</p></td>
<td><p>√</p></td>
</tr>
<tr>
<td><p>错误控制</p></td>
<td><br /></td>
<td><p>√</p></td>
<td><p>√</p></td>
</tr>
</tbody>
</table>
<br />
</body>
</div>

过程数据对象(PDO)服务

过程数据对象用于在节点之间传送过程数据,如I/O模块I/O状态读取和设定,模拟量采集和模拟量输出等等,本协议考虑从机硬件限制最多支持4组PDO,每组包含一个RPDO和一个TPDO,现已I/O模块为例进行说明:

设I/O模块有24输入,24输出,24输入通过TPDO传给监控终端或其他节点,24输出通过RPDO由控制节点对其进行设定,采用发送和接收PDO1组,即标识符TPDO:0x180+NODE_ID,RPDO:0x200+NODE_ID,用3个字节就可以表示24个I/O量,因此发送和接收PDO可以表示如下(假设NODE_ID=1):
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>385(0x181) </p></td>
<td colspan="3"><p>Data:输入I/O状态量</p></td>
</tr>
<tr>
<td><p>Input_Digital_1</p></td>
<td><p>Input_Digital_2</p></td>
<td><p>Input_Digital_3</p></td>
</tr>
</tbody>
</table>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>513(0x201)</p></td>
<td colspan="3"><p>Data:输出I/O状态量</p></td>
</tr>
<tr>
<td><p>Output_Digital_1</p></td>
<td><p>Output_Digital_2</p></td>
<td><p>Output_Digital_3</p></td>
</tr>
</tbody>
</table>
</body>
</div>

如果是8通道的模拟量采集模块则需要两个TPDO来传送采集的数据,不需要RPDO,因为每个通道产生一个16位数据,一共是16字节,每个TPDO只能够传送8字节。两个TPDO标识符分别为:0x180+NODE_ID,0x280+NODE_ID。

每帧数据格式如下表(设NODE_ID=2):

TPDO1
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0-1字节</p></td>
<td><p>2-3字节</p></td>
<td><p>4-5字节</p></td>
<td><p>6-7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>385(0x182)</p></td>
<td colspan="4"><p>Data:输入模拟量</p></td>
</tr>
<tr>
<td><p>Output_Anolog_1 </p></td>
<td><p>Output_Anolog_2 </p></td>
<td><p>Output_Anolog_3 </p></td>
<td><p>Output_Anolog_4 </p></td>
</tr>
</tbody>
</table>
</body>
</div>

TPDO2
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0-1字节</p></td>
<td><p>2-3字节</p></td>
<td><p>4-5字节</p></td>
<td><p>6-7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>385(0x182) </p></td>
<td colspan="4"><p>Data:输入模拟量</p></td>
</tr>
<tr>
<td><p>Output_Anolog_5 </p></td>
<td><p>Output_Anolog_6 </p></td>
<td><p>Output_Anolog_7 </p></td>
<td><p>Output_Anolog_8 </p></td>
</tr>
</tbody>
</table>
</body>
</div>

1. I/O节点←监控终端(RPDO)

2. I/O节点→监控终端(TPDO)

服务数据对象(SDO)服务

服务数据对象用于读写节点的对象字典,现 只实现SDO下载和上传的全速模式,不支持正常模式也不支持块下载和上传,因为数据量不大,实现的功能足够用,SDO全速下载和上传采用应答式服务,由监控终端向节点发送服务请求,由节点返回应答。

现假设某节点NODE_ID=3,协议报文如下:

读节点OD

假设,读SDO index = 0x1018 subindex = 0x00,返回的是1字节data=0x04

监控终端→节点(终端请求)
<div id="mytable">
<body>
<table border="1" align="left">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
<td><p>3字节</p></td>
<td><p>4字节</p></td>
<td><p>5字节</p></td>
<td><p>6字节</p></td>
<td><p>7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>1539(0x603) </p></td>
<td><p>命令</p></td>
<td colspan="2"><p>index</p></td>
<td><p>subindex </p></td>
<td colspan="4"><p>Reserved(保留)</p></td>
</tr>
<tr>
<td><p>0x40 </p></td>
<td><p>0x18 </p></td>
<td><p>0x10 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
</tr>
</tbody>
</table>
</body>
</div>

监控终端←节点(节点应答)
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
<td><p>3字节</p></td>
<td><p>4字节</p></td>
<td><p>5字节</p></td>
<td><p>6字节</p></td>
<td><p>7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>1411(0x583) </p></td>
<td><p>命令</p></td>
<td colspan="2"><p>index </p></td>
<td><p>subindex </p></td>
<td><p>数据</p></td>
<td colspan="3"><p>凑够8个字节</p></td>
</tr>
<tr>
<td><p>0x4F</p></td>
<td><p>0x18 </p></td>
<td><p>0x10 </p></td>
<td><p>0x00 </p></td>
<td><p>0x04 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
</tr>
</tbody>
</table>
</body>
</div>

写节点OD

假设,写SDO index=0x1017,subindex=0x00,写数据为0x07D0

监控终端→节点(终端请求)
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
<td><p>3字节</p></td>
<td><p>4字节</p></td>
<td><p>5字节</p></td>
<td><p>6字节</p></td>
<td><p>7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>1539(0x603) </p></td>
<td><p>命令</p></td>
<td colspan="2"><p>index </p></td>
<td><p>subindex </p></td>
<td colspan="2"><p>要写入的数据</p></td>
<td colspan="2"><p>凑8个字节</p></td>
</tr>
<tr>
<td><p>0x2B</p></td>
<td><p>0x17</p></td>
<td><p>0x10 </p></td>
<td><p>0x00 </p></td>
<td><p>0xD0</p></td>
<td><p>0x07</p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
</tr>
</tbody>
</table>
</body>
</div>

监控终端←节点(节点应答)
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
<td><p>3字节</p></td>
<td><p>4字节</p></td>
<td><p>5字节</p></td>
<td><p>6字节</p></td>
<td><p>7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>1411(0x583) </p></td>
<td><p>命令</p></td>
<td colspan="2"><p>index </p></td>
<td><p>subindex </p></td>
<td colspan="4"><p>凑够8个字节</p></td>
</tr>
<tr>
<td><p>0x60 </p></td>
<td><p>0x18 </p></td>
<td><p>0x10 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
<td><p>0x00 </p></td>
</tr>
</tbody>
</table>
</body>
</div>

如果监控终端发送的请求,节点处理出错,将返回SDO中止报文,报文如下:
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
<td><p>1字节</p></td>
<td><p>2字节</p></td>
<td><p>3字节</p></td>
<td><p>4字节</p></td>
<td><p>5字节</p></td>
<td><p>6字节</p></td>
<td><p>7字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>1411(0x583) </p></td>
<td><p>命令</p></td>
<td colspan="2"><p>index </p></td>
<td><p>subindex </p></td>
<td colspan="4"><p>错误代码</p></td>
</tr>
<tr>
<td><p>0x80 </p></td>
<td><p>0x18 </p></td>
<td><p>0x10 </p></td>
<td><p>0x00 </p></td>
<td><p>0-7位</p></td>
<td><p>8-15位</p></td>
<td><p>16-23位</p></td>
<td><p>24-31位</p></td>
</tr>
</tbody>
</table>
</body>
</div>

节点返回的错误代码如下:

SDO_ABORT_UNSUPPORTED 0x06010000UL 不支持该功能

SDO_ABORT_NOT_EXISTS 0x06020000UL 不存在的索引

SDO_ABORT_READONLY 0x06010002UL 只读,不可写

SDO_ABORT_TYPEMISMATCH 0x06070010UL 类型不匹配

SDO_ABORT_UNKNOWN_COMMAND 0x05040001UL 未知命令

SDO_ABORT_UNKNOWNSUB 0x06090011UL 未知子索引

注:命令字节含义请参考CANOpen协议文件DS301。

错误控制——心跳(或者叫脉动)协议(heatbeat)

错误控制用于了解总线上各节点的状态,本文只实现心跳协议,由各节点向监控终端定期发送心跳报文,报告本节点的状态,报文如下:

设节点NODE_ID=2

节点→监控终端
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>COB-ID</p></td>
<td><p>0字节</p></td>
</tr>
<tr>
<td rowspan="2"><p>1794(0x702) </p></td>
<td><p>节点状态</p></td>
</tr>
<tr>
<td><p>0x00</p></td>
</tr>
</tbody>
</table>
</body>
</div>

返回状态如下:
<div id="mytable">
<body>
<table border="1">
<tbody valign="top">
<tr>
<td><p>状态代码</p></td>
<td><p>状态含义</p></td>
</tr>
<tr>
<td><p>0x00</p></td>
<td><p>BOOTUP启动状态</p></td>
</tr>
<tr>
<td><p>0x04</p></td>
<td><p>STOPPED停止</p></td>
</tr>
<tr>
<td><p>0x05</p></td>
<td><p>OPERATIONAL可操作</p></td>
</tr>
<tr>
<td><p>0x7F </p></td>
<td><p>PRE-OPERATIONAL预操作</p></td>
</tr>
</tbody>
</table>
</body>
</div>

节点在启动之后发出BOOTUP心跳,然后按固定频率发出脉动消息帧,监控终端接收该报文用以检测该节点状态。

本文转载自: <span id="profileBt"><a href="https://mp.weixin.qq.com/s?__biz=MzI0NDU2OTk0Nw==&amp;mid=2247484519&am…;开源嵌入式</a></span>