modbus协议是一种应用层的报文传输协议;
RTU,ASCII,TCP
以下内容以modbus RTU通信协议为例说明;
01-存储区
存储区:输出线圈,输入线圈,输出寄存器,输入寄存器
(其实就只有输入,输出,线圈,寄存器)
输入:就是只读(read only),不能写的,一般用来保存结果或者保存状态;
输出:
那线圈和寄存器怎么理解?
可以说我这个存储区的最小单位是寄存器,
或者说我这个存储区的最小单位是线圈;
至于为啥叫线圈,这个可能跟物理上有关系,做硬件的那些人应该懂;
线圈:就表示一个boolean量,要么得电,要么失电;
所以我们用线圈来表示boolean 量;
如果我的存储区用线圈存储区,就表示我的最小单位是布尔;
怎么理解存储区?
就是我们电脑里面会有内存,
这种控制器最底层也是有cpu,或者存储的一个东西,
如果是输入存储区:
或者说输入线圈,或者输出线圈,这里的最小单位是一个bool;就是存一个1或者0;
那如果是寄存器存储区呢?这一个内存就会占16位,因为它的最小单位是寄存器,
一个寄存器等于16个位;
那么就容易理解了:
线圈和寄存器表示的就是最小单位;
其中线圈=布尔(bit),寄存器=16位=16个bit(2个字节)
或者说你要存布尔,你就用线圈存储区,
你要存数据,就用寄存器存储区;
这样可以做一个区分;
1字节(byte) = 8位(bit)
1个字节是8位,就是二进制8位
02-存储区范围
存储区范围:5位和6位,也叫标准地址和扩展地址
任何一个存储区都是有范围的,不存在无限的;
5位还不能全部来表示地址,
比如说:
Y XXXX:
其中第一位 Y 表示哪个存储区;
就是它会给每个存储区来个代号;
比如说输入线圈存储区是0,你可以通过这个代号知道是哪个存储区;
输出线圈 0
输入线圈 1
输出寄存器 4
输入寄存器 3
那除了这个代号,后面还有四个XXXX,
那这个最大就是9999
也就是说输出线圈的最大为09999,输入线圈的最大为19999,这样
那最小值呢?
也就是存储区的范围:
输出线圈 0
00001-09999
输入线圈 1
10001-19999
输出寄存器 4
40001-49999
输入寄存器 3
30001-39999
这样的话,给我这个地址,我就知道第一位是哪个存储区,通过后面四个位数就能知道是第多少个寄存器或者第多少个线圈;
地址:Y XXXX
但是上面这个是5位,也叫标准地址;
有些设备支持6位,也就是 Y XXXXXX,
也就是可以按照5位的规律?
输出线圈 0
00001-09999
000001-099999
输入线圈 1
10001-19999
100001-199999
输出寄存器 4
40001-49999
400001-499999
输入寄存器 3
30001-39999
300001-399999
但是其实不是这样算的,
6位最大是:
输出线圈 0
00001-09999
000001-065536
输入线圈 1
10001-19999
100001-165536
输出寄存器 4
40001-49999
400001-465536
输入寄存器 3
30001-39999
300001-365536
现在随便给一个地址就知道是什么了?????比如:
36543,03321,10894
03-通信
现在有上面那么多的存储区,我想跟他们通信,那就有很多操作;
操作其实就是
读和写;
但是我存储区有这么四个:
输出线圈
输入线圈
输出寄存器
输入寄存器
那么总共可以组合为多少种操作呢?
读的话:
读输出线圈
读输入线圈
读输出寄存器
读输入寄存器
写:
写输出线圈
写输出寄存器
那就至少有6种动作,也叫功能;
但是可以给每个功能来一个代号,叫做功能码。
(存储区有代码,每个功能也有代号-叫功能码)
读输出线圈 01
读输入线圈 02
读输出寄存器 03
读输入寄存器 04
写输出线圈 05
写输出寄存器 06
(为啥要用代号呢?因为我不能做每个功能是一串汉语或者英文)
(就是在计算机里面都会把它简化)
其实协议里面并不是这么简单,会把它进行区分,
比如它写入的时候会进行一个功能性的区分叫:
写单 或者 写多
写单个输出线圈 05
写单个输出寄存器 06
写多个输出线圈 15
写多个输出寄存器 16
这里的数字都是10进制,可能其他人的笔记这里是16进制,都行。
16进制:(前缀是0x)
对于小于10的,跟10进制都是一样的
大于10的,比如15就是0x0f;
上面就是modubs的功能码;
04-协议
协议就是规定了一种格式,
modbus RTU/ASCII
报文格式:从站地址(设备编号)+ 功能码(就是上面那个东西)+ 数据 + 校验
对于读取来说:
从站地址(设备编号):是为了区分设备(找谁?)
功能码:是为了做什么功能,干什么(干嘛?)
数据:具体做什么细节(具体干嘛的细节?)
校验:验证(验证)
对于写入来说:
从站地址(设备编号):找谁?
功能码:干嘛?
数据:具体干嘛的细节(比读取更多,多了个写入的具体数值)
校验:验证
从站地址(设备编号):1byte
功能码:1byte
数据:n个byte
校验:2个byte
1字节(byte) = 8位(bit)
1个字节是8位,二进制8位
比如:请求的一个报文如下(16进制的)
01 03 00 00 00 02 C4 0B
01 站地址
03 读输出寄存器
00 00 起始寄存器
00 02 寄存器长度
C4 0B CRC校验
这个报文的意思就是要去读站地址1,的输出寄存器,从0开始去读,读两个;
那相应报文如过是这个:
01 03 04 01 46 01 3B 5A 59
01 站地址
03 读输出寄存器
04 字节计数(就是返回了多少个字节)
01 46 01 3B 具体的4个字节
5A 59 CRC校验
注意:
如过写不进去怎么办?
或者说我发了东西它不回?
那肯定就是我发的不对,它没法回;
其他问题---
问题1: 地址说明:
一般业务方给的或者说明书的modbus地址如下:
400001
400002
这个地址是唯一的,绝对的;
但是我协议的地址是:就是通信报文中的地址都是相对地址,
相对地址就是从0开始的;
所以就有一种说法是:
对于输出寄存器来说:0对应的是400001,0-400001
那对于输入寄存器来说:1对应的是300001,0-300001
对于输出线圈来说,0对应的是000001
对于输入线圈来说,0对应的是100001
输出线圈 0
输入线圈 1
输出寄存器 4
输入寄存器 3
怎么快速记这个呢?
偶数都是输出,0,4
奇数都是输入,1,3
线圈是0和1(线圈是小一点的数 <= 1)
寄存器是3和4
(这里没有2)
输入==只读==read only
输出==读写
一般对接客户的时候说的都是绝对地址;
但是在协议里面都是相对地址,因为协议里面的功能码,从站地址这些都告诉我操作哪个存储区了,因此不需要再告诉我操作哪个存储区了;
所以:
每个存储区都会有0这个相对地址的,
每个存储区都是从0这个相对地址开始的;
问题2: 通讯都是16进制的?
这句活不对;
通讯底层都是二进制的,只是我写的时候可以写成10进制的,也可以写成16进制的;
一些博客中写的是0x03,或者0x6B,其实都是可以改的,只是他写的是16进制,
我对应写成10进制也是可以的,效果是一样的,等同的;
代码中,
代码前面加0x那就是16进制的写法,
代码前面不加0x,那就是10进制的写法;
问题3: 基础整理
字 word
字节 byte
位 bit
字长是指字的长度
1字节=8位(1 byte = 8bit)
1字=2字节(1 word = 2 byte)
一个字节的字长是8
一个字的字长为16