Modbus-RTU帧结构与功能码解析

Modbus-RTU 是一种基于串行通信(如 RS-232、RS-485)的 Modbus 协议变体,采用二进制编码,具有高数据密度和通信效率,广泛应用于工业自动化领域。

核心原理与帧结构Modbus-RTU 采用主从(Master-Slave)架构,通信由主设备发起,从设备响应。一个完整的数据帧由以下部分组成:

组成部分 长度(字节) 说明
从站地址 1 标识目标从设备的地址(1-247)。
功能码 1 定义请求的操作类型(如读、写)。
数据域 N 包含请求或响应的具体数据,长度可变。
CRC校验 2 循环冗余校验,用于检测传输错误。

帧与帧之间需保持至少 3.5个字符 的静默时间作为间隔。

常用功能码下表列出了部分核心功能码:

功能码(十进制) 名称 作用对象
01 (0x01) 读线圈状态 离散量输出(线圈)
02 (0x02) 读离散输入状态 离散量输入
03 (0x03) 读保持寄存器 保持寄存器
04 (0x04) 读输入寄存器 输入寄存器
05 (0x05) 写单个线圈 单个线圈
06 (0x06) 写单个寄存器 单个保持寄存器
15 (0x0F) 写多个线圈 多个线圈
16 (0x10) 写多个寄存器 多个保持寄存器

CRC-16校验计算

Modbus-RTU 使用 CRC-16(多项式为 0x8005,初始值为 0xFFFF)进行校验。以下是 C 语言实现示例:

c 复制代码
/**
 * @brief 计算 Modbus RTU帧的 CRC16 校验值
 * @param pData 指向待校验数据缓冲区的指针
 * @param length 数据长度(字节数)
 * @return计算得到的 CRC16 校验值(16位无符号整数)
 */
uint16_t Modbus_CRC16(uint8_t *pData, uint16_t length) {
    uint16_t crc = 0xFFFF; // CRC初始值 uint16_t i, j;

    for (i = 0; i < length; i++) {
        crc ^= pData[i]; // 与数据字节进行异或
        for (j = 0; j < 8; j++) {
            if (crc & 0x0001) { // 检查最低位是否为1
                crc >>= 1; // 右移一位
                crc ^= 0xA001; // 与多项式 0xA001 (0x8005 的位反转) 异或 } else {
                crc >>= 1; // 右移一位 }
        }
    }
    return crc;
}

通信示例:读取保持寄存器

假设主设备(地址 0x01)请求读取从设备地址为 0x01 的 2 个保持寄存器,起始地址为 0x0000。

1. 主设备请求帧:

复制代码
[从站地址] [功能码] [起始地址高] [起始地址低] [寄存器数量高] [寄存器数量低] [CRC低] [CRC高]
   0x01      0x03       0x00         0x00         0x00         0x02        0xC4      0x0B

*数据域:起始地址 0x0000,寄存器数量 0x0002

  • CRC 计算:对 01 03 00 00 00 02 计算得到 0x0BC4,低字节在前,故为 C4 0B

2. 从设备正常响应帧:

复制代码
[从站地址] [功能码] [字节计数] [寄存器1值高] [寄存器1值低] [寄存器2值高] [寄存器2值低] [CRC低] [CRC高]
   0x01      0x03      0x04        0x00          0x0A0x00          0x02        0xF8      0x48
  • 数据域:字节计数 0x04(4字节数据),两个寄存器的值分别为 0x000A(十进制10)和 0x0002(十进制2)。

代码实现示例(C# 使用 NModbus库)

以下 C# 代码演示了如何使用 NModbus 库通过串口实现 Modbus RTU 主站读取保持寄存器:

csharp 复制代码
using Modbus.Device;
using System.IO.Ports;

// 1. 配置串口
SerialPort serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
serialPort.Open();

// 2. 创建 Modbus RTU 主站实例
IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(serialPort);

// 3. 读取保持寄存器
byte slaveId = 1; // 从站地址
ushort startAddress = 0; // 起始寄存器地址
ushort numRegisters = 2; // 读取的寄存器数量
ushort[] registers = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);

// 4. 输出结果
Console.WriteLine($"Register 0: {registers[0]}");
Console.WriteLine($"Register 1: {registers[1]}");

// 5. 关闭连接
serialPort.Close();

调试与工具

  • 模拟器:可使用 Modbus Slave/Modbus Poll、ModScan32 等软件模拟从设备或主设备进行协议测试。
  • 接线:RS485 网络通常采用双绞线,需正确连接 A(+)、B(-) 信号线,并在总线两端安装终端电阻(通常为 120Ω)以消除信号反射。

参考来源

相关推荐
电子工程师成长日记-C512 小时前
51单片机录音笔(带闹钟)
单片机·嵌入式硬件·51单片机
传感器与混合集成电路3 小时前
电流频率转换模块选型要考虑哪些参数?量程匹配、精度等级与封装形式的综合决策
单片机·嵌入式硬件
JNX_SEMI4 小时前
AT2659 L1频段多模卫星导航低噪声放大器技术解析
前端·单片机·嵌入式硬件·物联网·硬件工程
KaifuZeng5 小时前
通信与接口协议面试四、SPI
单片机·嵌入式硬件·通信与接口协议
zlinear数据采集卡5 小时前
从协议解析到波形实时显示:硬核拆解ZLinear采集卡上位机软件的开发架构
arm开发·单片机·嵌入式硬件·fpga开发·架构·开源
GuHenryCheng6 小时前
【ESP32】ESP-IDF开发环境搭建(cursor)
git·stm32·单片机·学习
xyz_CDragon7 小时前
OpenAI发布首款自研芯片Jalapeño:9个月流片,AI设计芯片的时代来了
人工智能·单片机·深度学习·神经网络·芯片设计
wuyk5559 小时前
25. 函数指针表:用查表替代 switch-case,打造高效可维护的嵌入式状态机
c语言·stm32·单片机·mcu·51单片机
灯厂码农9 小时前
STM32三大通信协议详解——UART、I2C、SPI
stm32·单片机·嵌入式硬件