基於EncEthernet的FreeModbus-TCP 在stm32上的移植與測試

來源:互聯網
上載者:User

基於EncEthernet的FreeModbus-TCP

在stm32上的移植與測試

DanielLee_USTB  2013-3-27

QQ:382899443

       昨天移植好了modbus-RTU,今晚開始在EncEthernet上的free modbus-TCP的移植,使用的開發板為火牛開發板,stm32f103+enc28j60網路方案。主流的TCP/IP協議棧包括uIP、LwIP等,EncEthernet協議棧是一款比較簡單的協議棧,由廠家提供在stm32的開發板已經移植好,所以就直接使用,其他的協議的移植方法應該都大同小異。

       一、相關知識

       Modbus TCP/IP資料幀除了TCP已經有的包頭外,還有modbus TCP協議資料單元(ADU),包括MBAP幀頭以及與RTU資料內容相同的應用資料單元(PDU),地址碼除外。

       其中與單純的TCP/IP或是modbus-RTU相比,多的內容就是一個MBAP報文頭,這是個什麼東西,規定了什麼內容呢?先來看看都包含哪些東西。

MBAP報文頭定義

       可以看出來,MBAP報文頭主要添加了以下附加資訊,為了識別是請求還是響應而設定的事務元標識符、為了判斷協議類型設定的協議標識符、為了區分可變長度資料幀結束的資料幀長度、還有用於標識從站地址,與RTU不同的是,從地址放在了MBAP幀頭裡。

       二、代碼移植

       前兩天已經基於BARE工程移植好RTU模式,仿照相應思路實現TCP的一些函數功能,在mbtcp.c中可以發現,包括TCP初始化(xMBTCPPortInit)、TCP啟動(eMBTCPStart)、TCP停止(eMBTCPStop)、TCP接收一個資料包(xMBTCPPortGetRequest)、TCP發送一個資料包(xMBTCPPortSendResponse)等,為了實現free-modbus與EncEthernet對接,在port檔案夾下建立porttcp.c檔案,在其中包含標頭檔ip_arp_udp_tcp.h。

       (1) xMBTCPPortInit( ucTCPPort )

       這是乙太網路TCP連接埠初始化函數,怎麼覺得參數有點少呢,綁定TCP連接埠至少需要mac地址、ip地址以及連接埠地址吧,這裡面只與連接埠有關,看來只能把他們隱藏了。

       於是加入

       enc28j60Init(mymac);

       enc28j60PhyWrite(PHLCON,0x476);

       init_ip_arp_udp_tcp(mymac,myip,mywwwport);

       這個幾個函數作為TCP連接埠初始化。

      (2) eMBTCPStart

      其實在EncEthernet中只要進行了協議棧的初始化,就已經啟動了協議棧,可直接使用。

      (3) eMBTCPStop

這個函數是作為TCP連接埠關閉的函數,其實在modbus中調用它的是eMBClose,而在協議中沒有調用eMBClose把modbus給關掉,所以這個函數不用去實現。

      (4) xMBTCPPortGetRequestTCP接收一個資料包

調用enc28j60PacketReceive(BUFFER_SIZE, buf)進行判斷,當然希望移植的modbus除了能處理modbus-TCP包外還能對一些正常資料包進行響應,比如arp請求、ping命令等,所以在之後添加了包頭驗證,當確定傳入包是有資料的TCP/IP包才返回TRUE。

      (5) xMBTCPPortSendResponse TCP 發送一個資料包

封裝xMBTCPPortSendResponse發送寫好的TCP包即可。

介面部分移植完畢,下面軟體模擬一下看看modbusTCP啟動並執行流程。

       先看main函數:

       eMBErrorCode    eStatus;

       SystemInit();

       SPI_Enc28j60_Init();

       eStatus = eMBTCPInit(502 );

       eStatus =eMBEnable(  );

       for( ;; ){

         ( void)eMBPoll(  );

         usRegInputBuf[0]++;

      }

       與modbus-RTU模式類似,只是多了對ENC28J60SPI連接埠的初始化函數,使用eStatus = eMBTCPInit( 502 )對TCP/IP協議棧進行初始化,進入到eMBPoll()。到這裡的時候發現在   if( xMBPortEventGet(&eEvent ) == TRUE )條件一直為假,也就是事件隊列沒有事件,RTU模式下是定時器觸發的,TCP模式下到底應該誰去觸發它呢? TCP模式沒有控制狀態轉換狀態機器有木有!沒有東西修改隊列檢索事件的函數xMBPortEventGet,就不能被eMBPoll周期性地調用!這可是個大問題。

       由於網路資料包這裡並不是用的中斷方式,所以只能有以下解決方案:在eMBTCPStart中加上一個xMBPortEventPost(EV_FRAME_RECEIVED),那麼這樣進入到eMBPoll的時候就會調用eMBTCPReceiveàxMBTCPPortGetRequest去讀取資料包,如果是modbus-TCP包的話就把返回MB_ENOERR,再對MBAP幀頭進行判斷,查看協議類型,跳過MBAP幀頭,傳遞了正確的資料包後進入EV_EXECUTE狀態就會調用相應的函數進行處理,如果不是廣播幀則返回處理後的TCP資料包,調用完xMBTCPPortSendResponse再發送事件xMBPortEventPost(EV_FRAME_RECEIVED)即可。如果不是想要的TCP資料包,在xMBTCPPortGetRequest裡面也加入xMBPortEventPost(EV_FRAME_RECEIVED),進入下一輪查詢,這樣可以響應ARP、PING命令的資料包等。

測試下ping命令,設定開發板ip地址為222.28.40.18,與我的電腦接在同一個路由器上,可以看到返回的響應了!


Ping 目標板響應

       三、運行流程

       不過還不能高興的太早,能測試通ping只能是TCP/IP的功能,那麼接收乙太網路發來的modbus-TCP幀後是如何處理的呢?接下來分析一下,既然modbus-TCP發出來的資料包時TCP/IP包對modbus資料幀的封裝,那麼在調用peMBFrameReceiveCur(xMBTCPPortGetRequest)獲得的資料包是帶各種包頭的,要把帶乙太網路幀頭、IP頭、TCP頭去掉才行,除此以外還需要實現TCP協議的三向交握功能,這還得在xMBTCPPortGetRequest中修改,主要去判斷資料長度、是否為
arp請求、是否為空白ip包、對ping命令做出響應、實現TCP協議的三向交握功能、以及獲得modbus-TCP的MBAP幀頭。

通過對代碼的整體分析可以得出資料流在modbus協議棧是這樣流動的,:


                                Freemodbus-TCP中資料流向

       資料接收:

       由於在eMBPoll中資料是逐漸調用的,這裡不妨從資料接收的源頭開始理清思路。當一幀資料到來之後,通過enc28j60PacketReceive接收到了一個完整的TCP包,包括乙太網路頭、IP頭、TCP頭以及modbusTCP/IP 應用資料單元ADU。接著被xMBTCPPortGetRequest調用去掉了各種header,然後經過eMBTCPReceive(peMBFrameReceiveCur)對MBAP的分析,將指向MBAP報文頭的指標傳到eMBPoll:EV_FRAME_RECEIVED中。

       資料處理:

       在eMBPoll :EV_EXECUTE狀態下調用對應的處理函數,這裡以讀入寄存器狀態為例,調用的是eMBFuncReadInputRegister,對命令內容進行分析解碼,調用eMBRegInputCB 填好請求的資料。

       資料發送:

       如果不是廣播包的話,需要對資料進行回複,調用eMBTCPSend(peMBFrameSendCur)填好MBAP幀頭的長度資訊,再傳給xMBTCPPortSendResponse enc28j60PacketSend填好header以及校正發送出去。

       在EV_FRAME_RECEIVED接收完資料後,發現一個問題,按RTU的思路還要比較接收地址是不是給我們的地址,但是我們之前並沒有制定我們自己的裝置地址ucMBAddress,在xMBTCPPortInit初始化的時候只綁定了連接埠,就給地址設定帶來不便,只有在TCP連接埠初始化像設定mac地址、ip地址那樣設定從地址了,我們移植的原則是盡量不去修改下載的源碼,一切修改都在介面的實現中完成。尋根溯源,在eMBTCPReceive看到這樣的定義:

       eMBTCPReceive: *pucRcvAddress =MB_TCP_PSEUDO_ADDRESS;

       #defineMB_TCP_PSEUDO_ADDRESS   255 原來裝置從地址居然是一個宏定義!協議棧是這樣解釋的:

       /* Modbus TCP does not useany addresses. Fake the source address such

       * that the processing partdeals with this frame.*/

       原來modbus-TCP根本不需要從地址,因為已經有IP地址以及MAC地址進行了綁定!

       四、通訊測試

      
移植分析完畢,把程式下載到stm32板子中進行測試。

       (1)測試載入器

       上位機採用TCP&UDP測試載入器建立伺服器


       以及抓包工具wireshark


Wireshark 抓包工具

       (2)測試內容

       由於EncEthernet原協議TCP連接埠號碼只支援了一個位元組,而modbus-TCP要用到502連接埠,超出了這一範圍,稍加改動就可以正常與上位機串連了。

       在wireshark中可以捕獲到TCP協議三向交握串連以及斷開過程如所示:


TCP三向交握建立串連

       使用上位機TCP&UDP測試載入器發送讀取寄存器指令00 0000 00 00 00 ff  04 00 00 00 01


使用上位機發送的TCP資料幀

       來讀取GPIOA的資料,可是沒有資料返回,先Debug一下看看板子有沒有收到這個資料包,從結果看來確實收到了這個資料包,原來是資料發送有問題,修改xMBTCPPortSendResponse函數,先發送一個ACK應答包,再發送資料,就可以了!注意資料的格式。在wireshark中抓包如所示:


Wireshark收到的Modbus-TCP資料幀


上位機收到的modbus-TCP幀資料

       更改上位機指令,使其發送讀取3個寄存器(GPIOA-GPIOC)的值:


更改指令讀取3個寄存器返回的資料

       至此,經過了三天時間modbus-TCP模式終於能夠正常通訊了,真高興!其實無論是移植EncEthernet,或是uIP、Lwip基本思路都是一樣的,弄清楚資料接收以及發送的整個過程很重要,可以很好的協助理解協議棧是如何啟動並執行。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.