Modbus 协议常见误区(技术文档)

来源:  星创易联 发布时间: 2022-04-01 14:16:38
导读 如果您正在编写自己的 Modbus 通信软件,这篇文章应该很有用。如果您使用现有的 Modbus 软件或设备,这些问题通常不会导致问题。


概述

如果您正在编写自己的 Modbus 通信软件,这篇文章应该很有用。如果您使用现有的 Modbus 软件或设备,这些问题通常不会导致问题。


关于寄存器地址的混淆

在 Modbus.org 标准文档中,保持寄存器地址被赋予前缀“4”以区别于其他寄存器类型。例如,在他们的文档中,地址 1001 的保持寄存器被称为“41001”。然而,前导的“4”并不是地址的一部分,大多数程序都省去了这个额外的前缀,尤其是当程序上下文清楚地表明它指的是一个保持寄存器时。此外,Modbus.org 标准文档使用“从一”编号来引用寄存器地址。但是,在 Modbus 命令消息中实际发送的地址是“从零开始的”。例如,要读取寄存器 1001,命令消息中实际发送的地址是 1000。


1646898072715777.png



关于 Little-Endian 与 Big-Endian 字序的混淆

尽管 Modbus.org 标准文档为实现 Modbus 协议提供了一些指导,但它们并未解决 16 位寄存器级别之外的字序问题。对于 16 位寄存器值,最高有效字节总是在最低有效字节之前(换句话说,寄存器值以 Big-Endian 字节顺序传输)。这是因为在 1970 年代后期创建 Modbus 标准时,大多数处理器使用 Big-Endian 内存架构(其中多字节值的最重要部分存储在较低的内存地址中)。然而,当 1980 年代后来需要使用 Modbus 协议传输 32 位(即 4 字节)值时,Little-Endian Intel 处理器主导了 PC 市场,因此大多数供应商选择将最低有效字映射到寄存器对。


大于 16 位的值缺乏标准化导致 Modbus 实施者必须任意选择寄存器对的哪个地址包含 32 位值的最高有效字,例如 IEEE-754 单精度浮点数和有符号或无符号 32 位整数。大多数与 Modbus 从站通信的程序都可以配置为任一寄存器字顺序。


由于当今最常见的默认字序是 Little-Endian,这是 WattNode ®仪表中使用的字序。例如,十进制 16909060(或十六进制 0x01020304)的 32 位整数值映射到两个保持寄存器 1001 和 1002,其中寄存器 1001 包含最低有效字(0x0304),寄存器 1002 包含最高有效字(0x0102) .


通过分别读取寄存器 1001 和 1002 或寄存器 1201 和 1202, WattNode 仪表的EnergySum寄存器可用作 32 位浮点数或带符号的 32 位整数。对于有符号整数,您必须允许负值(2 的补码)。在 Little-Endian 机器上进行有符号整数转换的 C 语言代码示例如下:


1

Byte Result[4];      // Each Byte is an unsigned char   Byte ReplyBuf[256];  // Reply buffer. First 3 bytes are address, function                         //  code and byte count. 4th byte is start of Energy read   Int32 IntEnergy;     // Signed 32-bit integer energy sum for all 3 phases   float FltEnergy;     // Float energy sum for all 3 phases       // 5th byte from meter (MS byte of Big-endian 16-bit register which is LS word of Little-endian 32-bit value)   Result[0] = ReplyBuf[4];         // 4th byte from meter (LS byte of Big-endian 16-bit register which is LS word of Little-endian 32-bit value)   Result[1] = ReplyBuf[3];         // 7th byte from meter (MS byte of Big-endian 16-bit register which is MS word of Little-endian 32-bit value)   Result[2] = ReplyBuf[6];         // 6th byte from meter (LS byte of Big-endian 16-bit register which is MS word of Little-endian 32-bit value)   Result[3] = ReplyBuf[5];         // Cast 4-byte buffer to a signed 32-bit integer (EnergySum read from WattNode registers 1201 and 1202)   IntEnergy = *((Int32 *)Result);

从寄存器 1001 和 1002 读取浮点 EnergySum 时,唯一的区别在于转换的完成方式:



1646708565620039.png



1

// Cast 4-byte buffer to a float (EnergySum read from registers 1001 and 1002)     FltEnergy = *((float *)Result);

在上面的示例中,变量的大小和类型由以下 C 语言“typedef”语句定义,这些语句通常包含在 C 头文件(也称为“包含文件”)中:


1

typedef unsigned char Byte;   typedef signed long Int32;

这避免了由于各种 C 编译器的默认行为不一致而可能出现的细微类型转换问题(例如,一些编译器将 char 视为带符号的 8 位值,而其他编译器将其视为无符号值)。通过明确地告诉编译器变量的大小和类型(有符号与无符号)是什么,typedef 语法避免了对默认编译器行为的依赖以及由此可能导致的潜在类型转换错误。


上一篇:   Modbus TCP-RTU

文章对应产品

SR500工业路由器(双口)
SR510工业路由器(电力)
SR700路由器(单模双卡)
SR700-D路由器(双模)

产品对应解决方案

在线咨询
产品PK
样机申请
选购攻略

微信扫一扫

微信联系
返回顶部