ModbusRTU通信报文分析—功能码03读取保持型寄存器学习笔记

写给初学者的话
你是不是在调试Modbus设备时,看到一串十六进制数据(比如 03 03 00 05 00 02 D5 E8)就一头雾水?是不是搞不清“功能码03”、“起始地址”、“字节计数”这些词是什么意思?别担心!这篇笔记会从最基础的概念讲起,用生活化的比喻、清晰的表格和详细的步骤,带你彻底搞懂Modbus RTU协议中功能码03——读取保持型寄存器的完整过程。记住,功能码03就像一个“数字值查询器”,专门用来查看设备里存储的温度、压力、速度等具体数值。这篇笔记内容超详细,字数很多,但每一句都是为你量身打造,慢慢看,一定能学会!


1️⃣ 核心思想:功能码03 = 查询数字值

在前面的笔记中,我们已经学习了:

  • 功能码01:读取输出线圈 → 查询开关状态(开/关)
  • 功能码02:读取输入线圈 → 查询传感器状态(触发/未触发)

现在,我们进入更高级的数据类型——寄存器 (Register)

🔹 什么是“保持型寄存器”?

  • 保持型寄存器 (Holding Register):是设备内部用于存储“数字值”的内存区域。
  • 它可以存储16位整数(范围:0 ~ 65535),也可以存储有符号整数(范围:-32768 ~ 32767),甚至可以组合成32位浮点数
  • 在Modbus协议中,它对应的是 4区(输出寄存器区),但功能码03是用来“读取”它的,所以也叫“保持型寄存器”。

一句话总结
功能码03 = 我要查一个或多个数字值,比如温度、压力、速度、计数值等。


2️⃣ 功能码03的发送报文结构

图中给出了发送报文的结构:

✅ 发送报文 = 从站地址 + 03 + 起始寄存器地址 + 寄存器数量 + CRC16校验

我们来逐一分解每一个部分。

🔹 第一部分:从站地址(Slave Address)

  • 这是报文的第一字节,用来指定这封“电报”是发给哪个设备的。
  • 取值范围:1 ~ 255(十进制)
  • 实际应用中,常用的地址是 1 ~ 247

一句话总结
从站地址 = 收件人地址,告诉设备“这封信是给你的”。

🔹 第二部分:功能码 03

  • 这是报文的第二字节,固定为 03
  • 它告诉从站:“我要读取保持型寄存器的值”。

一句话总结
功能码03 = 我要查数字值

🔹 第三部分:起始寄存器地址(Starting Register Address)

  • 这是报文的第三和第四字节,用来指定从哪个地址开始读取。
  • 它是一个16位无符号整数,由高字节+低字节组成。
  • 地址是从 0 开始计数的(相对地址),而不是从 1 开始。

🧩 举例说明

你想从地址 40006 开始读取(也就是第6个保持型寄存器):

  • 相对地址 = 5(因为地址从1开始,偏移量从0开始)
  • 所以,起始地址 = 00 05(高字节=00,低字节=05)

💡 记忆口诀
“绝对地址 - 1 = 相对地址”
比如:400010400054400065

🔹 第四部分:寄存器数量(Register Quantity)

  • 这是报文的第五和第六字节,用来指定你要读取多少个寄存器。
  • 它也是一个16位无符号整数,由高字节+低字节组成。
  • 最大可以读取 125 个寄存器(协议限制)。

🧩 举例说明

你想读取2个寄存器:

  • 寄存器数量 = 2(十进制)
  • 十六进制 = 00 02(高字节=00,低字节=02)

一句话总结
寄存器数量 = 我要查几个数字值

🔹 第五部分:CRC16校验(Cyclic Redundancy Check)

  • 这是报文的最后两个字节,用来保证报文的准确性和完整性。
  • 如果在传输过程中数据被干扰或篡改,校验码就会不匹配,接收方会丢弃这个报文。
  • CRC16校验码是根据前面所有字节(从站地址到寄存器数量)计算出来的。

一句话总结
CRC16校验 = 数据的“验证码”,确保报文在传输过程中没有出错。


3️⃣ 功能码03的接收报文结构

图中也给出了接收报文的结构:

✅ 接收报文 = 从站地址 + 03 + 字节计数 + 具体数据 + CRC16校验

我们来逐一分解每一个部分。

🔹 第一部分:从站地址(Slave Address)

  • 这是报文的第一字节,与发送报文中的从站地址相同。
  • 它告诉主站:“这是谁回复的”。

一句话总结
从站地址 = 回复人地址,告诉主站“这是我回复的”。

🔹 第二部分:功能码 03

  • 这是报文的第二字节,固定为 03
  • 它告诉主站:“我正在回复你关于保持型寄存器的查询”。

一句话总结
功能码03 = 我在回复数字值

🔹 第三部分:字节计数(Byte Count)

  • 这是报文的第三字节,用来指定后面“具体数据”部分有多少个字节。
  • 它是一个8位无符号整数,最大值是 255
  • 因为每个寄存器占2个字节(16位),所以字节计数 = 寄存器数量 * 2

🧩 举例说明

你请求读取2个寄存器:

  • 2个寄存器需要多少字节?
    • 每个寄存器占2个字节 → 2 * 2 = 4字节
  • 所以,字节计数 = 04

一句话总结
字节计数 = 寄存器数量 * 2

🔹 第四部分:具体数据(Data)

  • 这是报文的第四字节及以后,用来存放实际的寄存器值。
  • 每个寄存器占2个字节(16位),由高字节+低字节组成。
  • 数据的顺序是从左到右,第一个寄存器在前,第二个寄存器在后。

🧩 举例说明

假设你读取了2个寄存器,它们的值分别是:

寄存器编号 1 2
25000 100

那么,具体数据应该如何排列?

  • 第1个寄存器:25000 → 十六进制 61 A8(高字节=61,低字节=A8)
  • 第2个寄存器:100 → 十六进制 00 64(高字节=00,低字节=64)

因此,具体数据 = 61 A8 00 64

💡 重要提醒
在Modbus协议中,数据的字节序大端序 (Big-Endian)!也就是说,高字节在前,低字节在后

所以,正确的排列应该是:

  • 寄存器1 → 高字节 61 + 低字节 A8
  • 寄存器2 → 高字节 00 + 低字节 64

🔹 第五部分:CRC16校验

  • 这是报文的最后两个字节,用来保证报文的准确性和完整性。
  • CRC16校验码是根据前面所有字节(从站地址到具体数据)计算出来的。

一句话总结
CRC16校验 = 数据的“验证码”,确保报文在传输过程中没有出错。


4️⃣ 完整案例解析:读取3号从站从5开始的2个寄存器

图中给出了一个完整的案例:

发送报文03 03 00 05 00 02 D5 E8
接收报文03 03 04 00 64 00 C8 99 BA

下面我们来一步一步解析这个案例。

📌 步骤1:发送报文解析

发送报文 = 03 03 00 05 00 02 D5 E8

部分 字节位置 内容 说明
从站地址 第1字节 03 发送给设备3
功能码 第2字节 03 读取保持型寄存器
起始地址 第3-4字节 00 05 从地址5开始(即40006)
寄存器数量 第5-6字节 00 02 读取2个寄存器
CRC16校验 第7-8字节 D5 E8 CRC16校验码

发送报文含义
“设备3,请告诉我从地址5开始的2个保持型寄存器的值。”

📌 步骤2:接收报文解析

接收报文 = 03 03 04 00 64 00 C8 99 BA

部分 字节位置 内容 说明
从站地址 第1字节 03 设备3回复的
功能码 第2字节 03 回复的是保持型寄存器值
字节计数 第3字节 04 数据部分有4个字节
具体数据 第4-7字节 00 64 00 C8 寄存器值数据
CRC16校验 第8-9字节 99 BA CRC16校验码

接收报文含义
“设备3回复:从地址5开始的2个保持型寄存器的值是 00 64 00 C8。”

📌 步骤3:解析具体数据 00 64 00 C8

现在,我们要把 00 64 00 C8 转换成2个寄存器的值。

🧮 第一步:按寄存器拆分数据

  • 第1个寄存器:00 64 → 高字节=00,低字节=64
  • 第2个寄存器:00 C8 → 高字节=00,低字节=C8

🧮 第二步:将十六进制转换成十进制

  • 第1个寄存器:00 64 → 十进制 = 0 * 256 + 100 = 100
  • 第2个寄存器:00 C8 → 十进制 = 0 * 256 + 200 = 200

最终结果

寄存器编号 1 2
100 200

🎉 恭喜你!你已经成功解析了功能码03的接收报文!


5️⃣ 常见问题与避坑指南

❓ 问题1:为什么我发送的报文没有响应?

  • 可能原因
    1. 从站地址错误 → 检查设备地址是否正确。
    2. 功能码错误 → 检查功能码是否为 03
    3. 起始地址错误 → 检查地址是否从 0 开始。
    4. 寄存器数量错误 → 检查数量是否在 1 ~ 125 之间。
    5. 校验码错误 → 使用正确的CRC16算法计算校验码。
    6. 通信参数错误 → 检查波特率、数据位、停止位、校验位是否一致。

❓ 问题2:为什么接收报文中的字节计数是4,而我只读了2个寄存器?

  • 原因:因为每个寄存器占2个字节,2个寄存器就是4个字节。
  • 字节计数 = 寄存器数量 * 2 = 2 * 2 = 4

一句话总结
字节计数 = 寄存器数量 * 2

❓ 问题3:如何手动计算CRC16校验码?

  • 不要手动计算! 使用Modbus调试软件或编程库(如Python的pymodbus)自动生成。
  • 如果你一定要手动计算,可以使用在线CRC计算器,选择“Modbus RTU CRC16”。

❓ 问题4:寄存器值是负数怎么办?

  • Modbus寄存器默认是无符号整数(0 ~ 65535)。
  • 如果你需要表示负数,通常使用有符号整数(补码表示)。
  • 例如:65535(无符号) = -1(有符号)
  • 你需要在软件中设置“数据类型”为“有符号16位整数”。

一句话总结
寄存器值可以是有符号或无符号,取决于你的应用需求


6️⃣ 实战模拟:手把手教你构造和解析报文

假设你现在有一个任务:

“我要读取设备 01 的保持型寄存器,从地址 40001 开始,读取1个寄存器。”

📌 步骤1:构造发送报文

  1. 从站地址 = 01
  2. 功能码 = 03
  3. 起始地址 = 00 00(相对地址0)
  4. 寄存器数量 = 00 01(读取1个寄存器)
  5. CRC16校验 = 84 0A(由软件自动计算)

发送报文 = 01 03 00 00 00 01 84 0A

📌 步骤2:假设接收报文

假设设备回复:

接收报文 = 01 03 02 00 64 8C 4B

📌 步骤3:解析接收报文

  1. 从站地址 = 01 → 设备1回复的
  2. 功能码 = 03 → 回复的是保持型寄存器值
  3. 字节计数 = 02 → 数据部分有2个字节
  4. 具体数据 = 00 64 → 第1个寄存器值
  5. CRC16校验 = 8C 4B → 校验通过

📌 步骤4:解析具体数据 00 64

  • 00 64 → 高字节=00,低字节=64
  • 十进制 = 0 * 256 + 100 = 100

最终结果:第1个寄存器的值是 100


🎯 下一步学习建议

  1. 动手实践:使用Modbus调试软件(如Modbus Poll, QModBus),输入不同的地址、寄存器数量,观察返回结果。
  2. 学习其他功能码:功能码04(读取输入寄存器)、06(写入单个寄存器)、10(写入多个寄存器)等,它们的结构类似,只是操作对象不同。
  3. 阅读设备手册:任何Modbus设备都会在手册中列出支持的功能码、地址范围和通信参数,一定要学会看手册!
  4. 学习数据类型转换:了解如何将16位寄存器值转换成32位浮点数、有符号整数等。
posted @ 2025-11-04 16:07  枕月听风  阅读(156)  评论(0)    收藏  举报