<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Modbus協(xié)議完全資料與程序解析

          Modbus協(xié)議完全資料與程序解析

          作者: 時間:2016-12-01 來源:網(wǎng)絡(luò) 收藏
          switch(Modbus_mode) //通過判斷模式來進(jìn)行對響應(yīng)的發(fā)送
          {
          case Modbus_read_coil:
          read_coil_proc();
          break;
          ……
          default:
          return;
          break;
          }
          這樣的做的話,就可以吧解析函數(shù),執(zhí)行函數(shù)和具體的實施函數(shù)分開來弄,層次多多少少要清晰一些
          下面就是針對01,02,03,04,05,06,15,16幾個功能碼的執(zhí)行及返回進(jìn)行說明
          在說明各功能函數(shù)之前,先說說響應(yīng)。
          上面說的那兩個函數(shù)只不過是對一幀的外圍進(jìn)行解析與判斷,至于具體的參數(shù),還需要功能函數(shù)去解析與返回,功能函數(shù)要做的事情有3個,1個是參數(shù)的解析,2是執(zhí)行,3是返回響應(yīng)。
          先說響應(yīng),響應(yīng)是有特點的,第一個字節(jié)肯定是自己的本機(jī)地址,第二個字節(jié)肯定是功能碼,最后兩個字節(jié)肯定是crc校驗,所以說,在發(fā)送緩沖中,基本上4個字節(jié)已經(jīng)定死了
          Modbus_send_buf[0] = Modbus_addr;
          Modbus_send_buf[1] = Modbus_read_input_reg; //相應(yīng)的功能碼,每個功能寒暑都不一樣
          再經(jīng)過執(zhí)行函數(shù)最后算crc
          modbus_crc = crc16(Modbus_send_buf,temp); //計算發(fā)送crc數(shù)據(jù)
          Modbus_send_buf[temp] = modbus_crc >> 8; //計算
          temp++;
          Modbus_send_buf[temp] = modbus_crc & 0xff; //return num 高位
          5.1 01 讀線圈狀態(tài)
          #define Modbus_read_coil 0x01
          其實表面上挺難理解的,啥線圈啥的,但你仔細(xì)看看就可以了解,就是讀輸出數(shù)字量,如果你寫下位機(jī)的話,其實就是控制讀取輸出io,說白了,就是把目前的io輸出狀態(tài)返回給主機(jī)。這些io連接的可能是繼電器,也可能是一些開關(guān)之類的東西,也就是些數(shù)字信號。讀數(shù)字輸出信號。
          計算機(jī)發(fā)送命令:[設(shè)備地址] [命令號01] [起始寄存器地址高8位] [低8位] [讀取的寄存器數(shù)高8位] [低8位]
          設(shè)備響應(yīng):[設(shè)備地址] [命令號01] [返回的字節(jié)個數(shù)][數(shù)據(jù)1][數(shù)據(jù)2]...[數(shù)據(jù)n][CRC校驗的低8位] [CRC校驗的高8位]
          簡單的說就是返回所有的輸出io的值,放在一個或者幾個字節(jié)里,可以用判斷的方法來實現(xiàn),當(dāng)然,也可以用與或的方式實現(xiàn)。
          if(P1_0 == 1)
          {
          temp |= (1<<8);
          }
          else
          {
          temp &= (1<<8);
          }
          將temp的值放入第四個緩沖區(qū),當(dāng)然這根據(jù)設(shè)備的io口,編程時就已經(jīng)確定了的。接下來就可以進(jìn)行crc計算了。最后發(fā)送即可。
          Modbus_send_buf[3] = temp;
          modbus_crc = crc16(Modbus_send_buf,4);
          Modbus_send_buf[4] = modbus_crc >> 8;
          Modbus_send_buf[5] = modbus_crc & 0xff; //return num 高位
          5.2 02 讀只可讀數(shù)字量寄存器(輸入狀態(tài))
          基本上和01意思差不多,只不過這個功能碼返回的數(shù)據(jù)是輸入io的數(shù)據(jù),和01的區(qū)別是01可讀可改,而02只可讀不可改。也就是輸入的狀態(tài)。數(shù)據(jù)不可由設(shè)備本身控制。程序方面和01程序一樣。
          5.3 03讀可讀寫模擬量寄存器(保持寄存器)
          說簡單點就是讀da,da屬于模擬量,也可以輸出,但是以模擬量的方式來進(jìn)行傳輸?shù)?div>計算機(jī)發(fā)送命令:[設(shè)備地址] [命令號03] [起始寄存器地址高8位] [低8位] [讀取的寄存器數(shù)高8位] [低8位] [CRC校驗的低8位] [CRC校驗的高8位]
          設(shè)備響應(yīng):[設(shè)備地址] [命令號03] [返回的字節(jié)個數(shù)][數(shù)據(jù)1][數(shù)據(jù)2]...[數(shù)據(jù)n][CRC校驗的低8位] [CRC校驗的高8位]
          其中返回字節(jié)個數(shù),為讀取寄存器數(shù)乘2
          寫程序時,首先要注意數(shù)據(jù)個數(shù),temp = Modbus_recevie_buf[5];一般寄存器個數(shù)不會超過255,個數(shù)取讀取寄存器個數(shù)的低八位即可。返回即乘2,temp = temp << 1;,下面要做的就是一個循環(huán)for(i = 0;i < temp ; i += 2),把需要的數(shù)據(jù)放入發(fā)送數(shù)組。其內(nèi)容是
          Modbus_send_buf[i+3]=(data_v&0xff00)>>8;
          Modbus_send_buf[i+4]=data_v&0x0ff;
          由于幀的前面3個是地址,功能碼,和返回字節(jié)個數(shù),所以循環(huán)從第四個數(shù)據(jù)開始存放。data_v為讀取的數(shù)據(jù),在程序中還需要其他語句配合。比如:data_v = updateValue();
          循環(huán)后就可以進(jìn)入crc校驗了可以利用返回字節(jié)數(shù)來確定crc的校驗個數(shù)temp = temp + 3;,最后計算發(fā)送字節(jié)的個數(shù)
          send_cnt = Modbus_recevie_buf[5]*2 + 5 ; //數(shù)據(jù)發(fā)送個數(shù) 數(shù)據(jù)+地址+命令+返回數(shù)據(jù)個數(shù)+crc低+crc高
          最后將數(shù)據(jù)發(fā)送出去即可。
          5.4 04讀只可讀模擬量寄存器(輸入寄存器)
          和03的區(qū)別是04就是讀ad,ad輸入輸入模擬兩,只能讀,不能改,同樣也是以模擬兩的方式來進(jìn)行傳輸?shù)?。其程? 與03類似
          5.5 05寫數(shù)字量(線圈狀態(tài))
          05則是修改io口輸出狀態(tài),數(shù)字量輸出。
          計算機(jī)發(fā)送命令:[設(shè)備地址] [命令號05] [需下置的寄存器地址高8位] [低8位] [下置的數(shù)據(jù)高8位] [低8位] [CRC校驗的低8位] [CRC校驗的高8位]
          設(shè)備響應(yīng):若執(zhí)行成功,則原樣返回
          寫程序時,首先確定需要修改的io口,然后根據(jù)0xff00或0x0000來置位或清零該數(shù)據(jù)位。執(zhí)行完成后,將接收到的數(shù)據(jù)重新發(fā)送即可 Uart0_senddata(Modbus_recevie_buf,8);
          5.6 06寫單個模擬量寄存器(保持寄存器)
          06為修改設(shè)備da數(shù)據(jù),模擬量傳輸數(shù)據(jù)。
          計算機(jī)發(fā)送命令:[設(shè)備地址] [命令號06] [需下置的寄存器地址高8位] [低8位] [下置的數(shù)據(jù)高8位] [低8位] [CRC校驗的低8位] [CRC校驗的高8位]
          設(shè)備響應(yīng):若執(zhí)行成功,原樣返回即可
          5.7 16主機(jī)設(shè)置寄存器
          簡單的說,就是一次設(shè)置多個da,以一個偏移量為準(zhǔn),一次設(shè)置多個輸出模擬里量
          計算機(jī)發(fā)送命令:[設(shè)備地址] [命令號10] [開始地址高8位] [低8位] [寄存器個數(shù)高8位] [低8位] [第一個寄存器數(shù)據(jù)高][第一個寄存器數(shù)據(jù)低][第二個寄存器數(shù)據(jù)高][第二個寄存器數(shù)據(jù)低]……[CRC校驗的低8位] [CRC校驗的高8位]
          命令響應(yīng):功能碼[0x10],寄存器起始地址高字節(jié),低字節(jié),要寫的寄存器數(shù)量的高字節(jié),低字節(jié),CRC校驗低字節(jié),高字節(jié)
          在程序中,首先要獲取寄存器個數(shù)
          num = Modbus_recevie_buf[6] - 2;
          然后進(jìn)入循環(huán),一次把寄存器數(shù)據(jù)提取出來for(i = 0; i < num; i = i + 2)
          在循環(huán)的內(nèi)部提取數(shù)據(jù)temp = (((unsigned int)(Modbus_recevie_buf[i+7])<<8)|(Modbus_recevie_buf[i+8]));
          以上就是我在項目中涉及到的一點modbus的通訊的下位機(jī)程序,不全,但總體的思路,接收數(shù)據(jù)并解析,解析后提取數(shù)據(jù)在設(shè)備上加載或采集,然后再按照響應(yīng)的方式發(fā)送回去。
          下回改進(jìn)的方向,1,增加功能碼2,增加宏定義及編譯定義,3增加單片主機(jī)的程序,和pc主從機(jī)的程序。4,增加ascii的程序,和rtu同時設(shè)置。Pc機(jī)程序,采用c#號編寫。
          完整的程序請參考:http://www.51hei.com/bbs/dpj-23230-1.html
          上一頁 1 2 下一頁

          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();