文獻標識碼: A
文章編號: 0258-7998(2013)05-0018-04
隨著嵌入式系統功能的多樣化及網絡在各個領域中的廣泛應用,具有網絡功能的嵌入式終端擁有更高的使用價值和更強的通用性。μC/OS-Ⅲ是一個可裁剪、可固化、可剝奪型的實時內核,管理任務的數目不受限制[1]。作為μC/OS系列的最新版本,μC/OS-Ⅲ提供了實時內核所能提供的所有服務,可保證網絡功能和其他諸多任務并發有序地執行。但μC/OS-Ⅲ僅僅是一個實時操作系統的內核,要實現網絡功能還需移植一款符合嵌入式系統要求的以太網協議棧。LwIP是由瑞典計算機科學研究院開發的輕量型TCP/IP協議棧,其特點是保持了以太網的基本功能,通過優化減少了對片內存儲資源的占用[2]。一般情況下,具有十幾KB SRAM和幾十KB Flash存儲能力的微控制器即可運行LwIP協議棧[3]。該特點使其廣泛使用于數據采集、工業控制等多個應用領域中。本文論述了使用LM3S9B95微控制器的嵌入式平臺實現LwIP 1.4.0版本在μC/OS-Ⅲ上的移植。LM3S9B95是TI公司推出的基于ARM Cortex-M3內核的微控制器,其內部具有以太網控制器模塊[4]。
1 LwIP的移植過程
LwIP的移植主要涉及兩個方面:操作系統模擬層和硬件驅動層。LwIP在設計時已考慮到在不同操作系統中的可移植性,其內部使用的函數和數據結構均為抽象定義[5]。開發者可根據不同的操作系統要求來具體實現相關的函數和數據結構。同時,硬件相關的驅動同樣預留了接口,開發者可針對實際使用情況編寫網絡控制芯片驅動函數。另外,對不同的編譯環境,開發者還需要編寫部分頭文件定義相關數據結構和宏。LwIP在μC/OS-Ⅲ嵌入式系統中的結構如圖1所示,其中的箭頭框為移植工作需要實現的模塊。
1.1 操作系統模擬層的編寫
1.1.1 編寫頭文件cc.h
cc.h文件中包含處理器相關的變量類型、數據結構及字節對齊的相關宏。
LwIP中使用的基本變量類型均以位數進行命名,為抽象的變量定義,開發者需要根據所用處理器具體定義。基本變量的定義有兩種方法:一種是將變量直接定義為C語言的基本類型,如unsigned char、int等;另一種是將變量定義為操作系統內對應的抽象變量。當使用操作系統時,應采用第二種方法。該方法的優點是變量對于處理器是“透明”的,應用程序更換硬件平臺時無需修改操作系統模擬層內的定義。μC/OS-Ⅲ中對基本變量的定義在cpu.h文件中,均以CPU為命名前綴。對于這些變量在μC/OS-Ⅲ中具體如何定義本文不做討論。LwIP要求定義8 bit、16 bit、32 bit和內存指針型變量:
typedef CPU_INT08U u8_t;
typedef CPU_INT08S s8_t;
typedef CPU_INT16U u16_t;
typedef CPU_INT16S s16_t;
typedef CPU_INT32U u32_t;
typedef CPU_INT32S s32_t;
typedef CPU_INT32U mem_ptr_t;
由于ARM處理器的編譯環境默認對變量存儲采取4 B對齊方式,而以太網數據包等結構體要求處理器按照變量的實際大小存儲和訪問,因此,需要定義相關的結構封裝宏,使得結構體內的成員變量不以4 B對齊的方式進行存儲。移植工作采用了IAR開發環境,需根據該環境定義如下相關的宏:
#if defined (__IAR_SYSTEMS_ICC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
1.1.2 編寫頭文件sys_arch.h
sys_arch.h文件要求定義操作系統相關的數據結構和宏。
LwIP多線程功能需要信號量和郵箱等結構體,用于多個任務的同步和消息的傳遞。μC/OS-Ⅲ中的信號量OS_SEM和消息隊列OS_Q可實現相應的功能。LwIP 1.4.0版本中使用了互斥信號量管理共享的資源,而有些嵌入式操作系統中不包含互斥信號量的變量類型。為了適應不同的操作系統,LwIP定義了宏LWIP_COMPAT_MUTEX。LWIP_COMPAT_MUTEX的值定義為1,則LwIP使用二值信號量代替互斥信號量以及相關的功能函數。雖然μC/OS-Ⅲ包含了互斥信號量OS_MUTEX,但LwIP中兩種數據結構可相互替換,選擇使用二值信號量可以減少一定的移植工作。
#define LWIP_COMPAT_MUTEX 1
typedef OS_SEM sys_sem_t;
typedef OS_Q sys_mbox_t;
LwIP中包含有必須完整執行而不可被打斷的代碼,因此需要使用臨界段代碼保護的功能。μC/OS-Ⅲ中提供了關閉中斷和鎖定調度器兩種臨界段代碼保護方法。LwIP中的臨界段代碼保護宏可直接定義為μC/OS-Ⅲ關閉中斷的對應臨界段代碼保護宏。
#define SYS_ARCH_DECL_PROTECT() CPU_SR_ALLOC()
#define SYS_ARCH_PROTECT() OS_CRITICAL_ENTER()
#define SYS_ARCH_UNPROTECT() OS_CRITICAL_EXIT()
1.1.3 編寫源文件sys_arch.c
sys_arch.c文件要求實現操作系統模擬層的接口函數,主要包括對信號量和郵箱等數據結構的操作以及LwIP線程的操作。
LwIP的信號量用于進程間的通信,相關操作主要包括以下幾個函數:
sys_sem_new() //新建信號量
sys_sem_free() //釋放信號量
sys_sem_signal() //發送信號量
sys_arch_sem_wait() //阻塞進程,等待指定信號量
sys_sem_valid() //檢查信號量可用性
sys_sem_set_invalid() //設置信號量不可用
LwIP的郵箱用于緩存和傳遞數據報文,相關操作主要包括以下幾個函數:
sys_mbox_new() //新建郵箱
sys_mbox_free() //刪除郵箱
sys_mbox_post() //阻塞進程,投遞消息至郵箱
sys_mbox_trypost() //投遞消息至郵箱,僅一次操作
sys_arch_mbox_fetch() //阻塞進程,從郵箱中提取消息
sys_arch_mbox_tryfetch() //從郵箱中提取消息,僅一次操作
sys_mbox_valid() //檢查郵箱可用性
sys_mbox_set_invalid() //設置郵箱不可用
LwIP使用了μC/OS-Ⅲ中的信號量OS_SEM和消息隊列OS_Q結構,以上函數的實現調用了μC/OS-Ⅲ的操作函數,包括OS?Create()、OS?Del()、OS?Post()和OS?Pend()。在實現sys_?_new()和sys_?_free()函數時,需加入臨界段代碼保護以確保OS?Create()和OS?Del()在執行時不被打斷,可避免出現系統資源管理錯誤。
err_t sys_?_new(……)
{
OS_ERR err;
CPU_SR_ALLOC(); //臨界代碼保護開始
CPU_CRITICAL_ENTER();
OS?Create(……,&err);
CPU_CRITICAL_EXIT(); //臨界代碼保護結束
return err;
}
void sys_?_free(……)
{
OS_ERR err;
CPU_SR_ALLOC(); //臨界代碼保護開始
CPU_CRITICAL_ENTER();
OS?Del(……, &err );
CPU_CRITICAL_EXIT(); //臨界代碼保護結束
}
LwIP 1.4.0版本新添加了sys_?_valid()和sys_?_set_
invalid(),這兩個函數的實現無需調用操作系統內部的函數,可由開發者根據實際需求實現。另外,二值信號量替換了互斥信號量,相關的操作函數也無需在此文件內實現。在LwIP內核中的sys.h文件給出了詳細的宏定義:
#if LWIP_COMPAT_MUTEX
#define sys_mutex_t sys_sem_t
#define sys_mutex_new(mutex) sys_sem_new(mutex, 1)
#define sys_mutex_lock(mutex) sys_sem_wait(mutex)
#define sys_mutex_unlock(mutex) sys_sem_signal(mutex)
#define sys_mutex_free(mutex) sys_sem_free(mutex)
#define sys_mutex_valid(mutex) sys_sem_valid(mutex)
#define sys_mutex_set_invalid(mutex) \
sys_sem_set_invalid(mutex)
LwIP建立新進程的接口函數sys_thread_new()要求成功建立一個任務并返回任務優先級。μC/OS-Ⅲ中加入了時間片輪轉調度功能,使得同一優先級可建立多個任務,避免了優先級重復導致任務建立失敗的情況。相比于使用μC/OS-Ⅱ或其他不支持同級任務的操作系統,sys_thread_new()的實現僅調用OSTaskCreate()即可,省略了一些查找可用優先級的容錯操作。
操作系統模擬層的初始化函數sys_init()由開發者根據實際情況進行編寫,沒有固定的規范要求。該函數可不執行任何操作,但必須在文件內實現。
1.2 硬件驅動層的編寫
LwIP內核文件中給出了驅動文件的參考模板ethernetif.c,開發者可根據其模板的架構結合實際使用的網絡控制芯片來編寫驅動。
以low_level為前綴的函數均為網絡控制芯片相關的接口函數,主要包含初始化、接收、發送等操作。LM3S9B95的以太網控制器模塊包含了MAC層和物理層,有別于傳統的MCU+PHY芯片的結構。因此,實現驅動函數時可直接對相應的寄存器進行操作,無需再次封裝PHY芯片的操作函數。
以ethernetif為前綴的函數要求開發者實現底層硬件與上層協議間的接口函數,包括底層設備描述結構體的相關操作、LwIP主線程和以太網中斷服務函數等。
1.3 LwIP功能裁剪和定制
LwIP為開發者提供了一個功能定制的接口文件lwipopt.h,可根據系統實際需求定義宏的值、裁剪功能和配置參數。例如使用TCP和UDP功能,則需添加下列定義:
#define LWIP_TCP 1
#define LWIP_UDP 1
內核文件opt.h是lwipopt.h的設計模板,包含了所有LwIP功能配置的宏。opt.h文件對宏定義均采用了#ifndef預編譯判斷,當開發者在lwipopt.h中沒有對某個宏給出定義時,該文件會定義一個默認值。
雖然修改opt.h中的宏定義和在lwipopt.h中編寫宏定義均可實現剪裁和定制LwIP的功能,但由于修改內核文件會破壞協議棧的封裝性,為今后的應用程序移植和維護造成隱患,所以開發者不應直接修改opt.h內的宏定義。
開發者在編寫lwipopt.h時,由于每個宏的默認值并不能保證LwIP的正確運行,所以應對opt.h中給出的所有宏進行定義。例如opt.h中對于TCP的一些宏定義如下:
#ifndef TCPIP_THREAD_STACKSIZE
#define TCPIP_THREAD_STACKSIZE 0
#endif
#ifndef TCPIP_MBOX_SIZE
#define TCPIP_MBOX_SIZE 0
#endif
LwIP默認的TCPIP進程堆棧空間為0,TCPIP使用的郵箱空間為0。若開發者在lwipopt.h中不對這些宏進行定義,當tcpip_init()對LwIP進行初始化時,就會出現錯誤致使LwIP無法正確運行。
2 測試
測試工作使用LM3S9B95嵌入式平臺作為TCP客戶端,一臺PC作為TCP主機端。測試程序中,嵌入式平臺的IP地址設為172.21.28.250,主機IP為172.21.28.253,端口為1020。測試程序中創建了兩個任務:一個是LwIP主線程,一個是測試任務。LwIP主線程處理以太網協議的數據包,測試任務負責接收主機端的數據并回傳至主機端。
測試任務首先初始化底層硬件和協議棧,包括使能LM3S9B95以太網硬件模塊和中斷、調用協議棧內核初始化函數tcpip_init()、初始化網絡接口的結構體。
void My_LwIP_Init(void)
{
/* 調用StellarisWare庫函數進行硬件初始化 */
……
/* 調用內核初始化函數 */
tcpip_init();
/* 初始化netif,設置本機的IP、子網掩碼、網關,綁定netif的回調函數 */
netif_add(……);
netif_set_default(……);
netif_set_up(……);
}
第二步是初始化客戶端。首先創建一個網絡連接結構體,再將其綁定至端口并連接到指定的服務器。
void TCP_Client_Init(void)
{
pstNetconn = netconn_new(NETCONN_TCP); //新建連接
netconn_bind(……); //綁定端口
netconn_connect(……); //連接主機
}
任務的主循環中調用了LwIP具有進程阻塞功能的函數netconn_recv()以接收來自主機的數據。若數據接收正確,則將數據發送回主機端的PC;若接收不正確,則刪除當前的連接,重新連接到主機。
while(1)
{
err = netconn_recv(……); // 接收數據
if(err == ERR_OK) // 數據正確
{
netconn_write(……); // 發送數據
netbuf_delete(……); // 刪除數據緩沖區
}
else
{
netconn_delete(……); // 刪除當前連接
/* 重新連接 */
……
}
}
PC主機端使用了銘心軟體工作室的網絡調試助手,通過該軟件向LM3S9B95客戶端發送測試數據,客戶端的回傳數據也在該軟件內顯示。測試結果如圖2所示。
LwIP是一款專為嵌入式系統設計的以太網協議棧,具有占用資源小、基本功能完備和便于移植等特點。其擁有很高的通用性,適用于多種嵌入式操作系統和硬件平臺[6]。在運行實時操作系統的應用環境中,移植工作要求開發者實現操作系統模擬層和硬件驅動層兩個部分。協議棧的主進程可作為實時操作系統的一個任務,完整地執行網絡通信功能。μC/OS-Ⅲ是μC/OS系列的最新產品,同樣是一款實時操作系統的內核,并不具備網絡通信功能。LwIP移植到μC/OS-Ⅲ中,可使得運行該實時內核的嵌入式終端擁有網絡通信功能,符合當今產品發展的趨勢,具有更廣泛的應用領域和更高的市場價值。
參考文獻
[1] LABROSSE J J著.嵌入式實時操作系統μC/OS-Ⅲ[M]. 邵貝貝,譯.北京:北京航空航天大學出版社,2012.
[2] DUNKELS A.Design and implementation of the LwIP TCP/ IP Stack[Z].Sweden:Swedish Institute of Computer Science,2001:21-30.
[3] 熊海泉.μC/OS II下LwIP協議的移植實現[J].科技廣場,2005(2):78-79.
[4] Texas Instruments.Stellaris?誖LM3S9B95 microcontroller data sheet[Z].2011.
[5] 程明,余中華,蘇艷蘋,等.μC/OS II下LwIP協議棧的移植和測試[J].微計算機信息,2008(23):79-81.
[6] 余坤杰.LWIP網口通訊協議在LM3S8962網口上的移植實現[J].設計與分析,2011(27):155-156.