uCOS平臺下的LwIP移植.doc_第1頁
uCOS平臺下的LwIP移植.doc_第2頁
uCOS平臺下的LwIP移植.doc_第3頁
uCOS平臺下的LwIP移植.doc_第4頁
uCOS平臺下的LwIP移植.doc_第5頁
已閱讀5頁,還剩57頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

此文檔收集于網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系網(wǎng)站刪除1下載LwIP. 22建立一個最基本的工程. 23把LwIP加入工程. 24編寫作業(yè)系統(tǒng)類比層相關(guān)代碼. 34.1作業(yè)系統(tǒng)類比層移植說明中文翻譯 . 34.2編寫作業(yè)系統(tǒng)類比層. 64.2.1準備工作建立文件、定義資料類型及其它 . 64.2.2信號量操作函數(shù). 84.2.3郵箱操作函數(shù). 134.2.4實現(xiàn)sys_thread_new()函數(shù) . 204.2.5實現(xiàn)sys_arch_timeouts()函數(shù) . 224.2.6實現(xiàn)臨界保護函數(shù). 254.2.7掃尾結(jié)束作業(yè)系統(tǒng)類比層的編寫 . 265LwIP介面初始設(shè)置及網(wǎng)路驅(qū)動. 285.1準備工作建立LwIP入口函數(shù)檔 . 285.2 ilvInitLwIP(). 295.3 ilvSetLwIP(). 305.4ethernetif_init()初始化底層介面 . 355.4.1ethernetif_init()函數(shù)分析 . 355.4.2low_level_output()鏈路層發(fā)送函數(shù) . 365.4.3low_level_init()網(wǎng)卡初始化函數(shù) . 385.4.4EMACInit()網(wǎng)卡初始化工作的實際完成者 . 405.4.5ethernetif_input()實現(xiàn)接收線程 . 475.4.6low_level_input()得到一整幀資料 . 495.4.7GetInputPacketLen()獲得幀長 . 505.4.8EMACReadPacket()複製,從接收緩衝區(qū)到pbuf . 535.4.9EMACSendPacket()發(fā)送一幀資料 . 555.4.10編譯ethernetif.c及l(fā)ib_emac.c . 566ping結(jié)束LwIP的移植. 576.1編譯、鏈結(jié)整個工程. 576.2ping測試. 59後記. 62此文檔僅供學習與交流本文將指導讀者一步步完成 LwIP 在 ADS1.2 開發(fā)環(huán)境下的移植工作,包括底層驅(qū)動的編寫。本文使用的 硬體平臺是 AT91SAM7X256 + RTL8201BL(PHY),至於軟體平臺,讀者從本文標題即可看出。我們使用 uC/OS-II 作為底層作業(yè)系統(tǒng),而 LwIP 的移植亦將主要圍繞 uC/OS-II 展開。好了,不再多說,開始吧1 下載 LwIP很簡單,到LwIP的官方網(wǎng)站即可:/projects/lwip/。如果你不想看看其他內(nèi)容 (可能對 你會很重 要 ),就只是想得到 源碼,好 的,直接 到這個地 址下載 : /releases/lwip/。目前官方發(fā)佈的最新版本是 1.1.1,找到 lwip-1.1.1.zip,然後下載、解壓縮,第一項工作完成。2 建立一個最基本的工程要想完成移植工作,我們必須要有一個包含 uC/OS-II 的工程才行,這一步我們就是要建立這個工程。 工程建立完畢後,編譯鏈結(jié)沒有問題,那麼,第二項工作也完成了。關(guān)於如何建立一個包含 uC/OS-II 的ADS 工 程的 問題,不在 本文描述範 圍之內(nèi),這 裏不做講述 。隨本筆記 一同發(fā)佈的 源碼文檔中 LwIPPortingTest_2 檔夾下包含了這個最基本工程的源碼,讀者可以直接使用。我的基本工程建立的 路徑是 D:workLwIPPortingTest,下文將以相對路徑進行講述,不再提供絕對路徑。3 把 LwIP 加入工程首先,在src文件夾下,建立 LwIP 文件夾,即:srcLwIP;然後將下載的 LwIP 源碼文件中 api、core、include、netif 文件複製到srcLwIP文件夾下,如下圖所示:圖 3.1然後,用 ADS 打開工程檔,按照 LwIP 源碼檔的實際存放路徑建立 LwIP 的工程結(jié)構(gòu),如下圖所示:圖 3.2這裏需要特別說明的是,源碼中的 IP V6、SLIP 及 PPP 部分我們沒有添加進來,主要是考慮我及大多數(shù) 讀者的網(wǎng)路還是 V4,而 SLIP、PPP 暫時不在我的考慮範圍之內(nèi)。另外,在移植層面 V6 也和 V4 相差不多, 這裏就不再講解這部分內(nèi)容了?,F(xiàn)在基礎(chǔ)工程結(jié)構(gòu)建立完畢,可以把 LwIP 源碼添加進來了。這一步很容 易,按照檔存放路徑,將源碼檔添加到相應(yīng)的工程結(jié)構(gòu)下即可。源碼添加完成後的工程參見所附源 碼檔的 LwIPPortingTest_3 文件夾。4 編寫作業(yè)系統(tǒng)類比層相關(guān)代碼LwIP 的作者為作業(yè)系統(tǒng)類比層提供了較為詳細的說明,檔案名為 sys_arch.txt,在 LwIP 的 doc 文 件夾下。我們的編寫工作根據(jù)這個說明進行。4.1作業(yè)系統(tǒng)類比層移植說明中文翻譯事先聲明,之所以筆者要翻譯該文檔,主要是筆者在撰寫這篇筆記時亦沒有通讀該文檔。筆者先前 使用的模擬層源碼是楊曄大俠的。為了真正弄懂 LwIP,筆者決定自己重新實現(xiàn) LwIP 的移植,本筆 記是跟隨移植同步進行的,因此,翻譯的文檔也放在了這篇筆記中,使讀者能夠真正瞭解筆者的移 植歷程。另外再說一句,這個文檔是為 LwIP 0.6+版編寫,筆者搜遍了整個 LwIP 官方網(wǎng)站,沒有 發(fā)現(xiàn)比這更新的,筆者只好認為作業(yè)系統(tǒng)類比層在 0.6+之後沒有任何改動,如果有誰發(fā)現(xiàn)了更新 的,一定通知筆者,先謝謝了。好的,言歸正傳,下面就是譯文:LwIP 0.6+ sys_arch 介面作者:Adam Dunkels作業(yè)系統(tǒng)類比層(sys_arch)存在的目的主要是為了方便 LwIP 的移植,它在底層作業(yè)系統(tǒng)和LwIP 之間提供了一個介面。這樣,我們在移植 LwIP 到一個新的目標系統(tǒng)時,只需修改這個介面即可。不過,不依賴底層作業(yè)系統(tǒng)的支援也可以實現(xiàn)這個介面。sys_arch 需要為 LwIP 提供信號量(semaphores)和郵箱(mailboxes)兩種進程間通訊方式(IPC)。如果想獲得 LwIP 的完整功能,sys_arch 還必須支持多線程。當然,對於僅需要基本功能的用戶來說,可以不去實現(xiàn)多線程。LwIP 以前的版本還要求 sys_arch 實現(xiàn)計時器調(diào)度,不過,從 LwIP0.5開始,這一需求在更高一層實現(xiàn)。除了上文所述的 sys_arch 原始檔案需要實現(xiàn)的功能外,LwIP 還要求用戶提供幾個頭檔,這幾個頭檔包含 LwIP 使用的巨集定義。下文將詳細講述 sys_arch 及頭文件的實現(xiàn)。信號量即可以是計數(shù)信號量,也可以是二值信號量LwIP 都可以正常工作。郵箱用於消息傳遞,用戶即可以將其實現(xiàn)為一個佇列,允許多條消息投遞到這個郵箱,也可以每次只允許投遞一個消息。這兩種方式 LwIP 都可以正常運作。不過,前者更加有效。需要用戶特別注意的是投遞到郵箱中的消息只能是一個指標。在 sys_arch.h 檔中,我們指定資料類型“sys_sem_t”表示信號量,“sys_mbox_t”表示郵箱。至於 sys_sem_t 和 sys_mbox_t 如何表示這兩種不同類型,LwIP 沒有任何限制。以下函數(shù)必須在 sys_arch 中實現(xiàn):- void sys_init(void)初始化 sys_arch 層。- sys_sem_t sys_sem_new(u8_t count)建立並返回一個新的信號量。參數(shù) count 指定信號量的初始狀態(tài)。- void sys_sem_free(sys_sem_t sem)釋放信號量。- void sys_sem_signal(sys_sem_t sem)發(fā)送一個信號。- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)等待指定的信號並阻塞線程。timeout 參數(shù)為 0,線程會一直被阻塞至收到指定的信號;非 0,則線程僅被阻塞至指定的 timeout 時間(單位為毫秒)。在 timeout 參數(shù)值非 0 的情況下,返回值為等待指定 的信號所 消耗的毫 秒數(shù)。如 果在指定 的時間內(nèi) 並沒有收 到信號, 返回 值 為SYS_ARCH_TIMEOUT。如果線程不必再等待這個信號(也就是說,已經(jīng)收到信號),返回值也可以為 0。注意,LwIP 實現(xiàn)了一個名稱與之相似的函數(shù)來調(diào)用這個函數(shù),sys_sem_wait(),注意區(qū)別。- sys_mbox_t sys_mbox_new(void)建立一個空的郵箱。- void sys_mbox_free(sys_mbox_t mbox)釋放一個郵箱。如果釋放時郵箱中還有消息,它表明 LwIP 中存在一個編程錯誤,應(yīng)該通知開發(fā)者(原文如此,這句話很費解。個人理解的意思是:當執(zhí)行 sys_mbox_free()這個函數(shù)時,按道理郵箱中不應(yīng)該再存在任何消息,如果用戶使用 LwIP 時發(fā)現(xiàn)郵箱中還存在消息,說明 LwIP 的開發(fā)者存在一個編程錯誤,不能把郵箱中的消息全部取出並處理掉。遇到這種情況,用戶應(yīng)該告訴 LwIP 的作者,糾正這個 bug,譯注)。- void sys_mbox_post(sys_mbox_t mbox, void *msg)投遞消息“msg”到指定的郵箱“mbox”。- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void *msg, u32_t timeout)阻塞 線程直至 郵箱收到 至少一條 消息。最 長阻塞時 間由 timeout 參 數(shù)指 定(與sys_arch_sem_wait()函數(shù)類似)。msg 是一個結(jié)果參數(shù),用來保存郵箱中的消息指標(即*msg = ptr),它的值由這個函數(shù)設(shè)置?!癿sg”參數(shù)有可能為空,這表明當前這條消息應(yīng)該被丟棄。返回值與 sys_arch_sem_wait()函數(shù)相同:等待的毫秒數(shù)或者 SYS_ARCH_TIMEOUT如果時間溢出的話。LwIP 實現(xiàn)的函數(shù)中,有一個名稱與之相似的sys_mbox_fetch(),注意區(qū)分。- struct sys_timeouts *sys_arch_timeouts(void)返回一個指向當前線程使用的 sys_timeouts 結(jié)構(gòu)的指標。LwIP 中,每一個線程都有一個timeouts 鏈表,這個鏈表由 sys_timeout 結(jié)構(gòu)組成,sys_timeouts 結(jié)構(gòu)則保存了指向這個鏈表的指針。這個函數(shù)由 LwIP 的超時調(diào)度程式調(diào)用,並且不能返回一個空(NULL)值。單線程 sys_arch 實現(xiàn)中,這個函數(shù)只需簡單返回一個指標即可。這個指標指向保存在 sys_arch 模塊中的 sys_timeouts 總體變數(shù)。如果底層作業(yè)系統(tǒng)支援多線程並且 LwIP 中需要這樣的功能,那麼,下面的函數(shù)必須實現(xiàn):- sys_thread_t sys_thread_new(void(*thread)(void *arg), void *arg, int prio)啟動一個由函數(shù)指標 thread 指定的新線程,arg 將作為參數(shù)傳遞給 thread()函數(shù),prio 指定這個新線程的優(yōu)先順序。返回值為這個新線程的 ID,ID 和優(yōu)先順序由底層作業(yè)系統(tǒng)決定。- sys_prot_t sys_arch_protect(void)這是一個可選函數(shù),它負責完成臨界區(qū)域保護並返回先前的保護狀態(tài)。該函數(shù)只有在小的臨界區(qū)域需要保護時才會被調(diào)用?;?ISR 驅(qū)動的嵌入式系統(tǒng)可以通過禁止中斷來實現(xiàn)這個函數(shù)。基於任務(wù)的系統(tǒng)可以通過互斥量或禁止任務(wù)來實現(xiàn)這個函數(shù)。該函數(shù)應(yīng)該支援來自於同一個任務(wù)或中斷的遞迴調(diào)用。換句話說,當該區(qū)域已經(jīng)被保護,sys_arch_protect()函數(shù)依然能被調(diào)用。這時,函數(shù)的返回值會通知調(diào)用者該區(qū)域已經(jīng)被保護。如果你的移植正在支援一個作業(yè)系統(tǒng),sys_arch_protect()函數(shù)僅僅是一個需要。- void sys_arch_unprotect(sys_prot_t pval)該函數(shù)同樣是一個可選函數(shù)。它的功能就是恢復受保護區(qū)域的先前保護狀態(tài),先前是受到保護還是沒有受到保護由參數(shù) pval 指定。它與 sys_arch_protect()函數(shù)配套使用,詳細資訊參看sys_arch_protect()函數(shù)。該函數(shù)的說明是按照譯者個人理解的意思翻譯,原文講述不是很清楚,如有錯誤,歡迎批評指正,譯注。-OS 支援的類比層需要添加的頭檔說明- cc.h與硬體平臺及編譯器相關(guān)的環(huán)境變數(shù)及資料類型聲明檔(一些或許應(yīng)該移到 sys_arch.h文件)。LwIP 使用的資料類型定義u8_t, s8_t, u16_t,s16_t,u32_t,s32_t,mem_ptr_t。與編譯器相關(guān)的 LwIP 結(jié)構(gòu)體封裝巨集:PACK_STRUCT_FIELD(x)PACK_STRUCT_STRUCTPACK_STRUCT_BEGINPACK_STRUCT_END與平臺相關(guān)的調(diào)試輸出:LWIP_PLATFORM_DIAG(X)- 非故障,輸出一條提示資訊。LWIP_PLATFORM_ASSERT(x) - 故障,輸出一條故障資訊並放棄執(zhí)行?!拜p便的(lightweight)”同步機制:SYS_ARCH_DECL_PROTECT(x)- 聲明一個保護狀態(tài)變數(shù)。SYS_ARCH_PROTECT(x)- 進入保護模式。SYS_ARCH_UNPROTECT(x)- 脫離保護模式。如果編譯器不提供 memset()函數(shù),這個檔必須包含它的定義,或者包含(include)一個定義它的文件。這個檔要麼包含一個本地系統(tǒng)(system-local)提供的頭檔這個檔定義了標準的*nix 錯誤編碼,要麼增加一條巨集定義語句:#defineLWIP_PROVIDE_ERRNO,這將使得lwip/arch.h 頭檔來定義這些編碼。這些編碼被用於 LwIP 的各個部分。- perf.h定義了性能測量使用的巨集,由 LwIP 調(diào)用,可以將其定義為一個空的宏。PERF_START- 開始測量。PERF_STOP(x)- 結(jié)束測量並記錄結(jié)果。- sys_arch.hsys_arch.c 的頭文件。定義 Arch(即整個移植所依賴的作業(yè)系統(tǒng)平臺,譯注)需要的資料類型:sys_sem_t,sys_mbox_t,sys_thread_t,以及可選類型:sys_prot_t。sys_mbox_t 和 sys_sem_t 變數(shù)的 NULL 值定義:SYS_MBOX_NULLNULLSYS_SEM_NULLNULL4.2編寫作業(yè)系統(tǒng)類比層4.1 節(jié)已經(jīng)明白的講述了如何實現(xiàn) sys_arch 介面,我們按照這個說明完成即可。4.2.1 準備工作建立文件、定義資料類型及其它在 ADS 工程 LwIP 組中添加一個新組 arch 並在這個組下面建立原始檔案 sys_arch.c,實際存 放路徑亦如此組織,如下圖所示:圖 4.2.1然後在 LwIP/include 組同樣建立一個新組 arch,在 arch 組建立新檔 sys_arch.h 及 cc.h, 如下圖示:圖 4.2.2 檔建立完成,我們先實現(xiàn)資料類型定義,這個實現(xiàn)完全按照移植說明一文進行。首先在cc.h 檔中定義常用的資料類型。這些常用資料類型不僅類比層介面函數(shù)使用,底層協(xié)定棧實 現(xiàn)亦要使用。在 cc.h 文件中添加如下語句(參見 LwIPPortingTest_4 文件夾下的 cc.h 文件): typedef unsigned charu8_t;typedef chars8_t; typedef unsigned short u16_t; typedef shorts16_t; typedef unsigned intu32_t; typedef ints32_t; typedef u32_tmem_ptr_t;在上面的資料類型定義中,除了 mem_ptr_t 之外其他類型均很直觀,不需解釋。至於 mem_ptr_t為什麼指定為 u32_t,而不是像它的名稱所表現(xiàn)的一樣將其指定為指針呢?其實原因很簡單, 筆者在定義它時首先找到了使用它的相關(guān)語句,從這些語句中才確定這樣聲明。讀者可找到 mem.h 文件看看裏面有關(guān) mem_ptr_t 的使用語句就能明白怎麼回事。好了,不再多說,讓我們 的準備工作接著進行。在 sys_arch.h 文件中添加如下語句:typedef HANDLER sys_sem_t;其中 HANDLER 是筆者本人自定義的一個宏,它是為了方便 uC/OS-II 的使用定義的。讀者可以在 所附源碼檔srcuCOS_IIAPIos_api.h 中找到相關(guān)定義:typedef OS_EVENT*HANDLER;它實際上就是指向 uCOS 中 OS_EVENT 結(jié)構(gòu)的指標。 聲明相關(guān)資料類型的語句添加完成後,我們再把這兩個檔 sys_arch.h 和 cc.h 添加到sys_arch.c 檔中,以使該檔裏的相關(guān)函數(shù)能夠使用這些新定義的資料類型:#include/LwIP/include/arch/cc.h#include/LwIP/include/arch/sys_arch.h這裏一定要注意順序,先包含 cc.h 檔,再包含 sys_arch.h 檔,因為 sys_arch.h 檔中有 些語句需要用到 cc.h 檔中的類型聲明。好了,準備工作已經(jīng)完成,現(xiàn)在開始編寫介面函數(shù)。4.2.2 信號量操作函數(shù)相關(guān)函數(shù)實現(xiàn)讀者也可直接參看 sys_arch.c 文件。- sys_new_sem()/*-/* 函數(shù)名稱 : sys_sem_new/* 功能描述 : 建立並返回一個新的信號量/* 入口參數(shù) : in 指定信號量的初始狀態(tài)/* 出口參數(shù) : 返回新的信號量/*-sys_sem_t sys_sem_new(u8_t count)return OSAPISemNew(count);這個函數(shù)的實現(xiàn)其實很簡單,因為 uC/OS-II 提供了信號量,我們只需直接調(diào)用建立信號量 的相關(guān)函數(shù)就行了。上面的源碼中 OSAPISemNew 是筆者本人為了統(tǒng)一對 OS 底層函數(shù)的調(diào)用重新 定義的一個介面函數(shù),這個介面函數(shù)負責調(diào)用 OS 底層函數(shù)完成相應(yīng)功能。在後面的類比層介面 函數(shù)實現(xiàn)中,筆者使用了很多這樣的 API。這些介面函數(shù)都以 OSAPI 作為函數(shù)名首碼,其實現(xiàn) 細節(jié)請參看srcuCOS_IIAPIos_api.c 檔,本文不再贅述。- sys_sem_signal()/*-/* 函數(shù)名稱 : sys_sem_signal/* 功能描述 : 發(fā)送信號/* 入口參數(shù) : in sem 指定要發(fā)送的信號/* 出口參數(shù) : 無/*-void sys_sem_signal(sys_sem_t sem)OSAPISemSend(sem);這個函數(shù)就不再多說了,與 sys_sem_new()函數(shù)的實現(xiàn)機制相同。- sys_sem_free()/*-/* 函數(shù)名稱 : sys_sem_free/* 功能描述 : 釋放信號量/* 入口參數(shù) : in 指定要釋放的信號量/* 出口參數(shù) : 無/*-void sys_sem_free(sys_sem_t sem)OSAPISemFreeExt(sem);與前兩個函數(shù)相似,不再贅述。- sys_arch_sem_wait()/*-/* 函數(shù)名稱 : sys_arch_sem_wait/* 功能描述 : 等待由參數(shù) sem 指定的信號並阻塞線程/* 入口參數(shù) :in sem 指定要發(fā)送的信號/*: in 指定等待的最長時間(單位為毫秒)。為 0,線程會一直/*:被阻塞直至收到指定的信號;非 0,指定線程最長等待時/*:間/* 出口參數(shù) : -0: 在指定時間內(nèi)等到指定信號/*: - SYS_ARCH_TIMEOUT: 在指定時間內(nèi)沒有等到指定信號/*-u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)if(OSAPISemWait(sem, timeout) = OS_NO_ERR)return 0;elsereturn SYS_ARCH_TIMEOUT;實現(xiàn)信號量等待的 OS 底層函數(shù)是 OSSemPend()。打開源碼文件srcuCOS_IIOs_sem.c, 找到這個函數(shù),我們會發(fā)現(xiàn)它與我們要實現(xiàn)的函數(shù)還是有些差別的:其一、OSSemPend()函數(shù)沒 有返回值,它使用了一個結(jié)果參數(shù) err 來代替返回值;其二、OSSemPend()函數(shù)的執(zhí)行結(jié)果與 sys_arch_sem_wait()函數(shù)的執(zhí)行結(jié)果(返回值)不相同,OSSemPend()函數(shù)有 5 種執(zhí)行結(jié)果,而 sys_arch_sem_wait()函數(shù)只有兩種結(jié)果;其三、timeout 參數(shù)的時間單位不同,一個是時鐘 節(jié)拍數(shù)(OSSemPend()),另一個則是毫秒數(shù)(sys_arch_sem_wait())。這些差別的存在導致我 們在調(diào)用 OSSemPend()函數(shù)時必須根據(jù)需求作出相應(yīng)的調(diào)整。讀者可以看到,在上面的代碼實現(xiàn)中,筆者使用了自定義的 OS 介面函數(shù)來代替對 OSSemPend()函數(shù)的直接調(diào)用。這個介面函數(shù)與 sys_arch_sem_wait()函數(shù)相比不存在一、三這兩種差別。唯一的差別就是返回值,OSAPISemWait()函數(shù)仍然保留了 OSSemPend()函數(shù)的全部 5種執(zhí)行 結(jié)果 ,而 LwIP 只關(guān)心 兩種 結(jié)果: 等到 指定信 號或 者超時 。筆 者為了 省事 , sys_arch_sem_wait()函 數(shù)隻判斷 是否等到 指定信號 ( OSAPISemWait(sem, timeout) = OS_NO_ERR),其他情況一律視為超時(實際上除了超時外,另外三種屬於程式的 BUG,一個運 行穩(wěn)定的系統(tǒng)不應(yīng)該存在),返回 SYS_ARCH_TIMEOUT。另外,按照筆者個人的理解(移植說明一文講述不是很清楚),在指定時間內(nèi)等到指定信號 時,函數(shù)即可以返回實際等待的毫秒數(shù)也可以返回 0,為了編程方便,筆者選擇返回 0。既然移植說明一文告訴我們等待超時要返回 SYS_ARCH_TIMEOUT,那麼在 LwIP 中一定會存 在這個巨集定義。使用 SYS_ARCH_TIMEOUT 作為關(guān)鍵字搜索整個 ADS 工程(CTRL+SHIFT+M)發(fā)現(xiàn) sys.h 定義了這個宏,因此,sys_arch.c 檔還需要包含這個檔:#include/LwIP/include/lwip/sys.h 把上面的語句添加到 sys_arch.c 中,如下圖示:圖 4.2.3好了,編寫到這裏,讓我們先把這個檔編譯一下,看看有什麼問題。讀者可以使用 ADS 直接 打開所附文檔LwIPPortingTest_4LwIPPortingTest_4_1(它是直接反映本文當前移植進度的 階段性文檔),找到 sys_arch.c 檔進行編譯。編譯完成,呵,錯誤真多,28 個,見下圖:圖 4.2.4 不怕,老話說的好:兵來將擋,水來土掩。跟著錯誤說明,讓我們一個個地消滅。第一個錯誤是說無法打開 lwipopts.h 檔,出錯的地方在 opt.h 文件。我們看看 opt.h 檔中關(guān)於lwipopts.h 文件的描述,如下圖示:圖 4.2.5上圖圈其來的注釋語句就是對 lwipopts.h 文件的說明。很簡單,它是用戶自定義的配置檔, 我們不需要這個,因此沒有建立這個檔,編譯器找不到它,只好報錯了。解決方法很簡單, 我們把這條語句注釋掉即可,如下圖示:圖 4.2.6再編譯看看,還有多少個錯誤,還有 27 個,少了一個,呵呵,進步啊。好,讓我們繼續(xù)進步 下面這個錯誤是說“sys_mbox_t”缺少相關(guān)類型聲明。移植說明一文已經(jīng)提到過 sys_mbox_t, 其用來表示一個郵箱,暫時還用不到,所以在這裏先將其隨便聲明為一種資料類型,先編譯通 過,以後再說。根據(jù)移植說明,我們在 sys_arch.h 頭文件裏定義它,打開 sys_arch.h,添加 如下語句:typedef HANDLER sys_mbox_t; 見圖示:圖 4.2.7 編譯,出乎意料,還剩三個錯誤。太好了,已經(jīng)看到勝利的彼岸了。剩下的這幾個錯誤,核心問題與上一個問題一樣,缺少聲明,這次是“sys_thread_t”。它是新線程的 ID,在 uCOS 中,ID 號實際就是優(yōu)先順序,也就是 0-63 之間的數(shù)字,所以在 sys_arch.h 文件中添加如下語句:typedef u8_t sys_thread_t; 如下圖所示:圖 4.2.8 再編譯,沒有任何錯誤,通過。好了,信號量相關(guān)的操作函數(shù)已經(jīng)完成,下面的工作就是實現(xiàn)郵箱操作函數(shù)。4.2.3 郵箱操作函數(shù)在動手實現(xiàn)之前,讓我們再回顧一下移植說明一文關(guān)於郵箱的描述:“郵箱用於消息傳遞, 用戶即可以將其實現(xiàn)為一個佇列,允許多條消息投遞到這個郵箱,也可以每次只允許投遞一個 消息,這兩種方式 LwIP 都可以正常運作。不過,前者更加有效。需要用戶特別注意的是投 遞到郵箱中的消息只能是一個指標?!皬倪@個描述中,我們得到兩條有價值的資訊:其一、郵箱 一次能夠接收多條消息比僅接收一條消息更加有效;其二、郵箱中的消息是一個指標。先說說第一條。很顯然,使用消息佇列帶來的性能提升是影響我們選擇的關(guān)鍵因素,我們 並不希望使用一個打了折扣的 LwIP。其實,在 uCOS 中實現(xiàn)消息佇列很簡單,它提供了非常豐 富的消息佇列管理函數(shù),我們只需在此基礎(chǔ)上實現(xiàn)即可。這也是影響我們?nèi)绱诉x擇的一個重要 因素。對於第二條,其帶給我們的資訊很直白,不需贅述,唯一需要交待的是uCOS 中投遞 到郵箱中的消息也是一個指標,這對我們來說實在是一個大好消息。好了,言歸正傳,下面談?wù)劰P者本人的設(shè)計思路,為讀者拋磚引玉。 我的想法是系統(tǒng)可以同時建立多個郵箱,這些郵箱通過一個單向鏈錶鏈接在一起。每個郵箱一次可以接收多條消息,接收消息的最大數(shù)量由消息陣列的大小決定。如下圖所示:圖 4.2.9我們在系統(tǒng)初始化階段完成建立郵箱和鏈表的工作,並把鏈表的首位址保存到總體變數(shù)pstCurFreeMbox 指針中。申請建立一個新郵箱時,我們先看看 pstCurFreeMBox 是否為空值。為空,表明鏈表已經(jīng)沒 有空閒節(jié)點,無法滿足申請。不為空,則立即將 pstCurFreeMBox 指向的當前節(jié)點從鏈表中取出 交給申請者使用,pstCurFreeMBox 指向下一個空閒節(jié)點(見圖 4.2.10)。在系統(tǒng)第一次申請的 時候,pstCurFreeMBox 指向節(jié)點 1,申請完畢,節(jié)點 1 與鏈表斷開鏈結(jié),整個鏈表減少了一個 節(jié)點。這時,pstCurFreeMBox 指標被賦值為節(jié)點 1 的 pstNext 值,其向後移動了一個節(jié)點,指 向了節(jié)點 2。以此類推,假設(shè)系統(tǒng)只能申請建立郵箱,不能撤銷申請將郵箱歸還給鏈表,這將 使得鏈表逐漸變短。當 pstCurFreeMBox 指針被賦值為節(jié)點 N 的 pstNext 值時,這意味著 pstCurFreeMBox 指標已經(jīng)越過了鏈表的最後一個節(jié)點,鏈表的長度縮減為 0,鏈表不再可用。 這時,如果系統(tǒng)再有新的申請產(chǎn)生,該申請將被拋棄。如果我們建立的鏈表長度過短,當網(wǎng)路 系統(tǒng)繁忙導致佔用的郵箱不能及時歸還時,很有可能出現(xiàn)鏈表不可用的情況。這將直接導致網(wǎng) 絡(luò)系統(tǒng)的回應(yīng)時間變長甚至不再回應(yīng)。這對於一個設(shè)計可靠的系統(tǒng)來說,是一個很嚴重的問題。 要想避免這個問題,就必須對網(wǎng)路系統(tǒng)的負荷能力作出正確的評估,從而為我們選擇一個合理 的鏈表長度提供判斷依據(jù)。筆者對這個問題的解決方法是:在申請郵箱的函數(shù)中增加記錄鏈表 不可用次數(shù)的代碼,然後類比各種情況對目標系統(tǒng)進行盡可能多的併發(fā)訪問,不斷調(diào)整鏈表長 度,直至不可用次數(shù)正好為 0。最後,把當前鏈表長度再增加一定數(shù)量以應(yīng)付意外情況的發(fā)生(一般再增加 25%-50%的長度即可),就得到了我們想要的值。 郵箱使用完畢,歸還給鏈表的過程參見圖 4.2.10 歸還節(jié)點部分。歸還節(jié)點實際上就是重建鏈表,鏈表會從無到有,從短到長,逐漸恢復原來的樣子。而 pstCurFreeMBox 指標的移動方向 正好與申請時相反,它是從鏈表的尾端向前移動。不過,這裏需要特別注意的是,鏈表的重建 將會打亂鏈表最初的節(jié)點順序,正如圖 4.2.10 所示的一樣。為什麼會出現(xiàn)這種情況呢?原因很 簡單,雖然申請節(jié)點時我們是從鏈表的首部開始順序申請,但是申請者佔用各個節(jié)點的時間是 不相同的,有的長有的短,與申請順序無關(guān)。最先申請的節(jié)點有可能最後被歸還,而最後申請 的節(jié)點則有可能最先被歸還。因為 pstCurFreeMBox 指標始終指向最後被歸還的節(jié)點,也就是鏈 表的首位址,因此,最後申請的節(jié)點成為了鏈表首部的節(jié)點,而原先首部的節(jié)點則成了重建後 鏈表的尾部節(jié)點。圖 4.2.10 很直觀的描述了這一過程。善於思考的讀者可能還會發(fā)現(xiàn)這種處理 機制的一個特點,假設(shè)系統(tǒng)只申請了一個郵箱,然後歸還,然後再申請,那麼再申請的郵箱還 是最先申請的那個郵箱。也就是說,如果系統(tǒng)最多只需要三個郵箱,而我們的鏈表共有五個郵 箱,那麼鏈表中的後兩個郵箱將一直不被使用,而前三個郵箱則使用頻繁。鏈表中的前三個節(jié) 點順序?qū)⒉粩嗟闹匦屡帕校?23、321、231),而節(jié)點 4 和節(jié)點 5 則會一直保持這個順序。 不過不用擔心,這種情況除了造成一定的記憶體浪費之外(當然,如果我們的鏈表長度合適,這 個問題也可避免),不會對系統(tǒng)的穩(wěn)定產(chǎn)生任何問題。另外,鏈表建立在 RAM 而不是 FLASH 中, 我們更不用考慮頻繁擦寫固定區(qū)域?qū)τ洃涹w造成的傷害。圖 4.2.10 不知讀者是否還記得在講解信號量操作函數(shù)時,我們曾經(jīng)提到過用於表示郵箱的自定義數(shù)據(jù)類型 sys_mbox_t。當時我們只是簡單的為其作了類型定義,並沒有考慮是否能夠滿足實際應(yīng) 用。春去春回、鬥轉(zhuǎn)星移,而現(xiàn)在我們對於如何實現(xiàn)郵箱已經(jīng)了然於胸。前文已經(jīng)說過,鏈表 中的每一個節(jié)點就是一個郵箱,用 C 語言來描述郵箱實際上就是一個結(jié)構(gòu)體。sys_mbox_t 是用來表示郵箱的,而郵箱就是結(jié)構(gòu)體,顯然 sys_mbox_t 應(yīng)該聲明為結(jié)構(gòu)體。為了操作簡便, 我們亦可以將其聲明為指向這個結(jié)構(gòu)體的指標?,F(xiàn)在我們要做的就是實現(xiàn)這個結(jié)構(gòu)體,然後再將 sys_mbox_t 聲明為指向這個結(jié)構(gòu)體的指標,完成郵箱操作的基本單元構(gòu)建工作。實現(xiàn)這個結(jié) 構(gòu)體的工作很簡單,聰明的讀者一定會發(fā)現(xiàn)筆者已經(jīng)在前文實現(xiàn)了這個結(jié)構(gòu)體,只是沒有明說 而已。沒錯,圖 4.2.9 已經(jīng)很直觀的給出了結(jié)構(gòu)體成員列表,我們用代碼實現(xiàn)即可。打開 sys_arch.h 檔,在這個檔裏定義這個結(jié)構(gòu)體並聲明 sys_mbox_t,相關(guān)代碼如下:#define MBOX_SIZE16/* 指定郵箱能夠接收的消息數(shù)量#define MBOX_NB8/* 指定郵箱個數(shù),也就是鏈表長度/* LwIP 郵箱結(jié)構(gòu) */typedef struct stLwIPMBoxstruct stLwIPMBox*pstNext;HANDLERhMBox;void*pvaMsgsMBOX_SIZE; ST_LWIP_MBOX, *PST_LWIP_MBOX;typedef PST_LWIP_MBOX sys_mbox_t;/* LwIP 郵箱有了 sys_mbox_t,我們就可以建立一個 sys_mbox_t 類型的陣列,為鏈表中的各個節(jié)點分 配最基本的存儲空間。打開 sys_arch.c 檔,建立一個具有全局屬性的 ST_LWIP_MBOX 陣列, 陣列大小由 MBO

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論