Auto commit.
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
---
|
||||
title: IEEE754标准的浮点数存储格式-转载
|
||||
author: TianZD
|
||||
top: true
|
||||
cover: true
|
||||
toc: true
|
||||
mathjax: false
|
||||
summary: IEEE754标准的浮点数存储格式-转载
|
||||
tags:
|
||||
- 浮点数
|
||||
- 数据格式
|
||||
categories:
|
||||
- 通信
|
||||
- 转载
|
||||
reprintPolicy: cc_by
|
||||
abbrlink: 8cc8b422
|
||||
date: 2022-04-29 13:38:14
|
||||
coverImg:
|
||||
img:
|
||||
password:
|
||||
---
|
||||
|
||||
[TOC]
|
||||
|
||||
|
||||
|
||||
# IEEE754标准的浮点数存储格式【转载】
|
||||
|
||||
转载自[查看原网页: www.cnblogs.com](https://www.cnblogs.com/MikeZhang/p/IEEE754FloatEncode20180117.html)
|
||||
|
||||
|
||||
|
||||
基本存储格式(从高到低) : Sign + Exponent + Fraction
|
||||
|
||||
Sign : 符号位
|
||||
|
||||
Exponent : 阶码
|
||||
|
||||
Fraction : 有效数字
|
||||
|
||||
[在线转换网址](http://www.speedfly.cn/tools/hexconvert/)
|
||||
|
||||
## 32位浮点数存储格式解析
|
||||
|
||||
Sign : 1 bit(第31个bit)
|
||||
|
||||
Exponent :8 bits (第 30 至 23 共 8 个bits)
|
||||
|
||||
Fraction :23 bits (第 22 至 0 共 23 个bits)
|
||||
|
||||
32位非0浮点数的真值为(python语法) :
|
||||
|
||||
(-1) \*\*Sign \* 2 \*\*(Exponent-127) \* (1 + Fraction)
|
||||
|
||||
示例如下:
|
||||
|
||||
a = 12.5
|
||||
|
||||
1、求解符号位
|
||||
|
||||
a大于0,则 Sign 为 0 ,用二进制表示为: 0
|
||||
|
||||
2、求解阶码
|
||||
|
||||
a表示为二进制为: 1100.0
|
||||
|
||||
小数点需要向左移动3位,则 Exponent 为 130 (127 + 3),用二进制表示为: 10000010
|
||||
|
||||
3、求解有效数字
|
||||
|
||||
有效数字需要去掉最高位隐含的1,则有效数字的整数部分为 : 100
|
||||
|
||||
将十进制的小数转换为二进制的小数的方法为将小数\*2,取整数部分,则小数部分为: 1
|
||||
|
||||
后面补0,则a的二进制可表示为: 01000001010010000000000000000000
|
||||
|
||||
即 : 0100 0001 0100 1000 0000 0000 0000 0000
|
||||
|
||||
用16进制表示 : 0x41480000
|
||||
|
||||
4、还原真值
|
||||
|
||||
```
|
||||
Sign = bin(0) = 0
|
||||
Exponent = bin(10000010) = 130
|
||||
Fraction = bin(0.1001) = 2 ** (-1) + 2 ** (-4) = 0.5625
|
||||
```
|
||||
|
||||
真值:
|
||||
|
||||
(-1) \*\*0 \* 2 \*\*(130\-127) \* (1 + 0.5625) = 12.5
|
||||
|
||||
32位浮点数二进制存储解析代码(c++):
|
||||
|
||||
[https://github.com/mike-zhang/cppExamples/blob/master/dataTypeOpt/IEEE754Relate/floatTest1.cpp](https://github.com/mike-zhang/cppExamples/blob/master/dataTypeOpt/IEEE754Relate/floatTest1.cpp)
|
||||
|
||||
运行效果:
|
||||
|
||||
```
|
||||
[root@localhost floatTest1]# ./floatToBin1
|
||||
sizeof(float) : 4
|
||||
sizeof(int) : 4
|
||||
a = 12.500000
|
||||
showFloat : 0x 41 48 00 00
|
||||
UFP : 0,82,480000
|
||||
b : 0x41480000
|
||||
showIEEE754 a = 12.500000
|
||||
showIEEE754 varTmp = 0x00c00000
|
||||
showIEEE754 c = 0x00400000
|
||||
showIEEE754 i = 19 , a1 = 1.000000 , showIEEE754 c = 00480000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 18 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 17 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 16 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 15 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 14 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 13 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 12 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 11 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 10 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 9 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 8 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 7 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 6 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 5 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 4 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 3 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 2 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 i = 1 , a1 = 0.000000 , showIEEE754 b = 0x41000000
|
||||
showIEEE754 : 0x41480000
|
||||
[root@localhost floatTest1]#
|
||||
```
|
||||
|
||||
## 64位浮点数存储格式解析
|
||||
|
||||
Sign : 1 bit(第31个bit)
|
||||
|
||||
Exponent :11 bits (第 62 至 52 共 11 个bits)
|
||||
|
||||
Fraction :52 bits (第 51 至 0 共 52 个bits)
|
||||
|
||||
64位非0浮点数的真值为(python语法) :
|
||||
|
||||
```(-1) **Sign * 2 **(Exponent-1023) * (1 + Fraction)```
|
||||
|
||||
示例如下:
|
||||
|
||||
a = 12.5
|
||||
|
||||
1、求解符号位
|
||||
|
||||
a大于0,则 Sign 为 0 ,用二进制表示为: 0
|
||||
|
||||
2、求解阶码
|
||||
|
||||
a表示为二进制为: 1100.0
|
||||
|
||||
小数点需要向左移动3位,则 Exponent 为 1026 (1023 + 3),用二进制表示为: 10000000010
|
||||
|
||||
3、求解有效数字
|
||||
|
||||
有效数字需要去掉最高位隐含的1,则有效数字的整数部分为 : 100
|
||||
|
||||
将十进制的小数转换为二进制的小数的方法为将小数\*2,取整数部分,则小数部分为: 1
|
||||
|
||||
后面补0,则a的二进制可表示为:
|
||||
|
||||
0100000000101001000000000000000000000000000000000000000000000000
|
||||
|
||||
即 : 0100 0000 0010 1001 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||
|
||||
用16进制表示 : 0x4029000000000000
|
||||
|
||||
4、还原真值
|
||||
|
||||
```
|
||||
Sign = bin(0) = 0
|
||||
Exponent \= bin(10000000010) = 1026
|
||||
Fraction \= bin(0.1001) = 2 \*\* (-1) + 2 \*\* (-4) = 0.5625
|
||||
```
|
||||
|
||||
真值:
|
||||
|
||||
```(-1) **0 * 2 **(1026-1023) * (1 + 0.5625) = 12.5```
|
||||
|
||||
64位浮点数二进制存储解析代码(c++):
|
||||
|
||||
[https://github.com/mike-zhang/cppExamples/blob/master/dataTypeOpt/IEEE754Relate/doubleTest1.cpp](https://github.com/mike-zhang/cppExamples/blob/master/dataTypeOpt/IEEE754Relate/doubleTest1.cpp)
|
||||
|
||||
运行效果:
|
||||
|
||||
```
|
||||
[root@localhost t1]# ./doubleToBin1
|
||||
sizeof(double) : 8
|
||||
sizeof(long) : 8
|
||||
a = 12.500000
|
||||
showDouble : 0x 40 29 00 00 00 00 00 00
|
||||
UFP : 0,402,0
|
||||
b : 0x0
|
||||
showIEEE754 a = 12.500000
|
||||
showIEEE754 logLen = 3
|
||||
showIEEE754 c = 4620693217682128896(0x4020000000000000)
|
||||
showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 varTmp = 0x8000000000000
|
||||
showIEEE754 c = 0x8000000000000
|
||||
showIEEE754 i = 48 , a1 = 1.000000 , showIEEE754 c = 9000000000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 47 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 46 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 45 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 44 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 43 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 42 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 41 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 40 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 39 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 38 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 37 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 36 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 35 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 34 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 33 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 32 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 31 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 30 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 29 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 28 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 27 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 26 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 25 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 24 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 23 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 22 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 21 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 20 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 19 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 18 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 17 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 16 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 15 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 14 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 13 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 12 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 11 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 10 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 9 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 8 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 7 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 6 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 5 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 4 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 3 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 2 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 i = 1 , a1 = 0.000000 , showIEEE754 b = 0x4020000000000000
|
||||
showIEEE754 : 0x4029000000000000
|
||||
[root@localhost t1]#
|
||||
```
|
||||
|
||||
|
||||
|
||||
好,就这些了,希望对你有帮助。
|
||||
|
||||
本文github地址:
|
||||
|
||||
[https://github.com/mike-zhang/mikeBlogEssays/blob/master/2018/20180117\_IEEE754标准的浮点数存储格式.rst-
|
||||
|
||||
](https://github.com/mike-zhang/mikeBlogEssays/blob/master/2018/20180117_IEEE754%E6%A0%87%E5%87%86%E7%9A%84%E6%B5%AE%E7%82%B9%E6%95%B0%E5%AD%98%E5%82%A8%E6%A0%BC%E5%BC%8F.rst)
|
||||
|
||||
欢迎补充
|
||||
|
||||
[查看原网页: www.cnblogs.com](https://www.cnblogs.com/MikeZhang/p/IEEE754FloatEncode20180117.html)
|
||||
@@ -0,0 +1,500 @@
|
||||
---
|
||||
title: ModbusTcp
|
||||
author: TianZD
|
||||
top: true
|
||||
cover: true
|
||||
toc: true
|
||||
mathjax: false
|
||||
summary: ModbusTcp通讯相关学习笔记,参考多篇博文,其中有些已经找不到原文链接
|
||||
tags:
|
||||
- modbus
|
||||
- 学习笔记
|
||||
categories:
|
||||
- Modbus
|
||||
- 转载
|
||||
reprintPolicy: cc_by
|
||||
abbrlink: c7022bb7
|
||||
date: 2022-04-29 13:49:25
|
||||
coverImg:
|
||||
img:
|
||||
password:
|
||||
---
|
||||
|
||||
\[toc\]
|
||||
|
||||
# ModBusTCP
|
||||
|
||||
## 术语
|
||||
|
||||
### 寄存器
|
||||
|
||||
> 寄存器的功能是存储[二进制代码](https://baike.baidu.com/item/%E4%BA%8C%E8%BF%9B%E5%88%B6%E4%BB%A3%E7%A0%81/4879654),它是由具有存储功能的[触发器](https://baike.baidu.com/item/%E8%A7%A6%E5%8F%91%E5%99%A8/193146)组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。
|
||||
|
||||
PLC中**输入寄存器**和**输出寄存器**均为16位,2个字节
|
||||
|
||||
00:一个字节:0
|
||||
|
||||
00 0A:两个字节:10
|
||||
|
||||
### 校验码
|
||||
|
||||
校验码是由前面的数据通过某种算法得出的,用以检验该组数据的正确性。代码作为数据在向计算机或其它设备进行输入时,容易产生输入错误,为了减少这种输入错误,编码专家发明了各种校验检错方法,并依据这些方法设置了校验码。
|
||||
|
||||
常用的校验有:累加和校验SUM、字节异或校验XOR、纵向冗余校验LRC、循环冗余校验CRC……
|
||||
|
||||
### 离散量输入
|
||||
|
||||
主要用来读取单个位的数据,如IO的状态
|
||||
|
||||
### 线圈
|
||||
|
||||
开关输出信号,主要用来写入单个位的数据,与离散量构成组成对位的操作;
|
||||
|
||||
## ModBus
|
||||
|
||||
### 概述
|
||||
|
||||
一种**应用层协议**
|
||||
|
||||
Modbus通信协议,是Modicon PLC所制定的资料交换通信接口标准,于1979年首先制定串行通信标准(含Modbus异步及Modbus Plus同步),于1997年制定网络通信标准(Modbus/TCP)。是属于OSI所定义的通信层的第七层应用层(Application Layer)。是为**Client/Server**或称为 **Master/Slave**型式的通信协议。由于Modbus的通信协议简单容易设计,结果广被许多控制设备或外围信号设备所采用,因此无形中成为自控业界的标准。Modbus异步的硬件架构简单,被使用的比率最高。Modbus Plus同步的协议可以提供高速的通信速度,适合主控制设备间大量资料交换。Modbus/TCP则是因应Ethernet网络的架构,近年来被大量使用的通信协议,也因为其速度及资料传送量远比Modbus Plus更快更大,所以已渐渐取代其功能。
|
||||
|
||||
Modbus通信协议基本上是遵循MasterandSlave的通信步骤,有一方扮演**Master角色采取主动询问方式**,送出QueryMessage给Slave方,然后由Slave方依据接到的QueryMessage内容准备ResponseMessage回传给Master。即使目前硬件通信已经可以达到双方互相主动通信的能力,但是于Modbus通信协议的规定,必须一方为Master,另一方为Slave不能互换角色。一般使用上,**监控系统(HMI)都为Maste**r,**PLC、电表、仪表等都为Slave**,HMI系统一直PollingSlave的各种relayandregister最新数值,然后做显示及各种逻辑计算及控制调整等处理。
|
||||
|
||||
Modbus大家庭,有三个兄弟:大哥(Modbus-RTU协议)、二哥(Modbus-ASCII协议)和ModbutTcp,都活跃在工业通信领域。大哥和二哥擅长串行通信,比如基于RS485或者RS232的通信,而ModbutTcp则擅长基于以太网的通信。由于底层所使用的结构不同,应用数据单元(Application Data Unit,ADU)有所不同。
|
||||
|
||||
### Modbus协议的数据模型
|
||||
|
||||
[深入理解Modbus协议的数据模型和地址模型](https://zhuanlan.zhihu.com/p/164885080)
|
||||
|
||||
数据模型是对可访问数据的一种抽象,Modbus协议的数据模型定义了四种可访问的数据,分别是:
|
||||
|
||||
- 离散量输入(Discrete Input);
|
||||
|
||||
- 线圈(Coils);
|
||||
|
||||
- 输入寄存器(Input registers);
|
||||
|
||||
- 保持寄存器(Holding registers);
|
||||
|
||||
其中,离散量输入和线圈只支持以**位(bit)的方式进行访问,输入寄存器和保持寄存器只支持以**字(WORD)**的方式进行访问;离散量输入和输入寄存器只支持以**只读的方式进行访问,而线圈和保持寄存器既可以读也可以写;
|
||||
|
||||
数据模型中成员的特点如下面的表格:
|
||||
|
||||

|
||||
|
||||
既然数据模型是一种抽象,在实际使用时必须将其映射到真实的物理存储区才能被访问。
|
||||
|
||||
Modbus协议允许设备将四种数据分别映射到**不同的存储区块**中,各个区块之间相互独立,使用不同的功能码可读取到不同的数值,如下图所示:
|
||||
|
||||

|
||||
|
||||
Modbus协议也允许设备将四种数据映射到同一存储区块中,这样通过不同的功能码读取数据可能会得到相同的数据(比如:输入寄存器和保持寄存器为同一物理区块),如下图所示:
|
||||
|
||||

|
||||
|
||||
数据模型中的每一种数据都最多允许有65536个元素(编号1~65536),元素的地址编号从0开始,因此地址的范围为:0~65535;
|
||||
|
||||
*需要说明的是:65536只是协议允许的最大元素范围,但并不要求全部实现。Modbus协议允许设备根据自己的实际情况实现部分元素,甚至不要求实现模型中全部四种数据;*
|
||||
|
||||
### Modbus协议的地址模型
|
||||
|
||||
为了简化数据模型与设备存储区的对应关系,引入了一种地址模型。该模型通过编号的方式对不同类型数据进行区分,各数据的地址编号请看下面的表格:
|
||||
|
||||

|
||||
|
||||
Modbus地址模型的编号从1开始。
|
||||
|
||||
由于每一种数据都最大支持65536个元素,因此理论上,
|
||||
|
||||
对于线圈型数据来说,其地址范围为:000001\~065536;
|
||||
|
||||
类似的,
|
||||
|
||||
离散量输入,其地址范围为:100001\~165536;
|
||||
|
||||
输入寄存器,其地址范围为:300001\~365536;
|
||||
|
||||
保持寄存器,其地址范围为:400001\~465536;
|
||||
|
||||
由于65536是比较大的数值,实际应用一般不需要这么大的存储区,因此PLC厂家普遍采用的是10000以内的地址范围,即:
|
||||
|
||||
线圈地址范围:00001\~09999;
|
||||
|
||||
离散量输入地址范围:10001\~19999;
|
||||
|
||||
输入寄存器地址范围:30001\~39999;
|
||||
|
||||
保持寄存器地址范围:40001\~49999;
|
||||
|
||||
有了该地址模型,我们就可以从Modbus寄存器的地址判断要访问的区块的类型。比如本文开头提到到地址40001就是保持存储器的第一个值的地址,而10001就是离散量输入的第一个值的地址;要注意的是,保持寄存器和输入寄存器的每个值的大小为16bits(字),而线圈和离散量输入每个值的大小为1bit(位);
|
||||
|
||||
各PLC厂家根据PLC的实际情况,将Modbus的地址模型映射到实际的存储区。一般来说,线圈对应过程输出映像区(Q);离散量输入对应过程输入映像区(I);输入寄存器对应模拟量输入(AI);保持寄存器对应数据块或V存储区或M存储区。
|
||||
|
||||
### 协议数据单元(Protocol Data Unit,PDU)
|
||||
|
||||
协议数据单元由功能码+数据构成,如下面这张图:
|
||||
|
||||

|
||||
|
||||
**功能码**的长度为**1个字节**,它表示要执行的功能。比如常见的:**01读取线圈;02读取离散量输入值;03读取保持寄存器值;05写单个线圈等**;
|
||||
|
||||
**数据**部分的长度为0\~252个字节,它表示要读的地址或者要写入的值,不同的功能码对应的数据有所不同。比如01功能码,其数据为4个字节,其中前两个字节表示要读取的线圈的地址,后两个字节表示要读取线圈的数量;而对于05功能码,其数据也是4个字节,前两个字节表示要写入线圈的地址,后面两字节表示要写入的值。
|
||||
|
||||
协议数据单元有三种类型:**请求型协议数据单元(Request PDU)**、**应答型协议数据单元(Response PDU)**、及**异常应答型协议数据单元(Exception Response PDU)**
|
||||
|
||||
协议数据单元是家族的**通用数据结构**,它**与底层物理结构无关**,三兄弟都使用相同的协议数据单元。
|
||||
|
||||
Modbus协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网线路上的Modbus协议映射能够在应用数据单元(ADU)上引入一些附加域。
|
||||
|
||||

|
||||
|
||||
### Modbus常用功能码
|
||||
|
||||
https://zhuanlan.zhihu.com/p/145546574
|
||||
|
||||
https://blog.csdn.net/sgmcumt/article/details/87435191?spm=1001.2014.3001.5502
|
||||
|
||||
**功能码**的长度为**1个字节**,它表示要执行的功能。比如常见的:**01读取线圈;02读取离散量输入值;03读取保持寄存器值;05写单个线圈等**;
|
||||
|
||||

|
||||
|
||||
#### 01(0x01)读线圈
|
||||
|
||||
1)功能:读取从站(远程设备)的1\~2000个连续线圈的状态数值;读取采用起始地址+线圈数量的方式;
|
||||
2)操作方式:位操作;
|
||||
3)说明:Modbus1号线圈的地址为0,2号线圈的地址为1,以此类推;因此,假设要读取1~10号线圈的值,其寄存器地址范围为:0~9;(相对地址??)
|
||||
4)发送指令示例:
|
||||
假设从站地址为0x03,要读取编号从33~42的10个连续线圈的状态值,其寄存器地址范围为:0x0020~0x0029,则发送指令下图所示:
|
||||
|
||||

|
||||
|
||||
5)应答格式:
|
||||
应答数据包括:从站地址+功能码+返回字节数+数据值+校验码
|
||||
其中,线圈的状态以位的形式返回。状态为ON时,其值为1;状态为OFF时,其值为0;
|
||||
数据以小端(Little Endian)的形式进行组织。即先存放LSB(最低权重位),再存放MSB。
|
||||
每8个位组成一个字节,**当线圈的数量不是8的倍数时,剩余的位数添0补位。**
|
||||
本例程读取**10个线圈,10/8商1余2,因此需要2个字节存放应答数据。**
|
||||
字节1存放线圈编号33\~40的数值(小端字节序,线圈40的值存放在bit7,线圈33的值存放在bit0);
|
||||
字节2存放线圈编号41\~42的数值,剩余位数添0补位;
|
||||
假设线圈状态及数值如下面两张图所示:
|
||||
|
||||

|
||||
|
||||
则,应答字节1的值为:11001011=0xCB;
|
||||
应答字节2的值为:10=0x02
|
||||
应答消息帧下图所示:
|
||||
|
||||

|
||||
|
||||
#### 02(0x02)读离散量输入
|
||||
|
||||
1)功能:读取从站1\~2000个连续离散量输入的状态值;读取采用起始地址+通道数量的方式;
|
||||
2)操作方式:位操作;
|
||||
3)离散量输入通道地址编号从1开始,寄存器地址编号从0开始;
|
||||
4)发送指令示例:
|
||||
假设要读取从站地址为0x03的第110\~119个数字量输入通道的数值,则发送指令如下图所示:
|
||||
|
||||

|
||||
|
||||
5)应答:应答格式与功能码01H类似
|
||||
应答数据包括:从站地址+功能码+返回字节数+数据值+校验码
|
||||
假设应答字节1的数据如下图所示:
|
||||
|
||||

|
||||
|
||||
应答字节2的内容如下图所示:
|
||||
|
||||

|
||||
|
||||
应答消息帧如下图所示:
|
||||
|
||||

|
||||
|
||||
#### 03(0x03)读保持寄存器
|
||||
|
||||
1)功能:读取远程从站若干个保持寄存器(Holding Register)的数值;
|
||||
2)操作方式:每个保持存储器的数值以字(2个字节)的形式进行应答;
|
||||
3)发送指令:
|
||||
假设要读取从机地址0x03的108~110保持存储器的数值,其寄存器地址范围为:0x006B~0x006D,指令格式如下图所示:
|
||||
|
||||

|
||||
|
||||
4)应答:
|
||||
从站应答数据包括:从站地址+功能码+应答字节数+寄存器1高字节+寄存器1低字节+...+寄存器N高字节+寄存器N低字节
|
||||
假设编号108\~110保持寄存器的数值如下图所示:
|
||||
|
||||

|
||||
|
||||
#### 04(0x04)读输入寄存器
|
||||
|
||||
1)功能:读1\~125个连续输入寄存器(Input Register)的数值;
|
||||
2)操作方式:每个输入寄存器存储器的数值以字(2个字节)的形式进行应答;
|
||||
3)发送指令:
|
||||
假设要读取从机地址0x03的9-10号输入存储器的数值,其寄存器地址范围为:0x0008\~0x0009,指令格式如下图所示:
|
||||
|
||||

|
||||
|
||||
4)应答:
|
||||
从站应答数据包括:从站地址+功能码+应答字节数+寄存器1高字节+寄存器1低字节+...+寄存器N高字节+寄存器N低字节(与功能码03H类似)
|
||||
假设寄存器的数据如下图所示:
|
||||
|
||||

|
||||
|
||||
#### 05(0x05)写单个线圈
|
||||
|
||||
1)功能:对单个线圈进行写操作。线圈编号从1开始,地址从0开始。写值0xFF00表示将线圈置为ON,写值0x0000表示将线圈置为OFF,其它值是无效的;
|
||||
2)操作方式:位操作
|
||||
3)发送指令:
|
||||
假设要将从站地址0x03的第33个线圈(地址:0x0020)的值设置ON,指令如下图所示:
|
||||
|
||||

|
||||
|
||||
4)应答:
|
||||
从站应答数据包括:从站地址+功能码+寄存器地址+写入值
|
||||
如果数据成功写入,则应答数据与请求数据一样,如下图所示:
|
||||
|
||||

|
||||
|
||||
#### 06(0x06)写单个寄存器
|
||||
|
||||
**写单个寄存器**
|
||||
|
||||
在一个远程设备中,使用该功能码写单个保持寄存器。
|
||||
|
||||
请求 PDU 说明了被写入寄存器的地址。从零开始寻址寄存器。因此,寻址寄存器 1 为 0。
|
||||
|
||||
正常响应是请求的应答,在写入寄存器内容之后返回这个正常响应。
|
||||
|
||||

|
||||
|
||||
这是一个请求将十六进制 00 03 写入寄存器2的实例:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 15 (0x0F) 写多个线圈
|
||||
|
||||
在一个远程设备中,使用该功能码强制线圈序列中的每个线圈为 ON 或 OFF。请求 PDU 说明了强制的线圈参考。从零开始寻址线圈。因此,寻址线圈 1 为 0。
|
||||
|
||||
请求数据域的内容说明了被请求的 ON/OFF 状态。域比特位置中的逻辑“1”请求相应输出为ON。域比特位置中的逻辑“0”请求相应输出为 OFF。
|
||||
|
||||
正常响应返回功能码、起始地址和强制的线圈数量。
|
||||
|
||||

|
||||
|
||||
这是一个请求从线圈 20 开始写入 10 个线圈的实例:
|
||||
|
||||
请求的数据内容为两个字节:十六进制 CD 01 (二进制 1100 1101 0000 0001)。使用下列方法,二进制比特对应输出。
|
||||
|
||||

|
||||
|
||||
传输的第一字节(十六进制 CD)寻址为输出 27-20,在这种设置中,最低有效比特寻址为最低输出(20)。
|
||||
|
||||
传输的下一字节(十六进制 01)寻址为输出 29-28,在这种设置中,最低有效比特寻址为最低输出(28)。
|
||||
|
||||
应该用零填充最后数据字节中的未使用比特。
|
||||
|
||||

|
||||
|
||||
#### 16 (0x10) 写多个寄存器
|
||||
|
||||
在一个远程设备中,使用该功能码写连续寄存器块(1 至约 120 个寄存器)。
|
||||
|
||||
在请求数据域中说明了请求写入的值。每个寄存器将数据分成两字节。
|
||||
|
||||
正常响应返回功能码、起始地址和被写入寄存器的数量。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 23 (0x17) 读/写多个寄存器
|
||||
|
||||
在一个单独 MODBUS 事务中,这个功能码实现了一个读操作和一个写操作的组合。从零开始寻址保持寄存器。因此,寻址保持寄存器 1-16为0-15。
|
||||
|
||||
请求说明了起始地址、被读取的保持寄存器号和起始地址、保持寄存器号以及被写入的数据。在写数据域中,字节数说明随后的字节号。
|
||||
|
||||
正常响应包括被读出的寄存器组的数据。在读数据域中,字节数域说明随后的字节数量。
|
||||
|
||||

|
||||
|
||||
这是一个请求从寄存器4开始读六个寄存器并且从寄存器15开始读三个寄存器的实例:
|
||||
|
||||

|
||||
|
||||
## ModBusTCP
|
||||
|
||||
### 初识
|
||||
|
||||
#### 概述
|
||||
|
||||
> MODBUS/TCP 使MODBUS_RTU协议运行于以太网,MODBUS TCP使用TCP/IP和以太网在站点间传送**MODBUS报文**,MODBUS TCP结合了以太网物理网络和网络标准TCP/IP以及以MODBUS作为应用协议标准的数据表示方法。MODBUS TCP通信报文被封装于以太网TCP/IP数据包中。与传统的串口方式,MODBUS TCP插入一个标准的MODBUS报文到TCP报文中,不再带有数据校验和地址。
|
||||
|
||||
简单的理解一下[Modbus](https://so.csdn.net/so/search?q=Modbus&spm=1001.2101.3001.7020) TCP/IP协议的内容,就是**去掉了**modbus协议本身的**CRC校验**,**增加了MBAP 报文头**。TCP/IP上的MODBUS的请求/响应如下图所示:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
#### 网络模型
|
||||
|
||||
通讯使用的以太网参考模型:
|
||||
|
||||
Modbus TCP传输过程中使用了TCP/IP以太网参考模型的5层:
|
||||
|
||||
第一层:物理层,提供设备物理接口,与市售介质/网络适配器相兼容
|
||||
|
||||
第二层:数据链路层,格式化信号到源/目硬件址数据帧
|
||||
|
||||
第三层:网络层,实现带有32位IP址IP报文包
|
||||
|
||||
第四层:传输层,实现可靠性连接、传输、查错、重发、端口服务、传输调度
|
||||
|
||||
第五层:应用层,Modbus协议报文
|
||||
|
||||
在网络通信中,通常需要写明IP地址和端口号,为什么ADU中没有相关的内容呢?
|
||||
|
||||
其实这是因为是一个应用层的协议,而IP地址和端口号属于传输层/网络层的协议。看图:
|
||||
|
||||

|
||||
|
||||
逻辑上是在TCP层上的。在发送数据的时候,应用数据单元首先向下传送给传输层,加上TCP协议的报文;再传送给网络层,加上IP协议的报文;再向下传送给数据链路层及物理层;接收的过程正好相反,从物理层一层一层的去掉相应层的报文,最终到达应用层。所以在进行数据传输的时候,是要配合TCP/IP协议来使用的。通常如果你使用电脑编程,就要用到SOCKET技术;如果是使用PLC编程,通常厂家已经把底层通信封装成库指令了,你只要直接调用就好了。比如西门子S7-200 SMART/1200/1500等PLC都有现成的Modbus-TCP指令库。
|
||||
|
||||
---
|
||||
|
||||
在Modbus服务器中按缺省协议使用Port 502 通信端口,在Modbus客户器程序中设置任意通信端口,为避免与其他通讯协议的冲突一般建议2000开始可以使用。
|
||||
|
||||
#### 通讯过程举例
|
||||
|
||||
在读寄存器的过程中,以Modbus TCP请求报文为例,具体的数据传输过程如下:
|
||||
|
||||
\\1) Modbus TCP客户端实况,用Connect()命令建立目标设备TCP 502端口连接数据通信过程;
|
||||
|
||||
\\2) 准备Modbus报文,包括7个字节MBAP内请求;
|
||||
|
||||
\\3) 使用send()命令发送;
|
||||
|
||||
\\4) 同一连接等待应答;
|
||||
|
||||
\\5) 同recv()读报文,完成一次数据交换过程;
|
||||
|
||||
\\6) 当通信任务结束时,关闭TCP连接,使服务器可以为其他服务。
|
||||
|
||||
### MBAP报文
|
||||
|
||||
MBAP是英文"ModBus APlication"的缩写,即"应用数据单元"的意思。
|
||||
|
||||
首先来看一下,MBAP 报文头都包括了哪些信息和内容
|
||||
|
||||

|
||||
|
||||
* **事务元标识符(2个字节):用于事务处理配对**。在响应中,MODBUS服务器复制请求的事务处理标识符。这里在以太网传输中存在一个问题,就是先发后至,我们可以利用这个事务处理标识符做一个**TCP序列号**,来防止这种情况所造成的数据收发错乱(这里我们先不讨论这种情况,这个事务处理标识符我们统一使用0x00,0x01)
|
||||
|
||||
* **协议标识符(2个字节)**:modbus协议标识符为0x00,0x00
|
||||
|
||||
* **长度(2个字节)**:长度域是下一个域的字节数,包括单元标识符和数据域。
|
||||
|
||||
* **单元标识符(1个字节)**:该设备的编号。(可以使用PLC的IP地址标识)。
|
||||
|
||||
**对 TCP/IP 来说,利用IP地址寻址MODBUS服务器;因此,MODBUS单元标识符是无用的。必需使用值0xFF。**
|
||||
|
||||
**注:0也可以用作与MODBUS/TCP设备直接通信。**
|
||||
|
||||
### 请求和响应
|
||||
|
||||
#### MODBUS请求的生成
|
||||
|
||||
在收到来自用户应用的需求后,客户端必须生成一个MODBUS请求,并发送到TCP管理。下表显示MODBUS请求ADU编码:
|
||||
|
||||

|
||||
|
||||
#### MODBUS响应的生成
|
||||
|
||||
一旦处理请求,MODBUS 服务器必须使用适当的MODBUS服务器事务处理生成一个响应,并且必须将响应发送到TCP管理组件。
|
||||
|
||||
根据处理结果,可以生成两类响应:
|
||||
|
||||
* 肯定的MODBUS响应:
|
||||
|
||||
* 响应功能码 = 请求功能码
|
||||
|
||||
* MODBUS异常响应:
|
||||
|
||||
* 目的是为客户机提供与处理过程检测到的错误相关的信息
|
||||
|
||||
* 响应功能码 = 请求功能码+0x80
|
||||
|
||||
* 提供异常码来表明出错的原因。
|
||||
|
||||

|
||||
|
||||
### SIMATIC S7-1200/S7-1500存储区寻址
|
||||
|
||||
| 存储区 | Modbus 设备中应用层的地址 | 传输报文中 Modbus 地址(数据链路层) |
|
||||
| -------------------- | ---------------------------------------------- | ------------------------------------ |
|
||||
| 线圈(输出) | 1 to 9999 | 0 to 9998 |
|
||||
| 离散输入(输入) | 10001 to 19999 | 0 to 9998 |
|
||||
| 输入寄存器(输入字) | 30001 to 39999 | 0 to 9998 |
|
||||
| 保持寄存器(输出字) | 40001 to 49999 400001 to 465536 扩展的地址空间 | 0 to 9998 0 to 65535 |
|
||||
|
||||
### 实例
|
||||
|
||||

|
||||
|
||||
*注意:MB_HOLD_REG中的数量不能大于数据块中数据的数量*
|
||||
|
||||
保持性寄存器示例如下,有word(两个字节,16位)类型和Real(4个字节,32位)两种组成,
|
||||
|
||||
注意:一个保持性寄存器为16位,两个字节,因此一个**real类型**占用**两个保持性寄存器**
|
||||
|
||||

|
||||
|
||||
发送读取请求,需要从0位(00 00)开始,读取15(00 0f)个保持性寄存器:
|
||||
|
||||
发送请求:
|
||||
|
||||
```
|
||||
00 00 00 00 00 06 FF 03 00 00 00 0f
|
||||
```
|
||||
|
||||
收到回复:
|
||||
|
||||
```
|
||||
00 00 00 00 00 21 FF 03 1E 00 01 00 02 00 03 00 04 00 05 40 D3 33 33 40 F6 66 66 41 0C CC CD 41 1E 66 66 BF 80 00 00
|
||||
```
|
||||
|
||||
其中a1-a5为word类型,分别占用一个保持性寄存器,两个字节表示,如a1(1)表示为:00 01
|
||||
|
||||
a6-a10为real类型,占用了两个保持性寄存器,用4个字节表示,如a6(6.6)表示为:40 D3 33 33,a10(-1.0)表示为:BF 80 00 00
|
||||
|
||||
注意:浮点数表示遵循[IEEE 754标准](https://www.cnblogs.com/MikeZhang/p/IEEE754FloatEncode20180117.html),在线转换:http://www.speedfly.cn/tools/hexconvert/
|
||||
|
||||
## ModbusTCP QT编程
|
||||
|
||||
### QModBusTcpClient类
|
||||
|
||||
QModBusTcpClient类>>QModbusClient>>QModbusDevice>>QObject
|
||||
|
||||
#### 信号
|
||||
|
||||
| 信号 | |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| errorOccurred | 有错误时发出 |
|
||||
| stateChanged | 每当设备的状态发生变化时,就会发出这个信号。新状态由状态表示。 |
|
||||
| finished() | 该信号在应答完成处理时发出。回复可能仍然返回了一个错误。在发出此信号后,应答的数据将不再有更新。 |
|
||||
|
||||
#### 函数
|
||||
|
||||
| | |
|
||||
| ------------------------------ | ------------------------------------------------------------ |
|
||||
| Error error() | 返回设备的错误状态 |
|
||||
| QString errorString() | 返回设备错误的描述性错误文本。 |
|
||||
| void disconnectDevice() | 断开连接的设备。 |
|
||||
| setConnectionParameter() | |
|
||||
| setTimeout() | |
|
||||
| setNumberOfRetries() | |
|
||||
| bool connectDevice() | 用于设备接入Modbus网络。成功时返回true;否则错误。 |
|
||||
| QMODBUSREPLY sendReadRequest() | 发送读请求 |
|
||||
| sendWriteRequest() | 发送一个读取read所指向数据的内容的请求。如果没有错误发生,返回一个新的有效的QModbusReply对象,否则为nullptr。Modbus网络可以有多个服务器,每个服务器都有唯一的serverAddress。 |
|
||||
| bool isFinished() | 当回复完成或中止时返回true。 |
|
||||
@@ -0,0 +1,179 @@
|
||||
---
|
||||
title: 通讯数据格式转换
|
||||
author: TianZD
|
||||
top: true
|
||||
cover: true
|
||||
toc: true
|
||||
mathjax: false
|
||||
summary: >-
|
||||
modubusTCP在传输时,通过读写保持性寄存器位进行数据传输,一个保持性寄存器占有2个byte,16个bite,传输的数据常为float型(4个byte,32位),需要进行转换
|
||||
tags:
|
||||
- 数据格式
|
||||
- modbus
|
||||
categories:
|
||||
- 通讯
|
||||
reprintPolicy: cc_by
|
||||
abbrlink: fffca2d8
|
||||
date: 2022-04-29 13:47:36
|
||||
coverImg:
|
||||
img:
|
||||
password:
|
||||
---
|
||||
|
||||
\[toc\]
|
||||
|
||||
# 通讯数据格式转换(float/real-word-byte)
|
||||
|
||||
## 前言
|
||||
|
||||
在上位机和下位机进行通讯的时候,通常要进行数据转换为字节(8位)或者word(16位)进行传输
|
||||
|
||||
modubusTCP在传输时,通过**读写保持性寄存器**位进行数据传输,一个保持性寄存器占有**2个byte**,**16个bite**,传输的数据常为**float型(4个byte,32位)**,需要进行转换
|
||||
|
||||
浮点数的表示通常采用**IEEE 754浮点数标准**,可以参考文章[IEEE754标准的浮点数存储格式](https://www.cnblogs.com/MikeZhang/p/IEEE754FloatEncode20180117.html)
|
||||
|
||||
IEEE754转换:[在线转换网址](http://www.speedfly.cn/tools/hexconvert/)
|
||||
|
||||
## 数据在内存中的存储格式
|
||||
|
||||
### 大端模式
|
||||
|
||||
大端模式是指数据的高字节保存在内存的低地址单元中,而数据的低字节保存在内存的高地址单元中,这样的存储模式有点类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
|
||||
|
||||
### 小端模式
|
||||
|
||||
小端模式是指数据的高字节保存在内存的高地址单元中,而数据的低字节保存在内存的低地址单元中。
|
||||
|
||||
## 转换
|
||||
|
||||
其中float-byte、byte-float代码转自CSDN文章:[float型数据与4字节之间的转换,作者tutu-hu](https://blog.csdn.net/weixin_42700740/article/details/103236885?share_token=7af34e32-1bce-4b09-9225-afa6e02006f6)
|
||||
|
||||
### float-byte(32位浮点数转4个8位)
|
||||
|
||||
**2进制,与运算,左移运算**
|
||||
|
||||
```c++
|
||||
/*将浮点数f转化为4个字节数据存放在byte[4]中*/
|
||||
unsigned char* Float_to_Byte(float f)
|
||||
{
|
||||
float float_data = 0;
|
||||
unsigned long longdata = 0;
|
||||
longdata = *(unsigned long*)&f; //注意,会丢失精度
|
||||
byte[0] = (longdata & 0xFF000000) >> 24;
|
||||
byte[1] = (longdata & 0x00FF0000) >> 16;
|
||||
byte[2] = (longdata & 0x0000FF00) >> 8;
|
||||
byte[3] = (longdata & 0x000000FF);
|
||||
return byte;
|
||||
}
|
||||
```
|
||||
|
||||
### float-word(32位浮点数转2个16位)
|
||||
|
||||
```c++
|
||||
unsigned int* Float_to_word(float f)
|
||||
{
|
||||
unsigned long longdata = 0;
|
||||
longdata = *(unsigned long*)&f; //注意,会丢失精度
|
||||
word[0] = (longdata & 0xFFFF0000) >> 16;
|
||||
word[1] = (longdata & 0x0000FFFF);
|
||||
return word;
|
||||
}
|
||||
```
|
||||
|
||||
### byte-float(4个8位转1个32位浮点数)
|
||||
|
||||
#### 方法1
|
||||
|
||||
把四个字节存储好之后,再把这个**存储区域的首地址强制转换为float指针类型**,这样就可以提取出这个浮点数了。
|
||||
|
||||
```c
|
||||
/*将4个字节数据byte[4]转化为浮点数存放在*f中*/
|
||||
float Byte_to_Float(unsigned char *p)
|
||||
{
|
||||
float float_data=0;
|
||||
unsigned long longdata = 0;
|
||||
longdata = (*p<< 24) + (*(p+1) << 16) + (*(p + 2) << 8) + (*(p + 3) << 0);
|
||||
float_data = *(float*)&longdata;
|
||||
return float_data;
|
||||
}
|
||||
```
|
||||
|
||||
#### 方法2
|
||||
|
||||
把四个字节存储好之后,再把这个**存储区域的首地址强制转换为float指针类型**,这样就可以提取出这个浮点数了。
|
||||
|
||||
这个和方法1类似
|
||||
|
||||
```c
|
||||
/**
|
||||
*作用:把u8四字节数组转为float
|
||||
*note:低地址放float的低字节
|
||||
**/
|
||||
float U8_to_Float(u8* str)
|
||||
{
|
||||
float data;
|
||||
data = *((float*)str);
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
#### 方法3
|
||||
|
||||
定义一个float变量,然后定义u8类型指针数组指向float变量地址,Modbus协议解析的时候只管向地址指向的存储单元填充数据,需要用浮点数的时候直接拿过来用就可以了。
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
float freq;
|
||||
char recv[4] = {0x41, 0xbc, 0x00, 0x00}; //接收到的数据,高字节到低字节排列
|
||||
char *Modbus_HoldReg[4]; //定义保持寄存器指针数组
|
||||
|
||||
//第一步:指针初始化
|
||||
Modbus_HoldReg[0] = ((char*)(&freq)) + 3; //低地址指向高位
|
||||
Modbus_HoldReg[1] = ((char*)(&freq)) + 2;
|
||||
Modbus_HoldReg[2] = ((char*)(&freq)) + 1;
|
||||
Modbus_HoldReg[3] = ((char*)(&freq)) + 0; //高地址指向低位
|
||||
|
||||
//第二步:给地址指定的内存单元赋值(对应Modbus协议中的数据解析)
|
||||
*Modbus_HoldReg[0] = recv[0];
|
||||
*Modbus_HoldReg[1] = recv[1];
|
||||
*Modbus_HoldReg[2] = recv[2];
|
||||
*Modbus_HoldReg[3] = recv[3];
|
||||
printf("%f\r\n", freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### word-float(2个16位合成1个32位浮点数)
|
||||
|
||||
```c
|
||||
float Byte_to_Float(unsigned char *p)
|
||||
{
|
||||
float float_data=0;
|
||||
unsigned long longdata = 0;
|
||||
longdata = (*p<< 16) + (*(p+1) << 0);
|
||||
float_data = *(float*)&longdata;
|
||||
return float_data;
|
||||
}
|
||||
```
|
||||
|
||||
### byte-word(2个8位合成1个16位)
|
||||
|
||||
两个8位数如何转化为16位数?
|
||||
|
||||
```c
|
||||
int data = (a<<8) & b;
|
||||
```
|
||||
|
||||
```c
|
||||
char a;//高位
|
||||
char b;//低位
|
||||
....
|
||||
int data = (a<<8)&0xFF00;
|
||||
data &= b;
|
||||
```
|
||||
Reference in New Issue
Block a user