女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用協議棧實現Modbus RTU主站應用

CHANBAEK ? 來源:木南創智 ? 作者:尹家軍 ? 2022-12-13 16:10 ? 次閱讀

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

1 、何為RTU主站

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

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

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

2 、如何實現RTU主站

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

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

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

2.1 、怎么生成數據請求

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

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

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

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

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

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

2.2 、怎么解析數據響應

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

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

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

這個函數有3個參數,其中RTULocalMasterType master為主站對象;uint8_trecievedMessage為接收到的響應消息;uint8_t *command為發送的命令序列。將這幾個參數傳遞給解析函數就可實現數據響應的解析。

RTULocalMasterType是一個結構體,用以生命一個主站對象,這個對象就是我們要實現各種操作的主站,這一結構體的定義如下:

/* 定義本地RTU主站對象類型 */
typedef struct LocalRTUMasterType{
  uint32_t flagWriteSlave[8];   //寫一個站控制標志位,最多256個站,與站地址對應。
  uint16_t slaveNumber;         //從站列表中從站的數量
  uint16_t readOrder;           //當前從站在從站列表中的位置
  RTUAccessedSlaveType*pSlave;         //從站列表
  UpdateCoilStatusTypepUpdateCoilStatus;       //更新線圈量函數
  UpdateInputStatusTypepUpdateInputStatus;     //更新輸入狀態量函數
  UpdateHoldingRegisterTypepUpdateHoldingRegister;     //更新保持寄存器量函數
  UpdateInputResgisterTypepUpdateInputResgister;       //更新輸入寄存器量函數
}RTULocalMasterType;

3 RTU****主站編碼

有了前面的說明,我們基于協議棧實現一個主站應用就很容易了。接下來我們就基于協議棧具體實現一個主站應用。

3.1 、定義主站對象

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

RTULocalMasterType rtuMaster;

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

/*初始化RTU主站對象*/
void InitializeRTUMasterObject(RTULocalMasterType*master,uint16_t slaveNumber,
                    RTUAccessedSlaveType*pSlave,
                    UpdateCoilStatusTypepUpdateCoilStatus,
                    UpdateInputStatusTypepUpdateInputStatus,
                    UpdateHoldingRegisterTypepUpdateHoldingRegister,
                    UpdateInputResgisterTypepUpdateInputResgister
                    )

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

/ 初始化RTU主站對象 /

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

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

3.2 、生成數據請求

在前面,我們已經描述了數據請求命令的生成函數,該函數有一個ObjAccessInfo參數,這個參數用于傳遞需要生成命令的信息。這是一個結構體,我們需要定義一個對象變量。

ObjAccessInfo hgraInfo;

然后使用這個對象來實現數據請求的生成。具體操作如下所示:

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

生成的數據請求什么時候發送給完全由主進程來實現已經與協議棧沒有關系了。

3.3 、解析數據響應

收到數據響應后我們需要對其進行解析。前面我們已經介紹了解析從站數據響應的函數。具體的調用形式如下:

ParsingSlaveRespondMessage(&hgraMaster,hgraRxBuffer,NULL);

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

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

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

我們可根據需要重定義這些函數,當然我們沒有響應的數據可以不必實現,如我們沒有使用輸入寄存器,那么更新輸入寄存器的回調函數則可以不用重定義。如下在我們的例子中重定義為:

/*更新讀回來的保持寄存器*/
voidUpdateHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t*registerValue)
{
  uint16_t startRegister=HoldingResterEndAddress+1;
   
  switch(salveAddress)
  {
  case BPQStationAddress:       //更新讀取的變頻器參數
    {
      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:                      //故障態
    {
     startRegister=HoldingResterEndAddress+1;
      break;
    }
  }
 
  if(startRegister<=HoldingResterEndAddress)
  {
    for(int i=0;i/*更新讀回來的輸入寄存器*/
void UpdateInputResgister(uint16_t startAddress,uint16_tquantity,uint16_t *registerValue)
{
  uint16_t startRegister=HoldingResterEndAddress+1;
   
  switch(salveAddress)
  {
  case BPQStationAddress:       //更新讀取的變頻器參數
    {
     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:                      //故障態
    {
     startRegister=HoldingResterEndAddress+1;
      break;
    }
  }
 
 if(startRegister<=HoldingResterEndAddress)
  {
    for(int i=0;i

4 RTU****主站小結

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

在使用協議棧實現RTU主站時需要注意,協議棧支持在同一設備上以不同的通訊端口實現不同的主站應用,而且每一臺主站都支持多個從站。具體實現只需要根據協議棧定義就可以了。

我們來總結一下使用協議棧實現主站應用的步驟,以方便大家使用協議棧實現Modbus RTU主站應用。

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

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

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

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • MODBUS
    +關注

    關注

    28

    文章

    1995

    瀏覽量

    78932
  • RTU
    RTU
    +關注

    關注

    0

    文章

    432

    瀏覽量

    29246
  • 協議棧
    +關注

    關注

    2

    文章

    145

    瀏覽量

    34003
收藏 人收藏

    評論

    相關推薦
    熱點推薦

    Modbus RTU的設計與實現

    前面我們已經詳細講解過Modbus協議的開發過程,并且利用協議封裝了Modbus RTU
    的頭像 發表于 12-13 15:27 ?2291次閱讀
    <b class='flag-5'>Modbus</b> <b class='flag-5'>RTU</b>的設計與<b class='flag-5'>實現</b>

    使用協議實現Modbus RTU應用

    自從開源了我們自己開發的Modbus協議之后,有很多朋友建議我針對性的做幾個示例。所以我們就基于平時我們的應用整理了幾個簡單但可以說明基本的應用方法的示例,這一篇中我們將使用協議
    的頭像 發表于 12-13 16:14 ?2413次閱讀
    使用<b class='flag-5'>協議</b><b class='flag-5'>棧</b><b class='flag-5'>實現</b><b class='flag-5'>Modbus</b> <b class='flag-5'>RTU</b>從<b class='flag-5'>站</b>應用

    使用協議實現Modbus ASCII從應用

    自從開源了我們自己開發的Modbus協議之后,有很多朋友建議我針對性的做幾個示例。所以我們就基于平時我們的應用整理了幾個簡單但可以說明基本的應用方法的示例,這一篇中我們來使用協議
    的頭像 發表于 12-13 17:12 ?1518次閱讀
    使用<b class='flag-5'>協議</b><b class='flag-5'>棧</b><b class='flag-5'>實現</b><b class='flag-5'>Modbus</b> ASCII從<b class='flag-5'>站</b>應用

    如何在PSoC 5LP中實現MODBUS RTU)?

    我正在開發 PSoC 5LP 板 (CY8CKIT-050),想知道如何在 PSoC 5LP 中實現 MODBUS RTU)。
    發表于 05-20 07:01

    Modbus 模擬器 !

    使用STM32F0開發的RS485變送器(使用Modbus RTU協議),為了調試,需要用 Modbus
    發表于 01-08 23:36

    請問在STM32上跑modbus rtu應該怎么做

    最近一個項目中需要用到485通信,下面的期間是modbus rtu協議,我是小白沒搞過modbus,有沒有移植過的大神給小弟指點迷津啊,要在STM32上跑
    發表于 01-11 09:04

    基于RT-Thread實現的Agile Modbus協議

    基于 RT-Thread 實現的支持 Modbus 固件升級的 Bootloader:HPM6750_Boot  特性  支持 rtu 及 tcp 協議,使用純 C 開發,不涉及任何硬
    發表于 10-08 15:04

    基于Modbus RTU協議實現的1多從自組網無線通信形式

      本方案是基于Modbus RTU協議實現的1多從自組網無線通信形式,
    發表于 03-10 14:54

    S7200 Modbus通訊協議遠程終端設備RTU和從示例

    本文檔的主要內容詳細介紹的是S7200 Modbus通訊協議遠程終端設備RTU和從示例。
    發表于 10-25 08:00 ?33次下載
    S7200 <b class='flag-5'>Modbus</b>通訊<b class='flag-5'>協議</b>遠程終端設備<b class='flag-5'>RTU</b><b class='flag-5'>主</b><b class='flag-5'>站</b>和從<b class='flag-5'>站</b>示例

    使用協議實現Modbus ASCII應用

    自從開源了我們自己開發的Modbus協議之后,有很多朋友建議我針對性的做幾個示例。所以我們就基于平時我們的應用整理了幾個簡單但可以說明基本的應用方法的示例,這一篇中我們來使用協議
    的頭像 發表于 12-13 17:09 ?1229次閱讀
    使用<b class='flag-5'>協議</b><b class='flag-5'>棧</b><b class='flag-5'>實現</b><b class='flag-5'>Modbus</b> ASCII<b class='flag-5'>主</b><b class='flag-5'>站</b>應用

    Profibus-DPmodbus RTU網關profibus多

    遠創智控YC-DPM-RTU網關在Profibus總線側實現功能,在Modbus串口側實現
    的頭像 發表于 07-14 16:24 ?1213次閱讀
    Profibus-DP<b class='flag-5'>主</b><b class='flag-5'>站</b>轉<b class='flag-5'>modbus</b> <b class='flag-5'>RTU</b>網關profibus多<b class='flag-5'>主</b><b class='flag-5'>站</b>

    Profibus DPModbus-RTU協議網關(JM-DPM-RTU

    一,設備主要功能 捷米特JM-DPM-RTU網關在Profibus DP總線側實現功能,在Modbus-RTU串口側
    的頭像 發表于 08-27 14:56 ?642次閱讀
    Profibus DP<b class='flag-5'>主</b><b class='flag-5'>站</b>轉<b class='flag-5'>Modbus-RTU</b><b class='flag-5'>協議</b>網關(JM-DPM-<b class='flag-5'>RTU</b>)

    Modbus RTU轉CC-Link協議網關(CC-Link轉Modbus RTU

    遠創智控YC-CCLK-RTU型網關實現了CC-Link從Modbus RTU
    的頭像 發表于 09-07 14:59 ?671次閱讀
    <b class='flag-5'>Modbus</b> <b class='flag-5'>RTU</b>轉CC-Link<b class='flag-5'>協議</b>網關(CC-Link轉<b class='flag-5'>Modbus</b> <b class='flag-5'>RTU</b>)

    CC-Link IEFBModbus RTU協議網關(YC-CCLKIEM-RTU

    一,設備主要功能 遠創智控YC-CCLKIEM-RTU型網關在CC-Link IEFB總線側實現功能,在Modbus
    的頭像 發表于 09-09 15:58 ?455次閱讀
    CC-Link IEFB<b class='flag-5'>主</b><b class='flag-5'>站</b>轉<b class='flag-5'>Modbus</b> <b class='flag-5'>RTU</b><b class='flag-5'>協議</b>網關(YC-CCLKIEM-<b class='flag-5'>RTU</b>)

    EtherCATModbus-RTU總線協議網關

    一,設備主要功能 捷米特JM-ECTM-RTU網關可將EtherCAT協議的設備接入到Modbus網絡中,設備串口側提供RS485和RS
    的頭像 發表于 09-12 11:12 ?523次閱讀
    EtherCAT<b class='flag-5'>主</b><b class='flag-5'>站</b>轉<b class='flag-5'>Modbus-RTU</b>總線<b class='flag-5'>協議</b>網關