因業(yè)務(wù)需要了解Modbus協(xié)議的使用,因此對(duì)Modbus的協(xié)議褂乍,以及相應(yīng)的C#處理應(yīng)用進(jìn)行了解吁脱,針對(duì)協(xié)議的幾種方式(RTU桑涎、ASCII、TCPIP)進(jìn)行了封裝兼贡,以及對(duì)Modbus的各種功能碼的特點(diǎn)進(jìn)行了詳細(xì)的了解攻冷,本篇隨筆基于這些知識(shí)進(jìn)行了一定的梳理和介紹,主要內(nèi)容包括Modbus協(xié)議簡要介紹遍希、Modbus模擬工具使用和Modbus應(yīng)用開發(fā)幾個(gè)部分等曼。
1)Modbus協(xié)議簡要介紹
Modbus 協(xié)議是應(yīng)用于電子控制器上的一種通用語言。通過此協(xié)議凿蒜,控制器相互之間禁谦、控制器經(jīng)由網(wǎng)絡(luò)(例如以太網(wǎng))和其它設(shè)備之間可以通信。它已經(jīng)成為一通用工業(yè)標(biāo)準(zhǔn)废封。有了它州泊,不同廠商生產(chǎn)的控制設(shè)備可以連成工業(yè)網(wǎng)絡(luò)稚茅,進(jìn)行集中監(jiān)控闽烙。
此協(xié)議定義了一個(gè)控制器能認(rèn)識(shí)使用的消息結(jié)構(gòu),而不管它們是經(jīng)過何種網(wǎng)絡(luò)進(jìn)行通信的。它描述了一控制器請(qǐng)求訪問其它設(shè)備的過程秉氧,如果回應(yīng)來自其它設(shè)備的請(qǐng)求刽漂,以及怎樣偵測(cè)錯(cuò)誤并記錄演训。它制定了消息域格局和內(nèi)容的公共格式。
當(dāng)在一Modbus網(wǎng)絡(luò)上通信時(shí)贝咙,此協(xié)議決定了每個(gè)控制器須要知道它們的設(shè)備地址样悟,識(shí)別按地址發(fā)來的消息,決定要產(chǎn)生何種行動(dòng)庭猩。如果需要回應(yīng)乌奇,控制器將生成反饋信息并用Modbus協(xié)議發(fā)出。在其它網(wǎng)絡(luò)上眯娱,包含了Modbus協(xié)議的消息轉(zhuǎn)換為在此網(wǎng)絡(luò)上使用的幀或包結(jié)構(gòu)。這種轉(zhuǎn)換也擴(kuò)展了根據(jù)具體的網(wǎng)絡(luò)解決節(jié)地址爬凑、路由路徑及錯(cuò)誤檢測(cè)的方法徙缴。
Modbus由MODICON公司于1979年開發(fā),是一種工業(yè)現(xiàn)場總線協(xié)議標(biāo)準(zhǔn)。1996年施耐德公司推出基于以太網(wǎng)TCP/IP的modbus協(xié)議:modbusTCP于样。
Modbus協(xié)議是一項(xiàng)應(yīng)用層報(bào)文傳輸協(xié)議疏叨,包括ASCII、RTU穿剖、TCP三種報(bào)文類型蚤蔓。
標(biāo)準(zhǔn)的Modbus協(xié)議物理層接口有RS232、RS422糊余、RS485和以太網(wǎng)接口秀又,采用master/slave方式通信。
對(duì)于串行連接贬芥,存在兩個(gè)變種吐辙,它們?cè)跀?shù)值數(shù)據(jù)表示不同和協(xié)議細(xì)節(jié)上略有不同。Modbus RTU是一種緊湊的蘸劈,采用二進(jìn)制表示數(shù)據(jù)的方式昏苏,Modbus ASCII是一種人類可讀的,冗長的表示方式威沫。這兩個(gè)變種都使用串行通信(serial communication)方式贤惯。RTU格式后續(xù)的命令/數(shù)據(jù)帶有循環(huán)冗余校驗(yàn)的校驗(yàn)和,而ASCII格式采用縱向冗余校驗(yàn)的校驗(yàn)和棒掠。被配置為RTU變種的節(jié)點(diǎn)不會(huì)和設(shè)置為ASCII變種的節(jié)點(diǎn)通信孵构,反之亦然。
對(duì)于通過TCP/IP(例如以太網(wǎng))的連接句柠,存在多個(gè)Modbus/TCP變種浦译,這種方式不需要校驗(yàn)和計(jì)算。
對(duì)于所有的這三種通信協(xié)議在數(shù)據(jù)模型和功能調(diào)用上都是相同的溯职,只有封裝方式是不同的精盅。
Modbus有一個(gè)擴(kuò)展版本Modbus Plus(Modbus+或者M(jìn)B+),不過此協(xié)議是Modicon專有的谜酒,和Modbus不同叹俏。它需要一個(gè)專門的協(xié)處理器來處理類似HDLC的高速令牌旋轉(zhuǎn)。它使用1Mbit/s的雙絞線僻族,并且每個(gè)節(jié)點(diǎn)都有轉(zhuǎn)換隔離裝置粘驰,是一種采用轉(zhuǎn)換/邊緣觸發(fā)而不是電壓/水平觸發(fā)的裝置。連接Modbus Plus到計(jì)算機(jī)需要特別的接口述么,通常是支持ISA(SA85)蝌数,PCI或者PMCIA總線的板卡。
MODBUS 協(xié)議定義了一個(gè)與基礎(chǔ)通信層無關(guān)的簡單協(xié)議數(shù)據(jù)單元(PDU)度秘。
Modbus串行連路上的的PDU如下所示顶伞。
錯(cuò)誤檢驗(yàn)域是對(duì)報(bào)文內(nèi)容執(zhí)行"冗余校驗(yàn)" 的計(jì)算結(jié)果。根據(jù)不同的傳輸模式(RTU or ASCII) 使用兩種不同的計(jì)算方法。
RTU的報(bào)文格式如下所示唆貌。
ASCII碼的報(bào)文格式如下所示滑潘。
在 ASCII 模式, 報(bào)文用特殊的字符區(qū)分幀起始和幀結(jié)束锨咙。一個(gè)報(bào)文必須以一個(gè)‘冒號(hào)’ ( : ) (ASCII 十六進(jìn)制3A )起始语卤,以‘回車-換行’ (CR LF) 對(duì)(ASCII 十六進(jìn)制0D 和0A) 結(jié)束。
而Modbus TCP數(shù)據(jù)幀包含報(bào)文頭酪刀、功能代碼和數(shù)據(jù)3部分粹舵。
MBAP Header長度共7個(gè)字節(jié),分別為Transaction identifier(事務(wù)標(biāo)識(shí)符),Protocol identifier(協(xié)議標(biāo)識(shí)符),Length(長度),
Unitidentifier(單元標(biāo)識(shí)符)組成蓖宦,具體如下表所示:
請(qǐng)求和響應(yīng)帶有六個(gè)字節(jié)的前綴齐婴,如下:
byte 0: 事務(wù)處理標(biāo)識(shí)符 –由服務(wù)器復(fù)制 –通常為 0
byte 1: 事務(wù)處理標(biāo)識(shí)符 –由服務(wù)器復(fù)制 –通常為 0
byte 2: 協(xié)議標(biāo)識(shí)符= 0
byte 3: 協(xié)議標(biāo)識(shí)符= 0
byte 4: 長度字段 (上半部分字節(jié)) = 0 (所有的消息長度小于256)
byte 5: 長度字段 (下半部分字節(jié)) = 后面字節(jié)的數(shù)量
byte 6: 單元標(biāo)識(shí)符 (原“從站地址”)
byte 7: MODBUS 功能代碼
byte 8 on: 所需的數(shù)據(jù)
數(shù)據(jù)區(qū):數(shù)據(jù)區(qū)是根據(jù)不同的功能碼而不同。數(shù)據(jù)區(qū)可以是實(shí)際數(shù)值稠茂、設(shè)置點(diǎn)柠偶、主機(jī)發(fā)送給從機(jī)或從機(jī)發(fā)送給主機(jī)的地址。
標(biāo)準(zhǔn)的Modicon控制器使用RS232C實(shí)現(xiàn)串行的Modbus睬关。Modbus的ASCII诱担、RTU協(xié)議規(guī)定了消息、數(shù)據(jù)的結(jié)構(gòu)电爹、命令和就答的方式蔫仙,數(shù)據(jù)通訊采用Maser/Slave方式。
Modbus協(xié)議需要對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)丐箩,串行協(xié)議中除有奇偶校驗(yàn)外摇邦,ASCII模式采用LRC校驗(yàn),RTU模式采用16位CRC校驗(yàn).
ModbusTCP模式?jīng)]有額外規(guī)定校驗(yàn)屎勘,因?yàn)門CP協(xié)議是一個(gè)面向連接的可靠協(xié)議施籍。
對(duì)于常規(guī)的Modbus串口協(xié)議,我們來看看03功能碼的讀取寄存器的操作請(qǐng)求和響應(yīng)代碼了解下概漱。
請(qǐng)求PDU格式如下所示丑慎。
響應(yīng)的PDU格式如下所示。
一個(gè)請(qǐng)求讀寄存器108-110 的實(shí)例:
可以注意到瓤摧,很多數(shù)據(jù)的處理竿裂,需要拆分高位低位,高位在前照弥,低位在后的模式腻异。
根據(jù)這些RTU、ASCII这揣、TCPIP的Modbus協(xié)議的不同悔常,我們可以構(gòu)建一個(gè)通用的處理程序來處理這些操作敢会,在后面的應(yīng)用開發(fā)部分繼續(xù)介紹。
2)Modbus模擬工具使用
一般在做Mobus前期的開發(fā)的時(shí)候这嚣,一般不是針對(duì)具體的Modbus設(shè)備進(jìn)行寄存器的處理,而是使用Modbus模擬工具來進(jìn)行調(diào)試塞俱,一般我們需要配合Modbus Slave姐帚、Modbus Poll、Virtual Serial Port Driver這幾個(gè)模擬軟件來進(jìn)行開發(fā)的障涯。
Modbus Poll :Modbus主機(jī)仿真器罐旗,用于測(cè)試和調(diào)試Modbus從設(shè)備。該軟件支持Modbus的RTU唯蝶、ASCII九秀、TCP/IP。用來幫助開發(fā)人員測(cè)試Modbus從設(shè)備粘我,或者其它Modbus協(xié)議的測(cè)試和仿真鼓蜒。
Modbus Slave: Modbus從設(shè)備仿真器,可以仿真32個(gè)從設(shè)備/地址域征字。每個(gè)接口都提供了對(duì)EXCEL報(bào)表的OLE自動(dòng)化支持都弹。主要用來模擬Modbus從站設(shè)備,接收主站的命令包,回送數(shù)據(jù)包。幫助Modbus通訊設(shè)備開發(fā)人員進(jìn)行Modbus通訊協(xié)議的模擬和測(cè)試匙姜,用于模擬畅厢、測(cè)試、調(diào)試Modbus通訊設(shè)備氮昧。
Virtual Serial Port Driver:虛擬串口工具框杜,不需要串口接線,提供虛擬的串口袖肥,適合學(xué)習(xí)和調(diào)試使用咪辱。
配合這幾款軟件,我們就可以實(shí)現(xiàn)串口Modbus協(xié)議的模擬測(cè)試了昭伸,如果我們使用Modbus的TCPIP協(xié)議梧乘,那么我們不需要VSPD也可以。
如果我們使用Modbus協(xié)議的串口通訊方式庐杨,那么我們先要使用VSPD進(jìn)行串口的配對(duì)模擬选调,模擬出兩個(gè)通訊的串口端口,端口配對(duì)模擬成功后灵份,我們可以看到設(shè)備管理器中增加了兩個(gè)端口了仁堪。
[圖片上傳失敗...(image-48202e-1595816355775)]
接著使用從機(jī)模擬器,模擬一個(gè)Modbus從機(jī)供測(cè)試填渠,通過菜單【Connection】【Connect】啟動(dòng)弦聂,我們選擇連接方式為串口鸟辅,端口則選擇我們配對(duì)的其中一個(gè)端口即可,如下圖所示莺葫。
其中模式選擇RTU或者ASCII都可以匪凉,這兩個(gè)模式協(xié)議有所不同,一旦從機(jī)選擇RTU模式捺檬,那么Modbus主機(jī)也需要選擇對(duì)應(yīng)的RTU模式再层,反之亦然。
其他串口設(shè)置堡纬,如波特率聂受、數(shù)據(jù)位、奇偶位烤镐、停止位等默認(rèn)配置即可蛋济。
如果我們選擇TCPIP模式,那么對(duì)應(yīng)Modbus主機(jī)也需要選擇TCPIP方式炮叶。
一旦Modbus從機(jī)啟動(dòng)碗旅,就會(huì)處理來自Modbus主機(jī)的指令請(qǐng)求(如果有的話),并做相應(yīng)處理悴灵,我們可以通過【Display】【Communication】菜單彈出的對(duì)話框扛芽,了解到對(duì)應(yīng)請(qǐng)求和應(yīng)答的協(xié)議詳細(xì)信息。
Modbus主機(jī)的啟動(dòng)和ModBus從機(jī)類似积瞒,我們根據(jù)ModBus從機(jī)的配置川尖,選擇對(duì)應(yīng)的主機(jī)配置,Modbus模擬主機(jī)啟動(dòng)和查看通訊記錄界面如下所示茫孔。
另外我們可以通過【Display】里面選擇內(nèi)容顯示的進(jìn)制格式叮喳。
在從機(jī)的設(shè)置里面,我們可以修改從機(jī)的定義信息缰贝,以便設(shè)置對(duì)應(yīng)的從機(jī)ID馍悟,功能碼,其實(shí)地址剩晴,長度或者數(shù)量的信息锣咒,如下界面所示。
我們可以根據(jù)實(shí)際的寄存器地址和數(shù)量赞弥,設(shè)置對(duì)應(yīng)的數(shù)值毅整,如下是顯示4個(gè)數(shù)據(jù)的內(nèi)容設(shè)置和顯示內(nèi)容。
設(shè)置后正常的內(nèi)容顯示如下绽左。
同時(shí)我們也需要設(shè)置對(duì)應(yīng)Modbus主機(jī)模擬器的地址和數(shù)量悼嫉,正確設(shè)置后可以正常顯示。
通過更深一步的設(shè)置或者調(diào)整拼窥,我們可以極大程度的進(jìn)行模擬Modbus實(shí)際設(shè)備的處理方式戏蔑,從而在沒有實(shí)際Modbus硬件設(shè)備的情況下盡可能通過前期的模擬完成常規(guī)功能的測(cè)試和準(zhǔn)備蹋凝。
在我們開發(fā)Modbus應(yīng)用的時(shí)候,我們對(duì)照相應(yīng)的主從機(jī)Modbus協(xié)議請(qǐng)求和應(yīng)答总棵,能夠檢查我們程序的輸出是否正常鳍寂,從而可以快速的開發(fā)Modbus的應(yīng)用處理功能。
3)Modbus應(yīng)用開發(fā)
為了模擬對(duì)接Modbus的RTU情龄、ASCII伐割、TCP/IP協(xié)議處理,我根據(jù)不同協(xié)議的處理方式定義了一個(gè)輔助函數(shù)刃唤,然后統(tǒng)一進(jìn)行處理,以便達(dá)到統(tǒng)一調(diào)用的處理便利白群。
首先我們來看看使用串口模式下(RTU尚胞、ASCII)的處理界面效果,這個(gè)直接獲取模擬器Modbus Slave從機(jī)的數(shù)值進(jìn)行顯示的帜慢。
[圖片上傳失敗...(image-bede9c-1595816355775)]
TCPIP網(wǎng)絡(luò)方式對(duì)接Modbus界面處理效果如下所示笼裳。
兩者數(shù)據(jù)均來源于Modbus Slave從機(jī)的數(shù)值,只是它們對(duì)接的方式不同粱玲。
串口的處理躬柬,我通過SerialPortUtil類來使用Windows的串口類,處理對(duì)應(yīng)的串口操作抽减,通過定義事件的方式允青,使得串口收到數(shù)據(jù)的時(shí)候,及時(shí)通知調(diào)用者進(jìn)行界面更新處理即可卵沉。
//使用字符串參數(shù)構(gòu)造
serial = new SerialPortUtil(portname, this.txtBaudRate.Text, this.txtParity.Text, this.txtDataBits.Text, this.txtStopBit.Text);
//收到數(shù)據(jù)處理的事件
serial.DataReceived += Serial_DataReceived;
serial.RTUMode = this.radRTU.Checked;//默認(rèn)RTU模式為True颠锉,否則使用ASCII模式
收到數(shù)據(jù)后,及時(shí)通過委托方式史汗,通知UI進(jìn)行界面的更新顯示琼掠。
/// <summary>
/// 收到串口響應(yīng)事件后,及時(shí)進(jìn)行處理(更新在界面上)
/// </summary>
/// <param name="e"></param>
private void Serial_DataReceived(DataReceivedEventArgs e)
{
//記錄在日志停撞,方便復(fù)制
LogTextHelper.Info(e.DataReceived);
//使用委托進(jìn)行處理界面控件的數(shù)據(jù)更新
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瓷蛙,如果是異常,則可以解析錯(cuò)誤
var newFunction = function - 0x80;
lblTips.Text = "響應(yīng)有異常戈毒,功能代碼:" + newFunction.ToString("D2");
lblTips.Text += ",錯(cuò)誤描述:" + ((ModBusExceptionCode)newFunction).ToString();
}
else
{
lblTips.Text = "響應(yīng)正常";//小于128的為正常響應(yīng)
}
}
}));
}
而對(duì)于網(wǎng)絡(luò)方式艰猬,我們先要定義一個(gè)Socket通訊的基類,封裝相關(guān)的通訊處理操作副硅。
然后簡單構(gòu)建一個(gè)子類進(jìn)行使用姥宝,如下所示。
/// <summary>
/// 通信類子類
/// </summary>
public class ModbusClient : BaseSocketClient
{
public ModbusClient()
{
this.Name = "ModbusClient";
}
}
界面處理的時(shí)候恐疲,我們只需要初始化一個(gè)ModbusClient類來使用即可腊满,如下代碼所示套么。
client = new ModbusClient();
//收到數(shù)據(jù)處理的事件
client.DataReceived += Client_DataReceived;
收到數(shù)據(jù)通知界面進(jìn)行更新的操作如下所示。
private void Client_DataReceived(DataReceivedEventArgs e)
{
//記錄在日志碳蛋,方便復(fù)制
LogTextHelper.Info(e.DataReceived);
//使用委托進(jìn)行處理界面控件的數(shù)據(jù)更新
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)
{
//串口功能碼為第二個(gè)字節(jié)胚泌,TCP/IP功能碼為第8個(gè)
var function = dataBytes[7];
if (function > 0x80)//128
{
//Modbus的異常代碼大于128,如果是異常肃弟,則可以解析錯(cuò)誤
var newFunction = function - 0x80;
lblTips.Text = "響應(yīng)有異常玷室,功能代碼:" + newFunction.ToString("D2");
lblTips.Text += ",錯(cuò)誤描述:" + ((ModBusExceptionCode)newFunction).ToString();
}
else
{
lblTips.Text = "響應(yīng)正常";//小于128的為正常響應(yīng)
}
}
}));
}
不管是串口的RTU或者ASCII,又或者是TCPIP的協(xié)議笤受,我們可以通過定義一個(gè)協(xié)議封裝的輔助類ModbusQueryHelper來處理協(xié)議的具體細(xì)節(jié)穷缤。
/// <summary>
/// Modbus查詢消息生成輔助類,可以用于串口RTU/ASCII協(xié)議箩兽,也可以用于TCPIP協(xié)議津肛。
/// 用于生成各種功能代碼的消息內(nèi)容。
/// </summary>
public class ModbusQueryHelper
{
/// <summary>
/// 是否為RTU模式汗贫,默認(rèn)為True身坐,否則為ASCII方式
/// </summary>
public ModbusProtocol Protocol { get; set; } = ModbusProtocol.RTU;
/// <summary>
/// 默認(rèn)函數(shù)
/// </summary>
public ModbusQueryHelper()
{
}
/// <summary>
/// 參數(shù)化構(gòu)造,指定RTU模式
/// </summary>
/// <param name="protocal">Modbus協(xié)議:ASCII,RTU, TCP落包,默認(rèn)為RTU</param>
public ModbusQueryHelper(ModbusProtocol protocal)
{
this.Protocol = protocal;
}
而其中ModbusProtocol是一個(gè)枚舉部蛇,定義如下所示。
/// <summary>
/// 幾種常用的Modbus協(xié)議
/// </summary>
public enum ModbusProtocol
{
/// <summary>
/// 串口的ASCII模式
/// </summary>
ASCII,
/// <summary>
/// 串口的RTU模式
/// </summary>
RTU,
/// <summary>
/// 網(wǎng)絡(luò)TCPIP模式
/// </summary>
TCP
}
我們通過ModbusQueryHelper 類咐蝇,可以處理不同協(xié)議之間的封裝細(xì)節(jié)涯鲁,并可以對(duì)各種功能碼的協(xié)議進(jìn)行封裝處理。
以上就是 相關(guān)Modbus的應(yīng)用處理和封裝有序,對(duì)于常規(guī)的Modbus協(xié)議可以極大簡化對(duì)接處理撮竿,在實(shí)際對(duì)接Modbus設(shè)備的時(shí)候,我們只需要根據(jù)對(duì)應(yīng)的說明書笔呀,獲取對(duì)應(yīng)的內(nèi)容幢踏,就可以把例如溫度、濕度许师、轉(zhuǎn)速等一些設(shè)備或者機(jī)器人的參數(shù)獲得房蝉,并記錄在數(shù)據(jù)庫里面,然后在應(yīng)用模塊中整合一些圖表展示就可以很好的實(shí)現(xiàn)看板功能了微渠。