STM32F4XX LWIP+freeRTOS移植(一)
有疑問請加扣扣技術交流群: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函式的內容全部完成!
相關文章
- 移植一個抖音貼紙元件到Flutter元件Flutter
- micropython STM32移植筆記(一)Python筆記
- 移植uboot-2015-10(一)boot
- 資料移植到Oracle資料庫(一)Oracle資料庫
- 移植一個cocos2d-x遊戲遊戲
- ncurses庫移植
- uboot移植rtcboot
- 使用migratepv移植系統及映象一例
- GPUImage移植總結GPUUI
- nginx移植記錄Nginx
- kvm移植技術
- 資料移植文件
- LWIP裸機移植
- docker快速建立輕量級的可移植的容器(一)Docker
- 將Abp移植進.NET MAUI專案(一):搭建專案UI
- Linux下關於互斥鎖及同步的移植(一)Linux
- QT移植到Linux總結(一)tslib1.0QTLinux
- 系統移植存放位置
- OpenHarmony Docker移植實踐Docker
- Qt5.2.1移植海思QT
- 移植到 Python 3Python
- 我把一個大型 C++ 庫移植到了瀏覽器!C++瀏覽器
- 這樣一來以後就更好的移植miui系統UI
- 上海一公司需要將原系統移植到Websphere平臺Web
- Linux下關於互斥鎖及同步的移植(一)薦Linux
- Chrome移植Win10 on ARMChromeWin10
- 5. U-Boot移植boot
- Ubuntu成功移植Xperia Tablet ZUbuntu
- uboot移植i2cboot
- Oracle向PostgresQL移植例項OracleSQL
- Java 的可移植性 (轉)Java
- 2.12.uboot的移植2-從uboot官方標準uboot開始移植boot
- 在上世紀80年代移植一款遊戲有多難遊戲
- Vulkan移植GPUImage的安卓Demo展示GPUUI安卓
- 可移植的python環境Python
- 移植到Windows CE 的經驗Windows
- Polar 投影c#版本移植C#
- alsa 移植到Linux3.0Linux