MODBUS RTU基础知识 第一章

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

一、MODBUS RTU是什么?

MODBUS RTU 是一种工业通信协议,广泛应用于工业自动化领域,用于连接各种电子设备;

为什么要用MODBUS RTU

  1. MODBUS协议简单、可靠、易于实现的特点
  2. 标准化的需求:
    在工业自动化领域,标准化的通信协议对于提高系统的互操作性、降低开发成本、加快项目进度具有重要意义。MODBUS RTU协议作为一种广泛采用的工业标准,为不同厂商的设备之间的互连互通提供了有力的支持。

二、MODBUS RTU 通信格式

整体格式

  1. 起始位:消息开始前的静默期(至少3.5个字符时间)。
  2. 地址域:1字节,表示目标设备的地址(0x01到0xFF)。
  3. 功能码:1字节,表示要执行的操作(例如0x03表示读取保持寄存器)。
  4. 数据域:可变长度,包含实际的数据。
  5. 校验域:2字节,使用CRC(循环冗余校验)。
  6. 停止位:消息结束后的静默期(至少3.5个字符时间)。
+-------------------+-------------------+-------------------+-------------------+-------------------+
|      起始位       |      地址域       |      功能码       |      数据域       |      校验域       |
| (至少3.5字符时间) | (1字节)           | (1字节)           | (可变长度)        | (2字节)           |
+-------------------+-------------------+-------------------+-------------------+-------------------+
|                   |                   |                   |                   |                   |
|                   |0x010xFF     |  例如0x03         |  例如0x0001, 0x0002 |  例如0xA0B1       |
|                   |                   |                   |                   |                   |
+-------------------+-------------------+-------------------+-------------------+-------------------+
常用几个指令如 MODBUS调试助手 exe 所示

在这里插入图片描述

详细格式

1.起始位:

在消息开始之前,必须有一个至少3.5个字符时间的静默期。这是为了确保接收方能够正确识别消息的开始。

2.地址域:

在MODBUS RTU协议中,地址域用于标识目标设备的地址。地址域占用1个字节,范围从0x00到0xFF。不同的地址范围有不同的用途,特别是对于单播(一对一)和广播(一对多)通信。
注意事项

  • 广播消息不接收响应:广播消息不会收到任何响应,因此不适用于需要应答的请求。
  • 设备地址唯一性:在同一个网络中,每个从设备的地址必须是唯一的,以避免地址冲突。
  • 地址0xFF:虽然0xFF在某些实现中可能被用作特殊用途,但根据MODBUS标准,它不是合法的设备地址
单播地址(Unicast Addresses)

范围:0x01 到 0xFE
用途:用于标识网络中的单个设备。每个设备都有一个唯一的地址,主设备通过这个地址与特定的从设备通信。
示例:
设备地址 0x01:表示地址为1的设备。
设备地址 0x0A:表示地址为10的设备。
设备地址 0xFE:表示地址为254的设备。
示例:单播请求
假设我们要从地址为0x01的设备读取保持寄存器0x0001到0x0002的值。

+-------------------+-------------------+-------------------+-------------------+-------------------+
|      起始位       |      地址域       |      功能码       |      数据域       |      校验域       |
| (至少3.5字符时间) | 0x01              | 0x03              | 0x00 0x01 0x00 0x02 | 0x1C 0x0F         |
+-------------------+-------------------+-------------------+-------------------+-------------------+
  • 地址域:0x01(目标设备地址)
  • 功能码:0x03(读保持寄存器)
  • 数据域:0x00 0x01(起始地址0x0001),0x00 0x02(读取2个寄存器)
  • 校验域:0x1C 0x0F(CRC校验值)
广播地址(Broadcast Address)

范围:0x00
用途:用于向网络中的所有设备发送广播消息。广播消息不会收到任何响应,因此不适用于需要应答的请求。
示例:
广播地址 0x00:表示向所有设备发送消息。
示例:广播请求
假设我们要向所有设备发送一个复位命令(假设功能码0x06用于复位)。

+-------------------+-------------------+-------------------+-------------------+-------------------+
|      起始位       |      地址域       |      功能码       |      数据域       |      校验域       |
| (至少3.5字符时间) | 0x00              | 0x06              | 0x00 0x00 0x00 0x00 | 0xXX 0XX          |
+-------------------+-------------------+-------------------+-------------------+-------------------+
  • 地址域:0x00(广播地址)
  • 功能码:0x06(假设用于复位)
  • 数据域:0x00 0x00(寄存器地址),0x00 0x00(寄存器值)
  • 校验域:0xXX 0XX(CRC校验值)

3.功能码:

常用的功能码
  1. 0x01 - 读线圈状态 (Read Coils)
    描述: 从设备读取一组线圈(离散输出)的状态。
    数据域:起始地址(2字节)、线圈数量(2字节)。
    响应:线圈状态(1位/线圈,按字节对齐)。
  2. 0x02 - 读离散输入状态 (Read Discrete Inputs)
    描述:从设备读取一组离散输入的状态。
    数据域:起始地址(2字节)、输入数量(2字节)。
    响应:输入状态(1位/输入,按字节对齐)。
  3. 0x03 - 读保持寄存器 (Read Holding Registers)
    描述:从设备读取一组保持寄存器的值。
    数据域:起始地址(2字节)、寄存器数量(2字节)。
    响应:寄存器值(2字节/寄存器)。
  4. 0x04 - 读输入寄存器 (Read Input Registers)
    描述:从设备读取一组输入寄存器的值。
    数据域:起始地址(2字节)、寄存器数量(2字节)。
    响应:寄存器值(2字节/寄存器)。
  5. 0x05 - 写单个线圈 (Write Single Coil)
    描述:向设备写入单个线圈的状态。
    数据域:线圈地址(2字节)、线圈状态(2字节,0x0000表示OFF,0xFF00表示ON)。
    响应:线圈地址(2字节)、线圈状态(2字节)。
  6. 0x06 - 写单个保持寄存器 (Write Single Register)
    描述:向设备写入单个保持寄存器的值。
    数据域:寄存器地址(2字节)、寄存器值(2字节)。
    响应:寄存器地址(2字节)、寄存器值(2字节)。
  7. 0x0F - 写多个线圈 (Write Multiple Coils)
    描述:向设备写入多个线圈的状态。
    数据域:起始地址(2字节)、线圈数量(2字节)、字节数(1字节)、线圈状态(N字节,1位/线圈)。
    响应:起始地址(2字节)、线圈数量(2字节)。
  8. 0x10 - 写多个保持寄存器 (Write Multiple Registers)
    描述:向设备写入多个保持寄存器的值。
    数据域:起始地址(2字节)、寄存器数量(2字节)、字节数(1字节)、寄存器值(N字节,2字节/寄存器)。
    响应:起始地址(2字节)、寄存器数量(2字节)。
其他功能码

除了上述常用的功能码,MODBUS还定义了一些其他功能码,用于更高级的通信和诊断:
9. 0x11 - 报告从设备标识 (Report Slave ID)
描述:请求从设备报告其标识信息。
数据域:无。
响应:设备标识信息。
10. 0x16 - 遮蔽写寄存器 (Mask Write Register)
描述:对指定的寄存器进行位操作。
数据域:寄存器地址(2字节)、AND屏蔽码(2字节)、OR屏蔽码(2字节)。
响应:寄存器地址(2字节)、AND屏蔽码(2字节)、OR屏蔽码(2字节)。
11. 0x17 - 读写多个寄存器 (Read/Write Multiple Registers)
描述:同时读取和写入多个寄存器。
数据域:读起始地址(2字节)、读寄存器数量(2字节)、写起始地址(2字节)、写寄存器数量(2字节)、字节数(1字节)、写寄存器值(N字节)。
响应:读寄存器值(2字节/寄存器)。

错误码

如果从设备无法执行请求的操作,它会返回一个异常响应,其中包含一个错误码。常见的错误码包括:

0x01 - 非法功能码 (Illegal Function)
0x02 - 非法数据地址 (Illegal Data Address)
0x03 - 非法数据值 (Illegal Data Value)
0x04 - 从设备故障 (Slave Device Failure)
0x05 - 从设备忙 (Acknowledge)
0x06 - 从设备内存区满 (Slave Device Busy)
0x07 - 负载设备故障 (Negative Acknowledge)
0x08 - 存储区检验失败 (Memory Parity Error)
0x0A - 闸道路径不可用 (Gateway Path Unavailable)
0x0B - 闸道目标设备未响应 (Gateway Target Device Failed to Respond)

4.数据域:

可变长度,包含实际的数据。数据的具体格式和长度取决于功能码。例如,读保持寄存器的功能码0x03可能包含起始地址和寄存器数量。

5.校验域:

  1. 提取数据部分:从接收到的报文中提取地址域、功能码和数据域,形成一个字节数组。
  2. 计算CRC:使用相同的CRC-16算法计算该字节数组的校验值。
  3. 比较校验值:将计算得到的CRC校验值与报文中的校验域进行比较。如果两者一致,则认为数据传输无误;否则,认为数据传输有误。
计算步骤
1.提取数据部分

假设接收到的报文如下:

+-------------------+-------------------+-------------------+-------------------+-------------------+
|      起始位       |      地址域       |      功能码       |      数据域       |      校验域       |
| (至少3.5字符时间) | 0x01              | 0x03              | 0x00 0x01 0x00 0x02 | 0x0F 0x1C         |
+-------------------+-------------------+-------------------+-------------------+-------------------+

提取的数据部分为:

uint8_t received_data[] = {0x01, 0x03, 0x00, 0x01, 0x00, 0x02};
2.计算CRC

使用CRC-16算法计算提取的数据部分的校验值。

#include <stdint.h>

uint16_t modbus_crc16(const uint8_t *data, size_t length)
{
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < length; ++i)
    {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; ++j)
        {
            if (crc & 0x0001)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    return crc;
}
3. 比较校验值
void VerifyModbusCRC( uint8_t *received_data, uint8_t length)
{
    // 计算CRC
    uint16_t calculated_crc = modbus_crc16(received_data, length - 2);

    // 提取报文中的CRC
    uint16_t received_crc = (received_data[length - 2] << 8) | received_data[length - 1];

    // 比较CRC
    if (calculated_crc == received_crc)
    {
        // CRC校验通过
        printf("CRC check passed.\n");
    }
    else
    {
        // CRC校验失败
        printf("CRC check failed. Calculated: 0x%04X, Received: 0x%04X\n", calculated_crc, received_crc);
    }
}

注意事项
校验域的顺序:MODBUS RTU协议规定CRC校验值的低字节在前,高字节在后。
CRC初始值:MODBUS RTU使用的CRC-16算法的初始值为0xFFFF。
多项式:MODBUS RTU使用的CRC-16多项式为0xA001。
校验算法

6.停止位:

在消息结束之后,必须有一个至少3.5个字符时间的静默期。这是为了确保接收方能够正确识别消息的结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值