教程簡介 MODBUS是一種基于主從結構的控制協議,由一個主機和多個從機組成。 MODBUS是施耐德電氣于1979年為可編程邏輯控制器(PLC)通信所開發并發布的協議,至今已有相當長的歷史。 以前它主要基于串口,但由于物聯網的發展,基于TCP的MODBUS現在被越來越廣泛地使用。恩智浦的i.MX RT 系列控制器的SDK中沒有相關的demo,本文的目標是向大家介紹如何在SDK中的lwip_ping_freertos例程基礎上添加MODBUS TCP。
MODBUS 協議棧的下載地址:
https://www.embedded-experts.at/en/freemodbus/
測試軟件QModMaster 的下載地址:
https://sourceforge.net/projects/qmodmaster/
我在本次教程使用了MIMXRT1024-EVK 作為硬件平臺,MIMXRT1024-EVK 和電腦都連接到一臺路由器組成一個測試網絡。使用 MCUXpresso IDE 作為開發環境,MIMXRT1024-EVK SDK 版本是 2.12.1。 一、文件復制
下載完協議棧后,先解壓,隨后將其中的 modbus 目錄完整復制到 lwip_ping_freertos 工程目錄下,添加 modbus 及其所有子文件夾到工程的 include paths 中。
在協議棧里有一個demo 目錄,里面是modbus 被移植到各個廠商的處理器上的demo, 可惜沒有i.MX RT。仔細對比各個 demo 就可以發現,原來這些 demo 有很多就是基于freertos 和 lwip 的。我們就從 MCF5235 TCP 下手,它既有freertos 又有lwip,完美符合我們的項目。可以把它的 port 目錄復制到我們項目的modbus 目錄下。把其中的文件全部涵蓋進來。
二、修改程序
接下來首先是在 lwip_ping_freertos.c 中include “mb.h”。這個文件包含了 freemodbus 協議棧提供的所有接口函數。
1.程序結構
Lwip 協議棧為用戶應用程序的編寫提供 3 種編程接口:
第一種是Raw Callback API,這種方式下協議棧與用戶程序間通過回調函數實現通信。而且協議棧同應用程序處在同一個進程中,彼此間的執行都會互相制約。
第二種是 Sequential API 方式,用戶向內核注冊回調函數,并通過直接調用內核 UDP 或TCP 相關操作函數來完成應用程序的編寫。在這種方式下,協議棧內核運行于進程 tcpip_thread, 而應用程序進程也是一個單獨的進程。獨立的進程結構可以使協議棧和應用程序的執行互不影響。通過使用郵箱和信號量等機制,內核進程可以直接將數據遞交給應用程序郵箱中然后繼續執行,不必阻塞等待。
第三種是使用 BSD socket 函數進行應用程序開發。本來這是最簡單的方式,但是由于BSD socket 函數在實現上高度抽象,不適合小型嵌入式TCP/IP 應用,所以 lwip 里的socket 函數并不是非常完整。
為實現與 lwip 協議棧之間的相互調用,freemodbus 協議棧采用的是sequential API 方式。具體的調用順序如下:在 mb.c 中提供了 eMBTCPInit() 函數, 這個函數調用eMBTCPDoInit()函數, 隨后層層調用下去,最后在 xMBTCPPortInit()函數中調用 tcp_bind()。這個函數是 lwip 的 tcp.c 提供的。下圖反映出了 modbus 協議棧的層次結構。
最下層的tcp_bind()函數的作用是將一個連接結構與本地 IP地址addr和端口號 port 進行綁定。作為服務器端程序,執行這一步操作是必要的,服務器必須與熟知端口進行綁定才能接受客戶端的連接請求。這里可以看到,freemodbus協議棧并沒有以最標準的方式調用netconn_bind()進行綁定,而是直接調用 tcp_bind()。原因應該是這樣做可以免去IPC 通信過程,在無操作系統的環境下也能工作。
tcp_listen()的任務是讓 tcp 內核監聽這個端口;tcp_accept()是為新的連接注冊一個回調函數prvxMBTCPPortAccept()。在tcpip_thread()進程監聽到有效連接后會回調這個函數。而這個函數會把 modbus 的數據包從 tcp 數據包中復制出來,隨后發送 mailbox 信息給modbus 的進程,用來處理消息。
2. 修改接口
隨后是修改porttcp.c。在這個文件中的 prvxMBTCPPortReceive()函數有一個致命問題必須被改正。這個函數是 tcpip_thread()核心進程在收到 modbus 包后的回調函數。當數據處理完成后它會調用pbuf_free()函數釋放pbuf包并返回 ERR_OK。tcpip_thread()核心進程收到ERR_OK就知道數據已經處理完了,就可以放心踏實的干別的事情了。然而不知為何,這里的 prvxMBTCPPortReceive() 函數在某些時候釋放了pbuf卻并不返回 ERR_OK。于是tcpip_thread()核心進程在沒有收到正確的返回值時會認為數據包沒有被處理,會把這個實際已經廢棄的包暫存下來,下次再處理。等到下次處理時就會產生 pbuf錯誤,使得整個程序被鎖死。
以下是修改的地方:
3. 上層代碼編寫
接下去是為上層應用寫的示例代碼。以下 code 全部都是在 lwip_ping_freertos.c 中。 3.1 Tcpip協議棧初始化 我沒有指定 IP 地址,而是由路由器來分配。
3.2Modbus協議棧初始化
這個進程首先是等 DHCP 拿到路由器給的 IP 地址,隨后就可以初始化 modbus 協議,并采用輪詢方式等待連接。
3.3編寫各種命令對應的程序
三、驗證 連接所有線路后編譯下載。我們的程序會在串口打印出路由器給它分配的 IP 地址。
打 開 QModMaster, Modbus 模式選 TCP;
選項->Modbus TCP->從機 IP 填獲得的 IP 地址,端口是 502;功能碼選Read Holding Register (0x03),起始地址 100;
寄存器數量選 6,Data Format 選 16 進制; 按連接按鈕,QModMaster 就能和我們的板子連上,按鈕變成連接起來的樣子;按旁邊讀寫按鈕,就能讀出register的值(見程序vTask_HoldingRegister())。 一切順利,移植成功!
QModMaster有一個總線監視器窗口,可以看到收發的數據。
審核編輯 :李倩
-
控制器
+關注
關注
112文章
16433瀏覽量
178950 -
協議棧
+關注
關注
2文章
144瀏覽量
33677 -
SDK
+關注
關注
3文章
1044瀏覽量
46123
原文標題:i.MX RT1024: 移植 MODBUS TCP協議棧教程
文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論