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

下載本文檔

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

文檔簡(jiǎn)介

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

溫馨提示

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

評(píng)論

0/150

提交評(píng)論