目錄
構建一個xenomai 3 應用
詳細的構建與安裝見https://gitlab.denx.de/Xenomai/xenomai/-/wikis/App_Setup_And_Init
總而言之,您應該使用xeno-config腳本來獲取適當的編譯和鏈接器標誌相關的Xenomai,以便
構建您的應用程序的Cobalt or Mercury core.
xeno-config腳本的完整用法可在以下https://xenomai.org/documentation/xenomai-3/html/man1/xeno-config/index.html找到。
對於沒有耐心的人,這裏有一個簡單的Makefile片段,用於檢索編譯器和用於構建單文件應用程序vxapp.c的標誌,通過VxWorks仿真API:
XENO_CONFIG := /usr/xenomai/bin/xeno-config
CFLAGS := $(shell $(XENO_CONFIG) --vxworks --cflags)
LDFLAGS := $(shell $(XENO_CONFIG) --vxworks --ldflags)
CC := $(shell $(XENO_CONFIG) --cc) EXECUTABLE := vxapp
all: $(EXECUTABLE)
%: %.c
$(CC) -o $@ $< $(CFLAGS) $(LDFLAGS)
編譯一個基於rtdm的模塊
構建常規內核模塊/驅動程序的規則也適用於基於rtdm的驅動程序,不需要額外的要求。
例如,用於構建some_driver.ko的Makefile,由foo.c和bar.c兩個文件組成,基於RTDM API:
obj-y += some_driver.o
some_driver-y := foo.o bar.o
從內核樹構建這個模塊應該從包含模塊源的目錄中完成,如下所示:
$ make -C /path/to/kernel/tree M=$PWD modules
從內核樹構建這個模塊應該在包含驅動程序模塊的目錄中完成,在爲雙重內核配置構建驅動程序模塊之前,目標內核樹必須已經準備好並按照這裏的文檔進行構建。
運行應用程序
運行一個xenomai 3應用
對於Cobalt 內核來說,您需要將實時內核構建到目標Linux內核中,安裝描述步驟:https://gitlab.denx.de/Xenomai/xenomai/-/wikis/Installing_Xenomai_3
對於Mercury 內核來說,到目前爲止,您不需要特定於xenomai的內核支持,除了您的主機Linux內核已經提供的。您的內核至少應該提供高分辨率計時器支持(CONFIG_HIGH_RES_TIMERS)
如果您的應用程序需要短且有限制的延遲,則可能需要完全搶佔(PREEMPT_RT)。任何基於xenomai的應用程序都可以識別一組可以在命令行上傳遞的標準選項,如本文檔所述。
此外,運行在Xenomai核心上的Alchemy、pSOS和VxWorks™api可以定義要使用的時鐘分辨率,以納米秒爲單位,即HZ=(1000000000 / ns),由--{Alchemy / pSOS / VxWorks}-clock-resolution=<ns>選項。
如果您的應用程序組合了多個api,您可以通過幾個時鐘分辨率開關來設置它們。默認值取決於所考慮的API。例如,VxWorks™和pSOS™模擬器默認爲毫秒時鐘速率。Alchemy API默認是無標記的,即--Alchemy -clock-resolution=1。指定大於1納秒的分辨率需要Xenomai庫提供低分辨率時鐘支持(請參閱--enable-lores-clock configuration開關)。
實時的I / O的支持
Real-time networking (RTnet)
-
關於RTnet
RTnet的設計目的是爲RTAl和Xenomai環境提供實時網絡功能。本文件旨在爲使用Xenomai進入RTnet的初學者提供一個跳板。
RTnet項目的代碼以模塊化的方式組織。通常的biq圖像是TDMA實時現場總線,但是您也可以使用RTnet執行更簡單的任務,比如實時點對點通信。對於更簡單的任務,可能不需要全部RTnet。
-
RTnet相關特性
安裝好RTnet後有兩個文件夾需要注意:
- 源文件
默認地址:_/usr/src/rtnet-x.y.z .
特別需要注意:
*/Documentation :包含RTnet功能的結構和使用的基本信息
*/example:包含已編譯並放在該文件的的實例的源代碼
- 安裝文件
默認地址:/usr/local/rtnet.
安裝文件夾包含運行RTnet所需的所有已編譯的可執行文件和內核模塊
etc:RTnet的兩個安裝配置文件位於此文件夾中。rtnet.conf和tdma.conf設置。
examples: 編譯後的RTnet示例放在這裏
sbin:在此文件夾中,有各種用於運行和配置RTnet的腳本。
include:該文件夾包含將用於編譯任何RTnet程序的包含文件。
modules: 爲RTnet編譯的模塊和使它與實時設備驅動程序一起高效運行的所有組件都位於這裏
-
RTnet 基本要素
使用以太網時,通信是不確定的,因爲在網絡上有一個集線器的幾個主機之間可能會發生衝突,或者由於交換機的延遲未知以。
太網協議不允許確定性通信,因爲可能會發生衝突,並由CSMA/CD機制(載波感知多址/衝突檢測)支持。
RTnet是一個協議棧,它運行在以太網層和應用層(或IP層)之間。它的目的是通過使用時間間隔(時隙)來進行確定性通信,通過禁用碰撞檢測CSMA/CD,防止網絡中的緩衝包。RTnet是一款在Linux內核上運行的軟件,具有RTAl或Xenomai實時擴展。它利用實時內核擴展來保證通信棧的確定性。在這個目標中,所有與此協議相關的指令都使用實時內核函數,而不是Linux函數,這些函數將延遲綁定到執行時間和中斷延遲,從而提供確定性的通信
- TDMA操作
通信分時段TDMA(時分多址)是協議決定論的基礎。使用分配給主機發送數據的時間間隔(時間槽)不能再使用衝突檢測機制(CDMA / CD),因此限制了消息的發送和接收之間的時間間隔。基於主從操作來同步網絡中每臺主機的時鐘併爲每臺主機分配時隙,協議主要依賴於定期發出同步消息的主機。Wiki: tdma.png ?600
- 同步
Rtnet採用時分多路複用TDMA(時分多址),避免了通信衝突,在每個週期的開始,主機發送一個包含全局時鐘信息的幀進行同步。同步幀在網絡和包含一個週期的定期發送4個字節編碼數量,大師的參考時間的價值在納秒和傳輸參考時間的值,這個值有助於減少全球時鐘的偏差。同步消息包含以下數據:爲了彌補集中式主從系統的不足,可以通過爲備份主系統預訂額外的插槽來建立備用主系統(備份主系統)。如果主主系統崩潰,則在考慮到前一幀同步的變化之後,將激活輔助主系統。當主主服務器恢復服務時,它與活動的輔助主服務器同步,然後禁用輔助主服務器。
- 協議棧
Rtnet協議棧包含OSI模型傳輸層的物理部分。它是專門針對實時內核RTAl和Xenomai開發的。爲了保證通信應用的確定性,所有功能使用的以太網控制器應用軟件都必須滿足硬實時約束。因此,爲了使用Linux的實時內核而不是通常使用的內核,必須實現網絡laver協議,特別是傳輸協議。Wiki: pile.png
- 物理層
物理層由以太網控制器支持,需要使用從Linux派生的特定驅動程序。處理內存、中斷處理機制或屏蔽中斷需要實時內核執行操作,以確保確定性。還要考慮在以太網驅動程序中最準確地檢測發射和接收的可能瞬間。從材料的角度來看,可以使用任何以太網控制器,並支持其他物理層(如火線)。管理控制器的初始化、設置和關機是不實時的,因此要特別注意收發功能。在發送時,保存全局時鐘的值,在接收時,需要保存接收時的全局時鐘的值,以便精確控制。
- 數據鏈路層
以太網作爲一種標準操作,不能提供硬實時通信,無論是在基於中繼器的網絡上,還是在存在衝突風險的交換機上。IEEE 802.1q提供的服務質量爲這個問題提供了一個很好的解決方案,但是交換機的集中是昂貴的佈線。Rtnet的數據鏈路協議是由內核模塊Rtmac提供的,它包含一個模塊TDMA。TDMA模塊管理網絡上的通信(同步、週期時間等)。雖然這一層是stack Rtnet的可選擴展,但它帶來了基於這些服務的確定性媒體訪問協議:
*將特定封包傳送至適當的服務
*使用高層協議交換控制消息和數據。
*在使用多個網絡接口的情況下,可以定義特定於媒體訪問控制的接口。
*通過服務隧道與非實時網絡交換數據。
Rtmac層是一個提供以下服務的模塊:
*截獲包信息,將它們重定向到適當的服務。
*上層並行數據應用程序中控制數據或特定信息的交換。
*通過建立隧道與非實時系統進行通信
*使用一個虛擬接口並添加一個標頭來區分這個流。
- TDMA層
Rtnet在數據鏈路層採用時分接入網,防止發生數據衝突。該協議利用主從通信對網段節點的時鐘進行同步,並對週期性傳輸的同步消息定義了數據的傳輸時間。如果一個節點知道自己的時間訪問權限,那麼它甚至可以在網絡啓動時加入網絡,並在該節點上手動配置或在網絡節點(rtcfa)上設置服務器配置。一旦獲得該參數,節點將通過向主同步發送查詢來評估返回網絡的時間,主同步將通過在發送的包中包含可以計算返回時間的計時信息來響應。
- 網絡和傳輸層
通過簡化路由過程和碎片優化,支持UDP/ IP和TCP/ IP網絡和傳輸層。地址解析協議(ARP)只在初始化期間進行操作。在總線上的通信環境中,這些協議可能是不必要的,但是與外部網絡的通信相關,通常是非實時的。IP協議實現在實時內核上運行,並支持發送大小超過MTU(最大傳輸單元)的數據包的分段。爲了執行路由,需要使用兩個表,第一個表是任務的路由表,它包含在LAN上可以訪問的主機的IP地址。第二個表是可選的,它將網關地址實時地用於其他網絡,允許建立結構更復雜的網絡。路由表的內容類似於標準IP棧的ARP表。對於Rtnet,此表用於路由過程,而對於標準電池則不一定如此。UDP和TCP在IP層(版本4)之上實現,UDP用於硬實時傳輸。反過來,TCP最近已經實現,其目的是與不運行Rtnet的網絡元素進行通信,TCP要複雜得多(在一百多個RFC中進行了描述)。因此,由於其用途有限,它的實現已大大簡化。UDP則是默認協議,用於在兩個Rtnet應用程序之間建立實時通信。Rtcfg是爲Rtnet有效配置網絡而定義的協議。爲此,將網絡節點用作服務器並將配置信息發送給其他節點(客戶端)。由於虛擬接口的存在,可以運行所有網絡層協議並在更高標準的Linux內核中實現。也有可能對於有限的通信網絡節點不使用路由協議和傳輸(socket類型RAW)。
-
RTnet 配置文件
。當啓動rtnet啓動腳本時,將讀取rtnet.conf文件,並在啓動時設置rtnet環境。下面討論這個文件中的條目:(**********重要**********)
RT_DRIVER:RT驅動程序:此條目指定用於運行網卡的實時驅動程序。不是所有的網卡都支持RTnet,要找到支持的驅動程序,請查看/drivers下驅動程序的源代碼。編譯後的驅動程序位於/modules…
RT_DRIVER_OPTIONS:RT驅動程序選項:系統上可能安裝多個網卡。要識別RTnet應該使用哪張卡,此條目用1指定它。爲了找出哪張卡是由設置配置中的哪一個位置表示的,請更改1的位置並在主模式下啓動rtnet。當你把網線插入集線器時,活動卡的LED會不停地閃爍,就像它在尋找從機一樣……
IPADDR:主機IP
NETMASK:子網掩碼
RT_LOOPBACK:如果在RTnet的安裝設置中選擇了LOOPBACK選項,那麼您可以使用此條目指定環回設備是否處於活動狀態。此環回用於測試RTnet的正確工作方式,請參閱安裝說明了解更多信息
使用細節:
RTCAP :RTCAP:爲了能夠使用帶有RTnet的以太網絡分析儀,使用此條目。這也只有在實時捕獲支持在RTnet安裝時被選中時纔有效。更多信息參見 /Documentation/README.rtcap 或https://www.rts.uni-hannover.de/rtnet/download/RTnet-ETFA05.pdf
TDMA_MODE :指定當前系統是主服務器還是客戶機
TDMA_SLAVES : 當這個系統被設置爲主機時,那麼網絡上的從機必須列在這裏。如果該系統被設置爲從屬系統,請保留空白
TDMA_CYCLE : TDMA週期:對於簡單的TMDA設置,這裏列出了以微秒爲單位的TDMA週期時間,僅當系統設置爲主系統時使用。
TDMA_OFFSET :。TDMA偏移量:對於簡單的TMDA設置,這裏列出了以微秒爲單位的TDMA偏移時間,僅當系統設置爲主系統時使用。
TDMA_CONFIG : TDMA配置:當沒有使用簡單的TDMA設置,而需要更復雜的設置時,該條目指定要使用的TDMA配置文件。它通常位於/etc/tdma.conf。更多設置細節/Documentation/README.rtmac
-
RTnet 測試
- 單節點測試(本地迴路)
檢測程序如下:
從正常的非實時ehternet網絡中斷開RTnet節點的網絡電纜
重新啓動系統到補丁的內核模式
使用該命令顯示網絡設置
ifconfig
這將顯示你有運行的etho(這是你的網卡)和運行的lo(這是本地環回)
您需要禁用網卡的非實時網絡操作如下:
ifconfig eth0 down
然後,卸載網卡的設備驅動程序。
sudo rmmod 8139too
加載RTnet操作所需的實時Linux擴展所需的模塊,如果你在xenomai2.x 構建內核時支持了RTDM,或者你正在運行xenomai3.x,跳過此步驟
在位於/usr/ocal/rtnet/etc/rtnet.conf的RTnet配置文件中編輯以下參數:
sudo ./rtnet start
RTnet正在等待連接。cntl +C結束等待。通過調用Ismod並在加載的模塊列表中查找網卡的實時驅動程序,可以看到RTnet正在運行。如果未列出,則RTnet沒有運行
按如下方式Ping局部環回
sudo ./rtping 127.0.0.1
若要停止RTnet傳輸,請運行:
sudo ./rtnet stop
- 多節點測試
測試程序如下:
從普通的非實時以太網網絡中斷開RTnet節點。
使用開關(或集線器)將RTnet節點彼此連接。
重新啓動系統到補丁的內核模式。
修改rtnet配置文件/usr/local/rtnet/etc/rtnet.conf。你應該檢查以下內容:
sudo ./rtnet start
使用以下命令檢查節點之間的通信,其中爲遠程節點的IP地址:
sudo ./rtping IP
停止命令 ctrl+C
卸載RTnet模塊
sudo ./rtnet stop
- Debugging RTnet
如果RTnet不能工作,或者只能間歇性地工作,那麼很可能是由於這些已知的問題之一造成的
①Xenomai在你的硬件上不能正常工作。
②將用於實時網絡設備的Linux驅動程序構建到內核中,並阻塞硬件。
③IRQ衝突。Xenomai能夠檢測衝突並將它們報告到內核控制檯。
④您使用的是rt e1000驅動程序和RTnet 0.9.9或更老版本。請參閱RTnet: rte1000查看解決方案。
解決問題的步驟:。
①檢查內核控制檯或系統日誌是否有可疑消息。
②驗證實時擴展的基本測試是否正常工作。Xenomai 還有一個基本的測試叫 latency
③收集有關您的設置(版本、配置、輸出消息)的信息,並在rtnet用戶上發佈支持請求
- 同時使用實時(RTnet)和非實時網絡
如果您的系統上有兩條(或更多)以太網線路可用,您可以確定其中一條使用RTnet(實時),另一條使用非實時網絡:
①卸載非實時驅動程序
② 加載網卡驅動並指定網卡參數(e.g. insmod rt_e1000.ko cards=1,0).
③加載非實時模塊並啓動它(使用insmod和ifup的標準方式)。
網卡 參數接受一個由0和1組成的數組。例如insmod rt e1000.ko card=0,1,0 使用3的“中間”網卡。
$ sudo insmod /usr/local/rtnet/modules/rt_e1000.ko cards=1,0 $ sudo insmod e1000 $ sudo ifup eth1
-
RTnet編程代碼
介紹
一旦您理解了RTnet如何與RTOS相關的概念,在RTnet中進行編程就非常簡單了。
RTnet本身實際上只是一個內核模塊,它與實時以太網設備驅動程序通信RTnet和RTOS之間的接口是由Xenomai實現的實時驅動程序Modell提供的。該接口在Xenomai文檔中有完整的文檔記錄。
RTDM規範位於Xenomai API樹的模塊/實時驅動程序模型/用戶APl部分。
基礎引導
爲了在RTnet中編程,遵循了標準的Linux套接字編程方法。如果您不熟悉Linux套接字編程,下面的鏈接將提供關於這個主題的一些額外信息。
本機rt dev xxx與POSIX服務調用(使用Xenomai)的區別:
Xenomai應用程序獨立於RTnet,可以調用Linux的正常網絡服務。他們只是在那一點上失去了時間保證。
POSI皮膚的編程模型允許您像編寫普通的Linux應用程序一樣使用套接字函數。如果您的呼叫地址是RTnet提供的服務(UDP或AF包),並且加載了RTnet,它將在實時約束下爲您處理。RTnet不知道的服務被傳遞到Linux,沒有時間隔離。順便說一句,同樣的模式也適用於AF CAN。
(兩點從RTnet郵件列表中複製)
RTnet中的示例項目提供了在這種環境中進行編程的基本介紹。例子中的一般步驟如下:
聲明兩個套接字描述符結構,一個用於本地套接字,另一個用於遠程套接字。
static struct sockaddr_in local_addr;
static struct sockaddr_in server_addr;
在用值填充這些結構之前清除它們
memset(&local_addr, 0, sizeof(struct sockaddr_in));
memset(&server_addr, 0, sizeof(struct sockaddr_in));
將地址格式設置爲Internet (IP)
local_addr.sin_family = AF_INET;
將本地地址設置爲主機地址
local_addr.sin_addr.s_addr = INADDR_ANY;
將sting轉換爲端口號並將其存儲在結構中
local_addr.sin_port = htons(atoi(argv[1]));
對於遠程端口設置,再次將設備格式設置爲IP
server_addr.sin_family = AF_INET;
將IP地址sting轉換爲網絡格式
server_addr.sin_addr.s_addr = rt_inet_aton(argv[2]);
設置遠程端口號
server_addr.sin_port = htons(atoi(argv[3]));
創建一個新的套接字來管理連接。
sockfd = rt_dev_socket(AF_INET, SOCK_DGRAM, 0);
將新的套接字綁定到本地端口
ret = rt_dev_bind(sockfd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in));
將套接字連接到遠程端口
rt_dev_connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
然後發送信息。
rt_dev_send(sockfd, msg, sizeof(msg), 0);
在接收端接收消息
ret = rt_dev_recv(sockfd, msg, sizeof(msg), 0);
* Close the socket after transmission
--------------------
rt_dev_close(sockfd);
---------------------