1. 程式人生 > >Modbus協議和應用開發介紹

Modbus協議和應用開發介紹

因業務需要了解Modbus協議的使用,因此對Modbus的協議,以及相應的C#處理應用進行了解,針對協議的幾種方式(RTU、ASCII、TCPIP)進行了封裝,以及對Modbus的各種功能碼的特點進行了詳細的瞭解,本篇隨筆基於這些知識進行了一定的梳理和介紹,主要內容包括Modbus協議簡要介紹、Modbus模擬工具使用和Modbus應用開發幾個部分。

1)Modbus協議簡要介紹

Modbus 協議是應用於電子控制器上的一種通用語言。通過此協議,控制器相互之間、控制器經由網路(例如乙太網)和其它裝置之間可以通訊。它已經成為一通用工業標準。有了它,不同廠商生產的控制裝置可以連成工業網路,進行集中監控。

此協議定義了一個控制器能認識使用的訊息結構,而不管它們是經過何種網路進行通訊的。它描述了一控制器請求訪問其它裝置的過程,如果迴應來自其它裝置的請求,以及怎樣偵測錯誤並記錄。它制定了訊息域格局和內容的公共格式。 

當在一Modbus網路上通訊時,此協議決定了每個控制器須要知道它們的裝置地址,識別按地址發來的訊息,決定要產生何種行動。如果需要回應,控制器將生成反饋資訊並用Modbus協議發出。在其它網路上,包含了Modbus協議的訊息轉換為在此網路上使用的幀或包結構。這種轉換也擴充套件了根據具體的網路解決節地址、路由路徑及錯誤檢測的方法。

Modbus由MODICON公司於1979年開發,是一種工業現場匯流排協議標準。1996年施耐德公司推出基於乙太網TCP/IP的modbus協議:modbusTCP。

Modbus協議是一項應用層報文傳輸協議,包括ASCII、RTU、TCP三種報文型別。

標準的Modbus協議物理層介面有RS232、RS422、RS485和乙太網介面,採用master/slave方式通訊。

對於序列連線,存在兩個變種,它們在數值資料表示不同和協議細節上略有不同。Modbus RTU是一種緊湊的,採用二進位制表示資料的方式,Modbus ASCII是一種人類可讀的,冗長的表示方式。這兩個變種都使用序列通訊(serial communication)方式。RTU格式後續的命令/資料帶有迴圈冗餘校驗的校驗和,而ASCII格式採用縱向冗餘校驗的校驗和。被配置為RTU變種的節點不會和設定為ASCII變種的節點通訊,反之亦然。

對於通過TCP/IP(例如乙太網)的連線,存在多個Modbus/TCP變種,這種方式不需要校驗和計算。
對於所有的這三種通訊協議在資料模型和功能呼叫上都是相同的,只有封裝方式是不同的。
Modbus有一個擴充套件版本Modbus Plus(Modbus+或者MB+),不過此協議是Modicon專有的,和Modbus不同。它需要一個專門的協處理器來處理類似HDLC的高速令牌旋轉。它使用1Mbit/s的雙絞線,並且每個節點都有轉換隔離裝置,是一種採用轉換/邊緣觸發而不是電壓/水平觸發的裝置。連線Modbus Plus到計算機需要特別的介面,通常是支援ISA(SA85),PCI或者PMCIA匯流排的板卡。

MODBUS 協議定義了一個與基礎通訊層無關的簡單協議資料單元(PDU)。

Modbus序列連路上的的PDU如下所示。

錯誤檢驗域是對報文內容執行"冗餘校驗" 的計算結果。根據不同的傳輸模式(RTU or ASCII) 使用兩種不同的計算方法。

RTU的報文格式如下所示。

ASCII碼的報文格式如下所示。

 

 在 ASCII 模式, 報文用特殊的字元區分幀起始和幀結束。一個報文必須以一個‘冒號’ ( : ) (ASCII 十六進位制3A )起始,以‘回車-換行’ (CR LF) 對(ASCII 十六進位制0D 和0A) 結束。

 

而Modbus TCP資料幀包含報文頭、功能程式碼和資料3部分。

MBAP Header長度共7個位元組,分別為Transaction identifier(事務識別符號),Protocol identifier(協議識別符號),Length(長度),

 Unitidentifier(單元識別符號)組成,具體如下表所示:

請求和響應帶有六個位元組的字首,如下:
      byte 0:     事務處理識別符號 –由伺服器複製 –通常為 0
      byte 1:     事務處理識別符號 –由伺服器複製 –通常為 0
      byte 2:     協議識別符號= 0
      byte 3:     協議識別符號= 0
      byte 4:     長度欄位 (上半部分位元組) = 0 (所有的訊息長度小於256)
      byte 5:     長度欄位  (下半部分位元組)   = 後面位元組的數量
      byte 6:     單元識別符號 (原“從站地址”)
      byte 7:     MODBUS 功能程式碼
      byte 8 on:   所需的資料

資料區:資料區是根據不同的功能碼而不同。資料區可以是實際數值、設定點、主機發送給從機或從機發送給主機的地址。

標準的Modicon控制器使用RS232C實現序列的Modbus。Modbus的ASCII、RTU協議規定了訊息、資料的結構、命令和就答的方式,資料通訊採用Maser/Slave方式。 
Modbus協議需要對資料進行校驗,序列協議中除有奇偶校驗外,ASCII模式採用LRC校驗,RTU模式採用16位CRC校驗.
ModbusTCP模式沒有額外規定校驗,因為TCP協議是一個面向連線的可靠協議。

對於常規的Modbus串列埠協議,我們來看看03功能碼的讀取暫存器的操作請求和響應程式碼瞭解下。

請求PDU格式如下所示。

 響應的PDU格式如下所示。

 一個請求讀暫存器108-110 的例項:

可以注意到,很多資料的處理,需要拆分高位低位,高位在前,低位在後的模式。 

根據這些RTU、ASCII、TCPIP的Modbus協議的不同,我們可以構建一個通用的處理程式來處理這些操作,在後面的應用開發部分繼續介紹。

 

2)Modbus模擬工具使用

一般在做Mobus前期的開發的時候,一般不是針對具體的Modbus裝置進行暫存器的處理,而是使用Modbus模擬工具來進行除錯,一般我們需要配合Modbus Slave、Modbus Poll、Virtual Serial Port Driver這幾個模擬軟體來進行開發的。

Modbus Poll :Modbus主機模擬器,用於測試和除錯Modbus從裝置。該軟體支援Modbus的RTU、ASCII、TCP/IP。用來幫助開發人員測試Modbus從裝置,或者其它Modbus協議的測試和模擬。

 Modbus Slave: Modbus從裝置模擬器,可以模擬32個從裝置/地址域。每個介面都提供了對EXCEL報表的OLE自動化支援。主要用來模擬Modbus從站裝置,接收主站的命令包,回送資料包。幫助Modbus通訊裝置開發人員進行Modbus通訊協議的模擬和測試,用於模擬、測試、除錯Modbus通訊裝置。 

 Virtual Serial Port Driver:虛擬串列埠工具,不需要串列埠接線,提供虛擬的串列埠,適合學習和除錯使用。

配合這幾款軟體,我們就可以實現串列埠Modbus協議的模擬測試了,如果我們使用Modbus的TCPIP協議,那麼我們不需要VSPD也可以。

如果我們使用Modbus協議的串列埠通訊方式,那麼我們先要使用VSPD進行串列埠的配對模擬,模擬出兩個通訊的串列埠埠,埠配對模擬成功後,我們可以看到裝置管理器中增加了兩個埠了。

 接著使用從機模擬器,模擬一個Modbus從機供測試,通過選單【Connection】【Connect】啟動,我們選擇連線方式為串列埠,埠則選擇我們配對的其中一個埠即可,如下圖所示。

 其中模式選擇RTU或者ASCII都可以,這兩個模式協議有所不同,一旦從機選擇RTU模式,那麼Modbus主機也需要選擇對應的RTU模式,反之亦然。 

其他串列埠設定,如波特率、資料位、奇偶位、停止位等預設配置即可。

如果我們選擇TCPIP模式,那麼對應Modbus主機也需要選擇TCPIP方式。

 一旦Modbus從機啟動,就會處理來自Modbus主機的指令請求(如果有的話),並做相應處理,我們可以通過【Display】【Communication】選單彈出的對話方塊,瞭解到對應請求和應答的協議詳細資訊。

 Modbus主機的啟動和ModBus從機類似,我們根據ModBus從機的配置,選擇對應的主機配置,Modbus模擬主機啟動和檢視通訊記錄介面如下所示。

另外我們可以通過【Display】裡面選擇內容顯示的進位制格式。

在從機的設定裡面,我們可以修改從機的定義資訊,以便設定對應的從機ID,功能碼,其實地址,長度或者數量的資訊,如下介面所示。

 我們可以根據實際的暫存器地址和數量,設定對應的數值,如下是顯示4個數據的內容設定和顯示內容。

設定後正常的內容顯示如下。

 同時我們也需要設定對應Modbus主機模擬器的地址和數量,正確設定後可以正常顯示。

通過更深一步的設定或者調整,我們可以極大程度的進行模擬Modbus實際裝置的處理方式,從而在沒有實際Modbus硬體裝置的情況下儘可能通過前期的模擬完成常規功能的測試和準備。

在我們開發Modbus應用的時候,我們對照相應的主從機Modbus協議請求和應答,能夠檢查我們程式的輸出是否正常,從而可以快速的開發Modbus的應用處理功能。

3)Modbus應用開發

為了模擬對接Modbus的RTU、ASCII、TCP/IP協議處理,我根據不同協議的處理方式定義了一個輔助函式,然後統一進行處理,以便達到統一呼叫的處理便利。

首先我們來看看使用串列埠模式下(RTU、ASCII)的處理介面效果,這個直接獲取模擬器Modbus Slave從機的數值進行顯示的。

TCPIP網路方式對接Modbus介面處理效果如下所示。

兩者資料均來源於Modbus Slave從機的數值,只是它們對接的方式不同。

 

串列埠的處理,我通過SerialPortUtil類來使用Windows的串列埠類,處理對應的串列埠操作,通過定義事件的方式,使得串列埠收到資料的時候,及時通知呼叫者進行介面更新處理即可。

//使用字串引數構造
serial = new SerialPortUtil(portname, this.txtBaudRate.Text, this.txtParity.Text, this.txtDataBits.Text, this.txtStopBit.Text);
//收到資料處理的事件
serial.DataReceived += Serial_DataReceived;
serial.RTUMode = this.radRTU.Checked;//預設RTU模式為True,否則使用ASCII模式

收到資料後,及時通過委託方式,通知UI進行介面的更新顯示。

/// <summary>
/// 收到串列埠響應事件後,及時進行處理(更新在介面上)
/// </summary>
/// <param name="e"></param>
private void Serial_DataReceived(DataReceivedEventArgs e)
{
    //記錄在日誌,方便複製
    LogTextHelper.Info(e.DataReceived);

    //使用委託進行處理介面控制元件的資料更新
    this.txtResponse.Invoke(new MethodInvoker(()=> 
    {
        //顯示在介面上
        this.txtResponse.AppendText(e.DataReceived);
        this.txtResponse.AppendText(Environment.NewLine);

        var dataBytes = e.BytesReceived;
        if(dataBytes != null && dataBytes.Length > 2)
        {
            var function = dataBytes[1];
            if(function > 0x80)//128
            {
                //Modbus的異常程式碼大於128,如果是異常,則可以解析錯誤
                var newFunction = function - 0x80;
                lblTips.Text = "響應有異常,功能程式碼:" + newFunction.ToString("D2");
                lblTips.Text += ",錯誤描述:" + ((ModBusExceptionCode)newFunction).ToString();
            }
            else
            {
                lblTips.Text = "響應正常";//小於128的為正常響應
            }
        }
    }));
}

而對於網路方式,我們先要定義一個Socket通訊的基類,封裝相關的通訊處理操作。

 

 

然後簡單構建一個子類進行使用,如下所示。

    /// <summary>
    /// 通訊類子類
    /// </summary>
    public class ModbusClient : BaseSocketClient
    {
        public ModbusClient()
        {
            this.Name = "ModbusClient";
        }
    }

介面處理的時候,我們只需要初始化一個ModbusClient類來使用即可,如下程式碼所示。

client = new ModbusClient();
//收到資料處理的事件
client.DataReceived += Client_DataReceived;

收到資料通知介面進行更新的操作如下所示。

private void Client_DataReceived(DataReceivedEventArgs e)
{
    //記錄在日誌,方便複製
    LogTextHelper.Info(e.DataReceived);

    //使用委託進行處理介面控制元件的資料更新
    this.txtResponse.Invoke(new MethodInvoker(() =>
    {
        this.txtResponse.AppendText(e.DataReceived);
        this.txtResponse.AppendText(Environment.NewLine);

        var dataBytes = e.BytesReceived;
        if (dataBytes != null && dataBytes.Length > 2)
        {
            //串列埠功能碼為第二個位元組,TCP/IP功能碼為第8個
            var function = dataBytes[7];
            if (function > 0x80)//128
            {
                //Modbus的異常程式碼大於128,如果是異常,則可以解析錯誤
                var newFunction = function - 0x80;
                lblTips.Text = "響應有異常,功能程式碼:" + newFunction.ToString("D2");
                lblTips.Text += ",錯誤描述:" + ((ModBusExceptionCode)newFunction).ToString();
            }
            else
            {
                lblTips.Text = "響應正常";//小於128的為正常響應
            }
        }
    }));
}

不管是串列埠的RTU或者ASCII,又或者是TCPIP的協議,我們可以通過定義一個協議封裝的輔助類ModbusQueryHelper來處理協議的具體細節。

    /// <summary>
    /// Modbus查詢訊息生成輔助類,可以用於串列埠RTU/ASCII協議,也可以用於TCPIP協議。
    /// 用於生成各種功能程式碼的訊息內容。
    /// </summary>
    public class ModbusQueryHelper
    {
        /// <summary>
        /// 是否為RTU模式,預設為True,否則為ASCII方式
        /// </summary>
        public ModbusProtocol Protocol { get; set; } = ModbusProtocol.RTU;

        /// <summary>
        /// 預設函式
        /// </summary>
        public ModbusQueryHelper()
        {
        }

        /// <summary>
        /// 引數化構造,指定RTU模式
        /// </summary>
        /// <param name="protocal">Modbus協議:ASCII,RTU, TCP,預設為RTU</param>
        public ModbusQueryHelper(ModbusProtocol protocal)
        {
            this.Protocol = protocal;
        }

而其中ModbusProtocol是一個列舉,定義如下所示。

    /// <summary>
    /// 幾種常用的Modbus協議
    /// </summary>
    public enum ModbusProtocol
    {
        /// <summary>
        /// 串列埠的ASCII模式
        /// </summary>
        ASCII,
        /// <summary>
        /// 串列埠的RTU模式
        /// </summary>
        RTU, 
        /// <summary>
        /// 網路TCPIP模式
        /// </summary>
        TCP
    }

我們通過ModbusQueryHelper 類,可以處理不同協議之間的封裝細節,並可以對各種功能碼的協議進行封裝處理。

 

 

 

 以上就是 相關Modbus的應用處理和封裝,對於常規的Modbus協議可以極大簡化對接處理,在實際對接Modbus裝置的時候,我們只需要根據對應的說明書,獲取對應的內容,就可以把例如溫度、溼度、轉速等一些裝置或者機器人的引數獲得,並記錄在資料庫裡面,然後在應用模組中整合一些圖表展示就可以很好的實現看板功能了。