前言
在之前的兩篇文章中,我們講解了串口的基礎知識和在安卓中使用串口通信的方法,如果還沒看過之前文章的同學們,建議先看一遍,不然可能會不理解這篇文章講的某些內容。
事實上,在實際應用中,我們很少會直接使用串口通信,一般都會使用到 Modbus。
因為正如我上篇文章所說,如果直接使用串口通信的話,需要我們自定義數據層協議,或者干脆就直接發送一個 byte 的數字進行通信,這顯然是不方便的,也不安全的。
例如我上篇提到過的一個問題,我所使用的驅動版廠商定義的協議中沒有定義數據長度(或者在數據中附上數據長度),也沒有定義停止符號,這會導致出現“沾包”或“分包”情況時不好區分數據。
并且自定義協議還需要自己去解析并處理數據,使用起來不是那么方便。
所以,我司在嘗試過直接使用串口通信后,最終還是決定放棄直接使用串口通信,而是改用 Modbus 通信。
本篇文章屬于系列文章的擴展篇,我們將講解 Modbus 的基礎知識以及如何在安卓中使用 Modbus。
本文中部分圖表來自文末標注的參考資料
Modbs 基礎
簡介
Modbus 是一種應用層報文傳輸協議,由 Modicon 公司在 1979 年發布,是為了解決 PLC 通信而研發的協議。
因為 Modbus 是開源的且無著作權要求、易于部署維護、可靠性強的特性,所以 Modbus 已經成為工業領域通信協議事實上的業界標準,并且現在是工業電子設備之間常用的連接方式。
由于 Modbus 定義的只是應用層的報文協議,所以它可以使用串口(RS232、RS485)、以太網作為物理層接口。
Modbus 分為三種傳輸模式:RTU、ASII、TCP。
在使用 Modbus 時,所有設備的傳輸模式必須相同。
RTU 使用二進制數據傳輸、ASCII 使用 ASCII 字符傳輸。
使用串口連接時支持 RTU 和 ASCII 模式。
使用以太網連接時支持 TCP 模式。
因為本系列文章的重點在于講解串口通信,所以我們不過多講解 TCP 模式,同時,由于 ASCII 模式在目前實際應用中比較少,我們一般都是使用的 RTU 模式。故,我們會重點講解 Modbus RTU。如果對其他傳輸模式感興趣的可以閱讀參考資料 4 的文檔。
額外說明一下,Modbus 和 RS232、RS485 的區別。
RS232、RS485定義的是物理層標準,即接線方式,電平高低,數據傳輸方式等。
而 Modbus 是應用層協議,即定義了上述物理層傳輸過來的數據應該以什么樣的格式去解析。
Modbus RTU
使用串口作為物理層協議時,通常采用的是 RS485 。
而我們在第一篇文章就說過,RS485 支持一主多從多個設備同時連接,所以使用 RS485 的 Modbus 同樣支持多個設備連接。在標準負載情況下,支持一個主機連接最多32個從機。并且在連接設備時,只能使用菊花鏈連接,不能使用星型網絡:
另外,Modbus 是一種請求/應答協議,即只能通過主站(主機)發送請求給從站后,從站響應數據給主站,而不能從站直接主動發送數據給主站。
儲存區數據模型
在 Modbus 中定義了4種不同的數據模型,具體如下:
名稱 | 數據類型 | 訪問類型 | 說明 |
---|---|---|---|
離散量輸入 | 單個比特(bit) | 只讀 | I/O系統提供 |
線圈 | 單個比特(bit) | 讀寫 | 可通過應用程序改寫 |
輸入寄存器 | 字(word,16bit) | 只讀 | I/O系統提供 |
保持寄存器 | 字(word,16bit) | 讀寫 | 可通過應用程序改寫 |
其中 線圈 和 離散量輸入 又可以稱為 輸出線圈 和 輸入線圈。
它們的數據長度都是一個 bit,即只能表示 1 或 0,表現在程序中就是一個 Boolean 類型的數據。對于安卓程序員來說,可能會疑惑啥是線圈,其實這兩個模型之所以叫做線圈是因為 Modbus 是為了 PLC 通信而編寫的協議,而在 PLC 中一些物理設備(例如繼電器)只有兩種狀態:斷開與接通(即 0 或 1 ,或者 Boolean 的 false 與 true ),這些物理設備的狀態切換一般都是依賴于線圈的通/斷電來實現,所以在 Modbus 中就將這種類型的數據稱為 線圈。
而 輸入寄存器 和 保持寄存器 又可以稱為 輸入寄存器 和 輸出寄存器。
它們的數據長度是一個 word,即 16 bit,2 byte,表現在程序中可以看成一個 Int 類型。
顯然,在同一個設備中不同的數據模型肯定不止一個可用的數據區塊,理論上來說,每種數據模型最大可以定義 65536 個數據區塊。
因此,每種數據模型的地址定義為如下:
數據模型 | 地址范圍 |
---|---|
線圈 | 00001-09999 |
離散輸入 | 10001-19999 |
輸入寄存器 | 30001-39999 |
保持寄存器 | 40001-49999 |
可以看到,雖然我們上面說每種模型理論上支持 65536 個數據區塊,但是實際使用中每種數據模型一般都只會定義最大 10000 個數據區塊。
Modbus 允許將四種不同的數據模型存放在不同的數據區塊,這樣使用不同的功能碼(下面會說什么是功能碼)讀到的是不同的數據:
同時,Modbus 也可以將不同的數據模型映射到同一個數據區塊中,這樣一來,不同的功能碼讀取到的可能是相同的數據:
功能碼
在上一節我們介紹了儲存區數據模型,那么我們要如何去讀取不同的數據模型數據呢?或者說,在 Modbus 中是怎么區分不同的數據模型?
此時,就要用到 功能碼。
在 Modbus 中定義了三種類型的功能碼:
- 公共功能碼 :Modbus 組織定義的標準的公開的通用的功能碼,包括已定義的和保留的功能碼
- 用戶自定義功能碼 :用戶可以自定義自己需要的功能碼,范圍在 65-72 和 100-110(都是十進制)之間。
- 保留功能碼 :一些公司的傳統設備中使用的功能碼,對公共功能碼無效。
公共功能碼定義了如下幾種:
而我們一般會使用到的有以下幾種:
可以看到,我們常用的有 8 個功能碼,其實仔細一看就能看出不過是讀所有數據模型;以及可寫數據模型和寫單個/寫多個的排列組合。
讀取數據時所有數據模型均支持只讀取單個和同時讀取多個數據,并且使用的都是同一個功能碼。
寫入數據同樣支持只寫入單個數據和同時寫入多個數據,但是寫入單個和寫入多個的功能碼是分開的。
可能有細心的讀者發現了,為什么表中的所有 寄存器地址 都是一樣的啊,這是因為上表中的 PLC 地址使用的是絕對地址,一般用于文檔中或程序中。
而實際設備的寄存器地址則使用的是相對地址。由于我們已經通過功能碼區分開了不同的數據區塊,所以為了節約傳輸時的字節占用,直接使用相對地址即可(如果使用絕對地址,那么現在的字節數不夠表示所有地址)。
主/從站
上文中提到過,使用串口的 Modbus 是主-從協議。即,在同一時刻,只有一個主節點和一個或多個子節點連接在同一個串行總線上。
Modbus 的通信總是由主節點發起,子節點響應。并且子節點之間不會相互通信。
在 Modbus 中,主節點沒有地址,每個子節點都有自己唯一的地址(1-247),通常稱為從站地址。
主節點有兩種方式發出請求:單播模式與廣播模式。
在單播模式中,主站(主節點)發送一個帶有從站(子節點)地址的請求給當前連接的所有設備,但是只有從站地址符合的從站會響應該請求,并返回數據。其他設備不會響應也不會執行任何操作(讀取到地址不符合后直接拋棄這個請求報文)。在這個模式中會產生兩個報文:主站的請求報文和從站的響應報文。
在廣播模式中所有從站都不會發送響應報文給主站,但是會執行請求的操作,并且主站的請求會發送給所有從站。廣播模式一般用于寫數據。此時主站發送的請求報文中的從站地址為 0 ,表示廣播。
數據幀
一個 Modbus RTU 的報文幀由 4 個部分組成:
8位從站地址+8位功能碼+最大252*8位數據+16位差錯校驗
在 RTU 中通常使用的錯誤校驗方式是 CRC 校驗(眼熟嗎?CRC 又出現了)
不知道你們有沒有發現,這里的功能碼使用了 2 byte ,但是上面介紹功能碼時明明最大才到 127 ,那么剩下的一半去哪兒呢?
在 Modbus 定義中,從機如果能夠正確處理主機的請求,則返回報文中的功能碼將和主機請求的功能碼一樣,如果出現錯誤,無法正確的處理請求,則從機返回報文的功能碼將是最高位為 1 的功能碼,即 128-255 。
數據位在不同的功能碼以及主機請求還有從機響應都有不同的數據內容和長度,例如請求讀取線圈則數據位的內容為:2字節數據表示讀取線圈起始地址+2字節數據表示要讀取的線圈數量。
此時從機將會按照請求讀取的線圈數量返回數據,數據格式為:1字節表示數據的字節數+N字節表示讀取到線圈狀態數據。如果讀取到的線圈狀態數據不是 8 位的整數,則會在后面填充 0 使其滿足 8 位的倍數。
數據位在某些情況下,可以為空。
下面舉一個數據幀的完整例子(例子來自參考資料 1)。
我們有一個從站是溫濕度傳感器,從站地址為 1,它會將采集到的濕度寫入保持寄存器的 40001 區塊中;溫度寫入保持寄存器的 40002 區塊中。此時我們發送讀取保持寄存器請求去獲取它的溫濕度信息。
則,主機的請求報文為:
0103040146013B5A59
分別拆解這個數據幀為:
01 :從站地址
03 :功能碼,讀保持寄存器
00 00 :讀取的起始寄存器地址(對應 40001 的相對地址)
00 02 :讀取的寄存器長度(這里表示連續讀取兩個寄存器)
C4 0B :CRC校驗碼
從機在接收到請求后,響應報文為:
0103040146013B5A59
拆解數據:
01:從站地址
03: 功能碼,讀保持寄存器
04 :讀取到的數據的字節長度(這里表示4字節)
01 46 01 3B :讀取到的數據,前兩個字節為濕度(換算成十進制為 326 ,即 32.6% ),后兩個字節為溫度(十進制為 315,即 31.5 攝氏度)
5A 59 :CRC校驗碼
這里提一句,別糾結為啥讀取到的溫濕度的值要除以 10 才是實際值,因為這是溫濕度傳感器廠家定義的。
-
MODBUS
+關注
關注
28文章
1821瀏覽量
77316 -
串口通信
+關注
關注
34文章
1627瀏覽量
55732 -
安卓
+關注
關注
5文章
2136瀏覽量
57603
發布評論請先 登錄
相關推薦
為何選擇智炫安卓胎壓監測?
為何要將CH340的ATD+Eclipse上的安卓工程移植到AndroidStudio
安卓與ESP8266串口WIFI模塊的通信實現相關資料推薦
安卓設備如何通過USB與多串設備通信?
用單片機串口和modbus poll 進行通信
![用單片機<b class='flag-5'>串口</b>和<b class='flag-5'>modbus</b> poll 進行<b class='flag-5'>通信</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論