资讯动态

Modbus 协议分析

发布时间:2026/8/15 16:34:34 来源:尧图企业网站定制
目录一、前言二、Modbus RTU 帧格式三、四种寄存器模型四、常用功能码报文拆解五、IDLE 中断与 Modbus 帧边界的天然契合六、从 OOP 视角看 Modbus 后端七、ModbusPoll 工具验证八、常见坑九、结尾一、前言大家好这里是Hello_Embed。上一篇我们把 UART 封装成了统一的UART_Device接口——Init、Send、RecvByte三个方法底层换串口上层。本篇进入项目核心协议Modbus RTU。它是整个工业互联设备管理系统中PC 上位机和 STM32H5 中控之间、中控和各路传感器之间的通用通信语言。二、Modbus RTU 帧格式| 地址(1B) | 功能码(1B) | 数据(NB) | CRC16(2B) | |----------|-----------|---------|-----------|地址1-247从站地址0广播所有从站都执行不回应功能码告诉从站读还是写、读写哪种寄存器数据长度由功能码决定可能包含寄存器地址、数量、写入值CRC16校验整帧完整性出错则丢弃帧与帧之间用3.5 字符时间的静默IDLE分隔。在 115200 波特率下1 字符 ≈ 87μs3.5 字符 ≈ 305μs。三、四种寄存器模型Modbus 从站内部维护四张表每种表对应不同的功能码类型前缀功能码(读/写)大小用途举例线圈 Coil0x01 / 05,151 bit继电器输出、LED 开关离散输入 DI1x02 / 无写1 bit按键、限位开关保持寄存器 HR4x03 / 06,1616 bit参数配置、设定值输入寄存器 IR3x04 / 无写16 bit温度、湿度、光照读数编号从 1 开始但协议地址从 0 开始。例如保持寄存器 1→ 协议地址0x0000。四、常用功能码报文拆解4.1 读保持寄存器 (03)请求从站 1读寄存器地址 0读 1 个01 03 00 00 00 01 84 0A │ │ └───┘ └───┘ └───┘ │ │ │ │ │ │ │ │ │ CRC16 │ │ │ 读取数量 1 个寄存器 │ │ 起始地址 0x0000 │ 功能码 03 读保持寄存器 从站地址 1响应01 03 02 00 05 38 47 │ │ │ └───┘ └───┘ │ │ │ │ │ │ │ │ │ CRC16 │ │ │ 寄存器值 0x0005 │ │ 数据字节数 2 │ 功能码 从站地址4.2 写单个保持寄存器 (06)请求写地址 1 的寄存器值为 1001 06 00 01 00 0A 19 CD │ │ └───┘ └───┘ └───┘ │ │ │ │ │ │ │ │ │ CRC16 │ │ │ 写入值 0x000A (10) │ │ 寄存器地址 0x0001 │ 功能码 06 写单个寄存器 从站地址 1响应原样回传确认写入成功。4.3 读离散输入 (02)请求读从站 1 的离散输入起始地址 0读 3 个01 02 00 00 00 03 38 0B响应返回 1 字节低 3 位对应 3 个离散输入的状态。五、IDLE 中断与 Modbus 帧边界的天然契合DMAIDLE 接收模式是专门为 Modbus RTU 量身定制的Modbus 帧 1 (N1字节) → 静默 305μs → Modbus 帧 2 (N2字节) → 静默 305μs → ... HAL_UARTEx_ReceiveToIdle_DMA 的行为: 收到帧 1 最后一个字节 → 1字符时间无数据 → IDLE 中断 → RxEventCallback(SizeN1) ← 完美Size 就是帧长度 → 入队 → 重启 DMA 收到帧 2 ...不需要自己拼帧——IDLE 中断天然告诉你了每帧的边界。上层只需从 Queue 取出 Size 个字节就是一帧完整报文。六、从 OOP 视角看 Modbus 后端libmodbus 通过modbus_new_rtu()接收设备名modbus_t*ctxmodbus_new_rtu(uart4,115200,N,8,1);内部流程modbus_new_rtu(uart4) → GetUARTDevice(uart4) // 拿到 OOP 设备指针 → ctx-backend.send pDev-send // 绑定 Send → ctx-backend.recv pDev-RecvByte // 绑定 RecvByte → ctx-backend.connect ... // 调用 pDev-Init这就是 OOP 封装的终极意义——libmodbus 完全不关心底层是 UART2、UART4 还是 USB CDC。协议栈只管send()和RecvByte()物理层由设备表管理。// 用板载 UART4接到 RS-485modbus_t*ch2modbus_new_rtu(uart4,115200,N,8,1);// 用 USB CDC虚拟串口接到 PC 上位机modbus_t*usbmodbus_new_rtu(usb,115200,N,8,1);// 上层 modbus_reply / modbus_receive 代码完全一样七、ModbusPoll 工具验证PC 端工具路径Tools/Modbus/ModbusPoll 主站 ModbusSlave 从站模拟器调试流程用 ModbusSlave 模拟从站设置地址、寄存器初始值用 ModbusPoll 发命令观察请求帧和响应帧的原始报文STM32 上先用 ModbusSlave 软件验证 PC 端主站逻辑正确再替换为 STM32 从站代码写好后验证链路PC ModbusPoll ←─USB CDC──→ STM32H5 ←─UART4(RS-485)──→ STM32F030传感器 主站 中控 从站八、常见坑8.1 CRC 计算CRC 是整帧计算地址功能码数据不包含 CRC 字段本身。8.2 寄存器地址偏移保持寄存器 1在协议里地址是0x0000不是0x0001。Modbus 编号从 1 开始协议地址从 0 开始。8.3 帧间间隔不够如果连续发两帧 Modbus 报文间隔小于 3.5 字符 305μs 115200从机会把两帧当成一帧CRC 校验失败。发送时必须保证至少 305μs 的静默。8.4 broadcast 无响应地址 0 是广播地址所有从站执行命令但不回应。不要等广播的响应。九、结尾本篇把 Modbus RTU 的核心概念全串起来了帧格式地址功能码数据CRC四种寄存器模型Coil/DI/HR/IRIDLE 中断天然适合 Modbus 帧边界检测UART_Device封装如何让 libmodbus 做到与硬件无关学习路径回顾Note 7-8: UART 三种方式查询/中断/DMA Note 9: SPI DMA Note 10: DMAIDLE Note 11: RTOS 信号量 Note 12: UART_Device OOP 封装 Note 13: Modbus 协议分析 ← 本篇下一篇预告libmodbus 源码走读——核心数据结构、发送/接收/回复的全景分析以及移植到 STM32H5 的关键步骤。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价