STM32F4XX LWIP+freeRTOS移植(一)

蘇守坤發表於2018-08-01

有疑問請加扣扣技術交流群:460189483

原始碼下載地址:https://download.csdn.net/download/u014453443/10698059

TM32F429IGT6原子開發板進行驗證的,PHY晶片為LAN8720

原子哥的程式都是ucos_ii+lwip的工程,沒有freeRTOS+lwip的工程,這裡對比二種系統的差異,來進行lwip的freeRTOS的移植

LWIP 無作業系統移植實驗的LWIP 資料夾可以發現有一個arch 資料夾,在arch 中有5 個檔案cc.h、cpu.h、perf.h、sys_arch.h和sys_arch.c。根據sys_arch.txt 中的描述,cc.h 主要完成了協議棧內部使用的資料型別的定義,如果使用作業系統的話還有臨界程式碼區保護等等。

lwip_comm資料夾,這個檔案中有lwip_comm.c、lwip_comm.h 和lwipopts.h這三個檔案,lwip_comm.c 和lwip_comm.h 是將LWIP 原始碼和前面的乙太網驅動庫結合起來的橋樑!這兩個檔案非常重要,這兩個檔案有ALIENTEK 提供。lwipopts.h 是用來裁剪和配置LWIP的檔案,以後我們想要使用LWIP 的什麼功能的話就在這個檔案中配置就行了。

綜上所述,ucos_ii+lwip的工程涉及到LWIP的總計8個檔案,分別是cc.h、cpu.h、perf.h、sys_arch.h、sys_arch.c、lwip_comm.c 、lwip_comm.h和lwipopts.h。要變更為freeRTOS+lwip,只需要更改cc.h、sys_arch.h、sys_arch.c三個檔案即可,其他與ucos_ii+lwip的保持一致即可(將裡面ucos_ii的函式替換為freeRTOS的即可)。這幾個檔案作用如下:

最主要是重寫sys_arch.c,sys_arch.h,cc.h這三個檔案即可,是參照lwip教程的實驗2進行修改進行,要重寫的函式基本上都在lwip_sys.h這個檔案中定義好了,只要將裡面的函式都實現了即可!

1. cc.h的重寫

第一步:包含的標頭檔案要改變,includes.h是指包含ucos_ii系統的所有標頭檔案,要使用freeRTOS必須包含FreeRTOS.h

下面如圖是使用ucos_ii系統時的標頭檔案

變更為如下所示的標頭檔案,因為在sys_arch.c中要使用到freeRTOS的任務控制程式碼,所以也要包含task.h

第二步:要改變的區域就是臨界區程式碼保護區域,ucos_ii的內容如下:

因為ucos_ii的臨界區保護有2種型別,型別根據OS_CRITICAL_METHOD來決定,一般都是用的3,而且ucos_ii的臨界區保護不分任務級與中斷級,所有保護函式只有一個,而freeRTOS是區分任務級與中斷級的,這一點一定要注意!

使用freeRTOS時,這一部分的程式碼更改如下:

其中,SCB_ICSR_REG定義的暫存器是用於區分當前是任務級還是中斷級的,主要在Enter_Critical()與Exit_Critical()中使用,

Enter_Critical()與Exit_Critical()這兩個函式是在sys_arch.c中實現的,所以此處加extern宣告為外部函式,Enter_Critical()用於宣告進入保護臨界區,Exit_Critical()宣告退出保護臨界區,cc.h的內容更改完成

2. sys_arch.h中的重寫

在使用ucos_ii時,該檔案內容如下:

標頭檔案中includes.h包含的是ucos_ii的標頭檔案,需要更改為FreeRTOS.h,sys_arch.c中要使用FreeRTOS的訊息佇列和訊號量,還必須要包含queue.h和semphr.h;ucos_ii沒有訊息佇列,所以這裡使用了自定義的訊息佇列結構,freeRTOS中本身就有訊息佇列,可以直接使用,所以變更如下:

經過對比,有些童鞋可能疑問為什麼ucos_ii系統中對sys_sem_t、sys_mutex_t、sys_mbox_t資料結構的定義是用的指標,而freeRTOS系統中沒有用指標,其實你定義為指標,或者非指標,都可以,對系統沒有什麼影響,不同之處在於sys_arch.c中函式的實現,如果使用的是指標,那麼對變數引用一定要 (*變數)->變數  這樣才可以,如果定義為非指標 變數->變數 這樣就可以了,大家完全可以按照自己的喜好來定義。

3. sys_arch.c中的重寫

sys_arch.c中要重寫的函式與ucos_ii一樣的,也是實現這幾個函式而已,現將兩個系統的函式拿來做一下對比,根據原子教程,要實現的函式如下所示:

如果大家想知道這些函式的原型定義,可以在檔案lwip_sys.h中找到

第一個:sys_sem_new函式,用於建立訊息郵箱,lwip_sys.h中函式原型如下:

使用ucos_ii系統時,實現方法如下:

因為是二級指標,所以要給mbox分配記憶體,分配記憶體使用的是原子的記憶體池的方法,從192k的內部sram中分配,然後使用ucos_II的佇列建立函式建立佇列,大小為size,如果建立失敗就釋放掉分配的記憶體,並返回佇列建立的狀態資訊

使用freeRTOS時變更如下:

使用freeRTOS的佇列建立函式建立佇列,佇列長度為size,訊息型別長度為指標長度(4位元組),這樣就建立了一個存放指標的訊息佇列,如果建立失敗,返回錯誤,與ucos_ii相比,預設了記憶體分配環節,這是因為xQueueCreate函式是動態建立訊息佇列,由freeRTOS系統自動分配記憶體,分配失敗了不需要自己釋放!

第二個:sys_mbox_free函式,用於刪除一個訊息郵箱,lwip_sys.h中函式原型如下:

使用ucos_ii系統時,實現方法如下:

先建立一個訊息佇列指標,儲存要刪除的訊息佇列,然後呼叫ucos_ii的函式刪除這個訊息佇列,然後呼叫原子的sh釋放函式釋放掉訊息佇列的記憶體,將訊息佇列指標賦值為nu'll,這兩個函式都比較好理解。

使用freeRTOS時變更如下:

第三個:sys_mbox_post函式,用於向訊息郵箱傳送訊息,阻塞式傳送,不成功一直等待,lwip_sys.h中函式原型如下:

ucos_ii系統時,該函式實現方法如下:

該函式比較好理解,就是向訊息郵箱傳送訊息,如果不成功則一直髮送,直到成功為止!

注意pvNullPointer是一個空指標的處理方法,如下值:

freeRTOS系統時,實現方法如下:

此處的空指標處理方法,NullMessage與上面不一樣,僅僅是定義了一下,並沒有賦值的操作,如下,可以賦值試試:

與ucos相比,分中斷中傳送訊息與任務中傳送訊息,如果返回pdPASS表示傳送成功,xHigherPriorityTaskWoken主要用於中斷中傳送完訊息後,進行判斷是否要進行任務切換!

其實如果細心的朋友一定會發現,在這個函式原型中有一句話,說明該函式僅僅用於在任務中傳送訊息,如下:

所以其實上面的在中斷中傳送訊息,並進行任務切換的操作是多餘的,本著嚴謹的態度還是寫上了,只是永遠不會執行到!

第四個:sys_mbox_trypost函式,嘗試向訊息郵箱傳送訊息,不阻塞式傳送,lwip_sys.h中函式原型如下:

在ucos_ii系統中的實現方式如下:

此函式比較簡單,只是嘗試向訊息郵箱傳送訊息,失敗就返回錯誤,不阻塞!

在freeRTOS中的實現方式如下:

因為該函式沒有講是在任務中還是中斷中使用,所以一定要將中斷考慮進去,比較簡單!

第五個:sys_arch_mbox_fetch函式,從訊息郵箱中等待一個新訊息,阻塞式等待,lwip_sys.h中函式原型如下:

在ucos_ii系統中的實現方法如下:其中timeout的單位是毫秒

此函式呼叫OSQPend從訊息佇列中獲取訊息,如ucos_timeout=0,就阻塞式等待直到新訊息的到來,如果ucos_timeout不為0,等待ucos_timeout個時鐘節拍後自動退出,返回OS_ERR_TIMEOUT,函式最終返回SYS_ARCH_TIMEOUT!通過判斷訊息地址是不是pvNullPointer來判斷訊息是不是NULL,該指標上面有說明。

freeRTOS的實現方式如下:

這部分的程式碼是經過優化的,一開始直接嘗試進行一次接收,如果有訊息就馬上返回,節省下了執行下面時間解析的時間,如果訊息是空的,再執行等待timeout時間,超時返回!有些人可能疑問freeRTOS系統是分任務級出隊函式與中斷級出隊函式的,為什麼我們這裡只用到了任務級出隊函式,主要是考慮到該函式需要delay timeout時間,應該不會在中斷中使用,否則就太傻了吧,中斷中使用的應該是下面 sys_arch_mbox_tryfetch這個函式,所以這裡只進行了任務級出隊函式,大家知道就好!

第六個:sys_arch_mbox_tryfetch函式,嘗試從訊息郵箱中接收一個新訊息,非阻塞式嘗試,lwip_sys.h中函式原型如下:

如果接收到訊息,返回0,如果沒有接收到訊息,返回SYS_MBOX_EMPTY

在ucos_ii系統中,該函式的實現方法如下:

這裡非常巧妙的呼叫sys_arch_mbox_fetch函式,將超時時間timeout設定為1毫秒,可能大家會疑惑,sys_arch_mbox_fetch超時時返回的是SYS_ARCH_TIMEOUT,而這裡需要返回SYS_MBOX_EMPTY,其實這兩個巨集定義是一個值,在lwip_sys.h中定義

如果不超時,sys_arch_mbox_fetch返回值是2毫秒,但是按照函式原型應該返回0毫秒才對,這一點沒搞懂,希望大神留言!

第7個:sys_mbox_valid函式,檢查一個郵箱是否有效,lwip_sys.h中函式原型如下:

返回1表示有效,返回0表示無效

ucos_ii系統中,該函式實現方式如下:

這個就是使用ucos自帶的函式實現,通過檢視返回值與佇列型別來判斷,不太理解的可以檢視該函式原型

返回值<2表示訊息佇列為NULL,OSNMsgs是訊息佇列中的訊息數(.OSQEntries的拷貝)OSQSize是訊息佇列的總的容量

freeRTOS的實現方法如下:

這裡的實現比較簡單,只是判斷該訊息佇列是否為NULL,感興趣的可以通過判斷佇列結構內容來進行判斷

第8個:sys_mbox_set_invalid函式,設定一個郵箱無效,lwip_sys.h中函式原型如下:

此函式只是將該一個訊息佇列設定為無效NULL即可,ucos_ii系統實現方式如下:

freeRTOS的實現方法也很簡單,如下:

第9個:sys_sem_new函式,建立一個訊號量,lwip_sys.h中函式原型如下:

建立一個訊號量,成功返回ERR_OK,失敗返回其它,count表示訊號量初值,

ucos_ii系統實現方式如下:

就是使用uc內部函式實現訊號量的建立,設定初值為count

freeRTOS系統實現方式如下:

freeRTOS中有二值訊號量、計數訊號量、互斥訊號量、遞迴訊號量,此處需要設定初值count,很明顯使用計數訊號量,第一個引數OXFF表示計數訊號量最大計數值,當訊號量值等於此值時釋放訊號量就會失敗

第10個:sys_arch_sem_wait函式,等待一個訊號量,lwip_sys.h中函式原型如下:

在規定的時間內等待一個訊號量,時間是毫秒,如果timeout是0,表示一直等待,成功的話返回值表示等待的時間,如果超時返回SYS_ARCH_TIMEOUT

ucos_ii系統實現方法如下:

比較好理解不解釋了,freeRTOS的實現方法如下:

這部分的程式碼是經過優化的,一開始直接嘗試進行一次訊號量接收,如果有訊號量就馬上返回,節省下了執行下面時間解析的時間,如果訊號量獲取失敗,再等待timeout時間,超時返回,或者一直等待,直到有訊號量為止!有些人可能疑問freeRTOS系統是分任務級獲取訊號量函式與中斷級獲取訊號量函式的,為什麼我們這裡只用到了任務級獲取訊號量函式,主要是考慮到該函式需要delay timeout時間,應該不會在中斷中使用,否則就太傻了吧,所以這裡只進行了任務級獲取訊號量函式,

第11個:sys_sem_signal函式,傳送(釋放)訊號量,lwip_sys.h中函式原型如下:

該函式也比較簡單,直接呼叫系統函式即可,ucos_ii系統實現方法如下:

freeRTOS系統實現方法如下:

這裡有一個問題,freeRTOS的訊號量釋放是區分任務與中斷的,ucos不用區分,所以這裡接下來會更新,更新如下:

第12個:sys_sem_free函式,刪除一個訊號量,lwip_sys.h中函式原型如下:

此函式比較簡單,就是呼叫系統函式刪除一個訊號量,ucos_ii系統實現方法如下:

freeRTOS系統實現方法如下:

第13個:sys_sem_valid函式,查詢一個訊號量是否有效,lwip_sys.h中函式原型如下:

查詢一個訊號量是否有效,返回1表示有效,返回0表示無效,ucos_ii實現方法如下:

freeRTOS系統沒有查詢訊號量API,簡單實現方法如下:

第14個:sys_sem_set_invalid函式,設定一個訊號量無效,lwip_sys.h中函式原型如下:

這個函式比較簡單,ucos_ii系統實現如下:

freeRTOS系統實現如下:

第15個:sys_thread_new函式,建立程式,lwip_sys.h中函式原型如下:

僅僅用於建立一個新的程式,該程式用於TCP IP核心程式,name表示執行緒名稱,thread表示執行緒執行函式,arg傳遞給執行緒的引數,stacksize執行緒堆疊大小,prio執行緒優先順序,ucos_ii系統實現方法如下:

freeRTOS系統實現方法如下:

與uc不同的是,多出來一個任務控制程式碼全域性變數的定義LWIP_ThreadHandler,是因為函式需要!

第16個:sys_init函式,初始化作業系統模擬層,lwip_sys.h中函式原型如下:

這個函式不需要填寫內容,寫一個空函式即可

第17個:sys_msleep函式,LWIP的延時函式,lwip_sys.h中函式原型如下:

ucos_ii系統、freeRTOS系統使用的原子的延時函式

第18個:sys_now函式,獲取當前系統時間,單位ms,lwip_sys.h中函式原型如下:

ucos_ii系統直接呼叫系統API獲得時間節拍,再轉化為毫秒即可,實現方式如下:

freeRTOS系統實現方式如下:

通過呼叫HAL_GetTick()獲得系統時鐘節拍,該函式在系統的滴答時鐘中斷中被週期的呼叫,來記錄系統時間,過程如下:

第18個:Enter_Critical、Exit_Critical函式,cc.h中宣告的臨界區程式碼保護函式,ucos_ii系統直接使用的系統臨界區保護函式,freeRTOS中因為分任務級與中斷級,所以需要做一個判斷,freeRTOS實現程式碼如下:

通過SCB_ICSR_REG&0XFF來判斷是在中斷還是任務中

至此,sys_arch.c函式的內容全部完成!

相關文章