網(wǎng)上看了很多關(guān)于uC/OS-II移植的文章和資料,自己也模仿著做了一個(gè)移植,為了加深理解, 把自己的理解和思路記錄下來(lái),水平有限,歡迎拍磚~
我使用的芯片是ST的STM32F103VCT6,是Cortex-M3內(nèi)核,Cortex-M3內(nèi)核是ARM公司推出的最新的基于ARMv7構(gòu)架的面向微控制領(lǐng)域的處理器。要想移植uC/OS-II,首先要了解uC/OS-II的內(nèi)核結(jié)構(gòu)和Cortex-M3內(nèi)核編程模型:uC/OS-II的內(nèi)核結(jié)構(gòu)如下圖:
現(xiàn)在從上往下看,用戶的應(yīng)用程序在整個(gè)uC/OS-II 的架構(gòu)的最上方,用戶的自己的應(yīng)用程序就屬于這一層的范疇。
接下來(lái)下面的左邊是uC/OS-II 的與處理器無(wú)關(guān)的代碼, 這部分是uC/OS-II 的主要部分, 在移植過(guò)程中是不需要修改的。
右邊是uC/OS-II 與應(yīng)用程序相關(guān)的代碼,OS_CFG.H是為了實(shí)現(xiàn)uC/OS-II 內(nèi)核功能的裁剪。通過(guò)配置這個(gè)頭文件,uC/OS-II 可以方便的實(shí)現(xiàn)裁剪,以適應(yīng)不同的嵌入式系統(tǒng)。而INCLUDE.H則包含了所有的頭文件,INCLUDE.H是一個(gè)主頭文件,它出現(xiàn)在每個(gè).C文件的第1行??梢酝ㄟ^(guò)重新編輯INCLUDE.H,增加自己的頭文件,但頭文件必須添加在頭文件列表的最后。這樣在應(yīng)用程序包含頭文件時(shí)只需要將此頭文件包含進(jìn)去,就能包含uC/OS-II 的所有頭文件了。
最下面是uC/OS-II 與處理器相關(guān)的代碼,這部分是移植工作的只要內(nèi)容,如圖所示,有三個(gè)文件:OS-CPU.H ,OS_CPU_A.ASM和OS_CPU_C.C
OS-CPU.H 包括了用﹟define語(yǔ)句定義的、與處理器相關(guān)的常數(shù)、宏以及類型。 如下所示:
--->定義與處理器無(wú)關(guān)的數(shù)據(jù)類型:
在STM32處理器及keil MDK或者IAR編譯環(huán)境中可以通過(guò)查手冊(cè)得知short 類型是16位而int 類型是32位,這對(duì)于Cortex-M3內(nèi)核是一致的。故這部分代碼無(wú)需修改。 盡管μC/OS-II定義了float 類型和double類型,但為了方便移植它們?cè)讦藽/OS-II源代碼中并未使用。為了方便使用堆棧,μC/OS-II定義了一個(gè)堆棧數(shù)據(jù)類型。在Cortex-M3中寄存器為32位,故定義堆棧的長(zhǎng)度也為32位。Cortex-M3狀態(tài)寄存器為32位,定義OS_CPU_SR主要是為了在進(jìn)出臨界代碼段保存狀態(tài)寄存器。
--->臨界代碼斷:
μC/OS-II 為了保證某段代碼的完整執(zhí)行,需要臨時(shí)的關(guān)閉中斷,在這段代碼執(zhí)行完成之后再打開(kāi)中斷。這樣的代碼段稱作臨界代碼段。μC/OSII 通過(guò)定義兩個(gè)宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()來(lái)分別實(shí)現(xiàn)中斷的關(guān)閉和打開(kāi)。一般來(lái)說(shuō),采用方法3 來(lái)實(shí)現(xiàn)這兩個(gè)宏。這兩個(gè)宏分別定義如下
--->棧的增長(zhǎng)方向
盡管μC/OS-II支持兩種方向生長(zhǎng)的棧,但對(duì)于以Cortex-M3為內(nèi)核的STM32微處理器來(lái)說(shuō),它支持向下增長(zhǎng)的滿棧,故需要定義棧增長(zhǎng)方向宏為1。即定義成如下形式
--->任務(wù)級(jí)任務(wù)切換
任務(wù)級(jí)任務(wù)切換調(diào)用宏OS_TASK_SW()來(lái)實(shí)現(xiàn)。因?yàn)檫@個(gè)宏也是與處理器相關(guān)的,因此這個(gè)宏在OS_CPU_A.ASM中描述。
--->其他函數(shù)聲明
在OS_CPU.H 中,還聲明了以下幾個(gè)函數(shù),這幾個(gè)函數(shù)均在OS_CPU_A.ASM中實(shí)現(xiàn)。
在OS_CPU_A.ASM中實(shí)現(xiàn)的是下面五個(gè)與處理器相關(guān)的函數(shù)。
下面介紹每個(gè)函數(shù)的具體實(shí)現(xiàn):
--->關(guān)中斷函數(shù)(OS_CPU_SR_Save())
定義方法3 來(lái)實(shí)現(xiàn)開(kāi)關(guān)中斷。即先保存當(dāng)前的狀態(tài)寄存器然后關(guān)中斷。故關(guān)中斷實(shí)現(xiàn)代碼如下
這也是宏OS_ENTER_CRITICAL()的最終實(shí)現(xiàn)。
--->恢復(fù)中斷函數(shù)(OS_CPU_SR_Restore())
這是宏OS_EXIT_CRITICAL()的最終實(shí)現(xiàn)。也就是將狀態(tài)寄存器的內(nèi)容從R0中恢復(fù),然后跳轉(zhuǎn)回去。此函數(shù)完成的將中斷狀態(tài)恢復(fù)到關(guān)中斷前的狀態(tài)。其代碼如下:
Cortex-M3處理器有單獨(dú)的指令來(lái)打開(kāi)或者關(guān)閉中斷,所以這兩個(gè)函數(shù)實(shí)現(xiàn)起來(lái)很簡(jiǎn)單。
--->啟動(dòng)最高優(yōu)先級(jí)任務(wù)運(yùn)行(OSStartHighRdy())
OSStart()調(diào)用OSStartHighRdy()來(lái)啟動(dòng)最高優(yōu)先級(jí)任務(wù)的運(yùn)行,從而啟動(dòng)整個(gè)系統(tǒng)。OSStartHighRdy()主要完成以下幾項(xiàng)工作:
① 為任務(wù)切換設(shè)置PendSV的優(yōu)先級(jí)
② 為第一次任務(wù)切換設(shè)置棧指針為0,
③ 設(shè)置OSRunning = TRUE,以表明系統(tǒng)正在運(yùn)行
④ 觸發(fā)一次PendSV,打開(kāi)中斷等待第一次任務(wù)的切換。
--->任務(wù)級(jí)和中斷級(jí)任務(wù)切換
因?yàn)镃ortex-M3 進(jìn)入異常自動(dòng)保存寄存器R3-R0,R12,LR,PC和xPSR這種的特殊機(jī)制,這兩個(gè)函數(shù)都是觸發(fā)一次PendSV來(lái)實(shí)現(xiàn)任務(wù)的切換。首先是微處理器自動(dòng)保存上面提到的寄存器,然后把當(dāng)前的堆棧指針保存到任務(wù)的棧中,將要切換的任務(wù)的優(yōu)先級(jí)和任務(wù)控制塊的指針賦值給運(yùn)行時(shí)的最高優(yōu)先級(jí)指針和運(yùn)行時(shí)的任務(wù)控制塊指針,最后再把要運(yùn)行的任務(wù)的堆棧指針賦值給微處理器的堆棧指針,這樣就可以退出中斷服務(wù)程序了。中斷服務(wù)程序退出的時(shí)候?qū)⒆詣?dòng)出棧R3-R0,R12,LR,PC 和xPSR。具體的PendSV服務(wù)程序的偽代碼如下:
OS_CPU_C.C定義了與CPU相關(guān)的C函數(shù)和鉤子函數(shù)、
這個(gè)文件中包含10個(gè)函數(shù),具體如下:
它是OSTaskStkInit ()。這個(gè)函數(shù)的功能是當(dāng)一個(gè)任務(wù)被創(chuàng)建時(shí),它完成這個(gè)任務(wù)堆棧的初始化。這個(gè)函數(shù)首先將用戶為任務(wù)分配的堆棧頂?shù)刂焚x值給一個(gè)棧指針變量,然后再通過(guò)這個(gè)棧指針向任務(wù)的棧空間寫入初值。這個(gè)初值無(wú)關(guān)緊要,為0就可以了。這個(gè)函數(shù)的代碼時(shí)下如下:
其他的鉤子函數(shù)都為空函數(shù)。
(1) OSTaskStkInit( ) 初始化任務(wù)的棧結(jié)構(gòu)
OSTaskCreate( )和OSTaskCreatExt()通過(guò)調(diào)用它來(lái)初始化任務(wù)的棧結(jié)構(gòu);因此,堆??雌饋?lái)就像中斷剛發(fā)生過(guò)一樣,所有的寄存器都保存在堆棧中。另外,在初始化堆棧以后,OSTaskStkInit( )應(yīng)當(dāng)返回堆棧指針?biāo)赶虻牡刂贰?br>(2) OSTaskCreateHook( )
每當(dāng)添加任務(wù)時(shí),OS-TCBInit()函數(shù)都會(huì)調(diào)用OSTaskCreateHook( )
函數(shù),當(dāng)其被調(diào)用時(shí),它會(huì)收到指向剛剛建立任務(wù)的任務(wù)控制塊的指針。這樣,它就可以訪問(wèn)任務(wù)控制塊結(jié)構(gòu)的所有的成員了。若用OSTaskCreate()建立任務(wù),OSTaskCreateHook( )的功能是有限的;但若使用OSTaskCreateExt()建立任務(wù)時(shí),會(huì)得到OS-TCB中的擴(kuò)展指針(OSTCBExtPtr)。該指針可用來(lái)訪問(wèn)任務(wù)的附加數(shù)據(jù),如浮點(diǎn)寄存器、MMU寄存器、任務(wù)計(jì)數(shù)器、以及調(diào)試信息??梢詸z查OS-TCBInit()看做了哪些工作。
(3) OSTaskDelHook( )
在任務(wù)從就緒列表或等待列表中被刪除后,OSTaskDel()就會(huì)調(diào)用OSTaskDelHook( )。當(dāng)調(diào)用其時(shí),它會(huì)收到一個(gè)指向正在被刪除任務(wù)的任務(wù)控制塊的指針,使它可以訪問(wèn)任務(wù)控制塊結(jié)構(gòu)的所有的成員。
(4) OSTaskSwHook( )
任務(wù)切換時(shí)被調(diào)用,可以直接訪問(wèn)OSTCBCur和OSTCBHighRdy這2個(gè)全局變量。OSTCBCur指向?qū)⒈磺袚Q出去的任務(wù)的任務(wù)控制塊,OSTCBHighRdy指向新任務(wù)的任務(wù)控制塊。
(5) OSTaskIdleHook( )
OSTaskIdle()可調(diào)用OSTaskIdleHook( )實(shí)現(xiàn)CPU的低功耗模式。
(6) OSTaskStatHook( )
每秒都會(huì)被統(tǒng)計(jì)任務(wù)OSTaskStat()調(diào)用一次,可以用其擴(kuò)展統(tǒng)計(jì)任務(wù)功能。例如,可以跟蹤并顯示每個(gè)任務(wù)的執(zhí)行時(shí)間、每個(gè)任務(wù)所用的CPU份額以及每個(gè)任務(wù)執(zhí)行的頻率等等。
(7) OSTimeTickHook( )
每個(gè)時(shí)鐘節(jié)拍都會(huì)被OSTimeTick()調(diào)用。
(8) OSInitHookBegin( )
進(jìn)入OSInit()函數(shù)后,OSInitHookBegin( )就立即被調(diào)用,添加其原因在于,這個(gè)函數(shù)使得用戶可以將自己特定代碼也放在OSInit()中,使代碼簡(jiǎn)潔明了。
(9) OSInitHookEnd( )
與OSInitHookBegin( )相似,只是它在OSInit()函數(shù)返回之前被調(diào)用。
(10)OSTCBInitHook( )
OS-TCBInit()函數(shù)在調(diào)用OSTaskCreateHook( )之前,會(huì)先調(diào)用OSTCBInitHook( )。原因在于,用戶可以在OSTCBInitHook( )中做一些與初始化控制塊OS-TCB有關(guān)的處理;在OSTaskCreateHook( )中做一些以初始化任務(wù)有關(guān)的處理。同OSTaskCreateHook( )一樣,OSTCBInitHook( )會(huì)收到指向新添加任務(wù)的任務(wù)控制塊的指針。
這樣整個(gè)移植的代碼就介紹完了。整個(gè)移植的過(guò)程比較容易。剩下的工作就是編寫用戶任務(wù)了
下面說(shuō)一下Cortex-M3內(nèi)核編程模型:
Cortex-M3內(nèi)部有20個(gè)寄存器,其模型為圖3-1所示[9]。其中通用寄存器為R0~R12。R13~R15有特殊的用途:R13用于堆棧指針(SP);R14用作鏈接寄存器(LR);R15用作程序計(jì)數(shù)器(PC)。程序狀態(tài)寄存器有三個(gè),分別記錄處理器在三種類型模式下的狀態(tài)。
它的三種狀態(tài)類型如下所述。Cortex-M3特殊的工作模式和狀態(tài)可以分為特權(quán)級(jí)線程模式、特權(quán)級(jí)處理者模式和用戶級(jí)線程模式。
Cortex-M3只支持Thumb指令(包括Thumb指令和Thumb2指令)。故在程序中若想跳轉(zhuǎn)到ARM指令狀態(tài)將會(huì)引發(fā)一個(gè)錯(cuò)誤。
Cortex-M3中斷采用NVIC(嵌套向量中斷控制器)來(lái)管理。除了復(fù)位、不可屏蔽中斷和硬故障外其他的中斷可以配置。它在響應(yīng)中斷進(jìn)入中斷服務(wù)程序時(shí)硬件將自動(dòng)將R0 R3, R12, LR, PSR ‐ 和PC壓棧,中斷服務(wù)程序結(jié)束時(shí),又將它們自動(dòng)彈出。這個(gè)入棧出棧順序極其重要。在后面講到的PendSV中會(huì)用到。
Cortex-M3內(nèi)核還粗線條的定義了存儲(chǔ)映射。在每一個(gè)范圍對(duì)應(yīng)哪一種外設(shè)都在內(nèi)核構(gòu)架上定義好了,半導(dǎo)體廠商可以根據(jù)自己具體的需要來(lái)實(shí)現(xiàn)存儲(chǔ)映射的細(xì)節(jié)。這一點(diǎn)很方便代碼在不同廠商生產(chǎn)的微處理器上移植。Cortex-M3寄存器模型如下所示:
聯(lián)系客服