modbus协议之-01-初次见面

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

上一篇:什么是Modbus TCP客户端、什么是Modbus TCP服务器


下一篇:CodeForces - 897E Willem, Chtholly and Seniorious(珂朵莉树)