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Ω)以消除信号反射。

参考来源

相关推荐
ACP广源盛139246256731 小时前
蚂蚁百灵 Ling‑3.0‑flash 开源 + 昇腾 0‑Day 原生适配@ACP#GSV9001E 在国产算力矩阵中的机会与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
Lzh编程小栈4 小时前
【嵌入式底层】SPI超透彻详解:通信原理、四线结构、四种模式、寄存器底层 + 面试全集
c语言·stm32·单片机·面试·职场和发展
水果里面有苹果4 小时前
28-DRV8825PWPR步进电机驱动器
单片机
Dr.kangder9 小时前
嵌入式面试总结(十九)——内存泄露
单片机·算法·面试·职场和发展·架构·硬件架构
萌新小白YXY11 小时前
SAE J1587/SAE J1708 协议笔记
笔记·单片机·嵌入式硬件
嘉子的秃头日记11 小时前
机械革命“自动修复”无法修复你的电脑
stm32·单片机·电脑
森旺电子11 小时前
电子负载使用介绍
单片机·嵌入式硬件
大爱编程♡11 小时前
STM32-SPI
stm32·单片机·嵌入式硬件
hsjiasb21 小时前
FreeRTOS学习(二十六)——动态内存管理heap_1到heap_5
stm32·单片机·学习·学习笔记·freertos
周洲08301 天前
STM32 GPIO 外部中断深度解析:边沿触发 / 电平触发、NVIC 优先级配置、中断嵌套实战
stm32·单片机·嵌入式硬件