用动态链接库实现Modbus应答
2009-10-26唐崇华
唐崇华
[摘要]MODBUS协议是应用于电子控制器上的一种通用协议。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一种通用工业标准。不同厂商生产的控制设备可以连成工业网络进行集中监控。此协议定义一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述控制器请求访问其它设备的过程,如何回应来自其它设备的请求,以及怎样侦测错误并记录。它制定消息域格局和内容的公共格式。
MODBUS是OSI模型第7层上的应用层报文传输协议,它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信。自从1979年出现工业串行链路的事实标准以来,MODBUS使成千上万的自动化设备能够通信。目前,继续增加对简单而雅观的MODBUS结构支持。
[关键词]ModbusTCP/IP工业控制
中图分类号:TP3文献标识码:A文章编号:1671-7597(2009)0710066-02
一、MODBUS协议描述
MODBUS是一个请求/应答协议,并且提供功能码规定的服务。目前,以以太网上的TCP/IP使用MODBUS比较常见。
图1ModBus协议格式
MODBUS协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网络上的MODBUS协议映射能够在应用数据单元(ADU)上引入一些附加域。Modbus协议格式如图1所示。TCP/IP上的MODBUS的请求/响应协议格式如图2所示。
启动MODBUS事务处理的客户机创建MODBUS应用数据单元。功能码向服务器指示将执行哪种操作。
MODBUS报文传输服务提供设备之间的客户机/服务器通信,这些设备联接在一个Ethernet(以太网)TCP/IP网络上。
这种客户机/服务器模式是基于4种类型报文:
MODBUS请求:客户机在网络上发送用来启动事务处理的报文。
MODBUS证实:在客户端接收的响应信息。
MODBUS指示:服务端接收的请求报文。
MODBUS响应:服务器发送的响应信息。
图2TCP/IP上的MODBUS的请求/响应协议格式
在TCP/IP上使用一种专用报文头识别MODBUS应用数据单元。将这种报文头称为MBAP报文头(MODBUS协议报文头)。
MBAP报文头包括下列域:
报文头为7个字节长:
事务处理标识符:用于事务处理配对。在响应中,MODBUS服务器复制请求的事务处理标识符。
协议标识符:用于系统内的多路复用。通过值0识别MODBUS协议。
长度:长度域是下一个域的字节数,包括单元标识符和数据域。
单元标识符:为了系统内路由使用这个域。专门用于通过以太网TCP-IP网络和MODBUS串行链路之间的网关对MODBUS或MODBUS+串行链路从站的通信。MODBUS客户机在请求中设置这个域,在响应中服务器必须利用相同的值返回这个域。
二、MODBUS功能码举例
以读保持寄存器(功能码3)为例。
在一个远程设备中,使用该功能码读取保持寄存器连续块的内容。请求PDU说明了起始寄存器地址和寄存器数量。从零开始寻址寄存器。将响应报文中的寄存器数据分成每个寄存器有两字节,在每个字节中直接地调整二进制内容。对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。
请求
响应(*N=寄存器的数量)
错误
这是一个请求读寄存器108-110的实例:
将寄存器108的内容表示为两个十六进制字节值02 2B,或十进制555。将寄存器109-110的内容分别表示为十六进制00 00和00 64,或十进制0和100。
三、用动态链接库封装MODBUS应答服务
采用动态链接库(Dynamic-Linbk Library)实现对MODBUS应答服务的封装。应客户要求此动态链接库主要的功能为响应客户MODBUS请求并按请求将从设备采集到的数据以MODBUS协议格式发送给客户。数据为32位浮点数,采用IEEE754格式编码。为便于用VB等开发工具开发的客户端调用,采用标准的动态链接库格式,即函数用__stdcall定义。
1.Modbus协议解析函数
函数processMsg(unsigned char mdreq):负责解析modbus请求。主要代码:
switch(mRrequest[7])//按照modbus协议该位置为功能号
{ case 3://请求保持寄存器
{unsigned regNo = getWord(mRrequest, 8);//起始寄存器地址
Unsigned regCount = getWord(mRrequest, 10);// 寄存器数量
if (len != 12) { //标准Modbus请求为12个字节,否则非法请求,填写错误响应
mRrequest [7] |= 0x83;//错误码
mRrequest [8] = 3;// 异常码
mRrequest [5] = 3; // length
break;
}
if (regCount < 1 || regCount > 125 || regNo >= num4xRegs || (regCount + regNo) > num4xRegs) {// 请求寄存器范围不合法,填写错误响应
mRrequest [7] |= 0x83;// //错误码
mRrequest [8] = 2; // 异常码
mRrequest [5] = 3; // length
break;
}
// 正确的请求,填写响应
mRrequest [8] = 2 * regCount;//数据字节数
mRrequest [5] = mRrequest [8] + 3;//响应信息长度
for (i=0;i {//访问设备,得到数据项并赋值。 … … } } break; … … } return6+ mRrequest [5]; 2.通信模型的选择 由于采用动态链接库的方式实现对Modbus通信帧的监听和解析,为了避免窗体的使用,所以采用SELECT模型负责网络通信工作。SELECT模式是Winsock中最常见的I/O模型。之所以称其为“select模式”,是由于它的中心思想是利用SELECT函数,实现对I/O的管理。利用SELECT函数,可以判断套接字上是否存在数据,或者能否向一个套接字写入数据,从而实现数据的收发工作。采用SELECT主要用到函数ioctlsocket、select和FD_ZERO、FD_SET、FD_ISSET和FD_CLR宏。 在应用开发时,通过以下步骤,完成对套接字的可读可写判断。 (1)使用FD_ZERO宏,初始化感兴趣的套接字集合fd_set,可以为可读套接字集合和可写套接字集合。例如FD_ZERO(ReadFD)。 (2)使用FD_SET宏将套接字分配给参与操作的fd_set集合。例如FD_SET(s,ReadFD)。 (3)以fd_set为参数调用select函数。等待在指定的fd_set集合中I/O活动设置好套接字。select函数完成后会返回在所有fd_set集合中设置的套接字句柄总数,并对每个集合进行相应的更新。 (4)select函数成功返回后,使用FD_ISSET宏,对每个fd_set集合进行检查。例如,FD_ISSET(s,ReadFD)。如果该宏的值为TRUE,则说明该套接字可读。 (5)调用相应的Windows Sockets API进行数据的接受和发送。 四、MODBUS异常响应 异常响应报文有两个与正常响应不同的域: 功能码域:在正常响应中,服务器利用响应功能码域来应答最初请求的功能码。所有功能码的最高有效位(MSB)都为0(它们的值都低于十六进制80)。在异常响应中,服务器设置功能码的MSB为1。这使得异常响应中的功能码值比正常响应中的功能码值高十六进制80。通过设置功能码的MSB,客户机的应用程序能够识别异常响应,并且能够检测异常码的数据域。 数据域:在正常响应中,服务器可以返回数据域中数据或统计表(请求中要求的任何报文)。在异常响应中,服务器返回数据域中的异常码。 客户机请求和服务器异常响应的实例: 在这个实例中,客户机对服务器设备寻址请求。功能码(01)用于读输出状态操作。它将请求地址1245(十六进制04A1)的输出状态。 如果在服务器设备中不存在输出地址,那么服务器将返回异常码(02)的异常响应。这就说明从站的非法数据地址。 参考文献: [1]《MODBUS Protocol Reference Guide》,http://www.Modbus-IDA.org. [2]《MODBUS MESSAGEING ON TCP/IP IMPLEMENTATION GUIDE V1.0a》, Http://www.Modbus-IDA.org. [3]《MODBUS Application Protocol Specification》,http://www.Modbus-IDA.org. [4]孙海民,《Windows Sockets 网络开发-基于Visual C++实现》,人民邮电出版社.