您现在的位置是:网站首页> 硬件

MODBUS资料收集

  • 硬件
  • 2024-10-01
  • 877人已阅读
摘要

MODBUS资料收集


MODBUS介绍

modbus协议简单说明

Modbus模块 485/232通讯继电器模组 工业数据IO扩展 串口控制模块

modbus tcp介绍

modbus rtu协议_手把手教你写ModbusRTU协议

Modbus RTU协议各知识点入门 + 实例

MODBUS协议最简单解释

MODBUS-RTU协议.pdf

Modbus 通信协议详解

Modbus




MODBUS介绍

Modbus协议包括ASCII、RTU、TCP

Modbus rtu和Modbus tcp两个协议的本质都是MODBUS协议,都是靠MODBUS寄存器地址来交换数据;但所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。现在市场上有很多协议转换器,可以轻松的将这些不同的协议相互转换 如:Intesisbox可以把modbus rtu转换成Modbus tcp

实际上Modbus协议包括ASCII、RTU、TCP。
标准的Modicon控制器使用RS232C实现串行的Modbus。Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式。
Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验.
ModbusTCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。
TCP和RTU协议非常类似,只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可


LRC校验,16位CRC校验

LRC (纵向冗余校验)

LRC是一种简单的校验算法,主要用于Modbus ASCII协议。


计算步骤:

将所有数据字节(不包括起始冒号和结束CR/LF)相加。

计算和的二进制补码(二补数)。

结果就是LRC值。


特点:

简单易实现

只能检测奇数位错误

8位校验和

示例: 假设我们有以下Modbus ASCII消息(不包括LRC): 01 03 00 00 00 02

计算过程:

求和: 01 + 03 + 00 + 00 + 00 + 02 = 06

二进制补码: 256 - 06 = 250 (FA in hex)

因此,LRC值为 FA。

完整的Modbus ASCII消息将是: :010300000002FACR LF


16位CRC校验

16位CRC校验详细介绍:

CRC(循环冗余校验)是一种检测数据传输或存储错误的常用技术。16位CRC特指使用16位宽度的CRC多项式。

在Modbus RTU中使用的是CRC-16-IBM(也称为CRC-16-ANSI),其多项式为x^16 + x^15 + x^2 + 1,通常表示为0x8005。

CRC的计算过程如下:

初始化一个16位寄存器为0xFFFF。

对消息中的每个字节执行以下操作: a. 将该字节与16位寄存器的低8位进行异或运算。 b. 对结果进行8次操作:

如果最低位(右移前)为1,将寄存器右移一位并与多项式0xA001(0x8005的反转)进行异或。

如果最低位为0,仅将寄存器右移一位。

最终寄存器中的值就是CRC校验码。


样例:

假设我们有以下Modbus RTU消息(不包括CRC): 01 03 00 00 00 02

计算过程(简化描述):


初始CRC = 0xFFFF

处理 0x01:CRC = 0xA001

处理 0x03:CRC = 0xB100

处理 0x00:CRC = 0xC0C1

处理 0x00:CRC = 0xC181

处理 0x00:CRC = 0x0141

处理 0x02:CRC = 0x0BC4

最终CRC值:0x0BC4


完整的Modbus RTU消息: 01 03 00 00 00 02 C4 0B (注意CRC的低字节先发送)

C语言代码实现:

#include <stdint.h>

#include <stdio.h>


uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)

{

    uint8_t crc_hi = 0xFF; /* high CRC byte initialized */

    uint8_t crc_lo = 0xFF; /* low CRC byte initialized */

    unsigned int i; /* will index into CRC lookup */


    /* pass through message buffer */

    while (buffer_length--) {

        i = crc_hi ^ *buffer++; /* calculate the CRC  */

        crc_hi = crc_lo ^ table_crc_hi[i];

        crc_lo = table_crc_lo[i];

    }


    return (crc_hi << 8 | crc_lo);

}


int main() {

    uint8_t message[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02};

    uint16_t crc = crc16(message, sizeof(message));

    

    printf("Calculated CRC: 0x%04X\n", crc);

    printf("Complete message: ");

    for (int i = 0; i < sizeof(message); i++) {

        printf("%02X ", message[i]);

    }

    printf("%02X %02X\n", crc & 0xFF, (crc >> 8) & 0xFF);


    return 0;

}

注意:这个实现使用了查找表(table_crc_hi 和 table_crc_lo)来加速计算。这些表需要预先计算并包含在代码中。为了简洁,这里没有包含完整的查找表。

使用查找表的方法大大提高了CRC计算的速度,特别是在处理大量数据或在资源受限的嵌入式系统中非常有用。


另一种不使用查找表的实现方式(虽然效率较低,但更容易理解):

uint16_t crc16_simple(uint8_t *buffer, uint16_t buffer_length)

{

    uint16_t crc = 0xFFFF;

    

    while (buffer_length--) {

        crc ^= *buffer++;

        for (int i = 0; i < 8; i++) {

            if (crc & 0x0001) {

                crc = (crc >> 1) ^ 0xA001;

            } else {

                crc = crc >> 1;

            }

        }

    }

    

    return crc;

}

这个简化版本直接实现了CRC-16-IBM的算法,不需要查找表,但计算速度较慢。

在实际应用中,选择哪种实现方式取决于你的具体需求,如速度要求、内存限制等。查表法通常是首选,除非在极度受限的环境中。




三种协议

Modbus ASCII

Modbus ASCII是Modbus协议的一种文本形式。它使用可打印的ASCII字符进行通信,这使得它易于读取和调试,但数据效率较低。

特点:

每个字节用两个ASCII字符表示

消息以冒号(:)开始,以回车换行(CR/LF)结束

允许字符之间有1秒的时间间隔

使用LRC(纵向冗余校验)进行错误检查

示例数据包: :010300000002F5CR LF


解析: : - 开始符 01 - 从站地址 03 - 功能码(读保持寄存器) 0000 - 起始寄存器地址 0002 - 寄存器数量 F5 - LRC校验 CR LF - 结束符(回车换行


Modbus RTU

Modbus RTU是一种紧凑的二进制表示形式,比ASCII更高效,但不易直接读取。

特点:

每个8位字节包含两个4位十六进制字符

消息以至少3.5个字符时间的静默期开始和结束

连续字符之间的间隔不能超过1.5个字符时间

使用CRC(循环冗余校验)进行错误检查

示例数据包: 01 03 00 00 00 02 C4 0B


解析: 01 - 从站地址 03 - 功能码(读保持寄存器) 00 00 - 起始寄存器地址 00 02 - 寄存器数量 C4 0B - CRC校验


Modbus TCP

Modbus TCP是Modbus协议的以太网版本,它将Modbus协议封装在TCP/IP包中。

特点:

使用客户端-服务器通信模型

默认使用502端口

不使用校验和(由TCP层处理)

使用MBAP(Modbus应用协议)头部

示例数据包: 00 01 00 00 00 06 01 03 00 00 00 02


解析: 00 01 - 事务标识符 00 00 - 协议标识符 00 06 - 长度字段(后续字节数) 01 - 单元标识符(从站地址) 03 - 功能码(读保持寄存器) 00 00 - 起始寄存器地址 00 02 - 寄存器数量


这三种Modbus变体各有其适用场景:

Modbus ASCII适用于需要直接读取和调试的场合

Modbus RTU适用于需要高效通信的场合,特别是在串行链路上

Modbus TCP适用于基于以太网的现代工业网络

选择哪种变体取决于具体的应用需求、硬件支持和网络环境。





modbus协议简单说明

MODBUS协议

   ASCII方式发送时的规范定义如下:

   起始符 +  设备地址   +  功能代码   +   数据  +   校验   +  结束符

   【1】起始符:MODBUS的ASCII方式起始符是一个冒号  “:”

   【2】设备地址:前面说过,MODBUS是单主站的主/从通信模式,一个主站下面可以接十多个从站

       设备地址是要求两个字符,比如发给2号站,则是“02”

       现在的组合是“:02”

   【3】功能代码:告诉从站应该做什么,比如读数据的命令是“3”,从站接收到这个命令,再根据下面数据要求的具体地址,把具体地址的数据返回给主站。

       功能代码也是要求两个字符,比如读命令3,则是“03”

       现在的组合是“:0203“

   【4】数据:

       1、告诉从站具体的元件通讯地址,写入到哪里,从哪里读。如读变频器的设定频率的通讯地址是00A0

          元件的通讯地址要求是四个字符,如果控制器的元件地址不足四个字符,则在前面补0,比如元件通讯地址是A0,则在前面补足两个0:“00A0”

       2、数据又有可能包括您要读取的字节数( 有的控制器是字数),比如连续读取PLC的两个十六位寄存器,其字节数为四个,则是“0004”。您看出来了吧,读取个数也是要求四个字符,不足四个,前面补零

          现在的组合是“:020300A00004“

       3、而当您要实现写入功能时,数据又可能包括写入的数据,比如写入一个十六位寄存器的值,则要包括是写入的数值,如“0D98”

          现在的组合是“:020600A00D98“ 06是单个寄存器的写入命令

       4、当连续写入多个寄存器时,这个数据包括的内容又不一样,它可能是:

          寄存器通讯地址(四个字符) +字数(四个字符)+字节数(两个字符)+ 要写入的数值

   【5】检验和:

       前面说过,通讯格式里的校验方式并不能保证每个字符都正确,所以这里就把所有字符的值加在一起,其和(检验值)传给接收方,接收再把接收到的字符的值加在一起,与发送方传送过来的检验值比较,如果相等,就算接收正确了。

       检验只是提高了校验的可靠性。并不能完全判断传输是否正确

       参加校验计算的字符是起始符与校验符之间的字符串(不含起始符与校验符)

       现在的组合是“:02030A000004FB“ (假设校验和为FB)

       02030A000004参加校验和计算。

   【6】结束符:

       接收到一串字符,总要知道在那个地方结束吧,这就是结束符的作用,接收方不管以后还会收到多少个字符。当接收到结束符时,以后再接收的字符就算是下一轮的东西了,从起始符到结束符之间的字符就是它要分析的字符! MODBUS的ASCII方式结束符是— Chr(13)+Chr(10)

       现在的组合是“:02030A000004FB“+ Chr(13)+Chr(10)

       至此,ASCII方式的发送就完成了,控制器接收到此串字符后,根据MODBUS协议定义的通讯规范分析此串字符的作用,然后返回相应的字符!

       注意:发送的字符都是以十六进制数表示!

    通用命令

    01 读线圈状态 读离散输出位

    02 读输入状态 读离散输入位 

    03 读保持寄存器  

    04 读输入寄存器 读16位寄存器,常用于读取整数或者浮点数

    05 写单个线圈 写数据使线圈On/OFF 

    06 写单个寄存器 16位格式写数据 

    08 回路诊断 检测通讯端口

    15 写多个线圈 写数据使多个线圈ON/OFF 

    16 写多个寄存器 16位格式写数据 


    功能码 

      01 01用于读数字量模块的输出状态。 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 线圈个数 高字节 线圈个数 低字节 

      例:读模块第1~8个线圈(地址为00017~00024)00017为11h 请求指令:01 01 00 11 00 08  

      响应指令的格式为: 模块地址 功能码 字节个数 数据 数据 

      例:线圈2与7 为ON,其他为OFF 响应为:01 01 01 42 42h 即为二进制的0100 0010


      02用于读数字量模块的输入状态。 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 线圈个数 高字节 线圈个数 低字节

      例:读模块第1~8个通道的输入(地址为00001~00008) 请求指令:01 02 00 01 00 08  

      响应指令的格式为: 模块地址 功能码 字节个数 数据 数据 

      例:输入2与3 为ON,其他为OFF 响应为:01 02 01 60 60h 即为二进制的0110 0000 


      03/04用于读模块的寄存器的值。 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 寄存器个数 高字节 寄存器个数 低字节

      例:读模拟量输入第1~2个通道的输入(地址为40001~40002) 请求指令:01 04 00 01 00 02  

      响应指令的格式为: 模块地址 功能码 字节个数 数据 数据 模拟量实际值与寄存器数据的对应关系: 若寄存器为16位。则最小值为0,最大值为FFFF,0对应量程最小值,FFFF对应量程最大值。假设量程为4~20mA,则0对应4mA,FFFF对应20mA  

      例:模拟量为0~10V,输入通道1的值为5V,通道2的值为6.5V 响应为:01 04 04 7F FF A6 65。 7FFF对应5V,A665对应6.5V。 (一个指令来回的字节数是6+2+3+2*8+2=29个字节)


      05用于写线圈为ON/OFF。ON/OFF的状态用数据序列表示。FF 00hex表示为ON,00 00hex表示为OFF,FF FF hex表示 释放。 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 强制数据的 高字节 强制数据的 低字节 

      例:设置线圈3(地址为00003)为ON 请求指令:01 05 00 03 FF00 

      响应指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 设置数据的高字节 设置数据的低字节 

      响应指令为请求指令的重复。


      06用于写单个寄存器的值。 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 设置数据的 高字节 设置数据 低字节 

      例:设置寄存器40002的值为00 04 h 请求指令:01 06 00 02 00 04  

      响应指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 设置数据的 高字节 设置数据 低字节

      响应指令为请求指令的重复。


      功能码 08 08用于诊断回路。发送的信息可以为任意长度,其最大值为数据缓冲器的长度减去8个字节。 

      请求指令的格式为: 模块地址 功能码 任意数据 

      例:  请求指令:01 08 00 02 00 04  

      响应指令的格式为: 模块地址 功能码 任意数据 

      例: 响应为:01 08 00 02 00 04 


      功能码 15(0Fh) 15用于设置线圈序列的值为ON/OFF 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 线圈个数 高字节 线圈个数 低字节  字节数 设置数据的高字节 设置数据的低字节

      例:设置10个线圈的值,起始地址为00017(11hex) 请求指令:01 0F 00 11 00 0A 02 CD 01。CD 01 等价于二进制的1100 1101 0000 0001。  

      响应指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 线圈个数的 高字节 线圈个数的 低字节 

      例:01 0F 00 11 00 0A 


      功能码 16(10h) 16用于设置多个保持寄存器的值 

      请求指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 寄存器个数的高字节 寄存器个数的低字节 字节数 数据  

      例:设置通道1(地址为40009)的值为100.0 请求指令:01 10 00 09 00 02 04 42 C8 00 00

。    响应指令的格式为: 模块地址 功能码 起始地址高字节 起始地址低字节 寄存器个数的高字节 寄存器个数的低字节

      例:01 10 00 09 00 02


Modbus模块 485/232通讯继电器模组 工业数据IO扩展 串口控制模块

地址

模块默认通讯参数为:
  站号:1
  波特率:9600
  校验位:无校验
  停止位:1位
模块的通讯参数用户可更具实际需求自行修改,修改方法参见模块说明书。

监控软件和资料的下载地址为:
http://www.bk163.com/down/html/8.html


modbus tcp介绍

Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。

modbus设备可分为主站(poll)和从站(slave)。主站只有一个,从站有多个,主站向各从站发送请求帧,从站给予响应。在使用TCP通信时,主站为client端,主动建立连接;从站为server端,等待连接。

如ASCII码的完整帧

起始符 +  设备地址   +  功能代码   +   数据  +   校验   +  结束符

ModbusTCP的数据帧可分为两部分:MBAP+PDU


报文头MBAP

MBAP为报文头,长度为7字节,组成如下:

事务处理标识协议标识长度单元标识符
2字节2字节2字节1字节

 

内容解释
事务处理标识可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文。
协议标识符00 00表示ModbusTCP协议。
长度表示接下来的数据长度,单位为字节。
单元标识符可以理解为设备地址。

帧结构PDU

PDU由功能码+数据组成。功能码为1字节,数据长度不定,由具体功能决定。

功能码

Modbus的操作对象有四种:线圈、离散输入、保持寄存器、输入寄存器。

对象含义
线圈PLC的输出位,开关量,在Modbus中可读可写
离散量PLC的输入位,开关量,在Modbus中只读
输入寄存器PLC中只能从模拟量输入端改变的寄存器,在Modbus中只读
保持寄存器PLC中用于输出模拟量信号的寄存器,在Modbus中可读可写

根据对象的不同,Modbus的功能码有:

功能码含义
0x01读线圈
0x05写单个线圈
0x0F写多个线圈
0x02读离散量输入
0x04读输入寄存器
0x03读保持寄存器
0x06写单个保持寄存器
0x10写多个保持寄存器

说明更详细的表

代码中文名称英文名位操作/字操作操作数量
01读线圈状态READ COIL STATUS位操作单个或多个
02读离散输入状态READ INPUT STATUS位操作单个或多个
03读保持寄存器READ HOLDING REGISTER字操作单个或多个
04读输入寄存器READ INPUT REGISTER字操作单个或多个
05写线圈状态WRITE SINGLE COIL位操作单个
06写单个保持寄存器WRITE SINGLE REGISTER字操作单个
15写多个线圈WRITE MULTIPLE COIL位操作多个
16写多个保持寄存器WRITE MULTIPLE REGISTER字操作多个

PDU详细结构

0x01:读线圈

在从站中读1~2000个连续线圈状态,ON=1,OFF=0

  • 请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
  • 响应:MBAP 功能码 数据长度 数据(一个地址的数据为1位)
  • 如:在从站0x01中,读取开始地址为0x0002的线圈数据,读0x0008位
    00 01 00 00 00 06 01 01 00 02 00 08
  • 回:数据长度为0x01个字节,数据为0x01,第一个线圈为ON,其余为OFF
    00 01 00 00 00 04 01 01 01 01

0x05:写单个线圈

将从站中的一个输出写成ON或OFF,0xFF00请求输出为ON,0x000请求输出为OFF

  • 请求:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
  • 响应:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
  • 如:将地址为0x0003的线圈设为ON
    00 01 00 00 00 06 01 05 00 03 FF 00
  • 回:写入成功
    00 01 00 00 00 06 01 05 00 03 FF 00

0x0F:写多个线圈

将一个从站中的一个线圈序列的每个线圈都强制为ON或OFF,数据域中置1的位请求相应输出位ON,置0的位请求响应输出为OFF

  • 请求:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L 字节长度 输出值H 输出值L
  • 响应:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L

0x02:读离散量输入

从一个从站中读1~2000个连续的离散量输入状态

  • 请求:MBAP 功能码 起始地址H 起始地址L 数量H 数量L(共12字节)
  • 响应:MBAP 功能码 数据长度 数据(长度:9+ceil(数量/8))
  • 如:从地址0x0000开始读0x0012个离散量输入
    00 01 00 00 00 06 01 02 00 00 00 12
  • 回:数据长度为0x03个字节,数据为0x01 04 00,表示第一个离散量输入和第11个离散量输入为ON,其余为OFF
    00 01 00 00 00 06 01 02 03 01 04 00

0x04:读输入寄存器

从一个远程设备中读1~2000个连续输入寄存器

  • 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
  • 响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
  • 如:读起始地址为0x0002,数量为0x0005的寄存器数据
    00 01 00 00 00 06 01 04 00 02 00 05
  • 回:数据长度为0x0A,第一个寄存器的数据为0x0c,其余为0x00
    00 01 00 00 00 0D 01 04 0A 00 0C 00 00 00 00 00 00 00 00

0x03:读保持寄存器

从远程设备中读保持寄存器连续块的内容

  • 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
  • 响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
  • 如:起始地址是0x0000,寄存器数量是 0x0003
    00 01 00 00 00 06 01 03 00 00 00 03
  • 回:数据长度为0x06,第一个寄存器的数据为0x21,其余为0x00
    00 01 00 00 00 09 01 03 06 00 21 00 00 00 00

0x06:写单个保持寄存器

在一个远程设备中写一个保持寄存器

  • 请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
  • 响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
  • 如:向地址是0x0000的寄存器写入数据0x000A
    00 01 00 00 00 06 01 06 00 00 00 0A
  • 回:写入成功
    00 01 00 00 00 06 01 06 00 00 00 0A

0x10:写多个保持寄存器

在一个远程设备中写连续寄存器块(1~123个寄存器)

  • 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L 字节长度 寄存器值(13+寄存器数量×2)
  • 响应:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
  • 如:向起始地址为0x0000,数量为0x0001的寄存器写入数据,数据长度为0x02,数据为0x000F
    00 01 00 00 00 09 01 10 00 00 00 01 02 00 0F
  • 回:写入成功
    00 01 00 00 00 06 01 10 00 00 00 01

Modbus TCP 示例报文

ModBusTcp与串行链路Modbus的数据域是一致的,具体数据域可以参考串行Modbus。这里给出几个ModbusTcp的链路解析说明,辅助新人分析报文。

1.png

2.png

3.png

4.png

ModbusTCP通信

通信方式

Modbus设备可分为主站(poll)和从站(slave)。主站只有一个,从站有多个,主站向各从站发送请求帧,从站给予响应。在使用TCP通信时,主站为client端,主动建立连接;从站为server端,等待连接。

  • 主站请求:功能码+数据
  • 从站正常响应:请求功能码+响应数据
  • 从站异常响应:异常功能码+异常码,其中异常功能码即将请求功能码的最高有效位置1,异常码指示差错类型
  • 注意:需要超时管理机制,避免无期限的等待可能不出现的应答

IANA(Internet Assigned Numbers Authority,互联网编号分配管理机构)给Modbus协议赋予TCP端口号为502,这是目前在仪表与自动化行业中唯一分配到的端口号。

通信过程

  1. connect 建立TCP连接
  2. 准备Modbus报文
  3. 使用send命令发送报文
  4. 在同一连接下等待应答
  5. 使用recv命令读取报文,完成一次数据交换
  6. 通信任务结束时,关闭TCP连接

仿真软件

  • Modbus poll 和Modbus slave是一组Modbus仿真软件,可以实现Modbus RTU、TCP、串口仿真等。
  • 仿真软件网址:https://modbustools.com/download.html
  • 在ModbusTCP中,Modbus poll 作为客户端请求数据,Modbus slave 作为服务器端处理请求。
  • 使用c语言编写客户端连接Modbus slave时,注意数据格式,一条指令一次性发出,否则连接会出错。
  • 使用软件时,需要指定功能码,在setup->slave definition或者poll definition中进行设置。
    – slave ID:从站编号(事务标识符)
    – function:功能码,0x01对应线圈操作,0x02对应离散量操作,0x03对应保持寄存器操作,0x04对应输入寄存器操作
    – address:开始地址
    – quantity:寄存器/线圈/离散量 的数量


modbus rtu协议_手把手教你写ModbusRTU协议

Modbus-RTU解析


1)串行传输模型


    modbus的串行传输模式分为:RTU模式和ASCII模式


    这里作者用一个小例子来解释这两种模式,便于大家理解这两种模式的传输,比如我们需要传输0x5A,那么我们如果使用串口进行RTU传输直接一帧数据1byte搞定,而如果我们选用ASCII来进行传输需要两个byte,分别传输0x35(ASCII对应:字符5)和0x41(ASCII对应:字符A)。


    从上面的小例子我们了解到用RTU模式传输的传输效率和数据密度是高于ASCII的,那么为什么还需要用定ASCII协议呢?那我们首先简答的介绍ASCII模型传输。


2)ASCII传输模式分析


    上面我们也简单的提到了ASCII的传输模式,这里简单画一下其帧格式大家应该就可以了解了:


1.png


    解析:ASCII模式以冒号开头,与回车-换行结束(0x0D-0x0A),基本上由之前的RTU一个字节变成了两个字节,传输效率大大降低。由于该协议本身存在起始和结束标识符,那么其字符接受间隔可以达到1s,而RTU帧与帧之间就只能有1.5个字符,而报文与报文之间应>3.5个字符。那么ASCII它虽然传输速度不如RTU,不过其可读性比较好,我们有时候可以直接通过串口调试助手即可直观的解读数据信息。ASCII不是我们的重点,这里不多讲。


小知识-LRC校验


Modbus的LRC校验:(一句话搞定)


    除去ADU中的冒号和LR-LF,其他数据以两个字符对应的hex格式数据相加,然后取反+1求补码,然后再把hex格式转回为ASCII的两个字符进行传输,同时记得"高位在前,低位在后"即可。


3)RTU传输模式中的1.5和3.5


    <1.5个字符:是RTU中1byte与另外一个byte的传输时间必须小于1.5个字符,表示字符与字符之间的最大间隔时间。


    >3.5个字符:表示RTU中1个报文与下一个报文之间的间隔需要>3.5个字符,即传输一个ADU与下一个ADU的时间间隔,这个主要是用于判断报文发送完毕。


    总结:其实modbus之间的1.5和3.5其实都是表示的是时间,那么很多小伙伴们会问这个时间该如何计算呢?


    我们以串口通讯中:RTU传输模式的串口格式为"1个起始位+8个数据位+1个奇偶校验位+1个停止位"= 11个bit,那么我们又知道我们的串口总线上的波特率是唯一的,比如以baudrate = 9600bit/s,那么传输11/9600 = 1个字符时间(s),这样就算出了我们的1个字符时间,顺便就可以算出1.5和3.5了。


    好了,今天的理论篇就讲这么多了,大家需要结合《向Modbus说So Easy!》一起阅读,我们平常一般用得比较多的是RTU模式,所以一般口头上都直接叫modbus协议。这里是公众号:"最后一个bug",我们下一期带来其超牛的Modbus代码master和Slave的实现,感谢大家的关注,也希望大家的分享,转发和再看。



Modbus RTU协议各知识点入门 + 实例

1. 起因

最近在做一个项目,我们的设备使用485作为从机与主机PLC通信,用到了modbus,因此整理了一份modbus的资料。

其实modbus还是很简单的,虽然有些名词初看不知道是啥,看懂之后发现就是绕人用的,换个名词就很清晰易懂的(对于我这种不做PLC的人)。


2. 几个重点

2.1 一些难懂的概念

线圈 就是“位变量”

离散输入 就是 只读 的 1位变量

线圈 就是 可读写 的 1位变量

寄存器就是“16位变量”

输入寄存器 就是 只读 的 16位变量

保持寄存器 就是 可读写 的 16位变量

具体可以见5章节,阅读的时候把这几个名词直接替换就好。


2.2 CRC的高低位

Modbus协议的CRC校验码是高位在前还是低位在前的问题。

有些资料说高位在前,有些资料是说低位在前。

其实是因为CRC生成函数,有些在内部已经做了高低位的转换,所以看起来像是高位在前。

实际上,CRC是低位在前发送。详细见第7节。


2.3 其他

不管任何时候,从机都不能主动向主机发送数据

从站的地址是1~247(注意没有0!! 0是广播!都不回复,都会收到!)

数字编码大端序,高字节在前

CRC校验位低字节在前

modbus的寄存器都是16位的,但是在其他地方(比如MCGS)也见过一些变种,比如32位的,比如float的。有些是把32位按照标准的modbus,拆分成了2个寄存器。有些就是按1个4字节的寄存器来算的。

3. 介绍

3.1 起源

modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。

1996年施耐德公司推出基于以太网TCP/IP的modbus协议:modbusTCP。


3.2 分类

Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。ASCII是明文的,但是实际效率低,用的比较普遍的就是RTU格式。

这里我也是只用了RTU,就只记录RTU的协议。


4. 格式

4.1 串口协议

串口波特率

串口设置

4.2 帧格式

起始位 地址码 功能码 数据区 错误校验码

3.5字符时间 1字节 1字节 0~252字节 CRC16校验码

1~247 见3.5节 CRC低位 CRC高位

注意这里,多字节的数据,是高位在前,低位在后。

但是CRC是低位在前,高位在后。

根据不同的功能码,数据区各不相同,具体可以看第8部分实例来分析。


5. 数据类型

数据类型 简写 读功能码 写功能码 对象 地址范围

离散输入 DI 02 只读的 1位变量 00001~0FFFF

线圈 DO 01 05,15 可读写的 1位变量 10001~1FFFF

输入寄存器 AI 04 只读的 16位变量 30001~3FFFF

保存寄存器 AO 03 06,16 可读写的 16位变量 40001~4FFFF

DI: 数字输入,离散输入,一个地址一个数据位,用户只能读取它的状态,不能修改。比如面板上的按键、开关状态,电机的故障状态。

DO: 数字输出,线圈输出,一个地址一个数据位,用户可以置位、复位,可以回读状态,比如继电器输出,电机的启停控制信号。

AI: 模拟输入,输入寄存器,一个地址16位数据,用户只能读,不能修改,比如一个电压值的读数。

AO: 模拟输出,保持寄存器,一个地址16位数据,用户可以写,也可以回读,比如一个控制变频器的电流值。

6. 功能码

功能码很多,用的比较多的就是以下8个:


功能码 功能码16进制 中文名称 操作位 操作寄存器数量

01 0x1 读线圈 1位 单个或多个

02 0x2 读离散输入 1位 单个或多个

03 0x3 读保存寄存器 16位 单个或多个

04 0x4 读输入寄存器 16位 单个或多个

05 0x5 写单个线圈 1位 单个

06 0x6 写单个保存寄存器 16位 单个

15 0xF 写多个线圈 1位 多个

16 0x10 写多个保持寄存器 16位 多个

最主要的就是1、2、3、4、5、6、15、16 这8个功能码。

个人翻译一下

01:读一个或多个可读写的 bit 变量

02:读一个或多个只能读的 bit 变量

03:读一个或多个可读写的 short 变量 (或其他2字节变量)

04:读一个或多个只能读的 short 变量 (或其他2字节变量)

05:写一个可读写的 bit 变量

06:写一个可读写的 short 变量

15:写多个可读写的 bit 变量

16:写多个可读写的 short变量 (可以拼凑成int 、long 、long long、float等变量)


读的时候,都是可以选择读“一个或多个”。

写的时候,按数量分功能码进行操作。


7. CRC16(modbus)

在CRC计算时只用8个数据位,起始位及停止位,如有奇偶校验位也包括奇偶校验位,都不参与CRC计算。

CRC计算方法是:


加载一值为0XFFFF的16位寄存器,此寄存器为CRC寄存器。

把第一个8位二进制数据(即通讯信息帧的第一个字节)与16位的CRC寄存器的相异或,异或的结果仍存放于该CRC寄存器中。

把CRC寄存器的内容右移一位,用0填补最高位,并检测移出位是0还是1。

如果移出位为零,则重复第三步(再次右移一位);如果移出位为1,CRC寄存器与0XA001进行异或。

重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理。

重复步骤2和5,进行通讯信息帧下一个字节的处理。

将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换

最后得到的CRC寄存器内容即为:CRC校验码。

注意!这个 高低字节交换 之后的,才是算出来的modbus的CRC校验码。

然后把这个交换后的校验码,按照 先低位,后高位 的 顺序加载到发送队列里。


8. 实例

从网上随便截取了一些别人的例子,部分来自示例


8.1 功能码1

1.png


8.2 功能码2

2.png


8.3 功能码3


3.png

8.4 功能码4

4.png

主机要读取从0x400地址的2个16位寄存器的值。

从机回复了4个字节。


8.5 功能码5

5.png

6.png



8.6 功能码6

1.png


8.7 功能码15

1.png


8.8 功能码16

1.png


9. 实现思路

9.1 我的实现思路

我这边实现思路比较简单,

利用串口中断判断收到的报文,如果满足报头(设备地址),就进行报文连续接收,收满一帧就放入处理队列。另一个线程收到这个数据会取出来解包处理。

寄存器方面就是申请一块连续buf(数组或其结构体),和寄存器地址一一对应上,要改值或者要读值都是根据这个buf的数据来做。


9.2 别人的实现思路(一些开源库)

用的比较广的是FreeModbus和LibModbus。



MODBUS协议最简单解释

Modbus是一种单主站的主/从通信模式。Modbus网络上只能有一个主站存在,主站在 Modbus网络上没有地址,从站的地址范围为 0 - 247,其中 0 为广播地址,从站的实际地址范围为 1 - 247。 Modbus通信标准协议可以通过各种传输方式传播,如 RS232C、RS485、光纤、无线电等。 
    Modbus具有两种串行传输模式,ASCII 和 RTU。它们定义了数据如何打包、解码的不同方式。支持 Modbus 协议的设备一般都支持 RTU 格式。通信双方必须同时支持上述模式中的一种。
    上面说的是官话,下面是我说的大白话:
    modbus协议也只是通讯协议的一种,没什么神秘的,通讯协议包括两个方面:

一、通讯格式,即: 波特率,检验方式,数据位,停止位


波特率:一秒钟传送的位数,也就是通讯速率;比如波特率为9600,即,一秒种可以传送9600个位数,位的概念看下面的数据位介绍
校验方式:奇校验或偶校验或无校验,目的是判断传输过程中是否有错误!它只是用于判断一个字符(比如八个位或是七个位组成一个字符)传输是否有错误。但是它并不能完全能够判断传输是否有错。比如偶校验,在检验送八个“11111111”时,如果到达接收方,由于干扰而变成了“10111101”,“1”的个数仍然是偶数,接收方就判断不出来传送的字符已经错误!
数据位:传输一个字符由几个位组成,计算机的基本单位就是“位”,其值非“0”即“1”,又如传送A,定义通讯格式时,是定义的八位,其传送的数据可能就是:00001010;
停止位:传输一个字符有几个停止位,用天判断某个字符是否传输结束,以便开始接收下一个字符。

    通讯格式的作用是规范发送方与接收方的传输格式,如果双方通讯格式不一样,接收方就不可能正确判断发送方发来的东西是什么。
    比如,接收方设置的波特率是10(一秒只接收十个位)位,而发送方的波特率是20(一秒发送二十个位),那么发送方一秒种发送的20个字符,接收方就不可能都收到,只能接收到10个,造成通讯出错。
    校验方式:双方校验方式不一样,就没有一个统一的标准认定传输是否有错误。
    数据位,接收方设定的七位,即它接收到七个位就认为是一个字符,而实际发送方设定的是八位,那么接收方认定的字符与发送方发送的字符就不一样了。
***参预通讯的双方设定的通讯格式必须一样的!!

二、通讯规范(这个词是我自己定义的,不能引用,会被人笑话的)

    通讯格式只是保证接收方正确地接收到发送方传输过来的每一个字符(实际如上所述,检验方式并不能保证完全正确,还要靠通讯规范中的校验和计算来验证整体正确性,下面会继续说明),那么接收到的整串字符做什么用呢,就要靠通讯规范了
MODBUS的通讯规范很简单!
    先说ASCII方式:
    ASCII方式发送时的规范定义如下:
起始符 +  设备地址   +  功能代码   +   数据  +   校验   +  结束符

【1】起始符:接收到一串字符,总要知道这串字符从哪个地方开始吧,这就是起始符的作用,接收方不管以前收到多少个字符。当接收到起始符时,以前的字符就不再理它了,从起始符开始分析以后的字符!  MODBUS的ASCII方式起始符是一个冒号  “:”

【2】设备地址:前面说过,MODBUS是单主站的主/从通信模式,一个主站下面可以接十多个从站。大家都挂在一条线,如果没有一个设备地址,就不知道是发给哪个从站的,大家都回应的话,这条线上的信号就乱七八糟了,主站也不知道接收到的是什么了。所以,设这么一个设备地址,告诉是给那个从站的。只要这个从站回答,其他的闭嘴!“二号,请您回答,其他人就不要吱声了。”
广播地址(0)是命令式的,不要求从站回答的。“都听着,晚上全体到我家喝酒去,不去者死,散会!”
设备地址是要求两个字符,比如发给2号站,则是“02”
现在的组合是“:02”

【3】功能代码:告诉从站应该做什么,比如读数据的命令是“3”,从站接收到这个命令,再根据下面数据要求的具体地址,把具体地址的数据返回给主站。
功能代码也是要求两个字符,比如读命令3,则是“03”
现在的组合是“:0203“

【4】数据:
1、告诉从站具体的元件通讯地址,写入到哪里,从哪里读。如读变频器的设定频率的通讯地址是00A0
元件的通讯地址要求是四个字符,如果控制器的元件地址不足四个字符,则在前面补0,比如元件通讯地址是A0,则在前面补足两个0:“00A0”
2、数据又有可能包括您要读取的字节数( 有的控制器是字数),比如连续读取PLC的两个十六位寄存器,其字节数为四个,则是“0004”。您看出来了吧,读取个数也是要求四个字符,不足四个,前面补零
现在的组合是“:020300A00004“
3、而当您要实现写入功能时,数据又可能包括写入的数据,比如写入一个十六位寄存器的值,则要包括是写入的数值,如“0D98”
现在的组合是“:020600A00D98“ 06是单个寄存器的写入命令
4、当连续写入多个寄存器时,这个数据包括的内容又不一样,它可能是:
寄存器通讯地址(四个字符) +字数(四个字符)+字节数(两个字符)+ 要写入的数值

您看乱了吗?没关系的,等您拿到具体控制器时,此控制器的通讯说明上会告诉您此数据都包括什么内容,以什么样的格式排列!您一定为我上面四点中的可能字样而生气,您认为讲解就应该讲解的具体,而不是可能什么又可能什么!
这又要重复说明一下MODBUS的通讯规范,
起始符 + 设备地址 + 功能代码 + 数据 + 校验 + 结束符
MODBUS是一种标准通讯协议,这种标准定义了上面红色字符的通讯规范,除了数据项,其他的都是固定字符个数。
数据呢,因为功能代码的不同,其包含的内容也不同!所以我只好说可能包含这个,可能包含那个。您无须担心此数据变来变去,造成接收方不知道如何分析。接收方在接收到功能代码时,就已经知道此数据包含多少个字符了!

【5】检验和:
前面说过,通讯格式里的校验方式并不能保证每个字符都正确,所以这里就把所有字符的值加在一起,其和(检验值)传给接收方,接收再把接收到的字符的值加在一起,与发送方传送过来的检验值比较,如果相等,就算接收正确了。
"这种方式极大地提高了传输的可靠性,保证了传输的顺利进行,为传输事业做出了巨大贡献"
这样的描述让您想到了什么?假话呗,检验只是提高了校验的可靠性。并不能完全判断传输是否正确。想要最大限度的提高传输的可靠性,唯有最大限度地降低干扰!于是产生了232,485,422传输方式,他们的区别就在于传输的可靠性!
1+2+3=6 3+2+1=6 这两个字符串的作用肯定是不一样的!但是其校验和是一样的,如果在传输过程中,由于干扰,1变3,3变1,根据校验和的计算,接收方并不知道由于干扰而造成传输错误,此时,或是出现通讯错误,或是出现通讯混乱。
如,命令码03,由于干扰而变成了30,此时校验和是一样的,而MODBUS并没有30这个命令码,接收不认识,于是出现通讯错误。
再如,读变频器的设定频率通讯地址是0001,由于干扰而变成了0010,此时校验和是一样的,但是通讯地址却变了,变频器就可能返回的是其他数据,造成通讯混乱!
校验字符是要求两个字符,如果计算结果超过两个字符,则取后两位!
**参加校验计算的字符是起始符与校验符之间的字符串(不含起始符与校验符)
现在的组合是“:02030A000004FB“ (假设校验和为FB)
02030A000004参加校验和计算。

【6】结束符:
接收到一串字符,总要知道在那个地方结束吧,这就是结束符的作用,接收方不管以后还会收到多少个字符。当接收到结束符时,以后再接收的字符就算是下一轮的东西了,从起始符到结束符之间的字符就是它要分析的字符! MODBUS的ASCII方式结束符是— Chr(13)+Chr(10)
现在的组合是“:02030A000004FB“+ Chr(13)+Chr(10)
至此,ASCII方式的发送就完成了,控制器接收到此串字符后,根据MODBUS协议定义的通讯规范分析此串字符的作用,然后返回相应的字符!
注意:发送的字符都是以十六进制数表示!
控制器返回的字符根据命令的不同而不同,此处不好讲解,在下面具体例子中会有说明!
再说RTU方式: RTU方式发送时的规范定义如下: 至少3.5个字符传输时间的停顿间隔时间标定消息的开始 设备地址 + 功能代码+ 数据 + 校验 + 至少3.5个字符传输时间的停顿间隔时间标定了消息的结束 其他的就不用说了,与ASCII方式一样的作用,唯独这3.5个字符的时间搞晕了很多人,实际我也不敢太解释,大致说一下吧,您就当听着玩,比如通讯格式是9600,E,8,1 波特率是做什么的?一秒传输多少个位(比如一秒传送9600个位),一个字符是多少个位呢?通讯格式已经标定了(7个位或是8个位),那3.5个字符的传送时间就好算了吧:
3.5*11(或10)=39个位(35个位),传3.5个字符需要的时间是:39/9600=4毫秒。
不是说八位嘛,怎么乘11,记住了,还有一个起始位,奇或偶的校验位(无奇偶校验,则没有此位),停止位(两个停止位就是2了。)
如9600,N,8,2为11个位,
9600,N,7,1.为9个位
就是说,您得保证发送字符串的连续性,中间停顿时间超过4毫秒,接收方就认为您已经发送完了这组消息,开始处理了。这就是至少3.5个字符传输时间的停顿间隔时间标定了消息的结束的含义
如果您发送的太连续,下一组消息与上一组消息之间的间隔时间没超过4毫秒,接收方就认为这些字符是一组消息,按一组消息去处理。所以,您发送结束一组命令后,必须间隔4毫秒才能发送下一组命令. 这就是至少3.5个字符传输时间的停顿间隔时间标定消息的开始的含义

至此我的大白话结束,有什么错误,请您批评,多谢!

三 总结


    通讯协议实际也就是这回事,任何一个协议都大同小异。通讯格式,通讯规范两种而已。您如果愿意,也可以自己定义一个通讯规范,用PLC或是VB语言按照您自己定义的这个规范处理,如果可靠性超过modbus,那您的通讯规范就是最流行的了!
    我经常问某些产品推广人员,“您设备的通讯协议是什么”,他回答:“232”或是“485”。今天您看了上面这些大白话,请您就不要再这样回答了。Modbus通信标准协议可以通过各种传输方式传播,如 RS232C、RS485、光纤、无线电等。“232”或是“485”只是一种线路传输方式,与协议是无关的!232传输抗干扰性差,485传输抗干扰相对强。MODBUS做为一种标准的协议,应用于各种PLC,控制器,仪表。这些仪表或是控制器应用中,元件的通讯地址肯定是不一样了;各个命令码的各部分组成的意义也许也会不同;但是,它一定会遵守MODBUS的协议规范。即,每个命令码的组成一定符合MODBUS的规范!一样不多,一样不少!
    下面针对某种支持MODBUS协议的控制器,说一下具体的读/写例子
【1】读某控制器的十五个寄存器值,发送的字符串是:(ASCII方式)
“: 01031000000FDD”+ Chr(13)+Chr(10)
起始符“:” + 站号(01) + 读命令(03)+ 起始寄存器通讯地址(1000)+ 字数(15;转换成十六进制000F)+ 校验和(DD)+ 结束符
控制器返回的字符数是71个
起始符“:” + 站号(01) + 读命令(03)+ 字数(0F)+ 60个数据字符(一个寄存器是4个,一共十五个) + 校验和(DD)+ 结束符(两个)

【2】分别向某控制器两个寄存器写入数值,发送的字符串是:(ASCII方式)
“: 01101000000204”+ 写入的数值(8个字符)+ 校验和 +Chr(13)+Chr(10)
起始符“:” + 站号(01) + 写命令(10)+ 起始寄存器通讯地址(1000)+ 字数(2;转换成十六进制0002)+ 字节数(04) + 校验和(因为写入数值是变化的,需要得经过计算得出校验和)+ 结束符
控制器返回的字符数是17个
起始符“:” + 站号(01) + 写命令(10)+ 起始寄存器通讯地址(1000)+ 字数(02)+ 错误码(2个) + 校验和(DD)+ 结束符(两个)
*错误码,当通讯正确时是什么,通讯错误时是什么,具体控制器会有说明
读其他仪表的某值时,参照上述读的规范,也就是更改一下起始寄存器通讯地址、字数、校验和(校验和是编制程序块自动计算的。)
要读多个仪表的同一个检测值更简单,更改站号就可以了。


Modbus 通信协议详解

Modbus 通信协议详解

一、Modbus 协议简介 

     Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间可以通信。它已经成为一通用工业标准。有了它,不同厂商生产的控制设备可以连成工业网络,进行集中监控。 

        此协议定义了一个控制器能认识使用的消息结构,而不管它们是经过何种网络进行通信的。它描述了一控制器请求访问其它设备的过程,如果回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。 

    当在一Modbus网络上通信时,此协议决定了每个控制器须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。如果需要回应,控制器将生成反馈信息并用Modbus协议发出。在其它网络上,包含了Modbus协议的消息转换为在此网络上使用的帧或包结构。这种转换也扩展了根据具体的网络解决节地址、路由路径及错误检测的方法。

 

1、在Modbus网络上转输

     标准的Modbus口是使用一RS-232C兼容串行接口,它定义了连接口的针脚、电缆、信号位、传输波特率、奇偶校验。控制器能直接或经由Modem组网。 

     控制器通信使用主—从技术,即仅一设备(主设备)能初始化传输(查询)。其它设备(从设备)根据主设备查询提供的数据作出相应反应。典型的主设备:主机和可编程仪表。典型的从设备:可编程控制器。 

    主设备可单独和从设备通信,也能以广播方式和所有从设备通信。如果单独通信,从设备返回一消息作为回应,如果是以广播方式查询的,则不作任何回应。Modbus协议建立了主设备查询的格式:设备(或广播)地址、功能代码、所有要发送的数据、一错误检测域。 

    从设备回应消息也由Modbus协议构成,包括确认要行动的域、任何要返回的数据、和一错误检测域。如果在消息接收过程中发生一错误,或从设备不能执行其命令,从设备将建立一错误消息并把它作为回应发送出去。

 

2、在其它类型网络上转输

    在其它网络上,控制器使用对等技术通信,故任何控制都能初始和其它控制器的通信。这样在单独的通信过程中,控制器既可作为主设备也可作为从设备。提供的多个内部通道可允许同时发生的传输进程。 

    在消息位,Modbus协议仍提供了主—从原则,尽管网络通信方法是“对等”。如果一控制器发送一消息,它只是作为主设备,并期望从从设备得到回应。同样,当控制器接收到一消息,它将建立一从设备回应格式并返回给发送的控制器。

3、查询—回应周期

1.jpg

(1)查询

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

(2)回应

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

 

二、两种传输方式

    控制器能设置为两种传输模式(ASCII或RTU)中的任何一种在标准的Modbus网络通信。用户选择想要的模式,包括串口通信参数(波特率、校验方式等),在配置每个控制器的时候,在一个Modbus网络上的所有设备都必须选择相同的传输模式和串口参数。

ASCII模式


: 地址 功能代码 数据数量 数据1 ... 数据n LRC高字节 LRC低字节 回车 换行

RTU模式


地址 功能代码 数据数量 数据1 ... 数据n CRC低字节 CRC高字节

 所选的ASCII或RTU方式仅适用于标准的Modbus网络,它定义了在这些网络上连续传输的消息段的每一位,以及决定怎样将信息打包成消息域和如何解码。

 在其它网络上(象MAP和Modbus Plus)Modbus消息被转成与串行传输无关的帧。

1、ASCII模式

     当控制器设为在Modbus网络上以ASCII(美国标准信息交换代码)模式通信,在消息中的每个8Bit字节都作为两个ASCII字符发送。这种方式的主要优点是字符发送的时间间隔可达到1秒而不产生错误。

 代码系统

  • 十六进制,ASCII字符0...9,A...F
  • 消息中的每个ASCII字符都是一个十六进制字符组成

    每个字节的位

  • 1个起始位
  • 7个数据位,最小的有效位先发送
  • 1个奇偶校验位,无校验则无
  • 1个停止位(有校验时),2个Bit(无校验时)

 错误检测域

  • LRC(纵向冗长检测)

2、RTU模式

     当控制器设为在Modbus网络上以RTU(远程终端单元)模式通信,在消息中的每个8Bit字节包含两个4Bit的十六进制字符。这种方式的主要优点是:在同样的波特率下,可比ASCII方式传送更多的数据。

代码系统

  • 8位二进制,十六进制数0...9,A...F
  • 消息中的每个8位域都是一个两个十六进制字符组成
  • 每个字节的位
  • 1个起始位
  • 8个数据位,最小的有效位先发送
  • 1个奇偶校验位,无校验则无
  • 1个停止位(有校验时),2个Bit(无校验时)

    错误检测域

  • CRC(循环冗长检测)

 

三、Modbus消息帧

     两种传输模式中(ASCII或RTU),传输设备以将Modbus消息转为有起点和终点的帧,这就允许接收的设备在消息起始处开始工作,读地址分配信息,判断哪一个设备被选中(广播方式则传给所有设备),判知何时信息已完成。部分的消息也能侦测到并且错误能设置为返回结果。

 

1、ASCII帧

   使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。 

   其它域可以使用的传输字符是十六进制的0...9,A...F。网络上的设备不断侦测“:”字符,当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。 

   消息中字符间发送的时间间隔最长不能超过1秒,否则接收的设备将认为传输错误。一个典型消息帧如下所示:

起始位设备地址功能代码数据LRC校验结束符
1个字符2个字符2个字符n个字符2个字符2个字符

图2 ASCII消息帧

2、RTU帧

    使用RTU模式,消息发送至少要以3.5个字符时间的停顿间隔开始。在网络波特率下多样的字符时间,这是最容易实现的(如下图的T1-T2-T3-T4所示)。传输的第一个域是设备地址。可以使用的传输字符是十六进制的0...9,A...F。网络设备不断侦测网络总线,包括停顿间隔时间内。当第一个域(地址域)接收到,每个设备都进行解码以判断是否发往自己的。在最后一个传输字符之后,一个至少3.5个字符时间的停顿标定了消息的结束。一个新的消息可在此停顿后开始。 

    整个消息帧必须作为一连续的流转输。如果在帧完成之前有超过1.5个字符时间的停顿时间,接收设备将刷新不完整的消息并假定下一字节是一个新消息的地址域。同样地,如果一个新消息在小于3.5个字符时间内接着前个消息开始,接收的设备将认为它是前一消息的延续。这将导致一个错误,因为在最后的CRC域的值不可能是正确的。一典型的消息帧如下所示:

 

起始位设备地址功能代码数据CRC校验结束符
T1-T2-T3-T48Bit8Bitn个8Bit16BitT1-T2-T3-T4

图3 RTU消息帧

3、地址域

    消息帧的地址域包含两个字符(ASCII)或8Bit(RTU)。可能的从设备地址是0...247 (十进制)。单个设备的地址范围是1...247。主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。当从设备发送回应消息时,它把自己的地址放入回应的地址域中,以便主设备知道是哪一个设备作出回应。 

    地址0是用作广播地址,以使所有的从设备都能认识。当Modbus协议用于更高水准的网络,广播可能不允许或以其它方式代替。

 

4、如何处理功能域

    消息帧中的功能代码域包含了两个字符(ASCII)或8Bits(RTU)。可能的代码范围是十进制的1...255。当然,有些代码是适用于所有控制器,有此是应用于某种控制器,还有些保留以备后用。 

    当消息从主设备发往从设备时,功能代码域将告之从设备需要执行哪些行为。例如去读取输入的开关状态,读一组寄存器的数据内容,读从设备的诊断状态,允许调入、记录、校验在从设备中的程序等。 

    当从设备回应时,它使用功能代码域来指示是正常回应(无误)还是有某种错误发生(称作异议回应)。对正常回应,从设备仅回应相应的功能代码。对异议回应,从设备返回一等同于正常代码的代码,但最重要的位置为逻辑1。

    例如:一从主设备发往从设备的消息要求读一组保持寄存器,将产生如下功能代码:0 0 0 0 0 0 1 1 (十六进制03H)

    对正常回应,从设备仅回应同样的功能代码。对异议回应,它返回:1 0 0 0 0 0 1 1 (十六进制83H)

    除功能代码因异议错误作了修改外,从设备将一独特的代码放到回应消息的数据域中,这能告诉主设备发生了什么错误。

    主设备应用程序得到异议的回应后,典型的处理过程是重发消息,或者诊断发给从设备的消息并报告给操作员。

 

5、数据域

     数据域是由两个十六进制数集合构成的,范围00...FF。根据网络传输模式,这可以是由一对ASCII字符组成或由一RTU字符组成。

     从主设备发给从设备消息的数据域包含附加的信息:从设备必须用于进行执行由功能代码所定义的所为。这包括了象不连续的寄存器地址,要处理项的数目,域中实际数据字节数。

     例如,如果主设备需要从设备读取一组保持寄存器(功能代码03),数据域指定了起始寄存器以及要读的寄存器数量。如果主设备写一组从设备的寄存器(功能代码10十六进制),数据域则指明了要写的起始寄存器以及要写的寄存器数量,数据域的数据字节数,要写入寄存器的数据。

     如果没有错误发生,从从设备返回的数据域包含请求的数据。如果有错误发生,此域包含一异议代码,主设备应用程序可以用来判断采取下一步行动。

     在某种消息中数据域可以是不存在的(0长度)。例如,主设备要求从设备回应通信事件记录(功能代码0B十六进制),从设备不需任何附加的信息。

 

6、错误检测域

    标准的Modbus网络有两种错误检测方法。错误检测域的内容视所选的检测方法而定。

ASCII

    当选用ASCII模式作字符帧,错误检测域包含两个ASCII字符。这是使用LRC(纵向冗长检测)方法对消息内容计算得出的,不包括开始的冒号符及回车换行符。LRC字符附加在回车换行符前面。

RTU

    当选用RTU模式作字符帧,错误检测域包含一16Bits值(用两个8位的字符来实现)。错误检测域的内容是通过对消息内容进行循环冗长检测方法得出的。CRC域附加在消息的最后,添加时先是低字节然后是高字节。故CRC的高位字节是发送消息的最后一个字节。

 

7、字符的连续传输

     当消息在标准的Modbus系列网络传输时,每个字符或字节以如下方式发送(从左到右):

     最低有效位...最高有效位

     使用ASCII字符帧时,位的序列是:

     有奇偶校验

启始位1234567奇偶位停止位

     无奇偶校验

启始位1234567停止位停止位

图4. 位顺序(ASCII)

     使用RTU字符帧时,位的序列是:

     有奇偶校验

启始位12345678奇偶位停止位

     无奇偶校验

启始位12345678停止位停止位

图4. 位顺序(RTU)

四、错误检测方法

    标准的Modbus串行网络采用两种错误检测方法。奇偶校验对每个字符都可用,帧检测(LRC或CRC)应用于整个消息。它们都是在消息发送前由主设备产生的,从设备在接收过程中检测每个字符和整个消息帧。

    用户要给主设备配置一预先定义的超时时间间隔,这个时间间隔要足够长,以使任何从设备都能作为正常反应。如果从设备测到一传输错误,消息将不会接收,也不会向主设备作出回应。这样超时事件将触发主设备来处理错误。发往不存在的从设备的地址也会产生超时。

 

1、奇偶校验

    用户可以配置控制器是奇或偶校验,或无校验。这将决定了每个字符中的奇偶校验位是如何设置的。

    如果指定了奇或偶校验,“1”的位数将算到每个字符的位数中(ASCII模式7个数据位,RTU中8个数据位)。例如RTU字符帧中包含以下8个数据位:1 1 0 0 0 1 0 1

    整个“1”的数目是4个。如果便用了偶校验,帧的奇偶校验位将是0,便得整个“1”的个数仍是4个。如果便用了奇校验,帧的奇偶校验位将是1,便得整个“1”的个数是5个。

    如果没有指定奇偶校验位,传输时就没有校验位,也不进行校验检测。代替一附加的停止位填充至要传输的字符帧中。

 

2、LRC检测 

   使用ASCII模式,消息包括了一基于LRC方法的错误检测域。LRC域检测了消息域中除开始的冒号及结束的回车换行号外的内容。

    LRC域是一个包含一个8位二进制值的字节。LRC值由传输设备来计算并放到消息帧中,接收设备在接收消息的过程中计算LRC,并将它和接收到消息中LRC域中的值比较,如果两值不等,说明有错误。

    LRC方法是将消息中的8Bit的字节连续累加,丢弃了进位。

    LRC简单函数如下:

复制代码
  ///C代码 
  static unsigned char LRC(auchMsg,usDataLen)  
  unsigned char *auchMsg ; /* 要进行计算的消息 */
  unsigned short usDataLen ; /* LRC 要处理的字节的数量*/ 
  { 
  unsigned char uchLRC = 0 ; /* LRC 字节初始化 */
  while (usDataLen--) /* 传送消息 */
  uchLRC += *auchMsg++ ; /* 累加*/
  return ((unsigned char)(-((char_uchLRC))) ;
  }
复制代码

3、CRC检测

     使用RTU模式,消息包括了一基于CRC方法的错误检测域。CRC域检测了整个消息的内容。

     CRC域是两个字节,包含一16位的二进制值。它由传输设备计算后加入到消息中。接收设备重新计算收到消息的CRC,并与接收到的CRC域中的值比较,如果两值不同,则有误。

     CRC是先调入一值是全“1”的16位寄存器,然后调用一过程将消息中连续的8位字节各当前寄存器中的值进行处理。仅每个字符中的8Bit数据对CRC有效,起始位和停止位以及奇偶校验位均无效。

     CRC产生过程中,每个8位字符都单独和寄存器内容相或(OR),结果向最低有效位方向移动,最高有效位以0填充。LSB被提取出来检测,如果LSB为1,寄存器单独和预置的值或一下,如果LSB为0,则不进行。整个过程要重复8次。在最后一位(第8位)完成后,下一个8位字节又单独和寄存器的当前值相或。最终寄存器中的值,是消息中所有的字节都执行之后的CRC值。

    CRC添加到消息中时,低字节先加入,然后高字节。

  

MODBUS通讯协议及编程【二】

 

  ModBus通讯协议分为RTU协议和ASCII协议,我公司的多种仪表都采用ModBus RTU通讯协议,如:CH2000智能电力监测仪、CH2000M电力参数采集模块、巡检表、数显表、光柱数显表等。下面就ModBus RTU协议简要介绍如下:

 

一、通讯协议

(一)、通讯传送方式:
   通讯传送分为独立的信息头,和发送的编码数据。以下的通讯传送方式定义也与MODBUS RTU通讯规约相兼容:

 

编 码
8位二进制
起始位
1位
数据位
8位
奇偶校验位
1位(偶校验位)
停止位
1位
错误校检
CRC(冗余循环码)

初始结构 = ≥4字节的时间 
地址码 = 1 字节
功能码 = 1 字节
数据区 = N 字节
错误校检 = 16位CRC码 
结束结构 = ≥4字节的时间


  地址码:地址码为通讯传送的第一个字节。这个字节表明由用户设定地址码的从机将接收由主机发送来的信息。并且每个从机都有具有唯一的地址码,并且响应回送均以各自的地址码开始。主机发送的地址码表明将发送到的从机地址,而从机发送的地址码表明回送的从机地址。

  功能码:通讯传送的第二个字节。ModBus通讯规约定义功能号为1到127。本仪表只利用其中的一部分功能码。作为主机请求发送,通过功能码告诉从机执行什么动作。作为从机响应,从机发送的功能码与从主机发送来的功能码一样,并表明从机已响应主机进行操作。如果从机发送的功能码的最高位为1(比如功能码大与此同时127),则表明从机没有响应操作或发送出错。

  数据区:数据区是根据不同的功能码而不同。数据区可以是实际数值、设置点、主机发送给从机或从机发送给主机的地址。

   CRC码:二字节的错误检测码。

(二)、通讯规约:

   当通讯命令发送至仪器时,符合相应地址码的设备接通讯命令,并除去地址码,读取信息,如果没有出错,则执行相应的任务;然后把执行结果返送给发送者。返送的信息中包括地址码、执行动作的功能码、执行动作后结果的数据以及错误校验码。如果出错就不发送任何信息。

1.信息帧结构

 

地址码
功能码
数据区
错误校验码
8位
8位
N × 8位
16位

  地址码:地址码是信息帧的第一字节(8位),从0到255。这个字节表明由用户设置地址的从机将接收由主机发送来的信息。每个从机都必须有唯一的地址码,并且只有符合地址码的从机才能响应回送。当从机回送信息时,相当的地址码表明该信息来自于何处。

   功能码:主机发送的功能码告诉从机执行什么任务。表1-1列出的功能码都有具体的含义及操作。

 

代码
含义
操作
03读取数据读取当前寄存器内一个或多个二进制值
06重置单一寄存器把设置的二进制值写入单一寄存器

  数据区:数据区包含需要从机执行什么动作或由从机采集的返送信息。这些信息可以是数值、参考地址等等。例如,功能码告诉从机读取寄存器的值,则数据区必需包含要读取寄存器的起始地址及读取长度。对于不同的从机,地址和数据信息都不相同。

  错误校验码:主机或从机可用校验码进行判别接收信息是否出错。有时,由于电子噪声或其它一些干扰,信息在传输过程中会发生细微的变化,错误校验码保证了主机或从机对在传送过程中出错的信息不起作用。这样增加了系统的安全和效率。错误校验采用CRC-16校验方法。

注:信息帧的格式都基本相同:地址码、功能码、数据区和错误校验码。

2.错误校验

   冗余循环码(CRC)包含2个字节,即16位二进制。CRC码由发送设备计算,放置于发送信息的尾部。接收信息的设备再重新计算接收到信息的 CRC码,比较计算得到的CRC码是否与接收到的相符,如果两者不相符,则表明出错。

  CRC码的计算方法是,先预置16位寄存器全为1。再逐步把每8位数据信息进行处理。在进行CRC码计算时只用8位数据位,起始位及停止位,如有奇偶校验位的话也包括奇偶校验位,都不参与CRC码计算。

   在计算CRC码时,8位数据与寄存器的数据相异或,得到的结果向低位移一字节,用0填补最高位。再检查最低位,如果最低位为1,把寄存器的内容与预置数相异或,如果最低位为0,不进行异或运算。

   这个过程一直重复8次。第8次移位后,下一个8位再与现在寄存器的内容相相异或,这个过程与以上一样重复8次。当所有的数据信息处理完后,最后寄存器的内容即为CRC码值。CRC码中的数据发送、接收时低字节在前。

   计算CRC码的步骤为:

  • 预置16位寄存器为十六进制FFFF(即全为1)。称此寄存器为CRC寄存器;
  • 把第一个8位数据与16位CRC寄存器的低位相异或,把结果放于CRC寄存器;
  • 把寄存器的内容右移一位(朝低位),用0填补最高位,检查最低位;
  • 如果最低位为0:重复第3步(再次移位); 如果最低位为1:CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或;
  • 重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
  • 重复步骤2到步骤5,进行下一个8位数据的处理;
  • 最后得到的CRC寄存器即为CRC码。

3.功能码03,读取点和返回值:

  仪表采用Modbus RTU通讯规约,利用通讯命令,可以进行读取点(“保持寄存器”) 或返回值(“输入寄存器” )的操作。保持和输入寄存器都是16位(2字节)值,并且高位在前。这样用于仪表的读取点和返回值都是2字节。一次最多可读取寄存器数是60。由于一些可编程控制器不用功能码03,所以功能码03被用作读取点和返回值。从机响应的命令格式是从机地址、功能码、数据区及CRC码。数据区中的寄存器数据都是每两个字节高字节在前。

4.功能码06,单点保存

  主机利用这条命令把单点数据保存到仪表的存储器。从机也用这个功能码向主机返送信息。


二、编程举例

  下面是一个用VC编写的ModBus RTU通讯的例子

(一)、通讯口设置


DCB dcb;

 hCom=CreateFile("COM1",

 GENERIC_READ|GENERIC_WRITE,

0,

NULL,

OPEN_EXISTING,

0,

NULL);


if(hCom==INVALID_HANDLE_VALUE)

{

MessageBox("createfile error,error");

}


 BOOL error=SetupComm(hCom,1024,1024);


 if(!error)

  MessageBox("setupcomm error");

 

 error=GetCommState(hCom,&dcb);

 

 if(!error)

   MessageBox("getcommstate,error");


 dcb.BaudRate=2400;

 dcb.ByteSize=8;

 dcb.Parity=EVENPARITY;//NOPARITY;

 dcb.StopBits=ONESTOPBIT;

 error=SetCommState(hCom,&dcb);

(二)、CRC校验码计算

 

UINT crc


void calccrc(BYTE crcbuf)

{

  BYTE i;


  crc=crc ^ crcbuf;

  for(i=0;i<8;i++)

  {

    BYTE TT;

    TT=crc&1;

    crc=crc>>1;

    crc=crc&0x7fff;

  if (TT==1)

    crc=crc^0xa001;

    crc=crc&0xffff;

  }

(三)、数据发送


zxaddr=11;//读取地址为11的巡检表数据


zxnum=10;//读取十个通道的数据


writebuf2[0]=zxaddr;

writebuf2[1]=3;

writebuf2[2]=0;

writebuf2[3]=0;

writebuf2[4]=0;

writebuf2[5]=zxnum;

crc=0xffff;

calccrc(writebuf2[0]);

calccrc(writebuf2[1]);

calccrc(writebuf2[2]);

calccrc(writebuf2[3]);

calccrc(writebuf2[4]);

calccrc(writebuf2[5]);


writebuf2[6]=crc & 0xff;

writebuf2[7]=crc/0x100;

WriteFile(hCom,writebuf2,8,&comnum,NULL);

(四)、数据读取

1 ReadFile(hCom,writebuf,5+zxnum*2,&comnum,NULL);//读取zxnum个通道数据

可增加错误处理程序,如地址码错误、CRC码错误判断、通讯故障处理等。

(五)、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 ^ *puchMsgg++ ; /* 计算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

} ;



Modbus

简介

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。 [1]  Modbus比其他通信协议使用的更广泛的主要原因有: [2] 

  1. 公开发表并且无版权要求
  2. 易于部署和维护
  3. 对供应商来说,修改移动本地的比特或字节没有很多限制

Modbus允许多个 (大约240个) 设备连接在同一个网络上进行通信,举个例子,一个由测量温度和湿度的装置,并且将结果发送给计算机。在数据采集与监视控制系统(SCADA)中,Modbus通常用来连接监控计算机和远程终端控制系统(RTU)

协议版本

Modbus协议目前存在用于串口以太网以及其他支持互联网协议的网络的版本。
大多数Modbus设备通信通过串口EIA-485物理层进行。
对于串行连接,存在两个变种,它们在数值数据表示不同和协议细节上略有不同。Modbus RTU是一种紧凑的,采用二进制表示数据的方式,Modbus ASCII是一种人类可读的,冗长的表示方式。这两个变种都使用串行通信(serial communication)方式。RTU格式后续的命令/数据带有循环冗余校验的校验和,而ASCII格式采用纵向冗余校验的校验和。被配置为RTU变种的节点不会和设置为ASCII变种的节点通信,反之亦然。
对于通过TCP/IP(例如以太网)的连接,存在多个Modbus/TCP变种,这种方式不需要校验和计算。
对于所有的这三种通信协议在数据模型和功能调用上都是相同的,只有封装方式是不同的。

Modbus有一个扩展版本Modbus Plus(Modbus+或者MB+),不过此协议是Modicon专有的,和Modbus不同。它需要一个专门的协处理器来处理类似HDLC的高速令牌旋转。它使用1Mbit/s的双绞线,并且每个节点都有转换隔离装置,是一种采用转换/边缘触发而不是电压/水平触发的装置。连接Modbus Plus到计算机需要特别的接口,通常是支持ISA(SA85),PCI或者PMCIA总线的板卡。

通信和设备

Modbus协议是一个master/slave架构的协议。有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点。每一个slave设备都有一个唯一的地址。在串行和MB+网络中,只有被指定为主节点的节点可以启动一个命令(在以太网上,任何一个设备都能发送一个Modbus命令,但是通常也只有一个主节点设备启动指令)。
一个ModBus命令包含了打算执行的设备的Modbus地址。所有设备都会收到命令,但只有指定位置的设备会执行及回应指令(地址0例外,指定地址0的指令是广播指令,所有收到指令的设备都会运行,不过不回应指令)。所有的Modbus命令包含了检查码,以确定到达的命令没有被破坏。基本的ModBus命令能指令一个RTU改变它的寄存器的某个值,控制或者读取一个I/O端口,以及指挥设备回送一个或者多个其寄存器中的数据。
有许多modems和网关支持Modbus协议,因为Modbus协议很简单而且容易复制。它们当中一些为这个协议特别设计的。有使用有线、无线通信甚至短消息和GPRS的不同实现。不过设计者需要克服一些包括高延迟和时序的问题。

限制

  • Modbus是在1970年末为可编程逻辑控制器通信开发的,这些有限的数据类型在那个时代是可以被PLC理解的,大型二进制对象数据是不支持的。
  • 对节点而言,没有一个标准的方法找到数据对象的描述信息,举个例子,确定一个寄存器数据是否表示一个介于30-175度之间的温度。
  • 由于Modbus是一个主/从协议,没有办法要求设备“报告异常”(构建在以太网的TCP/IP协议之上,被称为open-mbus除外)- 主节点必须循环的询问每个节点设备,并查找数据中的变化。在带宽可能比较宝贵的应用中,这种方式在应用中消耗带宽和网络时间,例如在低速率的无线链路上。
  • Modbus在一个数据链路上只能处理247个地址,这种情况限制了可以连接到主控站点的设备数量(再一次指出以太网TCP/IP除外)
  • Modbus传输在远端通讯设备之间缓冲数据的方式进行,有对通信一定是连续的限制,避免了传输中的缓冲区漏洞的问题
  • Modbus协议自身提供针对未经授权的命令或截取数据没有安全性

































上一篇:物联网硬件公司

下一篇:智能串口HMI

Top