日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Modbus协议栈应用实例之一:Modbus RTU主站应用

發(fā)布時間:2024/7/23 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Modbus协议栈应用实例之一:Modbus RTU主站应用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

自從開源了我們自己開發(fā)的Modbus協(xié)議棧之后,有很多朋友建議我針對性的做幾個示例。所以我們就基于平時我們的應(yīng)用整理了幾個簡單但可以說明基本的應(yīng)用方法的示例,在這一篇中我們先來使用協(xié)議棧實現(xiàn)Modbus RTU主站的示例。

1、何為RTU主站

Modbus協(xié)議是一個主從協(xié)議,那肯定就有主站和從站之分。所謂主站說的簡單一點就是能夠主動發(fā)起通訊的對象,所以主站就是發(fā)起通訊的一方。

對于RTU主站來說,自己并不會產(chǎn)生數(shù)據(jù),而是要從從站獲取數(shù)據(jù)。在Modbus RTU協(xié)議中從站不會主動向外發(fā)送數(shù)據(jù),所以需要主站發(fā)送數(shù)據(jù)請求,從站才會向其返回請求的數(shù)據(jù)。這一過程如下圖所示:

從上圖我們不難看出,首先主站要主動發(fā)起數(shù)據(jù)請求,這也是它為什么被稱之為主站的緣由。它首先告訴從站我需要哪些數(shù)據(jù)。然后從站按照主站的請求返回數(shù)據(jù)。主站得到響應(yīng)后解析數(shù)據(jù),這樣就完成了主從站之間的一次數(shù)據(jù)通訊。所以主站就需要主動發(fā)起每一次數(shù)據(jù)通訊的對象。

2、如何實現(xiàn)RTU主站

我們已經(jīng)簡單的說明了什么是RTU的主站,那么如何實現(xiàn)這一主站呢?其實在協(xié)議棧中,我們已經(jīng)實現(xiàn)了主站的數(shù)據(jù)請求命令的合成以及響應(yīng)數(shù)據(jù)的解析,所以我們使用協(xié)議棧時就是要控制何時將協(xié)議棧合成的主站請求命令發(fā)出以及如何解析數(shù)據(jù)響應(yīng)進而得到想要的數(shù)據(jù)的過程。

在我們的協(xié)議棧中實現(xiàn)了0x01、0x02、0x03、0x04、0x05、0x06、0x0F以及0x10等功能碼。也就是說主站對象可以生成面向這些功能碼的從站數(shù)據(jù)請求。也可以解析面向這些功能碼的從站數(shù)據(jù)響應(yīng)。可以表示為下圖所示:

從上圖我們很清楚,協(xié)議棧已經(jīng)實現(xiàn)了面向這些功能碼的數(shù)據(jù)請求命令的生成以及數(shù)據(jù)響應(yīng)消息的解析。我們使用協(xié)議棧時需要做的就是要告訴協(xié)議棧我要生成哪些數(shù)據(jù)請求命令以及如何解析數(shù)據(jù)響應(yīng)消息。

2.1、怎么生成數(shù)據(jù)請求

對于數(shù)據(jù)請求,我們不一定需要面向全部功能碼的請求,我們只需要根據(jù)我們的需求合成我們想要的請求。

在協(xié)議棧中,針對數(shù)據(jù)請求的生成我們定義了一個從站訪問命令生成函數(shù)。該函數(shù)的原型如下:

uint16_t CreateAccessSlaveCommand(ObjAccessInfo objInfo,void *dataList,uint8_t *commandBytes)

該函數(shù)有3個參數(shù),其中ObjAccessInfo objInfo為對象訪問信息;void *dataList為數(shù)據(jù)列表指針,該參數(shù)主要用于寫從站功能的命令生成;uint8_t *commandBytes為返回的從站訪問命令。

ObjAccessInfo是一個結(jié)構(gòu)體,向函數(shù)傳遞我們想要生成的從站訪問命令的相關(guān)信息,包括站地址,功能碼,起始地址和數(shù)量。該結(jié)構(gòu)體的定義如下:

/*定義用于傳遞要訪問從站(服務(wù)器)的信息*/ typedef struct{uint8_t unitID;FunctionCode functionCode;uint16_t startingAddress;uint16_t quantity; }ObjAccessInfo;

2.2、怎么解析數(shù)據(jù)響應(yīng)

對于數(shù)據(jù)響應(yīng),我們同樣不需要考慮全部的操作碼,我們一般需要考慮讀請求的響應(yīng),因為他們的數(shù)據(jù)需要解析。而對于寫請求返回數(shù)響應(yīng)只是告訴主站成功或者不成功,即使不成功只需要在寫一次就可以了,不存在數(shù)據(jù)更新的問題。

在協(xié)議棧中,我們實現(xiàn)了主站解析從站數(shù)據(jù)響應(yīng)的解析函數(shù)。使用這一函數(shù)我們只需要將收到的數(shù)據(jù)響應(yīng)報文傳遞給解析函數(shù)就可以完成解析。該函數(shù)的原型定義如下:

void ParsingSlaveRespondMessage(RTULocalMasterType *master,uint8_t *recievedMessage,uint8_t *command)

這個函數(shù)有3個參數(shù),其中RTULocalMasterType *master為主站對象;uint8_t *recievedMessage為接收到的響應(yīng)消息;uint8_t *command為發(fā)送的命令序列。將這幾個參數(shù)傳遞給解析函數(shù)就可實現(xiàn)數(shù)據(jù)響應(yīng)的解析。

RTULocalMasterType是一個結(jié)構(gòu)體,用以生命一個主站對象,這個對象就是我們要實現(xiàn)各種操作的主站,這一結(jié)構(gòu)體的定義如下:

/* 定義本地RTU主站對象類型 */ typedef struct LocalRTUMasterType{uint32_t flagWriteSlave[8];?? //寫一個站控制標志位,最多256個站,與站地址對應(yīng)。uint16_t slaveNumber;???????? //從站列表中從站的數(shù)量uint16_t readOrder;?????????? //當前從站在從站列表中的位置RTUAccessedSlaveType *pSlave;???????? //從站列表UpdateCoilStatusType pUpdateCoilStatus;?????? //更新線圈量函數(shù)UpdateInputStatusType pUpdateInputStatus;???? //更新輸入狀態(tài)量函數(shù)UpdateHoldingRegisterType pUpdateHoldingRegister;???? //更新保持寄存器量函數(shù)UpdateInputResgisterType pUpdateInputResgister;?????? //更新輸入寄存器量函數(shù) }RTULocalMasterType;

3RTU主站編碼

有了前面的說明,我們基于協(xié)議棧實現(xiàn)一個主站應(yīng)用就很容易了。接下來我們就基于協(xié)議棧具體實現(xiàn)一個主站應(yīng)用。

3.1、定義主站對象

首先我們要聲明一個主站對象,這是我們操作的基礎(chǔ)。在接下來的各種操作中我們都是基于這一對象來實現(xiàn)的。具體操作如下:

RTULocalMasterType rtuMaster;

定義了這個主站對象后,我們還需要對這一對象進行初始化。協(xié)議棧同樣提供了一個主站對象的初始化函數(shù)。函數(shù)的原型定義如下:

/*初始化RTU主站對象*/ void InitializeRTUMasterObject(RTULocalMasterType *master,uint16_t slaveNumber,RTUAccessedSlaveType *pSlave,UpdateCoilStatusType pUpdateCoilStatus,UpdateInputStatusType pUpdateInputStatus,UpdateHoldingRegisterType pUpdateHoldingRegister,UpdateInputResgisterType pUpdateInputResgister)

該函數(shù)的參數(shù)除了主站對象外,還有從站的數(shù)量即從站對象列表,還有四個數(shù)據(jù)更新函數(shù)指針。這幾個函數(shù)指針將應(yīng)用于數(shù)據(jù)響應(yīng)的解析過程中,具體在后面描述。使用這一初始化函數(shù)實現(xiàn)對主站對象的初始化,使其能夠?qū)崿F(xiàn)各項操作,具體如下:

/*初始化RTU主站對象*/

? InitializeRTUMasterObject(&hgraMaster,2,hgraSlave,NULL,NULL,NULL,NULL);

這里我們將幾個數(shù)據(jù)處理函數(shù)指針變量傳入NULL,表示初始化為默認的操作函數(shù),當然我們也可以編寫這些函數(shù),在后續(xù)的數(shù)據(jù)解析時將會詳細說明。

3.2、生成數(shù)據(jù)請求

在前面,我們已經(jīng)描述了數(shù)據(jù)請求命令的生成函數(shù),該函數(shù)有一個ObjAccessInfo參數(shù),這個參數(shù)用于傳遞需要生成命令的信息。這是一個結(jié)構(gòu)體,我們需要定義一個對象變量。

ObjAccessInfo hgraInfo;

然后使用這個對象來實現(xiàn)數(shù)據(jù)請求的生成。具體操作如下所示:

/* 生成1號從站訪問命令 */hgraInfo.unitID=hgraSlave[0].stationAddress;hgraInfo.functionCode=ReadCoilStatus;hgraInfo.startingAddress=0x0000;hgraInfo.quantity=8;CreateAccessSlaveCommand(hgraInfo,NULL,slave1ReadCommand[0]);

生成的數(shù)據(jù)請求什么時候發(fā)送給完全由主進程來實現(xiàn)已經(jīng)與協(xié)議棧沒有關(guān)系了。

3.3、解析數(shù)據(jù)響應(yīng)

收到數(shù)據(jù)響應(yīng)后我們需要對其進行解析。前面我們已經(jīng)介紹了解析從站數(shù)據(jù)響應(yīng)的函數(shù)。具體的調(diào)用形式如下:

ParsingSlaveRespondMessage(&hgraMaster,hgraRxBuffer,NULL);

我們對hgraMaster主站對象收到的從站響應(yīng)hgraRxBuffer進行解析。最后傳入的NULL表示我們不指定主站發(fā)送的數(shù)據(jù)請求,而是讓主站從請求列表中去自己查找。

當然我們需要實現(xiàn)數(shù)據(jù)更新處理回調(diào)函數(shù)。這幾個函數(shù)是在對象初始化的時候以函數(shù)指針的形式傳遞的。原型如下:

/*更新讀回來的線圈狀態(tài)*/ __weak void UpdateCoilStatus(uint8_t salveAddress,uint16_t startAddress,uint16_t quantity,bool *stateValue) {//在客戶端(主站)應(yīng)用中實現(xiàn) }/*更新讀回來的輸入狀態(tài)值*/ __weak void UpdateInputStatus(uint8_t salveAddress,uint16_t startAddress,uint16_t quantity,bool *stateValue) {//在客戶端(主站)應(yīng)用中實現(xiàn) }/*更新讀回來的保持寄存器*/ __weak void UpdateHoldingRegister(uint8_t salveAddress,uint16_t startAddress,uint16_t quantity,uint16_t *registerValue) {//在客戶端(主站)應(yīng)用中實現(xiàn) }/*更新讀回來的輸入寄存器*/ __weak void UpdateInputResgister(uint8_t salveAddress,uint16_t startAddress,uint16_t quantity,uint16_t *registerValue) {//在客戶端(主站)應(yīng)用中實現(xiàn) }

我們可根據(jù)需要重定義這些函數(shù),當然我們沒有響應(yīng)的數(shù)據(jù)可以不必實現(xiàn),如我們沒有使用輸入寄存器,那么更新輸入寄存器的回調(diào)函數(shù)則可以不用重定義。如下在我們的例子中重定義為:

/*更新讀回來的保持寄存器*/void UpdateHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue) {uint16_t startRegister=HoldingResterEndAddress+1;switch(salveAddress){case BPQStationAddress:?????? //更新讀取的變頻器參數(shù){startRegister=36;break;}case PUMPStationAddress:????? //更新蠕動泵{ //????? aPara.phyPara.pumpRotateSpeed=registerValue[1];startRegister=HoldingResterEndAddress+1;break;}case JIG1StationAddress:????? //更新擺臂小電機{startRegister=48;break;}case JIG2StationAddress:????? //更新擺臂小電機{startRegister=52;break;}case JIG3StationAddress:????? //更新擺臂小電機{startRegister=56;break;}case HLPStationAddress:?????? //更新紅外溫度{aPara.phyPara.hlpObjectTemperature=registerValue[0]/100.0;startRegister=HoldingResterEndAddress+1;break;}case ROL1StationAddress:????? //更新擺臂控制{startRegister=quantity<3?60:62;break;}case ROL2StationAddress:????? //更新擺臂控制{startRegister=quantity<3?70:72;break;}case ROL3StationAddress:????? //更新擺臂控制{startRegister=quantity<3?80:82;break;}case DRUMStationAddress:????? //更新滾筒電機{startRegister=quantity<3?90:92;break;}default:????????????????????? //故障態(tài){startRegister=HoldingResterEndAddress+1;break;}}if(startRegister<=HoldingResterEndAddress){for(int i=0;i<quantity;i++){aPara.holdingRegister[startRegister+i]=registerValue[i];}} }/*更新讀回來的輸入寄存器*/ void UpdateInputResgister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue) {uint16_t startRegister=HoldingResterEndAddress+1;switch(salveAddress){case BPQStationAddress:?????? //更新讀取的變頻器參數(shù){startRegister=HoldingResterEndAddress+1;break;}case PUMPStationAddress:????? //更新蠕動泵{//aPara.phyPara.pumpRotateSpeed=registerValue[1]; //第一版背板aPara.phyPara.pumpRotateSpeed=(uint16_t)((float)registerValue[1]*6.0/128.0+0.5); //第二版背板startRegister=HoldingResterEndAddress+1;break;}case JIG1StationAddress:????? //更新擺臂小電機{startRegister=HoldingResterEndAddress+1;break;}case JIG2StationAddress:????? //更新擺臂小電機{startRegister=HoldingResterEndAddress+1;break;}case JIG3StationAddress:????? //更新擺臂小電機{startRegister=HoldingResterEndAddress+1;break;}case ROL1StationAddress:????? //更新擺臂控制{startRegister=HoldingResterEndAddress+1;break;}case ROL2StationAddress:?? ???//更新擺臂控制{startRegister=HoldingResterEndAddress+1;break;}case ROL3StationAddress:????? //更新擺臂控制{startRegister=HoldingResterEndAddress+1;break;}case DRUMStationAddress:????? //更新滾筒電機{startRegister=HoldingResterEndAddress+1;break;}default:????????????????????? //故障態(tài){startRegister=HoldingResterEndAddress+1;break;}}if(startRegister<=HoldingResterEndAddress){for(int i=0;i<quantity;i++){aPara.holdingRegister[startRegister+i]=registerValue[i];}} }

4RTU主站小結(jié)

我們實現(xiàn)了這個RTU主站實例,我們可以使用如Modsim這樣的軟件在PC上模擬Modbus RTU從站來測試這個主站應(yīng)用,操作結(jié)果是沒有問題的。

在使用協(xié)議棧實現(xiàn)RTU主站時需要注意,協(xié)議棧支持在同一設(shè)備上以不同的通訊端口實現(xiàn)不同的主站應(yīng)用,而且每一臺主站都支持多個從站。具體實現(xiàn)只需要根據(jù)協(xié)議棧定義就可以了。

我們來總結(jié)一下使用協(xié)議棧實現(xiàn)主站應(yīng)用的步驟,以方便大家使用協(xié)議棧實現(xiàn)Modbus RTU主站應(yīng)用。

第一步,使用主站對象類型聲明一個主站對象。然后對這個主站對象進行初始化。初始化主站對象時。需要指定從站數(shù)量,從站列表以及更新數(shù)據(jù)的回調(diào)函數(shù)指針。

第二步,生成訪問從站的數(shù)據(jù)請求列表。這個數(shù)據(jù)請求列表是按每一臺從站來劃分的,將列表的指針存在對應(yīng)的從站對象中。然后在需要的時候發(fā)送相應(yīng)的數(shù)據(jù)請求。

第三步,解析接收的從站數(shù)據(jù)響應(yīng)。協(xié)議棧已經(jīng)定義好了解析函數(shù),只需傳入消息就可自動解析。但是更新數(shù)據(jù)的回調(diào)函數(shù)必須根據(jù)具體的變量來編寫。可以每臺主站獨立編寫也可使用默認的函數(shù)。不過建議每臺主站獨立編寫,這樣比較清晰。

源碼下載:https://download.csdn.net/download/foxclever/12882021

歡迎關(guān)注:

總結(jié)

以上是生活随笔為你收集整理的Modbus协议栈应用实例之一:Modbus RTU主站应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲视频网址 | 刘亦菲毛片一区二区三区 | 成人a网站 | 国产精品超碰 | 91九色网站| 亚洲激情三区 | 日本精品成人 | 在线观看污污网站 | 91视频精品 | 高清一区二区三区四区五区 | 欧美激情久久久久久 | 福利电影在线播放 | 国产精品一级片 | 黄色香蕉视频 | 韩国黄色大片 | 亚洲97视频 | av午夜在线观看 | 无码精品人妻一二三区红粉影视 | 亚洲人成色777777老人头 | 夜夜嗨av一区二区三区免费区 | 91久久精品www人人做人人爽 | 成人无码精品1区2区3区免费看 | 天天操天天射天天舔 | 久色影视 | 日韩图片一区 | 日本黄色的视频 | 一区二区三区偷拍 | 91福利影院 | 午夜国产| 色av综合网 | 色老头影视| 国产一区二区三区免费观看视频 | 美国免费高清电影在线观看 | 国产成人啪精品 | 中文字幕15页 | 高h免费视频 | 国产一区免费视频 | 自拍一区在线 | 黑人三级视频 | 亚洲精品视频专区 | 美女裸体跪姿扒开屁股无内裤 | 四虎网站在线播放 | 日本高清久久 | 97超碰伊人 | 午夜精品福利一区二区蜜股av | 999这里有精品 | 一级黄色性生活片 | 久久久精品中文字幕 | 伊人青青草原 | 精品一区二区三区入口 | 制服中文字幕 | 中文字幕一区二区三区人妻四季 | 97在线免费观看 | 日韩第1页 | 日韩欧美在线一区二区三区 | 在线观看国产欧美 | 中文字幕亚洲一区二区三区 | av三级在线观看 | 国产成人自拍一区 | 丰满大乳少妇在线观看网站 | 日本一区二区免费在线观看 | 亚洲av成人无码一区二区三区在线观看 | www精品| 久久免费视频99 | 国产精品老熟女一区二区 | 亚洲人免费视频 | 激情超碰在线 | 国产麻豆精品久久一二三 | 亚洲欧美日韩中文在线 | 日本亚洲欧洲色 | 一级绝黄 | 欧美性生活网 | 久久久久久69 | 色多多视频在线观看 | 国产精品乱码一区二三区小蝌蚪 | 久久av秘一区二区三区 | 色狠狠操 | 亚洲图片一区二区 | 日韩欧美在线视频观看 | 欧美一区二区三区观看 | 成人在线观看h | 精品久久人人 | 天堂网av手机版 | 国产精品久久久久桃色tv | 大陆农村乡下av | 日美韩一区二区三区 | 青青草华人在线 | 激情av网站 | 91麻豆国产 | 最近最新中文字幕 | 国产在线一区二区 | www.com在线观看 | 人人妻人人玩人人澡人人爽 | 亚州春色| 极品美女高潮 | 国产精品三 | 日韩欧美字幕 | 国产伦精品一区二区三区在线 | 五月激情五月婷婷 |