
摘要:本文介绍了ModbusTCP协议在PLC编程中的应用,包括其基本概念、数据帧结构、通信方式,以及在Codesys和OtoStudio中的编程示例,重点讲述了主站与从站如何通过ModbusTCP进行线圈、离散输入、保持寄存器和输入寄存器的读写操作。 ModbusTCP学习笔记+PLC相关功能块编程2023-07-18
PLC编程,Modbus TCP,Codesys V2.3,OtoStudio V2.3,ST语言 1、Modbus协议介绍Modbus是一种工业总线协议标准,包括ASCII、RTU、TCP三种报文类型。 Modbus协议物理层接口有RS232、RS485、RS422和以太网接口,采用master/slave方式通信。 Modbus -TCP,是基于在以太网TCP/IP上,将数据以Modbus帧格式进行传输。
具备数据准确性(帧头、帧尾),具有TCP传输快速性(物理层是RJ45网口、TCP传输层)。
图为Modbus数据帧内容:
PDU由功能码+数据组成。功能码为1字节,数据长度不定,由具体功能决定。 Modbus的操作对象有四种:线圈、离散输入、保持寄存器、输入寄存器。
Modbus分为主站和从站,主站给从站发送请求帧,从站响应。在使用TCP通信时,主站为client端,主动建立连接,从站为server端,等待连接。 Modbus TCP是较为特别的,举个例子,之前我们客户使用IDB3 控制器(PLC)与威纶通触摸屏通过Modbus TCP通信,IDB3就是作为Modbus从站,而Modbus从站作为TCP就是Server端。而触摸屏作为Modbus主站,Modbus主站在TCP就是Client端。 Modbus主站主动对Modbus从站发送消息请求,从站做回应。 这个在IDB3的Modbus TCP库里可以详细了解到,Modbus从站是需要或可以配置本机port(端口)、IP地址 的。而Modbus主站则需要设置远端IP,以及端口。 2、编程示例从站:- iDEABox 3控制器,在此文中简称IDB3,是PLC,是Softlink品牌的控制器,我使用的是固高科技的OtostudioV2.3进行编程,其内核也是CodysysV2.3版本。
ModbusTCP从站作为TCP主站。也即是其有TCPServer的属性。 从站使用的代码如下: VAR_GLOBAL
SlaveArrayBool:ARRAY [1..60] OF BOOL;// HMI布尔数据
SlaveArrayReal:ARRAY [1..60] OF REAL;// HMI字数据
END_VAR
// 从站功能块调用
SlaveTcp(
pCoils:=ADR(SlaveArrayBool[1]),//指定线圈地址
pDiscreteInput:=,//离散量地址
pInputReg:=,//输入寄存器
pHoldingReg:=ADR(SlaveArrayReal),//保持寄存器
CMax:=32,//线圈大小
DIMax:=,
IRMax:=,
HRMax:=SIZEOF(SlaveArrayReal)/SIZEOF(REAL)*2,//保持寄存器大小
Enable:= bEnable,
port:=4455,//指定端口号
Enable_WatchDog:=,
tWatchDogTime:=,
ClientStatus=>,
wErrorId=>,
bError=>);
上述代码中,SlaveTcp是ModbusTcp 从站功能块实例。其需要的参数入代码中所示。主要可配置以下:
(1)指定 “Coils线圈、离散量、输入寄存器、保持寄存器” 4部分的地址及大小。
(2)指定端口
(3)看门狗配置(可以不用,不设置) 从站指定线圈地址 pCoils 是地址开头,CMax 是线圈大小,指的是以bool为单位的长度。 如上例子:pCoils:= ADR(aybyHMIData[1]) 指线圈从aybyHMIData[1]开始,长度32Bit(位),也就是4个Byte(字节)。 这里的线圈变量类型是 《布尔(BOOL)数组》 指定保持寄存器地址开头 aywHMIData,也就是aywHMIData[1]的地址,长度位60*2=120,因为Real占4个字节,比word大2倍,所以要传real值时,要把地址乘以2。这个配置就占了保持寄存器120个地址。 这里的保持寄存器变量类型 是 《实数(REAL)数组》 主站:- 主站也是在IDB3中建立,因为ModbusTCP是基于TCP的通信,所以可以在本机上实现主从站并存。
- 当有多个设备需要通过ModbusTCP协议通信,那么把主从站运行在不同设备上即可。
- ModbusTCP作TCP从站,其有TCPClient的属性。
以下是主站用到的代码: VAR_GLOBAL
MasterArrayByte:ARRAY [1..60] OF BYTE;// HMI布尔数据
MasterArrayReal:ARRAY [1..60] OF REAL;// HMI字数据
END_VAR
// ModbusTcp主站
ModbusTcpMaster(
Client:=ADR(Client),
StationID:=1,// 设置站号
IP:= remoteIP,// 远端服务器的IP
Port:= remotePort,// 远端服务器端口
Enable:= bMasterEnable,
Enable_WatchDog:=,
WatchDogTime:=,
wErrorId=>,
bError=>);
IF bMasterEnable THEN
IF bReadEnable THEN
bReadEnable:=FALSE;
ELSE
bReadEnable:=TRUE;
END_IF
IF bWriteEnable THEN
bWriteEnable:=FALSE;
ELSE
bWriteEnable:=TRUE;
END_IF
END_IF
MasterTcpRead(
Execute:= bReadEnable,
Client:=ADR(client),
Watch_Time:=,
ReSendTimes:=,
DataModel:=4,// 读取保存寄存器模式
DataAddress:=0,// 地址 0 ~ 8
DataLength:=4*2,// 共读出 8/2 = 4 个REAL
pData:=ADR(MasterArrayReal),// 存放地址
ResponseDone=>,
Exception=>,
ExceptionCode=>,
ExceptionCnt=>,
SendCnt=>,
TimeOut=>,
Error=>,
ErrCode=>);
MasterTcpReadB(
Execute:= bReadEnable,
Client:=ADR(client),
Watch_Time:=,
ReSendTimes:=,
DataModel:=0,// 读取线圈模式
DataAddress:=0,// 地址 0 ~ 8
DataLength:=8,// 共读出 8/8 = 1 个BYTE
pData:=ADR(MasterArrayByte[1]),// 存放地址
ResponseDone=>,
Exception=>,
ExceptionCode=>,
ExceptionCnt=>,
SendCnt=>,
TimeOut=>,
Error=>,
ErrCode=>);
MasterTcpWrite(
Execute:= bWriteEnable,
Client:=ADR(client),
Watch_Time:=,
ReSendTimes:=,
DataModel:=4,// 写保持寄存器
DataAddress:=60,// 地址 60 ~ 120
DataLength:=60,// 共写入 60/2 = 30 个REAL
pData:=ADR(MasterArrayReal[31]),// 要写入的变量地址
ResponseDone=>,
Exception=>,
ExceptionCode=>,
ExceptionCnt=>,
SendCnt=>,
TimeOut=>,
Error=>,
ErrCode=>);
MasterTcpWriteB(
Execute:= bWriteEnable,
Client:=ADR(client),
Watch_Time:=,
ReSendTimes:=,
DataModel:=0,// 写线圈
DataAddress:=8,// 地址 8 ~ 24
DataLength:=24,// 共写入 24/8 = 3 个BYTE
pData:=ADR(MasterArrayByte[2]),// 要写入的变量地址
ResponseDone=>,
Exception=>,
ExceptionCode=>,
ExceptionCnt=>,
SendCnt=>,
TimeOut=>,
Error=>,
ErrCode=>);
上述代码中,ModbusTcpMaster是ModbusTCP主站功能块。而主站并不是只用这一个功能块,还需要使用 “读/写” 的功能块。MasterTcpRead、MasterTcpReadB是 ”ModbusTCP主站读“ 功能块,MasterTcpWrite、MasterTcpWriteB是 ”ModbusTCP主站写“ 功能块。 在读写功能块里面,每个功能块实例只能同时对一个区域进行操作,所以上面代码就调用了2个读写功能块进行对线圈的读写、保持寄存器的读写。 在读写功能块中,也需要像从站那样配置其数据地址及大小。 主站需要配置的内容:
(1)要读写的内容( Coils线圈、离散量、输入寄存器、保持寄存器)的地址和大小。
(2)配置从站(TCP服务器)的IP地址,和端口。
(3)看门狗(可以不用,不配置) 读写的都是同一个 字节BYTE数组 MasterArrayByte[] ,区别在于,读取的是第一个元素,MasterArrayByte[1],而要写入的是MasterArrayByte[2…4] 第2~4个元素。 这里用到的数组类型是 字节BYTE数组 ,和Slave站的不一致?为什么? 上面从站配置的线圈,配置地址从0开始,32长度,就是第031位,也就是第03 个BYTE。 而主站读功能块配置的线圈,配置地址从0开始,8长度,指的是第0~7个位,也就是第0个BYTE。但是其指定的需要用Byte数组来处理,这里试过用BOOL数组,就会有异常。 而同理,主站写功能块配置线圈,地址从8开始,24长度,指的是831个位,也就是第13个BYTE。也同样用byte数组来操作。 读写保持寄存器也都是用同一个 实数REAL数组 MasterArrayReal[],区别在于,读取的是MasterArrayReal[0…3]共4个real。写入的是MasterArrayReal[31…60]共30个real。 以上主从站线圈的对应图如下:
主从站保持寄存器对应图如下:
布置好以上内容后,启动功能块,即可实现ModbusTCP主从站的通信了。
|