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