一、介绍
从OSI网络模型的角度来看同,现场总线网络一般只实现了第1层(物理层)、第2层(数据链路层)、第7层(应用层)。因为现场总线通常只包括一个网段,因此不需要第3层(传输层)和第4层(网络层),也不需要第5层(会话层)第6层(描述层)的作用。
CAN(Controller Area Network)现场总线仅仅定义了第1层、第2层(见ISO11898标准);实际设计中,这两层完全由硬件实现,设计人员无需再为此开发相关软件(Software)或固件(Firmware)。同时,CAN只定义物理层和数据链路层,没有规定应用层,本身并不完整,需要一个高层协议来定义CAN报文中的11/29位标识符、8字节数据的使用。而且,基于CAN总线的工业自动化应用中,越来越需要一个开放的、标准化的高层协议:这个协议支持各种CAN厂商设备的互用性、互换性,能够实现在CAN网络中提供标准的、统一的系统通讯模式,提供设备功能描述方式,执行网络管理功能。
应用层(Application layer):为网络中每一个有效设备都能够提供一组有用的服务与协议。
通讯描述(Communication profile):提供配置设备、通讯数据的含义,定义数据通讯方式。
设备描述(Device proflile):为设备(类)增加符合规范的行为。
下面将介绍基于CAN的高层协议:CAL协议和基于CAL协议扩展的CANopen协议。CANopen协议是CAN-in-Automation(CiA)定义的标准之一,并且在发布后不久就获得了广泛的承认。尤其是在欧洲,CANopen协议被认为是在基于CAN的工业系统中占领导地位的标准。大多数重要的设备类型,例如数字和模拟的输入输出模块、驱动设备、操作设备、控制器、可编程控制器或编码器,都在称为“设备描述”的协议中进行描述;“设备描述”定义了不同类型的标准设备及其相应的功能。依靠CANopen协议的支持,可以对不同厂商的设备通过总线进行配置。在OSI模型中,CAN标准、CANopen协议之间的关系如下图所示:
图1 CAN、CANopen标准在OSI网络模型中的位置框图
二、CAL 协议
CAL(CAN Application Layer)协议是目前基于CAN的高层通讯协议中的一种,最早由Philips医疗设备部门制定。现在CAL由独立的CAN用户和制造商集团CiA(CAN in Automation)协会负责管理、发展和推广。
CAL提供了4种应用层服务功能:
CMS (CAN-based Message Specification)
CMS提供了一个开放的、面向对象的环境,用于实现用户的应用。CMS提供基于变量、事件、域类型的对象,以设计和规定一个设备(节点)的功能如何被访问(例如,如何上载下载超过8字节的一组数据(域),并且有终止传输的功能)。CMS从MMS (Manufacturing Message Specification)继承而来。MMS是OSI为工业设备的远程控制和监控而制定的应用层规范。
NMT (Network ManagemenT)
提供网络管理(如初始化、启动和停止节点,侦测失效节点)服务。这种服务是采用主从通讯模式(所以只有一个NMT主节点)来实现的。
DBT (DistriBuTor)
提供动态分配CAN ID(正式名称为COB-ID,Communication Object Identifier)服务。这种服务是采用主从通讯模式(所以只有一个DBT主节点)来实现的。
LMT (Layer ManagemenT)
LMT提供修改层参数的服务:一个节点(LMT Master)可以设置另外一个节点(LMT Slave)的某层参数(如改变一个节点的NMT地址,或改变CAN接口的位定时和波特率)。
CMS为它的消息定义了8个优先级,每个优先级拥有220个COB-ID,范围从1到1760。剩余的标志(0,1761-2031)保留给NMT,DBT和LMT,见下表。
映射到CAL服务和对象的COB-ID(11位CAN标识符) | 服务或对象 |
0 | NMT 启动/停止服务 |
1 - 220 | CMS对象 优先级0 |
221 - 440 | CMS对象 优先级1 |
441 - 660 | CMS对象 优先级2 |
661 - 880 | CMS对象 优先级3 |
881 - 1100 | CMS对象 优先级4 |
1101 - 1320 | CMS对象 优先级5 |
1321 - 1540 | CMS对象 优先级6 |
1541 - 1760 | CMS对象 优先级7 |
1761 - 2015 | NMT 节点保护 |
2016 - 2031 | NMT,LMT,DBT服务 |
三、变频器与贝加莱人机界面连接如下
图2
四、CPS_Modbus RTU Gateway (I-7232D)配置
在计算机上安装I-7232D U TILITY配置软件,并运行如下图所示
图3
运行配置软件后显示如下图,I-7232D上电时将INT脚与GND短接,使I-7232D工作在配置模式
图4
选择正确的通讯COM口,点击“CONNETCT”连接I-7232D,连接成功后,点击CAN-bus将出现如下窗口图所示:
图5
I-7232D的CANopen端:
波特率:250KBPS
节点号为:1
然后点击RS-485按钮如下图
图6
I-7232D的RS-485端:
波特率:19200BPS
数据长度:8
奇偶校验:无
停止位:1
超时:50ms
在配置完通讯端口参数后击点“NEXT”弹出如下窗口,如图所示
图7
然后点击“ADD”按钮添加MODBUS地址映射,”Modbus Device NodeID”,这里输入被映射的变频器RS-485节点地址,”Relay Address”,本栏里选择MODBUS RTU功能码,这里我们只用两个功能码:Analog Output(写入)和Analog Input(读取),“Register/Relay Address”此处填入要读/写的变频器寄存器地址,“Data Length”这里设置读/写变频器寄存器的数据长度。在这里我们的映射地址如下表所示。在完成映射后点击“Modbus Information”和“PDO Information”可以察看映射参数列表如下图所示
Instance NO. | Modbus Device NodeID | Relay Address | Register/Relay Address | Data Length |
1 | 1 | Analog Output | 32769 | 1 |
2 | 1 | Analog Output | 32770 | 1 |
3 | 1 | Analog Input | 33036 | 1 |
4 | 1 | Analog Input | 33079 | 1 |
5 | 1 | Analog Input | 33081 | 1 |
6 | 2 | Analog Output | 32769 | 1 |
7 | 2 | Analog Output | 32770 | 1 |
8 | 2 | Analog Input | 33036 | 1 |
9 | 2 | Analog Input | 33079 | 1 |
10 | 2 | Analog Input | 33081 | 1 |
图8
图9
完成以上操作后点击“NEXT”出现如下窗口
图10
然后点击“Finish”保存配置的参数到I-7232D同时在应用程序根目录下生成EDS文件,保存成功后返回主窗口。完成配置后掉电重启I-7232D。
PP45CANopen程序:
CAN通讯口初始化程序
(* init program *)
CANopen_0.enable= 1
CANopen_0.baud_rate= 25;250k
CANopen_0.cob_anz= 20
CANopen_0.error_adr= adr(ERR)
CANopen_0.device= "SS1.IF3" ;设备名称
CANopen_0.info= 0
CANopen_0 FUB CANopen() ;初始化CAN
NMT_Data[0]=1 ;CANOPEN命令01-
NMT_Data[1]=0
;requst cob cofg
UserType.RTR_COB[1]=$281 ;COB-ID 281 读取
UserType.RTR_COB[2]=$381 ;.. .. ..
UserType.RTR_COB[3]=$481
;cmmd cob cofg
UserType.Send_COB[1]=$301 ;COB-ID 301 写入
UserType.Send_COB[2]=$301 ;.. .. ..
UserType.Send_COB[3]=$401 ; .. .. ..
;node id cofg
UserType.Node[1]=$01 ;RS-485节点地址
UserType.Node[2]=$02 ;..
UserType.Node[3]=$03
PDO程序;
(* cyclic program *)
loop i=1to 3 do
m[i] =workstep[i]
select workstep[i]
state Send_NMT
CANwrite_NMT[i].enable= 1
CANwrite_NMT[i].us_ident=CANopen_0.us_ident
CANwrite_NMT[i].can_id=NMT_COB;0
CANwrite_NMT[i].data_adr= adr(NMT_Data[0])
CANwrite_NMT[i].data_lng= sizeof(NMT_Data)
CANwrite_NMT[i] FUB CANwrite() ----使I-78232D进入运行状态
when CANwrite_NMT[i].status<>MSG_BUSY;1
next Send_Data1
state Send_Data1
(*PDO1RX*)
CANwrite_data1[i].enable= 1
CANwrite_data1[i].us_ident=CANopen_0.us_ident
CANwrite_data1[i].can_id= UserType.Send_COB[i]
CANwrite_data1[i].data_adr=adr(UserType.Txdata[i].Data[0]) 将要写入的数据
CANwrite_data1[i].data_lng= sizeof(UserType.Txdata[i])
CANwrite_data1[i] FUB CANwrite()
when CANwrite_data1[i].status<>MSG_BUSY;1
next Send_Data2
stateSend_Data2 (*PDO1TX*)
(*读取模式为异步RTR*)
CANrtr_01[i].request = not CANrtr_01[i].request
CANrtr_01[i].enable = 1
(*enable the functionblock CANrtr*)
CANrtr_01[i].us_ident = CANopen_0.us_ident
(*set us_id with the value from CANopen*)
CANrtr_01[i].data_adr = ADR(UserType.Rxdata[i].Data[0]) ;存储读入的数据
(*set the destination adress*)
CANrtr_01[i].can_id = UserType.RTR_COB[i]
(*set can_id*)
CANrtr_01[i] FUB CANrtr() ;发送请求读取数据
(*call CANrtr*)
when CANrtr_01[i].status<>MSG_BUSY;1
next Send_Data1
endselect
endloop
数据转换程序:
(* cyclic program *)
loop i=1 to 2 do
if i=1 then
memcpy(adr(mActFreq[i]),adr(UserType.Rxdata[i].Data[2]),2)
MotorApp[i].rActFreq=real(mActFreq[i])/100.0;act freqence
memcpy(adr(MotorApp[i].rStatus),adr(UserType.Rxdata[i].Data[0]),2);status word
UserType.Txdata[i].Data[0]=byte(MotorApp[i].wCtrlWord);set contrl word
UserType.Txdata[i].Data[1]=byte(lsr(MotorApp[i].wCtrlWord,8))
UserType.Txdata[i].Data[2]=byte(uint(MotorApp[i].wSetfreq*100));set Freqence
UserType.Txdata[i].Data[3]=byte(lsr(uint(MotorApp[i].wSetfreq*100),8))
UserType.Txdata[i].Data[4]= UserType.Txdata[i+1].Data[4]
UserType.Txdata[i].Data[5]= UserType.Txdata[i+1].Data[5]
UserType.Txdata[i].Data[6]= UserType.Txdata[i+1].Data[6]
UserType.Txdata[i].Data[7]= UserType.Txdata[i+1].Data[7]
else
memcpy(adr(mActFreq[i]),adr(UserType.Rxdata[i].Data[0]),2)
MotorApp[i].rActFreq=real(mActFreq[i])/100.0;act freqence
memcpy(adr(MotorApp[i].rStatus),adr(UserType.Rxdata[i].Data[2]),2);status word
UserType.Txdata[i].Data[4]=byte(MotorApp[i].wCtrlWord);set contrl word
UserType.Txdata[i].Data[5]=byte(lsr(MotorApp[i].wCtrlWord,8))
UserType.Txdata[i].Data[6]=byte(uint(MotorApp[i].wSetfreq*100));set Freqence
UserType.Txdata[i].Data[7]=byte(lsr(uint(MotorApp[i].wSetfreq*100),8))
UserType.Txdata[i].Data[0]= UserType.Txdata[i-1].Data[0]
UserType.Txdata[i].Data[1]= UserType.Txdata[i-1].Data[1]
UserType.Txdata[i].Data[2]= UserType.Txdata[i-1].Data[2]
UserType.Txdata[i].Data[3]= UserType.Txdata[i-1].Data[3]
endif
status_1 = UserType.Rxdata[1].Data[1]
status_2 = UserType.Rxdata[1].Data[7]
endloop
注意:在MODBUS地址映射时没有按COB-ID对齐(节省空间地址)如在写入时COB-ID为0X301一次可写入4个字,前两个字为#1号变频器的MODBUS地址,后两个字为#2号变频器MODBUS地址,读取地址原理相同,具体查看I-7232D的PDO的参数列表