多平臺下Modbus通信協議庫的設計(一)

来源:http://www.cnblogs.com/kmust/archive/2016/07/30/badwell.html
-Advertisement-
Play Games

1.背景 1.1.範圍 MODBUS 是 OSI 模型第 7 層上的應用層報文傳輸協議, 它在連接至不同類型匯流排或網路的設備之間提供客戶機/伺服器通信。 自從 1979 年出現工業串列鏈路的事實標準以來, MODBUS 使成千上萬的自動化設備能夠通信。 目前,繼續增加對簡單而雅觀的 MODBUS 結 ...


 

1.背景

1.1.範圍

MODBUS OSI 模型第 7 層上的應用層報文傳輸協議, 它在連接至不同類型匯流排或網路的設備之間提供客戶機/伺服器通信。

自從 1979 年出現工業串列鏈路的事實標準以來, MODBUS 使成千上萬的自動化設備能夠通信。

目前,繼續增加對簡單而雅觀的 MODBUS 結構支持。互聯網組織能夠使 TCP/IP 棧上的保留系統埠 502 訪問 MODBUS

MODBUS 是一個請求/應答協議,並且提供功能碼規定的服務。MODBUS 功能碼是 MODBUS請求/應答 PDU 的元素。

 

1.2.縮略語

ADU 應用數據單元

HDLC 高級數據鏈路控制

HMI 人機界面

IETF 網際網路工程工作組

I/O 輸入/輸出設備

IP 互連網協議

MAC 介質訪問控制

MB MODBUS 協議

MBAP  MODBUS應用協議

PDU 協議數據單元

PLC 可編程邏輯控制器

TCP 傳輸控制協議

1.3.MODBUS體繫結構實例

 

每種設備(PLCHMI、控制面板、驅動程式、動作控制、輸入/輸出設備)都能使用 MODBUS協議來啟動遠程操作。

在基於串列鏈路和以太 TCP/IP 網路的 MODBUS 上可以進行相同通信。

一些網關允許在幾種使用 MODBUS 協議的匯流排或網路之間進行通信。

1.4.協議描述

MODBUS 協議定義了一個與基礎通信層無關的簡單協議數據單元(PDU) 。特定匯流排或網路上的 MODBUS 協議映射能夠在應用數據單元(ADU)上引入一些附加域。

 

啟動 MODBUS 事務處理的客戶機創建 MODBUS 應用數據單元。 功能碼向伺服器指示將執行哪種操作。

MODBUS 協議建立了客戶機啟動的請求格式。

用一個位元組編碼 MODBUS 數據單元的功能碼域。有效的碼字範圍是十進位 1-255128-255 為異常響應保留) 。當從客戶機向伺服器設備發送報文時,功能碼域通知伺服器執行哪種操作。

向一些功能碼加入子功能碼來定義多項操作。

從客戶機向伺服器設備發送的報文數據域包括附加信息,伺服器使用這個信息執行功能碼定義的操作。這個域還包括離散項目和寄存器地址、處理的項目數量以及域中的實際數據位元組數。

在某種請求中,數據域可以是不存在的(0 長度) ,在此情況下伺服器不需要任何附加信息。功能碼僅說明操作。

如果在一個正確接收的 MODBUS ADU 中,不出現與請求 MODBUS 功能有關的差錯,那麼伺服器至客戶機的響應數據域包括請求數據。如果出現與請求 MODBUS 功能有關的差錯,那麼域包括一個異常碼,伺服器應用能夠使用這個域確定下一個執行的操作。

例如, 客戶機能夠讀一組離散量輸出或輸入的開/關狀態, 或者客戶機能夠讀/寫一組寄存器的數據內容。

當伺服器對客戶機響應時,它使用功能碼域來指示正常(無差錯)響應或者出現某種差錯(稱為異常響應) 。對於一個正常響應來說,伺服器僅對原始功能碼響應。

 

對於異常響應,伺服器返回一個與原始功能碼等同的碼,設置該原始功能碼的最高有效位為邏輯 1

 

  註釋:需要管理超時,以便明確地等待可能不會出現的應答。

串列鏈路上第一個MODBUS執行的長度約束限制了MODBUS PDU大小 (最大RS485ADU=256位元組) 。

因此, 對串列鏈路通信來說,MODBUS PDU=256-伺服器地址(1 位元組)-CRC2 位元組)=253位元組。

從而:

RS232 / RS485 ADU = 253 位元組+伺服器地址(1 byte) + CRC (2 位元組) = 256  位元組。

TCP MODBUS ADU = 249 位元組+ MBAP (7 位元組) = 256  位元組。

MODBUS 協議定義了三種 PDU。它們是:

MODBUS 請求 ,modbus_request

MODBUS 響應 ,modbus_reply

MODBUS 異常響應 ,modbus_reply_exception

1.5.數據模型

MODBUS 以一系列具有不同特征表格上的數據模型為基礎。四個基本表格為:

基本表格

對象類型

訪問類型  

內容

離散量輸入  

單個比特  

只讀  

I/O 系統提供這種類型數據

線圈

單個比特  

讀寫

通過應用程式改變這種類型數據

輸入寄存器

16-比特字  

只讀  

I/O 系統提供這種類型數據

保持寄存器

16-比特字  

讀寫

通過應用程式改變這種類型數據

 

1.6.設計背景

因為公司發展需要需要研發一個基於ARM9晶元的中央控制器(如下:設計架構圖和硬體設計圖),可以用來控制前端設備,並與雲平臺和用戶進行交互。其中一個重要的功能就是要求設備設備能採集前端設備(表示在控制器之前的所有設計,就是前段設備)的信息,同時也可以對前端設備採集來的信息進行反饋控制。經過市場研究和調查,現在採集設備485通信用的比較多,而且大多設備都支持MODBUS通信協議,因此開發一個Modbus協議庫,越來越有必要。

設計架構圖:

 

硬體結構設計:

 

在我們進行軟體設計的同時,也同步進行硬體的設計,但是一些前段設備,我們都是從外面的產家進行購買的,包括氣體感測器(如COCH4等)、電量採集、流量採集(水流、氣體等)的採集,控制一類的主要有燈光控制、門禁、水泵等。同時,如果有相關的同行,或者產家也可以和我聯繫,我們正在進行採購測試的,如果合適的話,我們也可以建立長期的合作伙伴。

2.功能碼

2.1.功能碼分類

有三類 MODBUS 功能碼。它們是:

公共功能碼

  •  是較好地被定義的功能碼,
  • 保證是唯一的,
  •  MODBUS 組織可改變的,
  • 公開證明的,
  • 具有可用的一致性測試,
  • MB IETF RFC 中證明的,
  • 包含已被定義的公共指配功能碼和未來使用的未指配保留供功能碼。

用戶定義功能碼

  • 有兩個用戶定義功能碼的定義範圍,即 65 72 和十進位 100 110
  • 用戶沒有 MODBUS 組織的任何批准就可以選擇和實現一個功能碼
  • 不能保證被選功能碼的使用是唯一的。
  • 如果用戶要重新設置功能作為一個公共功能碼,那麼用戶必須啟動 RFC,以便將改變引入
  •  公共分類中,並且指配一個新的公共功能碼。

保留功能碼

  • 一些公司對傳統產品通常使用的功能碼,並且對公共使用是無效的功能碼。

 

 

 

2.2.公共功能碼定義

 

 

 

3.MODBUS通信模塊設計

3.1.模塊概述

Modbus通信模塊是多功能控制器中必不可少的一個功能,有了它才能使外部設備(如除濕裝置、熒光測溫、溫濕度檢測、六氟化硫檢測)與COM控制器的進行數據傳輸、遠程式控制制。因此Modbus通信協議的地位自然不言而喻。

3.2.設計目標

實現對外設數據的讀取和控制功能。

3.3.設計原則

儘量做到模塊的分層設計。

3.4.運行環境

操作系統:Linux

3.5.模塊結構設計

 

 

 

4.模塊功能設計

 

4.1.發送組包功能設計

 

 

 

 

 

4.2.接收解包功能設計

 

 

4.3.串口管理模塊設計

 

 

 

4.3.1.電腦串口的引腳說明

序號

信號名稱

符號

流向

功能

2

發送數據

TXD

DTE→DCE

DTE發送串列數據

3

接收數據

RXD

DTE←DCE

DTE 接收串列數據

4

請求發送

RTS

DTE→DCE

DTE 請求 DCE 將線路切換到發送方式

5

允許發送

CTS

DTE←DCE

DCE 告訴 DTE 線路已接通可以發送數據

6

數據設備準備好

DSR

DTE←DCE

DCE 準備好

7

信號地

 

 

   信號公共地

8

載波檢測

DCD

DTE←DCE

表示 DCE 接收到遠程載波

20

數據終端準備好

DTR

DTE→DCE

DTE 準備好

22

振鈴指示

RI

DTE←DCE

表示 DCE 與線路接通,出現振鈴

 4.3.2.串口操作的頭文件定義

#include <stdio.h>      /*標準輸入輸出定義*/

#include <stdlib.h>     /*標準函數庫定義*/

#include <unistd.h>     /*Unix 標準函數定義*/

#include <sys/types.h>  /*數據類型,比如一些XXX_t的那種*/

#include<sys/stat.h>   /*定義了一些返回值的結構,沒看明白*/

#include<fcntl.h>      /*文件控制定義*/

#include<termios.h>    /*PPSIX 終端控制定義*/

#include<errno.h>      /*錯誤號定義*/

4.3.3、串口設置

(1)、串口文件位於/dev目錄下,而且以tty開飛的,


其中:串口一 為 /dev/ttyS0,串口二 為 /dev/ttyS1,等等。其中/dev/ttyUSB* 表示USB轉串口。
如:

(2)、串口的打開和設置

打開串口是通過使用標準的文件打開函數操作:

int fd;

/*以讀寫方式打開串口*/

fd = open( "/dev/ttyS0", O_RDWR);

if (-1 == fd){

/* 不能打開串口一*/

MFS_LOG_TRACE_ERR(" 提示錯誤!");

}

 

(3)、設置串口

最基本的設置串口包括波特率設置,效驗位和停止位設置,串口的設置主要是設置 struct termios 結構體的各成員值。

 

struct termio

{   

unsigned short  c_iflag; /* 輸入模式標誌 */

    unsigned short  c_oflag;     /* 輸出模式標誌 */

    unsigned short  c_cflag;     /* 控制模式標誌*/  

    unsigned short  c_lflag;     /* local mode flags */  

    unsigned char    c_line;           /* line discipline */

 

    unsigned char    c_cc[NCC];    /* control characters */

};

 

(4)、串口的讀寫

如果不是開發終端之類的,只是串口傳輸數據,而不需要串口來處理,那麼使用原始模式(Raw Mode)方式來通訊。

發送數據:

char  buffer[1024];

int    Length;

int    nByte;

nByte = write(fd, buffer ,Length)

 

 

讀取串口數據:

使用文件操作read函數讀取,如果設置為原始模式(Raw Mode)傳輸數據,那麼read函數返回的字元數是實際串口收到的字元數。

可以使用操作文件的函數來實現非同步讀取,如fcntl,或者select等來操作。

char  buff[1024];

int    Len;

int  readByte = read(fd,buff,Len);

關閉串口:

關閉串口就是關閉文件。

close(fd);

 

 

 

4.4.介面設計

Modbus通信協議設計:

 

/************************外部介面************************************/

/*發送組包*/

/*參數說明:

Modbus_t *ctx : 操作設備的簡要信息

modbus_msg *msg:modbus消息結構體,指需要進行打包或解包的信息

*/

 int  modbus_pack(modbus_t  *ctx,msg_src *src ,modbus_msg  *msg);//pack *msg);

 

/*參數說明:

Modbus_t *ctx : 操作設備的簡要信息

unsigned int *src:數據的目標地址

modbus_msg *msg:modbus消息結構體,指需要進行打包或解包的信息

*/

 Int modbus_unpack(modbus_t  *ctx,msg_src* req,msg_src* rsp,resolve_src* dest);

 

/*modbus上下文信息結構體*/

typedef struct modbus_t{

modbus_type_t  type;    //modbus的通信類型,rtu、ascii、tcp等

int slave;    //客戶端地址

int *s;    //表示實例化之後的串口編號

unsigned int devicecode;    //設備編碼

unsigned int functiontype;    //功能類別的編碼

struct timeval timeout;    //延時

char *devicename;    //設備名稱

modbus_error_recovery_mode error_recovery;    //錯誤的恢復模式

int debug;

 

};

 

 

說明:設備類別編碼優先順序大於功能類別編碼,解決部分設備可能由於更換產家等原因導致功能相同,但是數據協議不同的情況

 

/*Modbus消息結構體*/

typedef struct modbus_msg{

uint8_t function_code;
//modbus的功能碼 int start_addr; //數據的起始地址 int data_length; //數據長度(數據個數) int write_data; //寫入數據的值 uint8_t *s_dest; //small dest uint16_t *dest; //線圈、離散量數據 uint16_t *regisdate; //寄存器操作的數據 }

 

/*********************內部介面**************************************/

 

 

 

 

//源消息結構體

typedef struct  _modbus_src_t

{

 uint8_t  *msg_src;    //數組的地址

 int  msg_len;    //數組的長度

}msg_src;

 

typedef enum

{

    MODBUS_ERROR_RECOVERY_NONE          = 0,

    MODBUS_ERROR_RECOVERY_LINK          = (1<<1),

    MODBUS_ERROR_RECOVERY_PROTOCOL      = (1<<2)

} modbus_error_recovery_mode;

 

 

 

串口管理模塊設計:

 

/*************************介面的設計*************************/

typedef struct  _dts

{

serial_mode  serial_mode;    //串口的通信類型,RS485、RS232等

int s;    //表示實例化之後的串口編號

unsigned int devicecode;    //設備編碼

struct timeval timeout;    //延時

char *devicename;    //設備名稱

int error_recovery;    //錯誤的恢復模式

int debug;

void *backend_data;

}dts_t,dts;

(命名方式:)device to seial

dts* serial_set_new(const char *device, int baud, char parity,  int data_bit, int stop_bit,struct timeval timeout);

 

//串口的結構設計

typedef struct _serial {

    /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */

    char *device;

    /* Bauds: 9600, 19200, 57600, 115200, etc */

    int baud;

    /* Data bit */

    uint8_t data_bit;

    /* Stop bit */

    uint8_t stop_bit;

    /* Parity: 'N', 'O', 'E' */

    char parity;

#if defined(_WIN32)

    struct win32_ser w_ser;

    DCB old_dcb;

#else

    /* Save old termios settings */

    struct termios old_tios;

#endif

#if HAVE_DECL_TIOCSRS485

    int serial_mode;

#endif

#if HAVE_DECL_TIOCM_RTS

    int rts;

    int rts_delay;

    int onebyte_time;

    void (*set_rts) (dts *ctx, int on);

#endif

    /* To handle many slaves on the same link */

    int confirmation_to_ignore;

} serial_t;

 

//串口的工作模式

typedef enum _serial_mode

{

SERIAL_RS232=0,

SERIAL_RS485

}serial_mode;

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • intern()方法: public String intern() JDK源代碼如下圖: 返回字元串對象的規範化表示形式。 一個初始時為空的字元串池,它由類 String 私有地維護。 當調用 intern 方法時,如果池已經包含一個等於此 String 對象的字元串(該對象由 equals(Ob ...
  • 1、位運算符 位運算符用來對整型數的指定位進行置位,如果被操作數是字元串,則對該字元串的ASCII碼值進行操作。 2、遞增遞減運算符 3、三元運算符 三元運算符(?:),又稱三目運算符,作用類似選擇語句,用於根據一個表達式的真假,從另外兩個表達式中選擇一個。用法如下: 邏輯表達式 ? 表達式1 : ...
  • 反射:java.lang.reflect 要被載入的類: 載入運行的類: 輸出的結果: Program start.loading WaitingforLoaderConstructor of WaitingforLoader without anything.Constructor of Wait ...
  • 運行結果: 60SundaySundayTuesdayWednesdaytrue7MondayTuesdayWednesdayThursadyFridaySaturdaySunday ...
  • 1. 併發:在操作系統中,是指一個時間段中有幾個程式都處於已啟動運行到運行完畢之間,且這幾個程式都是在同一個處理機上運行。其中兩種併發關係分別是同步和互斥 2. 互斥:進程間相互排斥的使用臨界資源的現象,就叫互斥。 3. 同步:進程之間的關係不是相互排斥臨界資源的關係,而是相互依賴的關係。進一步的說 ...
  • 發大水 ...
  • 在上一節中,我們已經創建了一個Django模型Post,並使Post模型與資料庫同步。這一節中,我們將介紹Django管理站點,通過Django管理站點來管理我們創建的Post模型實例。 為你的模型創建一個控制管理站點 好了,我們已經定義了一個post模型,現在,我們將要創建一個簡單的管理站點,來管 ...
  • 類是組成java程式的基本要素,是java中的一種重要的複合數據類型。它封裝了一類對象的狀態和方法,是這一類對象的原型。一個類的實現包括兩個部分:類聲明和類體,基本格式: class <class name> { 屬性 方法 } 其中,class是關鍵字,用來定義類。“class <class na ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...