3. 协议帧结构:Modbus RTU 报文格式详解

好,咱们今天来聊聊Modbus RTU的报文格式。说实话,这是整个Modbus协议里最核心的部分。你搞懂了报文结构,就等于拿到了串口通信的钥匙。我当年刚入行时,就是靠死磕这个格式,才把一堆乱七八糟的设备给调通的。

Modbus RTU的报文,说白了就是一串字节。它遵循一个固定的套路:地址码、功能码、数据区、CRC校验。这四个部分缺一不可。咱们一个一个来看。

3.1 地址码:谁在说话?

地址码占1个字节。取值范围是0到247。其中0是广播地址,所有从机都得响应。1到247是每个从机的唯一身份。

我遇到过一个小坑:有些设备出厂默认地址是1,结果现场两台设备都设成1,总线直接乱套。所以我的习惯是,项目一开始就规划好地址表,从1开始顺序分配,留几个备用地址。

地址码要点:
  • 主机发送的请求帧,地址码指向目标从机
  • 从机回复的响应帧,地址码就是自己的地址
  • 广播地址0,所有从机都要处理,但不需要回复

3.2 功能码:要干什么?

功能码也是1个字节。它告诉从机:你要执行什么操作。常用的就那么几个,我列个表给你看。

功能码 含义 操作对象
0x01 读取线圈状态 DO(数字量输出)
0x02 读取离散输入 DI(数字量输入)
0x03 读取保持寄存器 AO(模拟量输出/参数)
0x04 读取输入寄存器 AI(模拟量输入)
0x05 写单个线圈 单个DO
0x06 写单个寄存器 单个AO
0x0F 写多个线圈 多个DO
0x10 写多个寄存器 多个AO

你看,功能码的设计其实很清晰。读操作是0x01到0x04,写操作是0x05、0x06、0x0F、0x10。我建议你先把0x03和0x06这两个搞熟,因为90%的现场应用都离不开它们。

我的小技巧: 调试时如果从机没反应,先检查功能码对不对。我曾经花了一下午,结果发现是把0x03写成了0x02。嗯,这种低级错误,犯过一次就记住了。

3.3 数据区:具体参数

数据区是变长的。它跟在功能码后面,具体内容取决于功能码。比如读操作,数据区里放的是起始地址和读取数量。写操作,数据区里放的是地址、数据值,还有字节数。

举个例子,读取从机地址1的保持寄存器,从地址0开始读2个寄存器:

请求帧:01 03 00 00 00 02 C4 0B
地址码:01
功能码:03
起始地址高字节:00
起始地址低字节:00
寄存器数量高字节:00
寄存器数量低字节:02
CRC校验:C4 0B

你想想看,这个报文是不是很直观?地址1的从机,执行功能码03,从地址0开始读2个寄存器。数据区就是起始地址和数量。

响应帧呢?从机会这样回复:

响应帧:01 03 04 00 0A 00 14 7A 5E
地址码:01
功能码:03
字节数:04(表示后面有4个字节的数据)
数据1高字节:00
数据1低字节:0A(十进制10)
数据2高字节:00
数据2低字节:14(十进制20)
CRC校验:7A 5E

这里要注意,Modbus的数据是大端模式。高位字节在前,低位字节在后。我刚开始做项目时,因为大小端搞反,读出来的温度值直接翻了好几倍。后来养成了习惯,每次解析数据前先确认字节序。

3.4 CRC校验:数据可靠吗?

CRC校验占2个字节,放在报文末尾。它是对地址码、功能码、数据区这三个部分做的循环冗余校验。算法是固定的,叫CRC-16-Modbus。

说白了,CRC就是用来检查报文在传输过程中有没有被干扰。现场环境复杂,电机启停、变频器干扰,都可能让数据出错。没有CRC,你根本不知道收到的数据对不对。

避坑指南: 我曾经遇到一个项目,从机老是返回异常码。排查了半天,发现是主机计算的CRC算法有bug。CRC计算时,初始值必须是0xFFFF,计算完还要跟0x0000异或。很多现成的库函数默认参数不对,一定要确认。

CRC的计算过程,我简单说一下:

  1. 初始化CRC寄存器为0xFFFF
  2. 对每个字节,与CRC寄存器低8位异或
  3. 右移1位,如果移出的位是1,则与0xA001异或
  4. 重复8次,处理完一个字节
  5. 所有字节处理完后,CRC寄存器取反

你不需要手写这个算法,网上有很多现成的代码。但我建议你至少理解它的原理,这样出问题时能快速定位。

3.5 报文格式总结

咱们用一张图来总结一下Modbus RTU的报文结构。我画了个SVG图,把四个部分的关系展示清楚。

Modbus RTU 报文帧结构 地址码 1字节 功能码 1字节 数据区 N字节(变长) CRC校验 2字节 从机地址 0-247 操作指令 如0x03 参数/数据 变长 校验码 CRC-16 报文总长度 = 1(地址码)+ 1(功能码)+ N(数据区)+ 2(CRC) 最小4字节,最大256字节

你看这张图,报文从左到右依次排列。地址码告诉谁干活,功能码告诉干什么活,数据区是干活的具体参数,CRC保证活干得没出错。这个逻辑,你想想看,是不是很清晰?

最后说一句,实际调试时,我习惯用串口助手抓报文。先看地址码对不对,再看功能码,然后解析数据区,最后验证CRC。一步一步来,问题很快就能定位。

核心要点回顾:
  • 地址码:1字节,0为广播,1-247为从机地址
  • 功能码:1字节,定义操作类型
  • 数据区:变长,大端模式,内容取决于功能码
  • CRC校验:2字节,CRC-16-Modbus算法,初始值0xFFFF

嗯,报文格式就讲到这里。你把这个结构刻在脑子里,后面讲异常处理、多机通信时,你会觉得特别顺。