一、簡介
ModBus是Modicon公司為其PLC通訊而開發的一種通訊協議。如今Modicon公司已經被施耐德收購成為了施耐德旗下品牌。從1979年問世至今,已經成為工業通訊領域的業界標準。
ModBus具有兩種串行傳輸模式,ASCII 和 RTU。它們定義了數據如何打包、解碼的不同方式。支持 Modbus 協議的設備一般都支持 RTU 格式。通信雙方必須同時支持上述模式中的一種。
二、寄存器類型
MODBUS寄存器分類
寄存器種類 | 讀寫狀態 | 數據類型 | 功能碼 | PLC地址 |
---|---|---|---|---|
線圈寄存器 | 讀/寫 | 位(bit) | 01H(讀單/多個位); 05H(寫單個位); 0FH(寫多個位) | 00001-09999 |
離散輸入寄存器 | 只讀 | 位(bit) | 02H (讀單/多個位) | 10001-19999 |
保持寄存器 | 讀/寫 | 字(byte) | 03H(讀); 06H(寫單個字節); 0FH(寫多個字節) | 30001-39999 |
輸入寄存器 | 只讀 | 字(byte) | 04H (讀單/多個字) | 40001-49999 |
線圈寄存器:實際上可以表示一個開關量,線圈操作位(bit)一個bit對應一個開關信號,即(0/false,1/true),每個byte字節就能代表8個的位的開關信號,線圈寄存器支持讀和寫,Modbus的功能碼又能對線圈的單個或多個進行一個讀取寫入操作,其實就是在操作字節的位。實對應上面的功能碼也就是:0x01 0x05 0x0f
離散輸入寄存器:離散輸入寄存器就相當于線圈寄存器的只讀模式,功能跟上面基本一致,除了不能寫入。所以功能碼也簡單就一個讀的 0x02
保持寄存器:保持寄存器是對字節進行的操作,每兩個字節對應一個寄存器,支持讀取和寫入。功能碼有對應的三個:0x03 0x06 0x10
輸入寄存器:,這個和保持寄存器類似,但是也是只支持讀而不能寫。一個寄存器地址是占據兩個byte的空間,也就是16個位。對應的功能碼也就一個 0x04
言外之話:
當需要對保持寄存器操作時,不同的數據類型占據的字節長度其實是不一致的,一個寄存器地址占兩個字節16位代表的只是一個單精度的數據。如C#的數據類型占據的長度如下
C# 數據類型和字節長度
所以在讀取寫入保持寄存器的時候不同類型需要讀取不同長度的字節才能拿到對應類型的值,寫入的時候同理。如(int類型讀取2個數量為int的時候需要乘以2,也就是讀取4個寄存器才能拿到兩個int類型的數據)
三、MODBUS常用功能碼
功能碼 | 名稱 | 操作數據類型 | 作用描述 | PLC地址 |
---|---|---|---|---|
01H | 讀線圈寄存器 | 位(bit) | 獲得一組開關線圈的當前狀態(ON/OFF ) | 00001-09999 |
02H | 讀離散輸入寄存器 | 位(bit) | 獲得一組開關線圈的當前狀態(ON/OFF ) | 10001-19999 |
03H | 讀保持寄存器 | 字(byte) | 在一個或多個保持寄存器中取得當前的二進制值 | 30001-39999 |
04H | 讀輸入寄存器 | 字(byte) | 在一個或多個輸入寄存器中取得當前的二進制值 | 40001-49999 |
05H | 寫單個線圈寄存器 | 位(bit) | 設置一個單獨的線圈狀態(ON/OFF ) | 00001-09999 |
06H | 寫單個保持寄存器 | 字(byte) | 寫單個保持寄存器,將兩個字節寫入到寄存器 | 40001-49999 |
0FH | 寫多個線圈寄存器 | 位(bit) | 寫多個線圈寄存器,可以設置多個bit開關狀態 | 00001-09999 |
10H | 寫多個保持寄存器 | 字(byte) | 寫多個保持寄存器,將多個字節寫入到寄存器 | 40001-49999 |
MODBUS通訊協議格式
讀線圈寄存器——01H
從站發送讀取格式:
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 讀取數量高位 | 讀取數量低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x01 | 0x00 | 0x00 | 0x00 | 0x02 | BD | CB |
主站返回讀取格式:
從站地址 | 功能碼 | 返回字節數 | data1 | CRC高位 | CRC低位 |
---|---|---|---|---|---|
0x01 | 0x01 | 0x01 | 0x00 | 51 | 88 |
解讀:
發送格式:
從站地址:01
功能碼:01
起始地址: 00 00
讀取數量: 00 02
CRC校驗:BD CB
接收格式:
從站地址:01
功能碼:01
返回字節數: 01
Data數據: 00(一個byte有8個bit,讀取兩個bit返回會補成字節用一個byte返回讀取兩個bit就行)
CRC校驗:BD CB
讀離散輸入狀態——02H
從站發送讀取格式:
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 讀取數量高位 | 讀取數量低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x02 | 0x00 | 0x00 | 0x00 | 0x02 | F9 | CB |
主站返回讀取格式:
從站地址 | 功能碼 | 返回字節數 | data1 | CRC高位 | CRC低位 |
---|---|---|---|---|---|
0x01 | 0x02 | 0x01 | 0x00 | A1 | 88 |
讀保持寄存器——03H
從站發送讀取格式:
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 讀取數量高位 | 讀取數量低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x03 | 0x00 | 0x00 | 0x00 | 0x02 | C4 | CB |
主站返回讀取格式:
從站地址 | 功能碼 | 返回字節數 | data1 | data2 | data3 | data4 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|---|
0x01 | 0x03 | 0x04 | 0x00 | 0x0A | 0x00 | 0x00 | 0A | 31 |
解析讀取返回:
為啥讀取是讀取兩個地址數量會返回四個byte字節?因為在保持寄存器里面兩個字節才對應一個寄存器,你讀取兩個寄存器地址數量就需要四個字節來表示。
注意:在解析字節的時候數據的高低位順序倒序后才能表示一個數據
讀輸入寄存器——04H
同讀取保存寄存器一樣的格式,同上
寫單個線圈——05H
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | data1 | data2 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x05 | 0x00 | 0x00 | 0xFF | 0x00 | 8C | 3A |
主站返回讀取格式:
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 寫入數據高位 | 寫入數據低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x05 | 0x00 | 0x00 | 0xFF | 0x00 | 8C | 3A |
解析:
寫入無誤的話返回同發送指令一樣;通斷標志為FF00H表示寫ON,0000H表示寫OFF,
寫單個保持寄存器——06H
將地址0的保存寄存器的數據設置為10(單精度)
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | data1 | data2 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x06 | 0x00 | 0x00 | 0x00 | 0x0A | 09 | CD |
主站返回讀取格式:
響應:同發送指令;
寫多個線圈——0FH
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 寫入數量高位 | 寫入數據低位 | 字節長度 | data1 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|---|---|
0x01 | 0x0F | 0x00 | 0x00 | 0x00 | 0x02 | 0x01 | 0x03 | 9E | 96 |
主站返回讀取格式:
響應:
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 寫入數量高位 | 寫入數據低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x0F | 0x00 | 0x00 | 0x00 | 0x02 | 9E | 96 |
返回數據格式不返回Data數據
詳情:寫入數量為2的線圈,其中data1數據0x03代表0000 0011 將連續兩個線圈置為ON
寫多個保持寄存器——10H
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 寫入數量高位 | 寫入數據低位 | 字節長度 | data1 | data2 | data3 | data4 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0x01 | 0x10 | 0x00 | 0x00 | 0x00 | 0x02 | 0x04 | 0x0C | 0x02 | 0x12 | 0x45 | 9C | 6C |
主站返回讀取格式:
響應:
從站地址 | 功能碼 | 起始地址高位 | 起始地址低位 | 寫入數量高位 | 寫入數據低位 | CRC高位 | CRC低位 |
---|---|---|---|---|---|---|---|
0x01 | 0x10 | 0x00 | 0x00 | 0x00 | 0x02 | 41 | CB |
返回數據格式不返回Data數據
詳情:寫入數量為2的線圈,其中data1,data2,data3,data4數據兩個代表一個單精度數據
0C 02表示一個3074 ,12 45代表一個4677
MODBUS TCP通訊協議格式
ModbusTCP的數據幀可分為兩部分:MBAP+PDU。
| 事務處理標識| 協議標識 | 字節長度 | 單元標識符 | 功能碼 |起始地址H | 起始地址L |數量H | 數量L
事務處理標識 | 協議標識 | 字節長度 | 單元標識符 | 功能碼 | 起始地址H | 起始地址L | 數量H | 數量L |
---|---|---|---|---|---|---|---|---|
2字節 | 2字節 | 2字節 | 1字節 | 1字節 | 1字節 | 1字節 | 1字節 | 1字節 |
事務處理標識: 可以理解為報文的序列號,一般每次通信之后就要加1以區別不同的通信數據報文。
協議標識符: 00 00表示ModbusTCP協議。
字節長度: 表示接下來的數據長度,單位為字節
單元標識符: 可以理解為設備地址。
TCP跟RTU的協議格式其實基本上是一致的,只是TCP增加了報文頭MBAP,取消了RTU的從站地址跟CRC校驗