前世今生
照例簡單說下這個協議的歷時,Modicon公司于1979年制定了Modbus協議標準,并用在其PLC產品上。后來Modicon公司被施耐德收購。已成為一種事實標準協議,同時也被IEC-61158工業(yè)通信總線規(guī)范收錄于type 15子集。所謂一流的企業(yè)做標準,二流的企業(yè)做品牌,三流的企業(yè)做產品。這些標準國人都基本是使用者,而非締造者,所以使用一下,產品上印個標志,做做相關的測試認證都要給老外交錢。這里只是順帶牢騷幾句,與本文想說的無關。打??!Modbus的應用除了常見的過程控制系統(tǒng),在其他很多領域都有其身影,比如一些樓宇控制,消防控制等等都有大量的產品采用Modbus協議,因為這個協議實現簡單,工作可靠,還是標準化的協議!Modbus分很多實現版本,總的來說是一種應用層協議。從OSI七層模型來看,位于第七層應用層。它定義了在不同類型的總線或網絡上連接的設備之間提供 ”客戶端/服務器“ 通信。對于使用串口的版本,也定義了layer 1 和 layer 2,實現在主站和一個或多個從站之間交換MODBUS 報文。具體有哪些版本呢?其實主要分兩種:- Modbus RTU(Remote Terminal Unit 遠程終端單元):這種方式常采用RS-485做為物理層,一般利用芯片的串口實現數據報文的收發(fā),報文數據采用二進制數據進行通信。
- Modbus ASCII :報文使用 ASCII 字符。ASCII 格式使用縱向冗余校驗和。Modbus ASCII 報文由冒號 (":")開始 和換行符 (CR/LF) 結尾構成。
- Modbus TCP/IP 或 Modbus TCP :這是一種 Modbus 變體版本,使用 TCP/IP 網絡進行通信,通過 502 端口進行連接。報文不需要校驗和計算,因為以太網底層已經實現了CRC32 數據完整性校驗。
- Modbus over TCP/IP 或 Modbus over TCP 或 Modbus RTU/IP :這也是一種 Modbus 變體,與 Modbus TCP 的不同之處在于,與 Modbus RTU 一樣,校驗和包含在報文中。
- Modbus UDP:也有在UDP上傳輸Modbus報文的,不過需要做錯誤重傳機制,這么干的應該不多。
Modbu標準
概況
Modbus-串口版本基本定義了物理層、鏈路層以及應用層:物理層可以使用485或232, 這里EIA/TIA都是標準協會的簡稱,也常寫成RS-485/RS-232。- RS-485:半雙工收發(fā)接口,這是最為常用的modbus物理層,信號采用差分電平編碼,用一對雙絞線現場布線,抗干擾性能也不錯
- RS-422:全雙工收發(fā)接口,這種物理層也有比較多的應用,信號采用差分電平編碼,需要兩對雙絞線現場布線,抗干擾性能也不錯。與RS-485相比,其優(yōu)勢在于可以實現全雙工,通信的效率高些,所需要的代價就是現場布線需要兩對雙絞線,增加了一定的成本。
- RS-232:全雙工收發(fā)接口,這個基本用在點對點通信場景下,不適合多點拓撲連接,采用共模電平編碼,一般需要Rxd/Txd/Gnd三根線連接。
鏈路層
單播與廣播
modbus從鏈路控制的角度屬于主(Master)/從(Slave)方式,比較簡單。對介質的訪問控制相當于時分復用。通訊總是由主站發(fā)起,但可分為單播和廣播兩種方式,單播就是主站向特定的從站發(fā)出通訊請求,廣播是向總線所有的設備發(fā)起通訊請求。看下面兩個圖就比較清楚了:- 單播(unicast): 報文中的地址字段指定所需要訪問的設備,該設備收到請求后作出對應的應答。
- 廣播(Broadcast):主站向總線所有設備發(fā)出廣播報文,所有從設備都不做應答,報文中地址為0則為廣播請求:
尋址
?modbus-RTU從設備都具有一個單字節(jié)地址,其地址分配定義為:- 地址0:廣播地址,所有的從設備必須處理廣播報文。
- 1-247:從設備地址,主設備是沒有地址的,這一點需要注意。
- 248-255:保留地址
報文結構
前面說過,通信模式是主/從方式,也即主請求、從應答的方式。無論主請求報文,還是從應答報文其結構都是如下圖這樣的:- 地址:取值范圍是0-247,如果是0,就是主站廣播報文;如果是1-247,則有可能是主站請求或者從站應答。
- 功能碼:也就是報文命令,代表主站對從站的操作,讀或者寫
- 數據:數據字段,主請求報文,從應答報文會有所差異。也就是說假設抓取總線報文,如何區(qū)分是主站請求還是從站應答,則需要通過數據字段進行區(qū)分了。
- CRC校驗:采樣CRC16,16位循環(huán)冗余校驗。
主站狀態(tài)機
鏈路層一個最最重要的職責就是對通訊介質的管理,如果沒有介質的管理,就不能成其為總線。Modbus如何進行介質管理呢?前面介紹了從鏈路管理的角度來看,總線介質上發(fā)送報文的有兩種設備,一種是主設備,另一種是從設備。對于主設備來說,它會有兩種報文會向總線介質發(fā)送:一種是廣播報文,另一種是單播報文。那么究竟是怎么控制介質的呢?其實很簡單,看看下面這個狀態(tài)機就很清楚了:圖中的事件的產生,將會觸發(fā)主設備鏈路狀態(tài)機從一個狀態(tài)遷移到另一個狀態(tài),再事件觸發(fā)后,還伴隨動作需要執(zhí)行。- 空閑:主設備處于空閑態(tài)。如果此時應用程序需要發(fā)送一個從設備請求,就會切換到等待應答狀態(tài);如果此時應用需要發(fā)送總線廣播,此時,主設備就切換到廣播延時。
- 等待應答:在等待應答狀態(tài)時,主設備將等待來自從設備的應答報文,如果接收到從站的報文,則進入應答處理。如果等待超時則進入錯誤處理狀態(tài)機;在等待過程中,狀態(tài)機不發(fā)生遷移。
- 廣播延時:如果主設備需要發(fā)送廣播報文,則發(fā)送完就進入廣播延時狀態(tài)。這里為什么要延時呢?延時的設計目的就是留給從設備一點時間去處理接收到的廣播請求。如果主設備沒有這個延時,那么如果應用馬上在發(fā)一個請求,則從設備有可能來不及處理。但是從設備只做接收處理,任何從設備都不可以對廣播報文進行應答。
從站狀態(tài)機
對于從設備來說,只接收主設備請求或者發(fā)送應答,因此從設備的狀態(tài)機就更簡單了。從設備的狀態(tài)機很簡單,系統(tǒng)一上電就進入空閑狀態(tài),空閑態(tài)一直監(jiān)聽總線報文,當收到一個完整的報文時,首先校驗報文的正確性,再檢查報文是否是發(fā)給該設備的,如果是請求本設備的,則先完成請求的操作,然后準備好應答報文,如果出錯則將出錯信息發(fā)送給主站。如果收到的是主站廣播請求,則僅僅處理相應請求,不做任何應答。字節(jié)編碼格式
報文以字節(jié)為單位,每個字節(jié)再物理層又以什么格式出現再總線呢?其字節(jié)編碼格式為:1個起始位,8個數據位,1個校驗位,1個停止位。校驗位可選擇奇校驗、偶校驗、或無校驗,有一個地方標準特別說明了一下,如果選擇無校驗,會采用2個停止位。介質管理
對于幀的時間管理,其實就是對介質的沖突管理,modbus-RTU對于介質管理規(guī)定了2個重要的時間參數,以實現成幀、沖突管理等。來看看下面這幾個圖:這個圖可以用于斷幀,也就時判斷是否接收到一個完整的幀,因此只需要使用一個定時器在每次收到一個字節(jié)后,就重啟一個3.5字節(jié)定時器,如果這個3.5字節(jié)定時器中斷了,就證明收到了一個Modbus報文,至于這個報文是不是正確的報文,可以在進一步根據幀格式進行校驗。另外還規(guī)定了報文需要連續(xù)發(fā)送,字節(jié)間隔不得超過1.5字節(jié)時間。上面對于介質管理所規(guī)定得時分復用,可以用一個狀態(tài)機來描述:當T3.5定時器超時后,對于modbus-RTU來說,幀校驗采用CRC-16。對于CRC-16得實現,標準給出了查表法得實現栗子:查表法:static?unsigned?char?auchCRCHi[]?=?{?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,
0x40,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,
0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,
0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,
0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,
0x40,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,?0xC0,
0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,
0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,
0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,
0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,
0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,
0xC0,?0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,
0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,
0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,
0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,
0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,?0x40,?0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,
0x00,?0xC1,?0x81,?0x40,?0x01,?0xC0,?0x80,?0x41,?0x01,?0xC0,?0x80,?0x41,?0x00,?0xC1,?0x81,
0x40?}?;?
?
static?char?auchCRCLo[]?=?{?
0x00,?0xC0,?0xC1,?0x01,?0xC3,?0x03,?0x02,?0xC2,?0xC6,?0x06,?0x07,?0xC7,?0x05,?0xC5,?0xC4,
0x04,?0xCC,?0x0C,?0x0D,?0xCD,?0x0F,?0xCF,?0xCE,?0x0E,?0x0A,?0xCA,?0xCB,?0x0B,?0xC9,?0x09,
0x08,?0xC8,?0xD8,?0x18,?0x19,?0xD9,?0x1B,?0xDB,?0xDA,?0x1A,?0x1E,?0xDE,?0xDF,?0x1F,?0xDD,
0x1D,?0x1C,?0xDC,?0x14,?0xD4,?0xD5,?0x15,?0xD7,?0x17,?0x16,?0xD6,?0xD2,?0x12,?0x13,?0xD3,
0x11,?0xD1,?0xD0,?0x10,?0xF0,?0x30,?0x31,?0xF1,?0x33,?0xF3,?0xF2,?0x32,?0x36,?0xF6,?0xF7,
0x37,?0xF5,?0x35,?0x34,?0xF4,?0x3C,?0xFC,?0xFD,?0x3D,?0xFF,?0x3F,?0x3E,?0xFE,?0xFA,?0x3A,
0x3B,?0xFB,?0x39,?0xF9,?0xF8,?0x38,?0x28,?0xE8,?0xE9,?0x29,?0xEB,?0x2B,?0x2A,?0xEA,?0xEE,
0x2E,?0x2F,?0xEF,?0x2D,?0xED,?0xEC,?0x2C,?0xE4,?0x24,?0x25,?0xE5,?0x27,?0xE7,?0xE6,?0x26,
0x22,?0xE2,?0xE3,?0x23,?0xE1,?0x21,?0x20,?0xE0,?0xA0,?0x60,?0x61,?0xA1,?0x63,?0xA3,?0xA2,
0x62,?0x66,?0xA6,?0xA7,?0x67,?0xA5,?0x65,?0x64,?0xA4,?0x6C,?0xAC,?0xAD,?0x6D,?0xAF,?0x6F,
0x6E,?0xAE,?0xAA,?0x6A,?0x6B,?0xAB,?0x69,?0xA9,?0xA8,?0x68,?0x78,?0xB8,?0xB9,?0x79,?0xBB,
0x7B,?0x7A,?0xBA,?0xBE,?0x7E,?0x7F,?0xBF,?0x7D,?0xBD,?0xBC,?0x7C,?0xB4,?0x74,?0x75,?0xB5,
0x77,?0xB7,?0xB6,?0x76,?0x72,?0xB2,?0xB3,?0x73,?0xB1,?0x71,?0x70,?0xB0,?0x50,?0x90,?0x91,
0x51,?0x93,?0x53,?0x52,?0x92,?0x96,?0x56,?0x57,?0x97,?0x55,?0x95,?0x94,?0x54,?0x9C,?0x5C,
0x5D,?0x9D,?0x5F,?0x9F,?0x9E,?0x5E,?0x5A,?0x9A,?0x9B,?0x5B,?0x99,?0x59,?0x58,?0x98,?0x88,
0x48,?0x49,?0x89,?0x4B,?0x8B,?0x8A,?0x4A,?0x4E,?0x8E,?0x8F,?0x4F,?0x8D,?0x4D,?0x4C,?0x8C,
0x44,?0x84,?0x85,?0x45,?0x87,?0x47,?0x46,?0x86,?0x82,?0x42,?0x43,?0x83,?0x41,?0x81,?0x80,
0x40?};?
unsigned?short?CRC16?(?unsigned?char?*puchMsg,?unsigned?short?usDataLen?)??
{?
?unsigned?char?uchCRCHi?=?0xFF?;??/*?高字節(jié)初始化值???*/?
?unsigned?char?uchCRCLo?=?0xFF?;??/*?低字節(jié)初始化值???*/?
?unsigned?uIndex?;??
??
?while?(usDataLen--)?
?{?
??uIndex?=?uchCRCLo?^?*puchMsg ?;???
??uchCRCLo?=?uchCRCHi?^?auchCRCHi[uIndex]?;?
??uchCRCHi?=?auchCRCLo[uIndex]?;?
?}?
?return?(uchCRCHi?<8?|?uchCRCLo)?;?
}?
應用層
Client/Server模型
前面OSI模型說到了,應用層通信可以看成是client/server方式,或許會與前面說得主從模式攪合在一起,導致理解起來費勁。其實這里client/server是從應用得角度描述得,modbus-RTU中,主設備其應用層就是client側,而slave設備就是應用的server。modbus標準文檔有種把簡單問題復雜描述之嫌。其實就是這樣一個簡單的圖:- 無錯誤:Client(主站)向從站發(fā)出請求,Server(從站)執(zhí)行命令請求的操作,然后發(fā)送應答給Client(主站),這里的操作,有可能是讀取參數,設置參數,或者執(zhí)行某個動作,具體取決于產品怎么設計。
- 有錯誤:Client(主站)向從站發(fā)出請求,Server(從站)檢測到錯誤,然后發(fā)送異常應答給Client(主站)。這里的錯誤,有可能是讀取失敗,寄存器地址非法,寫失敗,執(zhí)行動作失敗等。
數據模型
Modbus將采用大端字節(jié)順序傳輸報文,什么意思呢?比如一個16位數據0x55AA,先傳輸高字節(jié)0x55,再傳輸低字節(jié)0xAA。Modbus將數據抽象成四張表:看到這個表,可能會讓人覺得費解,我的設備里哪來什么線圈?這大概是modbus協議原本是Modicon公司針對其PLC產品開發(fā)的協議,與其特殊的工業(yè)PLC控制編程有很大的關系。作為使用modbus協議進行應用開發(fā)而言,則不必費力研究為什么叫這些名字。這四個表本質上就是將應用數據規(guī)劃為離散位開關量,以及寄存器變量,其中線圈與保持寄存器表為可讀可寫,其他兩個表為只讀。這個四個表中將應用數據都利用寄存器地址進行索引。地址范圍為0x0000-0xFFFF。需要理解的是,這里的地址與芯片的地址空間完全是兩個概念,把它簡單理解成modbus可以索引0x0000-0xFFFF這么多個用戶應用16位數據即可。其中有的可能是開關量,有的可能利用兩個連續(xù)寄存器對應用戶的浮點數,字符串等等,都有可能。這些地址是modbus請求命令中的一個字段,比如使用的最最頻繁的兩條命令,就是0x03,0x10兩條命令:下圖來自modbus標準中的0x03號命令的請求以及應答定義:又比如0x10號命令:這兩條命令中的Starting Address就是上面這4個表中寄存器對應的地址。Modbus命令
modbus-RTU支持的命令或者叫操作碼,就如下面這個表:其中最為常用的命令是0x03,0x04,0x10號命令,一般的應用而言,單個位開關量通信效率不免低下,現在很多產品開發(fā)已很少使用。其實對于這樣的離散量也完全可以直接放在輸入寄存器表以及保持寄存器表中。modbus對于用戶應用并沒有嚴格的規(guī)定。用戶可以自由進行寄存器地址(或叫索引) 映射。總結一下
modbus-RTU是一種比較簡單、可靠的協議,本文梳理了一下標準中一些比較重要的點。下文將分享一下,如何在單片機中實現幾條常用命令。—END—往期精彩推薦,點擊即可閱讀▲萬變不離其宗之單片機串口問題?▲萬變不離其宗之I2C總線要點總結▲萬變不離其宗之SPI總線要點總結▲長文圖解工業(yè)HART總線協議?▲RS-485總線,這篇很詳細