当前位置: 首页 > news >正文

PLC通信协议的转化

在自动化程序设计中,常常需要对通信协议进行相互转化。例如,某个控制器需要通过PLC控制设备的某个部件的运动,但PLC只支持ModbusTCP协议,而控制器只支持CanOpen通讯协议。这时,就需要一个网关进行通信协议的转化。网关有可编程网关和不可编程网关之分,其中后者只能支持简单的转化,如将一句Modbus的报文完整转换为CanOpen,但无法实现将Modbus报文的第一字节转换为CanOpen的第三个PDO的第四个字节等这样的复杂逻辑。所以,如需要对不同的通信协议的报文内容进行任意的拼接组合,则需要使用可编程网关。

本文介绍一下使用有Codesys运行环境的可编程网关进行ModbusTCP和CanOpen通信协议相互转化的程序。这里以广成科技的GCAN-410产品的Codesys版本为例[3],说明如何令该可编程网关同时做CanOpen的从站和ModbusTCP的主站,从而让做CanOpen主站的控制器和做ModbusTCP从站的PLC通过该网关进行通信。其它的协议互转的思路也类似。

一、Codesys简介

首先,Codesys是一种国际通用的工业自动化领域的编程系统,支持IEC 61131-3标准的PLC编程语言[1]。这里主要使用ST语言进行可编程网关的通信协议转换程序设计。例如,可编程网关可以通过Modbus得到PLC的一些变量的值,然后将这些值嵌套在通过CanOpen发出的PDO报文中的规定位置,使得CanOpen主站的控制器可以得到PLC里的数据信息;当然,可编程网关也可通过CanOpen接收到控制器发出的报文,从收到的PDO中提取出数值,并将数值通过Modbus传递给PLC。

二、Modbus简介

Modbus是一种工业通信协议的标准,是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型[2]。这里使用ModbusTCP。简单地说,在Modbus通信中,通信设备有主站也有从站,主站主动发送消息,从站被动回复消息。Modbus的消息分为读取离散输入,读/写线圈,读取输入寄存器,读/写保持寄存器这几种。当主站发出"读取地址从A到B的离散输入"后,从站会将地址在A和B之间的离散输入回复给主站;输入寄存器同理,区别在于离散输入是布尔量,输入寄存器是16位的数值变量;而主站发出"写地址A的线圈为某值"后,从站会把自己的内部的Modbus地址为A的变量写成该值,并返回"写成功"的消息;保存寄存器同理。

Modbus从站中可以设置,或已经内置了每个Modbus地址和PLC内部变量地址的对应关系,如数字输入1对应Modbus的离散输入的地址1,数字输出1对应Modbus的线圈地址1,内部只读变量x对应Modbus的输入寄存器地址10000,内部可读可写变量y对应Modbus的保持寄存器地址20000。所以,Modbus主站通过Modbus报文,可以读取PLC的变量,也可以对PLC的变量进行修改。

三、CanOpen简介

CanOpen是一种基于CAN,在工业自动化中广泛应用的通信协议[4]。一般来说,CanOpen的主站和从站都会发送4个PDO报文,每个PDO报文长度不超过8字节。设备刚启动时,主站会通过NMT报文对从站进行初始化,确保通信能正常进行。控制器,或PLC,都可以设置或已经内置PDO报文里的每一个字节对应的系统内部变量。发出的PDO值里的各字节是PLC内置的各变量的值,而当收到PDO时,里面的字节内容会被用于更新PLC内置的变量值。

四、可编程网关的程序设计

广成科技的GCAN-410产品有一些内置的函数,可以把要通过Modbus和CanOpen发送的报文的数据保存在数组型变量里,同时又有数组型变量存放收到的报文中的数据。

在这里通过调用一些函数,得到这些报文携带的数据。

CanOpenSend(arTPDO_Data := DataCanTransmit, arRPDO_Data => DataCanReceive);
ModbusVariable(arReadBufDiscreteInput => DataModbusReceiveDiscrete, arReadBufInputRegister => DataModbusReceiveAnalog,arWriteBufCoil := DataModbusTransmitDiscrete, arWriteBufHolding := DataModbusTransmitAnalog);

在以上代码中,CanOpenSend是将该网关需要发送和接收的CanOpen报文数据与网关内部的变量绑定在一起的函数。该函数的实现方式随PLC品牌的不同而不同,此处不赘述。该可编程网关要发送的CanOpen的PDO保存在变量DataCanTransmit中,而接受到的PDO保存在变量DataCanReceive中。这两个变量数组均为2维数组,即包含4个8字节数组的大数组。所以,DataCanReceive[1][4]代表接收到的PDO2的第5字节(ST语言中,数组下标从0开始)。发送同理。函数ModbusVariable也类似,但只是1维数组,且在网关中已经设置了Modbus读写的地址范围。

(一)字节变量的转换

例如,我要将网关通过CanOpen收到的PDO2的第8字节对应到Modbus从站的PLC的地址为10的保持寄存器对应的变量,又假定我在ModbusVariable函数中已经设定了网关对Modbus保持寄存器的写动作作用地址为10,则代码如下

DataModbusTransmitAnalog[0] := DataCanReceive[1][7];

DataModbusTransmitAnalog[0]即为写给保持寄存器的地址10的第1字节的数据(注意Modbus的寄存器的一个地址对应2个字节)。

(二)二进制比特的提取和转换

另外,如果在CanOpen收到的PDO3的第3字节是控制器的8个数字输入(一个数字输入占据一个二进制比特),而我希望把第1和第2个数字输入传到PLC的Modbus的第1和第7线圈,如何实现?

DataModbusTransmitDiscrete[0] := DataCanReceive[2][2] AND 16#1;
DataModbusTransmitDiscrete[6] := DataCanReceive[2][2] AND 16#2;

上述代码实现了该功能。DataCanReceive[2][2]是收到的整个字节,而通过将其和16#1及16#2(16#在PLC的ST语言中代表16进制,所以它们用二进制表示则为00000001和00000010)进行AND运算,故只有第1位会进入DataModbusTransmitDiscrete[0]即第1线圈,第2位会进入DataModbusTransmitDiscrete[6]即第7线圈,其它位均被屏蔽。

如果是反过来,要通过读取多个Modbus的离散输入,并将这些比特输入并排形成一个字节传入CanOpen,如何实现?

DataCanTransmit[0][2] := DataModbusReceiveDiscrete[6] AND 16#1;
DataCanTransmit[0][2] := DataCanTransmit[0][2] + (DataModbusReceiveDiscrete[7] AND 16#1) * 2;

在这里,DataModbusReceiveDiscrete[6]是Modbus PLC的地址为6的离散输入,DataModbusReceiveDiscrete[7]是Modbus PLC的地址为7的离散输入,该代码将前者用作PDO1的第3字节的第1比特,后者用作PDO1的第3字节的第2比特。

第一行,将第1比特赋予了地址为6的离散输入的值。第二行,对于地址为7的离散输入的值,在DataModbusReceiveDiscrete[7]中同样处于第1比特。所以要让它移位到第2比特,即提升1个比特,需要用数学运算进行,这里的运算即为* 2。对一个数值乘以2^n,相当于把该数值的所有2进制位提升了n位[5];当然,整除2^n也相当于把该数值的所有二进制位下降了n位。

五、小结

有Codesys运行环境的可编程网关,例如广成科技的GCAN-410,可以通过编程实现各种通讯协议之间的互转,且报文可以互相拼接。拼接的方式,就是将报文的内容对应的变量拆解开,然后将数值赋予另一个通讯协议的变量。对于二进制比特,用AND逻辑进行屏蔽,以及用乘除法进行移位。

参考资料

[1]玩转CODESYS 入门篇(一)-- 认识CODESYS-CSDN博客

[2]ModbusTCP协议 - ioufev - 博客园

[3]沈阳广成科技有限公司

[4]CANopen Tutorial - Siemens

[5]计算机中二进制的移位运算_二进制的乘法移位原理-CSDN博客


http://www.mrgr.cn/news/31463.html

相关文章:

  • (实战)WebApi第14讲:前端(JS)想要同步执行得使用回调、岗位搜索功能【在前端实现,不推荐】、同步/异步执行
  • 骨传导耳机哪家好?这五大热门高口碑骨传导耳机别错过!
  • 深入理解接口测试:实用指南与最佳实践5.0(一)
  • Qt 学习第十六天:文件和事件
  • 物联网技术及其在智慧城市中的应用
  • 制作自己的刷题小题库,提高刷题效率
  • xhs 小红书 x-s web 分析
  • 如何下载ComfyUI开发版
  • SpringBoot开发-数据加密
  • Vue3DevTools7是如何在vscode定位指定文件位置的?
  • 浅谈穷举法
  • 【C/C++语言系列】实现单例模式
  • 在PyQt的QLabel控件上显示图像指南
  • 50.面向对象进阶训练-学生类
  • 达梦数据库踩坑
  • 线性规划中可行域为什么一定是凸的--证明
  • 前端框架对比选择:如何在众多技术中找到最适合你的
  • C++ 中noexcept关键字的含义和使用方法
  • shell脚本(9.20)
  • lettuce引起的Redis command timeout异常
  • 大数据新视界 --大数据大厂之AI 与大数据的融合:开创智能未来的新篇章
  • sql中的union与union all区别
  • 队列基础概念
  • 基于机器学习的癌症数据分析与预测系统实现,有三种算法,bootstrap前端+flask
  • 【读书笔记-《30天自制操作系统》-23】Day24
  • 每天五分钟计算机视觉:将人脸识别问题转换为二分类问题