嵌入式
返回首页

ModBUS协议通讯的应用

2012-06-09 来源:中国传动网

引言

    在燃气蓄热式窑炉控制系统中,不仅需要将温度、压力、流量等模拟参数进行采集、计算控制输出至执行器,同时还应将火焰状态、电磁阀状态、换相阀状态进行采集,随时控制点火器点火,并进行周期性换相,换相周期会根据温度的不同而变化。面对众多参量的采集控制,需要建立时变系统和非线性系统的状态空间表达式进行复杂的多相关变量的运算,得到稳定输出以控制相关变量。事实上,取得大量数据,建立相应数据库,进行数据分析,单靠低端的数字控制设备难以完成。通常的做法是信号的数据采集、滤波、简单PID运算、输出量、及输出量的互锁由低端控制器完成。为了完成上述功能,我们采用ABB公司生产的PLC,配置如下:

       名称               数量                    描述

     07KT51               1块                     50CPU,8DI,6DO

     XM06B5              2块                     Extension I/O,4AI,2AO

     XK08F1              1块                     Extension I/O,4DI,6DO

    PLC软件采用AC31 SOFTWARE,上位机程序用VB编制,实现与上位工控机通讯的过程中需要使用MODBUS协议。本文重点介绍其通讯实现的过程。

一、 MODBUS简介

    MODBUS协议是应用于电子控制器的一种通讯语言。利用这个协议,控制器相互之间(例如485、232C等),控制器通过网络(例如以太网)和其他设备之间进行通讯。它定义了一种控制器能认识使用的消息结构,而不管它们是通过何种网络实现;描述的是控制器请求访问其它设备的过程,如何回应来自其它设备的请求,以及怎样侦错并记录;制定了消息域格局和内容的公共格式。

    MODBUS通讯实现有两种传输模式(ASCII或RTU),ABB公司的50系列PLC产品采用的是RTU(远程终端单元)模式,在消息中的每个8Bit字节包含两个4Bit的十六进制字符。这种方式的主要优点是:在同样的波特率下,可比ASCII方式传送更多的数据。

1、代码系统:

    ·   8位二进制,十六进制数0...9,A...F

    ·   消息中的每个8位域都是一个两个十六进制字符组成每个字节的位

    ·   1个起始位

    ·   8个数据位,最小的有效位先发送

    ·   1个奇偶校验位,无校验则无

    ·   1个停止位(有校验时),2个Bit(无校验时)

    ·   错误检测域     CRC(循环冗长检测)

2、基于RS485的RTU模式MODBUS通讯格式为:


    它定义了在这些网络上连续传输的消息段的每一位,以及决定怎样将信息打包成消息域和如何解码。

3、其查询回应周见下图:


    (1)查询    查询消息中的功能代码告之被选中的从设备要执行何种功能。数据段包含了从设备要执行功能的任何附加信息。例如功能代码03是要求从设备读保持寄存器并返回它们的内容。数据段必须包含要告之从设备的信息:从何寄存器开始读及要读的寄存器数量。错误检测域为从设备提供了一种验证消息内容是否正确的方法。

    (2)回应    如果从设备产生一正常的回应,在回应消息中的功能代码是在查询消息中的功能代码的回应。数据段包括了从设备收集的数据:象寄存器值或状态。如果有错误发生,功能代码将被修改以用于指出回应消息是错误的,同时数据段包含了描述此错误信息的代码。错误检测域允许主设备确认消息内容是否可用。

二、通讯的实现:

1、PLC端设置:

    07KT51在出厂时拥有默认设置:该中心单元为从设备,波特率为19200,1位停止位,8位数据位,无校验位。有关数据采集的端口、传输变量的物理地址的计算,功能代码的涵义,校验等等,在AC31说明书上已有了详尽的介绍,在此不复赘述。事实上,由于AC31对MODBUS的封装,使我们的通讯工作变的异常简单,有了通讯参数,调用通讯功能模块,无须进行通讯过程的代码描述。下面是一个取地址的例子:

    定义一个输出量O 62.15和内存变量M 232.01的地址为:

    O 62.15=4096+(16*62)+15=5103

    M 232.01=8192+(16*232)+1=11905

    当然,根据AC31提供的地址表进行推算,使过程显得更容易。

2、  工控机的设置:

    (1)、进行端口的配置。端口采用MOXA公司生产的Transio 53智能型RS232与RS422/485双向转换器,充分利用其传输速率: 50 bps~ 921.6 Kps带宽,内建终端电阻的特性,保证长达1000米的通讯不受干扰。特别是当 2 个信号在 RS-422/485 端短接在一起时,具有电流过载保护功能;浪涌保护:(25 KV ESD ),光电隔离(2 KV)等特性,为系统长期可靠运行提供了硬件保障。软件利用VB 实现,在VB中通过Timer控件定时引用Mscomm控件实现串口通讯,利用窗体加载事件对其初始化:

Private Sub From_Load( )

        Timer1.Enabled=True

        Timer1.Interval=500                              ‘定时传输间隔为500毫秒

        Mscomm1.Commport=1                        ‘通讯串口选择Com1

        Mscomm1.Settings=”19200,n,8,1”        ‘通讯参数设置

        Mscomm1.OutBufferSize=0                 ‘清除缓冲区

        Mscomm1.InputMod=1                         ‘采用二进制通讯

End Sub

(2)、Modbus的CRC校验:

    使用RTU模式,消息包括了一基于CRC方法的错误检测域。 CRC域是两个字节,包含一16位的二进制值,它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。

    CRC校验运算需要对位操作,根据Modbus的相关介绍用VC++做一个动态连接库函数checkCRC。

CRC简单函数如下:

unsigned short CRC16(puchMsg, usDataLen)

unsigned char *puchMsg ; /* 要进行CRC校验的消息 */

unsigned short usDataLen ; /* 消息中字节数 */

unsigned char uchCRCHi = 0xFF ; /* 高CRC字节初始化 */

unsigned char uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */

unsigned uIndex ; /* CRC循环中的索引 */

while (usDataLen——) /* 传输消息缓冲区 */

uIndex = uchCRCHi ︿ *puchMsg++ ; /* 计算CRC */

uchCRCHi = uchCRCLo ︿ auchCRCHi[uIndex} ;

uchCRCLo = auchCRCLo[uIndex] ;

return (uchCRCHi << 8 | uchCRCLo) ;

/* CRC 高位字节值表 */

static unsigned char auchCRCHi[] = {

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,

0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,

0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,

0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,

0x80, 0x41, 0x00, 0xC1, 0x81, 0x40

} ;

/* CRC低位字节值表*/

static char auchCRCLo[] = {

0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,

0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,

0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,

0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,

0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,

0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,

0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,

0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,

0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,

0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,

0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,

0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,

0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,

0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,

0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,

0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,

0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,

0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,

0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,

0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,

0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,

0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,

0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,

0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,

0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,

0x43, 0x83, 0x41, 0x81, 0x80, 0x40

} ; 


(3)、Modbus消息帧:

    传输设备将Modbus消息转为有起点和终点的帧,这就允许接收的设备在消息起始处开始工作,读地址分配信息,判断哪一个变量被选中,判知何时信息已完成。部分消息也能侦测到错误并且能设置为返回结果。使用RTU模式,消息发送至少要以3.5个字符时间的停顿间隔开始。传输的第一个域是变量地址。可以使用的传输字符是十六进制的0...9,A...F。网络设备不断侦测网络总线,包括停顿间隔时间内。当第一个域(地址域)接收到,每个设备都进行解码以判断是否发往自己的。在最后一个传输字符之后,一个至少3.5个字符时间的停顿标定了消息的结束。一个新的消息可在此停顿后开始。

    整个消息帧必须作为一连续的流转输。如果在帧完成之前有超过1.5个字符时间的停顿时间,接收设备将刷新不完整的消息并假定下一字节是一个新消息的地址域。同样地,如果一个新消息在小于3.5个字符时间内接着前个消息开始,接收的设备将认为它是前一消息的延续。这将导致一个错误,因为在最后的CRC域的值不可能是正确的。所以在通讯端口初始化时,Timer1定时间隔设置为500ms(根据PLC传输字的延迟时间决定)。

(4)、代码实现过程:

    在公共模块做下面函数声明:

    Declare Function W_checkCRC Lib "checkCRC.dll" (ByVal data0 As String,_ & ByVal data1 As Integer ) As String

    以下是设置/读取开关量状态:

    Private Sub Timer1_Timer()

    Dim inbyte() As Byte, outbyte() As Byte

    Dim  ReturnB As Boolean

       If ReturnB Then

        ReturnB = False

        If MSComm1.InBufferCount > 0 Then

            inbyte = MSComm1.Input

            n = UBound(inbyte)

            If n >= 1 and inbyte(3)>”03” Then ‘判断是否有有数据读取及功能代码是否含有错误

                If inbyte(5) and 1 then ‘开关量位处理

                If inbyte(5) and 2 then ‘开关量位处理 

                If inbyte(5) and 4 then ‘开关量位处理

                  ……

            End If

        End If

       Else

        ‘发送主设备查询消息

        ReDim outbyte(6)

        outbyte(0) = addr1      ‘设备地址

        outbyte(1) = option        ‘功能代码

        outbyte(2) = 1              ‘数据量

        outbyte(3) = data1         ‘数据

    ‘以下是校验码

    outbyte(4) = left(W_checkCRC(add1 & add2 & option & “1” _ &  & data1,2)

outbyte(5) = Mid(W_checkCRC(add1 & add2 & option & “1” _&  & data1,3,2)

        MSComm1.InBufferCount = 0

        MSComm1.Output = outbyte

    End If

End Sub

三、结束语

    ModBus协议在工业控制系统中已经应用十分广泛,但在多数系统中使用的是由专业供应商提供的组态软件,有时候使用组态软件提供的数据格式并不能达到要求,直接使用ModBus协议进行数据通讯是十分有用的。

参考书目:

    1、AC31 SOFTWARE  www.abb.com/lowvoltage
    2、《VB开发人员指南》  Eric Brierley 1999-01  机械工业出版社

进入嵌入式查看更多内容>>
相关视频
  • PX4固件二次开发课程

  • RISC-V嵌入式系统开发

  • NuttX Workshop 2024

  • 自己动手写操作系统

  • SOC系统级芯片设计实验

  • 自己动手做一台计算机

精选电路图
  • 简洁的过零调功器电路设计与分析

  • 单稳态控制电路设计与分析

  • IGBT模块通过控制门极阻断过电流

  • MT3608构成3.7V转12V的升压电路图

  • 运算放大器IC741的基本工作原理及在电路中的实现方式

  • 一个简单的红外耳机电路

    相关电子头条文章