Modbus協議棧開發筆記之一:實現功能的基本設計

Modbus作爲開放式的工業通訊協議,在各種工業設備中應用極其廣泛。本人也使用Modbus通訊很多年了,或者用現成的,或者針對具體應用開發,一直以來都想要開發一個比較通用的協議棧能在後續的項目中複用,而不必每次都寫一遍。現在利用項目研發的機會,開發一個自己的Modbus協議棧。

Modbus有國際標準,也有國家標準,內容是完全一樣的。在標準鍾支持2種物理鏈路:一是基於RS485(RS232)的串行鏈路;二是基於以太網的TCP/IP鏈路。事實上,Modbus協議作爲一種應用層協議對物理鍵子並沒有特別的要求,光纖、無線等都是可以實現的。

本次主要是開發Modbus RTU和Modbus TCP兩種標準協議方式,支持常用的功能碼:

功能碼

名稱

實現

描述

0x01

讀線圈

對可讀寫型的狀態量進行讀取

0x02

讀離散輸入

對只讀型的狀態量進行讀取

0x03

讀保持寄存器

對可讀寫型的寄存器量進行讀取

0x04

讀輸入寄存器

對只讀型的寄存器量進行讀取

0x05

寫單個線圈

對單個的讀寫型的狀態量進行寫入

0x06

寫單個寄存器

對單個的讀寫型的寄存器量進行寫入

0x0F

寫多個線圈

對多個的讀寫型的狀態量進行寫入

0x10

寫多個寄存器

對多個的讀寫型的寄存器量進行寫入

Modbus協議是一種主從(或者說客戶端/服務器)模式協議,有主站(客戶端)發起事務請求,從站(服務器)響應事務請求。一般我們想要訪問(或者說被訪問)的設備即爲從站(服務器),而我們通常去訪問別人的設備就是主站(客戶端)。通常在RTU時被稱爲主站和從站,而在TCP方式時被稱爲服務器和客戶端。這種稱呼只是叫法不同,但在本質上是沒有區別我的。後續這兩種稱呼我們會同時使用。在這次開發中,我們計劃同時實現主站和從站的功能。

1、標準流程

啓動MODBUS 事務處理的客戶機創建 MODBUS 應用數據單元。當從客戶機向服務器設備發送報文時,功能碼向服務器指示將執行哪種操作。

從客戶機向服務器設備發送的報文數據域包括附加信息,服務器使用這個信息執行功能碼定義的操作。如果在一個正確接收的 MODBUS ADU 中,不出現與請求 MODBUS 功能有關的差錯,那麼服務器至客戶機的響應數據域包括請求數據。

正常事務處理流程

如果出現與請求 MODBUS 功能有關的差錯,那麼域包括一個異常碼,服務器應用能夠使用這個域確定下一個執行的操作。當服務器對客戶機響應時,它使用功能碼域來指示正常(無差錯)響應或者出現某種差錯(稱爲異常響應)。對於一個正常響應來說,服務器僅對原始功能碼響應。

異常事務處理流程

2、操作設計

根據我們對Modbus標準事務流程的理解,我們來設計主站和從站的具體操作過程。

主站操作流程:

(1)、初始化主站,站標識符,定時器、

(2)、生成訪問命令

生成命令ADU分兩層實現:網絡層和鏈路層。

網絡層實現與具體鏈路無關的PDU(包括功能碼和數據,這裏的數據指傳送的數據,並非指變量的值,事實上數量和地址也包含在內)。對應的文件是mbpdu.c/.h,實現對pud單元的數據處理,包括命令和響應。

鏈路層實現ADU的封裝。對應的文件是mbrtu.c/.h(和mbtcp.c/.h和mbascii.c/.h),實現ADU單元的封裝;同時作爲從站時,寫操作的響應也在此處封裝。

(3)、定時發送訪問命令,讀命令定是輪詢,寫命令具有更高的優先級。在有寫命令時暫停讀命令,

(4)、解析接收到的數據,進行相應操作

從站操作流程:

(1)、協議棧初始化:從站地址設置,創建數據存儲域,訪問控制

(2)、從站接收消息並解析

解析消息後操作數據(讀寫對應對象的值)

生成響應

(3)、生成響應數據並回復

3、命令格式

不同的功能碼具有不同的消息格式,我們只簡單說一下我們將要實現的以上8中功能碼的信息格式。

(1)功能碼0x01:讀線圈

在一個遠程設備中,使用該功能碼讀取線圈的1至2000個連續狀態。請求 PDU詳細說明了起始地址,即指定的第一個線圈地址和線圈編號。從零開始尋址線圈。因此尋址線圈 1-16 爲 0-15。

根據數據域的每個比特將響應報文中的線圈分成爲一個線圈。指示狀態爲 1= ON 和 0= OFF。第一個數據字節的 LSB(最低有效位)包括在詢問中尋址的輸出。其它線圈依次類推,一直到這個字節的高位端爲止,並在後續字節中從低位到高位的順序。

如果返回的輸出數量不是八的倍數,將用零填充最後數據字節中的剩餘比特(一直到字節的高位端)。字節數量域說明了數據的完整字節數。具體格式舉例如下:

(2)功能碼0x02:讀離散量輸入

在一個遠程設備中,使用該功能碼讀取離散量輸入的 1 至 2000 連續狀態。請求 PDU 詳細說明了起始地址,即指定的第一個輸入地址和輸入編號。從零開始尋址輸入。因此尋址輸入 1-16 爲 0-15。

根據數據域的每個比特將響應報文中的離散量輸入分成爲一個輸入。指示狀態爲1=ON 和0=OFF。第一個數據字節的 LSB(最低有效位)包括在詢問中尋址的輸入。其它輸入依次類推,一直到這個字節的高位端爲止,並在後續字節中從低位到高位的順序。

如果返回的輸入數量不是八的倍數,將用零填充最後數據字節中的剩餘比特(一直到字節的高位端)。字節數量域說明了數據的完整字節數。具體格式舉例如下:

事實上,功能碼0x01和0x02的數據格式是完全一樣的,操作方式也是完全一樣的,所不同只是操作的對象不同。

(3)功能碼0x03:讀保持寄存器

在一個遠程設備中,使用該功能碼讀取保持寄存器連續塊的內容。請求 PDU 說明了起始寄存器地址和寄存器數量。從零開始尋址寄存器。因此,尋址寄存器 1-16 爲 0-15。

將響應報文中的寄存器數據分成每個寄存器有兩字節,在每個字節中直接地調整二進制內容。

對於每個寄存器,第一個字節包括高位比特,並且第二個字節包括低位比特。具體的格式舉例如下:

響應信息的長度與具體讀取的數據量有關。

(4)功能碼0x04:讀輸入寄存器

在一個遠程設備中,使用該功能碼讀取 1 至大約 125 的連續輸入寄存器。請求 PDU 說明了起始地址和寄存器數量。從零開始尋址寄存器。因此,尋址輸入寄存器 1-16 爲 0-15。

將響應報文中的寄存器數據分成每個寄存器爲兩字節,在每個字節中直接地調整二進制內容。

對於每個寄存器,第一個字節包括高位比特,並且第二個字節包括低位比特。具體的格式舉例如下:

事實上,功能碼0x03和0x04的數據格式是完全一樣的,操作方式也是完全一樣的,所不同只是操作的對象不同。響應信息的長度與具體讀取的數據量有關。

(5)功能碼0x05:寫單個線圈

在一個遠程設備上,使用該功能碼寫單個輸出爲 ON 或 OFF。

請求數據域中的常量說明請求的ON/OFF狀態。十六進制值0xFF00請求輸出爲 ON。十六進制值0x0000 請求輸出爲OFF。其它所有值均是非法的,並且對輸出不起作用。

請求 PDU 說明了強制的線圈地址。從零開始尋址線圈。因此,尋址線圈1爲0。線圈值域的常量說明請求的 ON/OFF 狀態。十六進制值 0XFF00 請求線圈爲 ON。十六進制值 0X0000 請求線圈爲OFF。其它所有值均爲非法的,並且對線圈不起作用。

正常響應是請求的應答,在寫入線圈狀態之後返回這個正常響應。具體的格式舉例如下:

(6)功能碼0x06:寫單個寄存器

在一個遠程設備中,使用該功能碼寫單個保持寄存器。

請求 PDU 說明了被寫入寄存器的地址。從零開始尋址寄存器。因此,尋址寄存器1爲0。

正常響應是請求的應答,在寫入寄存器內容之後返回這個正常響應。具體的格式舉例如下:

(7)功能碼0x0F:寫多個線圈

在一個遠程設備中,使用該功能碼強制線圈序列中的每個線圈爲ON或OFF。請求PDU說明了強制的線圈參考。從零開始尋址線圈。因此,尋址線圈1爲0。

請求數據域的內容說明了被請求的ON/OFF狀態。域比特位置中的邏輯“1”請求相應輸出爲ON。域比特位置中的邏輯“0”請求相應輸出爲 OFF。

正常響應返回功能碼、起始地址和強制的線圈數量。具體的格式舉例如下:

(8)功能碼0x10:寫多個寄存器

在一個遠程設備中,使用該功能碼寫連續寄存器塊(1至約120個寄存器)。

在請求數據域中說明了請求寫入的值。每個寄存器將數據分成兩字節。

正常響應返回功能碼、起始地址和被寫入寄存器的數量。具體的格式舉例如下:

清楚了以上這些說明,我們就可以開始Modbus協議的開發之旅了。

源碼網址是:https://github.com/foxclever/Modbus

歡迎關注:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章