蘇州永節(jié)電子科技有限公司
設(shè)為首頁 | 收藏本站
新聞詳情

嵌入式開發(fā),單片機(jī)開發(fā) UC/OS 的移植

瀏覽數(shù):3
文章附圖

第八章 移植μC/OS-Ⅱ

這一章介紹如何將μC/OS-Ⅱ移植到不同的單片機(jī)開發(fā)和嵌入式系統(tǒng)上。所謂移植,就是使一個(gè)實(shí)時(shí)內(nèi)核能在某個(gè)微處理器或微控制器上運(yùn)行。為了方便移植,大部分的μC/OS-Ⅱ代碼是用C語言寫的;但仍需要用C和匯編語言寫一些與處理器相關(guān)的代碼,這是因?yàn)棣藽/OS-Ⅱ在讀寫處理器寄存器時(shí)只能通過匯編語言來實(shí)現(xiàn)。由于μC/OS-Ⅱ在設(shè)計(jì)時(shí)就已經(jīng)充分考慮了可移植性,所以μC/OS-Ⅱ的移植相對來說是比較容易的。如果已經(jīng)有人在您使用的處理器上成功地移植了μC/OS-Ⅱ,您也得到了相關(guān)代碼,就不必看本章了。當(dāng)然,本章介紹的內(nèi)容將有助于用戶了解μC/OS-Ⅱ中與處理器相關(guān)的代碼。

要使μC/OS-Ⅱ正常運(yùn)行,處理器必須滿足以下要求:

1. 處理器的C編譯器能產(chǎn)生可重入代碼。

2
用C語言就可以打開和關(guān)閉中斷。

3
處理器支持中斷,并且能產(chǎn)生定時(shí)中斷(通常在10至100Hz之間)。

4
處理器支持能夠容納一定量數(shù)據(jù)(可能是幾千字節(jié))的硬件堆棧。

5
處理器有將堆棧指針和其它CPU寄存器讀出和存儲(chǔ)到堆棧或內(nèi)存中的指令。

像Motorola 6805系列的處理器不能滿足上面的第4條和第5條要求,所以μC/OS-Ⅱ不能在這類處理器上運(yùn)行。

圖8.1說明了μC/OS-Ⅱ的結(jié)構(gòu)以及它與硬件的關(guān)系。由于μC/OS-Ⅱ?yàn)樽杂绍浖?dāng)用戶用到μC/OS-Ⅱ時(shí),有責(zé)任公開應(yīng)用軟件和μC/OS-Ⅱ的配置代碼。這本書和磁盤包含了所有與處理器無關(guān)的代碼和Intel 80x86實(shí)模式下的與處理器相關(guān)的代碼(C編譯器大模式下編譯)。如果用戶打算在其它處理器上使用μC/OS-Ⅱ,最好能找到一個(gè)現(xiàn)成的移植實(shí)例,如果沒有只好自己編寫了。用戶可以在正式的μC/OS-Ⅱ網(wǎng)站www. μCOS-Ⅱ.com中查找一些移植實(shí)例。

圖 8.1 μC/OS-II 硬件和軟件體系結(jié)構(gòu)



如果用戶理解了處理器和C編譯器的技術(shù)細(xì)節(jié),移植μC/OS-Ⅱ的工作實(shí)際上是非常簡單的。前提是您的處理器和編譯器滿足了μC/OS-Ⅱ的要求,并且已經(jīng)有了必要工具。移植工作包括以下幾個(gè)內(nèi)容:

?用#define設(shè)置一個(gè)常量的值(OS_CPU.H)

?聲明10個(gè)數(shù)據(jù)類型(OS_CPU.H)

?用#define聲明三個(gè)宏(OS_CPU.H)

?用C語言編寫六個(gè)簡單的函數(shù)(OS_CPU_C.C)

?編寫四個(gè)匯編語言函數(shù)(OS_CPU_A.ASM)

根據(jù)處理器的不同,一個(gè)移植實(shí)例可能需要編寫或改寫50至300行的代碼,需要的時(shí)間從幾個(gè)小時(shí)到一星期不等。

一旦代碼移植結(jié)束,下一步工作就是測試。測試一個(gè)象μC/OS-Ⅱ一樣的多任務(wù)實(shí)時(shí)內(nèi)核并不復(fù)雜。甚至可以在沒有應(yīng)用程序的情況下測試。換句話說,就是讓內(nèi)核自己測試自己。這樣做有兩個(gè)好處:第一,避免使本來就復(fù)雜的事情更加復(fù)雜;第二,如果出現(xiàn)問題,可以知道問題出在內(nèi)核代碼上而不是應(yīng)用程序。剛開始的時(shí)候可以運(yùn)行一些簡單的任務(wù)和時(shí)鐘節(jié)拍中斷服務(wù)例程。一旦多任務(wù)調(diào)度成功地運(yùn)行了,再添加應(yīng)用程序的任務(wù)就是非常簡單的工作了。

8.00 開發(fā)工具

如前所述,移植μC/OS-Ⅱ需要一個(gè)C編譯器,并且是針對用戶用的CPU的。因?yàn)棣藽/OS-Ⅱ是一個(gè)可剝奪型內(nèi)核,用戶只有通過C編譯器來產(chǎn)生可重入代碼;C編譯器還要支持匯編語言程序。絕大部分的C編譯器都是為嵌入式系統(tǒng)設(shè)計(jì)的,它包括匯編器、連接器和定位器。連接器用來將不同的模塊(編譯過和匯編過的文件)連接成目標(biāo)文件。定位器則允許用戶將代碼和數(shù)據(jù)放置在目標(biāo)處理器的指定內(nèi)存映射空間中。所用的C編譯器還必須提供一個(gè)機(jī)制來從C中打開和關(guān)閉中斷。一些編譯器允許用戶在C源代碼中插入?yún)R編語言。這就使得插入合適的處理器指令來允許和禁止中斷變得非常容易了。還有一些編譯器實(shí)際上包括了語言擴(kuò)展功能,可以直接從C中允許和禁止中斷。

8.01 目錄和文件

本書所付的磁盤中提供了μC/OS-Ⅱ的安裝程序,可在硬盤上安裝μC/OS-Ⅱ和移植實(shí)例代碼(Intel 80x86實(shí)模式,大模式編譯)。我設(shè)計(jì)了一個(gè)連續(xù)的目錄結(jié)構(gòu),使得用戶更容易找到目標(biāo)處理器的文件。如果想增加一個(gè)其它處理器的移植實(shí)例,您可以考慮采取同樣的方法(包括目錄的建立和文件的命名等等)。

所有的移植實(shí)例都應(yīng)放在用戶硬盤的\SOFTWARE\μCOS-Ⅱ目錄下。各個(gè)微處理器或微控制器的移植源代碼必須在以下兩個(gè)或三個(gè)文件中找到:OS_CPU.H,OS_CPU_C.C,OS_CPU_A.ASM。匯編語言文件OS_CPU_A.ASM是可選擇的,因?yàn)槟承〤編譯器允許用戶在C語言中插入?yún)R編語言,所以用戶可以將所需的匯編語言代碼直接放到OS_CPU_C.C中。放置移植實(shí)例的目錄決定于用戶所用的處理器,例如在下面的表中所示的放置不同移植實(shí)例的目錄結(jié)構(gòu)。注意,各個(gè)目錄雖然針對完全不同的目標(biāo)處理器,但都包括了相同的文件名。



Intel/AMD 80186

\SOFTWARE\uCOS-II\Ix86S






\OS_CPU_A.ASM


\OS_CPU_C.C


\SOFTWARE\uCOS-II\Ix86L


\OS_CPU.H


\OS_CPU_A.ASM


\OS_CPU_C.C

Motorola 68HC11

\SOFTWARE\uCOS-II\68HC11   


\OS_CPU.H


\OS_CPU_A.ASM


\OS_CPU_C.C


8.02 INCLUDES.H

在第一章中曾提到過,INCLUDES.H是一個(gè)頭文件,它在所有.C文件的第一行被包含。


#include "includes.h"


INCLUDES.H使得用戶項(xiàng)目中的每個(gè).C文件不用分別去考慮它實(shí)際上需要哪些頭文件。使用INCLUDES.H的唯一缺點(diǎn)是它可能會(huì)包含一些實(shí)際不相關(guān)的頭文件。這意味著每個(gè)文件的編譯時(shí)間可能會(huì)增加。但由于它增強(qiáng)了代碼的可移植性,所以我們還是決定使用這一方法。用戶可以通過編輯INCLUDES.H來增加自己的頭文件,但是用戶的頭文件必須添加在頭文件列表的最后。

8.03 OS_CPU.H

OS_CPU.H包括了用#defines定義的與處理器相關(guān)的常量,宏和類型定義。OS_CPU.H的大體結(jié)構(gòu)如程序清單 L8.1所示。

程序清單 L 8.1 OS_CPU.H.

#ifdef   OS_CPU_GLOBALS

#define OS_CPU_EXT

#else

#define OS_CPU_EXT   extern

#endif


/*

************************************************************************

*                                數(shù)據(jù)類型

*                            (與編譯器相關(guān))

************************************************************************

*/


typedef unsigned char   BOOLEAN;

typedef unsigned char   INT8U;      /* 無符號8位整數(shù)    */    (1)

typedef signed   char   INT8S;       /* 有符號8位整數(shù)    */

typedef unsigned int   INT16U;     /* 無符號16位整數(shù)   */

typedef signed   int   INT16S;      /* 有符號16位整數(shù)   */

typedef unsigned long   INT32U;     /* 無符號32位整數(shù)   */

typedef signed   long   INT32S;      /* 有符號32位整數(shù)   */

typedef float          FP32;         /* 單精度浮點(diǎn)數(shù)      */ (2)

typedef double         FP64;         /* 雙精度浮點(diǎn)數(shù)     */


typedef unsigned int   OS_STK;      /* 堆棧入口寬度為16位 */


/*

*************************************************************************

*                             與處理器相關(guān)的代碼

*************************************************************************

*/


#define   OS_ENTER_CRITICAL()   ???   /* 禁止中斷              */ (3)

#define   OS_EXIT_CRITICAL()   ???   /* 允許中斷              */


#define   OS_STK_GROWTH        1    /* 定義堆棧的增長方向: 1=向下, 0=向上 */ (4)


#define   OS_TASK_SW()         ??? (5)


8.03.01 與編譯器相關(guān)的數(shù)據(jù)類型

因?yàn)椴煌奈⑻幚砥饔胁煌淖珠L,所以μC/OS-Ⅱ的移植包括了一系列的類型定義以確保其可移植性。尤其是,μC/OS-Ⅱ代碼從不使用C的short,int和long等數(shù)據(jù)類型,因?yàn)樗鼈兪桥c編譯器相關(guān)的,不可移植。相反的,我定義的整型數(shù)據(jù)結(jié)構(gòu)既是可移植的又是直觀的[L8.1(2)]。為了方便,雖然μC/OS-Ⅱ不使用浮點(diǎn)數(shù)據(jù),但我還是定義了浮點(diǎn)數(shù)據(jù)類型[L8.1(2)]。

例如,INT16U數(shù)據(jù)類型總是代表16位的無符號整數(shù)。現(xiàn)在,μC/OS-Ⅱ和用戶的應(yīng)用程序就可以估計(jì)出聲明為該數(shù)據(jù)類型的變量的數(shù)值范圍是0-65535。將μC/OS-Ⅱ移植到32位的處理器上也就意味著INT16U實(shí)際被聲明為無符號短整型數(shù)據(jù)結(jié)構(gòu)而不是無符號整型數(shù)據(jù)結(jié)構(gòu)。但是,μC/OS-Ⅱ所處理的仍然是INT16U。

用戶必須將任務(wù)堆棧的數(shù)據(jù)類型告訴給μC/OS-Ⅱ。這個(gè)過程是通過為OS_STK聲明正確的C數(shù)據(jù)類型來完成的。如果用戶的處理器上的堆棧成員是32位的,并且用戶的編譯文件指定整型為32位數(shù),那么就應(yīng)該將OS_STK聲明位無符號整型數(shù)據(jù)類型。所有的任務(wù)堆棧都必須用OS_STK來聲明數(shù)據(jù)類型。

用戶所必須要做的就是查看編譯器手冊,并找到對應(yīng)于μC/OS-Ⅱ的標(biāo)準(zhǔn)C數(shù)據(jù)類型。


8.03.02 OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()

與所有的實(shí)時(shí)內(nèi)核一樣,μC/OS-Ⅱ需要先禁止中斷再訪問代碼的臨界段,并且在訪問完畢后重新允許中斷。這就使得μC/OS-Ⅱ能夠保護(hù)臨界段代碼免受多任務(wù)或中斷服務(wù)例程(ISRs)的破壞。中斷禁止時(shí)間是商業(yè)實(shí)時(shí)內(nèi)核公司提供的重要指標(biāo)之一,因?yàn)樗鼘⒂绊懙接脩舻南到y(tǒng)對實(shí)時(shí)事件的響應(yīng)能力。雖然μC/OS-Ⅱ盡量使中斷禁止時(shí)間達(dá)到最短,但是μC/OS-Ⅱ的中斷禁止時(shí)間還主要依賴于處理器結(jié)構(gòu)和編譯器產(chǎn)生的代碼的質(zhì)量。通常每個(gè)處理器都會(huì)提供一定的指令來禁止/允許中斷,因此用戶的C編譯器必須要有一定的機(jī)制來直接從C中執(zhí)行這些操作。有些編譯器能夠允許用戶在C源代碼中插入?yún)R編語言聲明。這樣就使得插入處理器指令來允許和禁止中斷變得很容易了。其它一些編譯器實(shí)際上包括了語言擴(kuò)展功能,可以直接從C中允許和禁止中斷。為了隱藏編譯器廠商提供的具體實(shí)現(xiàn)方法,μC/OS-Ⅱ定義了兩個(gè)宏來禁止和允許中斷:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()[L8.1(3)]。


{

    OS_ENTER_CRITICAL();

    /* ?μC/OS-II 臨界代碼段 */

    OS_EXIT_CRITICAL();

}


方法1

執(zhí)行這兩個(gè)宏的第一個(gè)也是最簡單的方法是在OS_ENTER_CRITICAL()中調(diào)用處理器指令來禁止中斷,以及在OS_EXIT_CRITICAL()中調(diào)用允許中斷指令。但是,在這個(gè)過程中還存在著小小的問題。如果用戶在禁止中斷的情況下調(diào)用μC/OS-Ⅱ函數(shù),在從μC/OS-Ⅱ返回的時(shí)候,中斷可能會(huì)變成是允許的了!如果用戶禁止中斷就表明用戶想在從μC/OS-Ⅱ函數(shù)返回的時(shí)候中斷還是禁止的。在這種情況下,光靠這種執(zhí)行方法可能是不夠的。

方法2

執(zhí)行OS_ENTER_CRITICAL()的第二個(gè)方法是先將中斷禁止?fàn)顟B(tài)保存到堆棧中,然后禁止中斷。而執(zhí)行OS_EXIT_CRITICAL()的時(shí)候只是從堆棧中恢復(fù)中斷狀態(tài)。如果用這個(gè)方法的話,不管用戶是在中斷禁止還是允許的情況下調(diào)用μC/OS-Ⅱ服務(wù),在整個(gè)調(diào)用過程中都不會(huì)改變中斷狀態(tài)。如果用戶在中斷禁止的時(shí)候調(diào)用μC/OS-Ⅱ服務(wù),其實(shí)用戶是在延長應(yīng)用程序的中斷響應(yīng)時(shí)間。用戶的應(yīng)用程序還可以用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()來保護(hù)代碼的臨界段。但是,用戶在使用這種方法的時(shí)候還得十分小心,因?yàn)槿绻脩粼谡{(diào)用象OSTimeDly()之類的服務(wù)之前就禁止中斷,很有可能用戶的應(yīng)用程序會(huì)崩潰。發(fā)生這種情況的原因是任務(wù)被掛起直到時(shí)間期滿,而中斷是禁止的,因而用戶不可能獲得節(jié)拍中斷!很明顯,所有的PEND調(diào)用都會(huì)涉及到這個(gè)問題,用戶得十分小心。一個(gè)通用的辦法是用戶應(yīng)該在中斷允許的情況下調(diào)用μC/OS-Ⅱ的系統(tǒng)服務(wù)!

問題是:哪種方法更好一點(diǎn)?這就得看用戶想犧牲些什么。如果用戶并不關(guān)心在調(diào)用μC/OS-Ⅱ服務(wù)后用戶的應(yīng)用程序中中斷是否是允許的,那么用戶應(yīng)該選擇第一種方法執(zhí)行。如果用戶想在調(diào)用μC/OS-Ⅱ服務(wù)過程中保持中斷禁止?fàn)顟B(tài),那么很明顯用戶應(yīng)該選擇第二種方法。

給用戶舉個(gè)例子吧,通過執(zhí)行STI命令在Intel 80186上禁止中斷,并用CLI命令來允許中斷。用戶可以用下面的方法來執(zhí)行這兩個(gè)宏:


#define OS_ENTER_CRITICAL()   asm CLI

#define OS_EXIT_CRITICAL()   asm STI


CLI和SCI指令都會(huì)在兩個(gè)時(shí)鐘周期內(nèi)被馬上執(zhí)行(總共為四個(gè)周期)。為了保持中斷狀態(tài),用戶需要用下面的方法來執(zhí)行宏:


#define OS_ENTER_CRITICAL()   asm PUSHF; CLI

#define OS_EXIT_CRITICAL()   asm POPF


在這種情況下,OS_ENTER_CRITICAL()需要12個(gè)時(shí)鐘周期,而OS_EXIT_CRITICAL()需要另外的8個(gè)時(shí)鐘周期(總共有20個(gè)周期)。這樣,保持中斷禁止?fàn)顟B(tài)要比簡單的禁止/允許中斷多花16個(gè)時(shí)鐘周期的時(shí)間(至少在80186上是這樣的)。當(dāng)然,如果用戶有一個(gè)速度比較快的處理器(如Intel Pentium Ⅱ),那么這兩種方法的時(shí)間差別會(huì)很小。

8.03.03 OS_STK_GROWTH

絕大多數(shù)的微處理器和微控制器的堆棧是從上往下長的。但是某些處理器是用另外一種方式工作的。μC/OS-Ⅱ被設(shè)計(jì)成兩種情況都可以處理,只要在結(jié)構(gòu)常量OS_STK_GROWTH [L8.1(4)]中指定堆棧的生長方式(如下所示)就可以了。

置OS_STK_GROWTH為0表示堆棧從下往上長。

置OS_STK_GROWTH為1表示堆棧從上往下長。

8.03.04 OS_TASK_SW()

OS_TASK_SW()[L8.1(5)]是一個(gè)宏,它是在μC/OS-Ⅱ從低優(yōu)先級任務(wù)切換到最高優(yōu)先級任務(wù)時(shí)被調(diào)用的。OS_TASK_SW()總是在任務(wù)級代碼中被調(diào)用的。另一個(gè)函數(shù)OSIntExit()被用來在ISR使得更高優(yōu)先級任務(wù)處于就緒狀態(tài)時(shí),執(zhí)行任務(wù)切換功能。任務(wù)切換只是簡單的將處理器寄存器保存到將被掛起的任務(wù)的堆棧中,并且將更高優(yōu)先級的任務(wù)從堆棧中恢復(fù)出來。

在μC/OS-Ⅱ中,處于就緒狀態(tài)的任務(wù)的堆棧結(jié)構(gòu)看起來就像剛發(fā)生過中斷并將所有的寄存器保存到堆棧中的情形一樣。換句話說,μC/OS-Ⅱ要運(yùn)行處于就緒狀態(tài)的任務(wù)必須要做的事就是將所有處理器寄存器從任務(wù)堆棧中恢復(fù)出來,并且執(zhí)行中斷的返回。為了切換任務(wù)可以通過執(zhí)行OS_TASK_SW()來產(chǎn)生中斷。大部分的處理器會(huì)提供軟中斷或是陷阱(TRAP)指令來完成這個(gè)功能。ISR或是陷阱處理函數(shù)(也叫做異常處理函數(shù))的向量地址必須指向匯編語言函數(shù)OSCtxSw()(參看8.04.02)。

例如,在Intel或者AMD 80x86處理器上可以使用INT指令。但是中斷處理向量需要指向OSCtxSw()。Motorola 68HC11處理器使用的是SWI指令,同樣,SWI的向量地址仍是OSCtxSw()。還有,Motorola 680x0/CPU32可能會(huì)使用16個(gè)陷阱指令中的一個(gè)。當(dāng)然,選中的陷阱向量地址還是OSCtxSw()。

一些處理器如Zilog Z80并不提供軟中斷機(jī)制。在這種情況下,用戶需要盡自己的所能將堆棧結(jié)構(gòu)設(shè)置成與中斷堆棧結(jié)構(gòu)一樣。OS_TASK_SW()只會(huì)簡單的調(diào)用OSCtxSw()而不是將某個(gè)向量指向OSCtxSw()。μC/OS已經(jīng)被移植到了Z80處理器上,μC/OS-Ⅱ也同樣可以。

8.04 OS_CPU_A.ASM

μC/OS-Ⅱ的移植實(shí)例要求用戶編寫四個(gè)簡單的匯編語言函數(shù):

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

如果用戶的編譯器支持插入?yún)R編語言代碼的話,用戶就可以將所有與處理器相關(guān)的代碼放到OS_CPU_C.C文件中,而不必再擁有一些分散的匯編語言文件。

8.04.01 OSStartHighRdy()

使就緒狀態(tài)的任務(wù)開始運(yùn)行的函數(shù)叫做OSStart(),如下所示。在用戶調(diào)用OSStart()之前,用戶必須至少已經(jīng)建立了自己的一個(gè)任務(wù)(參看OSTaskCreate()和OSTaskCteateExt())。OSStartHighRdy()假設(shè)OSTCBHighRdy指向的是優(yōu)先級最高的任務(wù)的任務(wù)控制塊。前面曾提到過,在μC/OS-Ⅱ中處于就緒狀態(tài)的任務(wù)的堆棧結(jié)構(gòu)看起來就像剛發(fā)生過中斷并將所有的寄存器保存到堆棧中的情形一樣。要想運(yùn)行最高優(yōu)先級任務(wù),用戶所要做的是將所有處理器寄存器按順序從任務(wù)堆棧中恢復(fù)出來,并且執(zhí)行中斷的返回。為了簡單一點(diǎn),堆棧指針總是儲(chǔ)存在任務(wù)控制塊(即它的OS_TCB)的開頭。換句話說,也就是要想恢復(fù)的任務(wù)堆棧指針總是儲(chǔ)存在OS_TCB的0偏址內(nèi)存單元中。


void OSStartHighRdy (void)

{

    Call user definable OSTaskSwHook();                                   

    Get the stack pointer of the task to resume:                           

        Stack pointer = OSTCBHighRdy->OSTCBStkPtr;

    OSRunning = TRUE;

    Restore all processor registers from the new task's stack;             

    Execute a return from interrupt instruction;                           

}


注意,OSStartHighRdy()必須調(diào)用OSTaskSwHook(),因?yàn)橛脩粽谶M(jìn)行任務(wù)切換的部分工作——用戶在恢復(fù)最高優(yōu)先級任務(wù)的寄存器。而OSTaskSwHook()可以通過檢查OSRunning來知道是OSStartHighRdy()在調(diào)用它(OSRunning為FALSE)還是正常的任務(wù)切換在調(diào)用它(OSRunning為TRUE).

OSStartHighRdy()還必須在最高優(yōu)先級任務(wù)恢復(fù)之前和調(diào)用OSTaskSwHook()之后設(shè)置OSRunning為TRUE。

8.04.02 OSCtxSw()

如前面所述,任務(wù)級的切換問題是通過發(fā)軟中斷命令或依靠處理器執(zhí)行陷阱指令來完成的。中斷服務(wù)例程,陷阱或異常處理例程的向量地址必須指向OSCtxSw()。

如果當(dāng)前任務(wù)調(diào)用μC/OS-Ⅱ提供的系統(tǒng)服務(wù),并使得更高優(yōu)先級任務(wù)處于就緒狀態(tài),μC/OS-Ⅱ就會(huì)借助上面提到的向量地址找到OSCtxSw()。在系統(tǒng)服務(wù)調(diào)用的最后,μC/OS-Ⅱ會(huì)調(diào)用OSSched(),并由此來推斷當(dāng)前任務(wù)不再是要運(yùn)行的最重要的任務(wù)了。OSSched()先將最高優(yōu)先級任務(wù)的地址裝載到OSTCBHighRdy中,再通過調(diào)用OS_TASK_SW()來執(zhí)行軟中斷或陷阱指令。注意,變量OSTCBCur早就包含了指向當(dāng)前任務(wù)的任務(wù)控制塊(OS_TCB)的指針。軟中斷 (或陷阱) 指令會(huì)強(qiáng)制一些處理器寄存器(比如返回地址和處理器狀態(tài)字)到當(dāng)前任務(wù)的堆棧中,并使處理器執(zhí)行OSCtxSw()。OSCtxSw()的原型如程序清單 L8.2所示。這些代碼必須寫在匯編語言中,因?yàn)橛脩舨荒苤苯訌腃中訪問CPU寄存器。注意在OSCtxSw()和用戶定義的函數(shù)OSTaskSwHook()的執(zhí)行過程中,中斷是禁止的。


程序清單 L 8.2 OSCtxSw()的原型

void OSCtxSw(void)

{

    保存處理器寄存器;

    將當(dāng)前任務(wù)的堆棧指針保存到當(dāng)前任務(wù)的OS_TCB中:

        OSTCBCur->OSTCBStkPtr = Stack pointer;

    調(diào)用用戶定義的OSTaskSwHook();

    OSTCBCur   = OSTCBHighRdy;

    OSPrioCur = OSPrioHighRdy;

    得到需要恢復(fù)的任務(wù)的堆棧指針:

        Stack pointer = OSTCBHighRdy->OSTCBStkPtr;

    將所有處理器寄存器從新任務(wù)的堆棧中恢復(fù)出來;

    執(zhí)行中斷返回指令;

}


8.04.03 OSIntCtxSw()

OSIntExit()通過調(diào)用OSIntCtxSw()來從ISR中執(zhí)行切換功能。因?yàn)镺SIntCtxSw()是在ISR中被調(diào)用的,所以可以斷定所有的處理器寄存器都被正確地保存到了被中斷的任務(wù)的堆棧之中。實(shí)際上除了我們需要的東西外,堆棧結(jié)構(gòu)中還有其它的一些東西。OSIntCtxSw()必須要清理堆棧,這樣被中斷的任務(wù)的堆棧結(jié)構(gòu)內(nèi)容才能滿足我們的需要。

要想了解OSIntCtxSw(),用戶可以看看μC/OS-Ⅱ調(diào)用該函數(shù)的過程。用戶可以參看圖8.2來幫助理解下面的描述。假定中斷不能嵌套(即ISR不會(huì)被中斷),中斷是允許的,并且處理器正在執(zhí)行任務(wù)級的代碼。當(dāng)中斷來臨的時(shí)候,處理器會(huì)結(jié)束當(dāng)前的指令,識(shí)別中斷并且初始化中斷處理過程,包括將處理器的狀態(tài)寄存器和返回被中斷的任務(wù)的地址保存到堆棧中[F8.2(1)]。至于究竟哪些寄存器保存到了堆棧上,以及保存的順序是怎樣的,并不重要。

圖 8.2 在ISR執(zhí)行過程中的堆棧內(nèi)容.



接著,CPU會(huì)調(diào)用正確的ISR。μC/OS-Ⅱ要求用戶的ISR在開始時(shí)要保存剩下的處理器寄存器[F8.2(2)]。一旦寄存器保存好了,μC/OS-Ⅱ就要求用戶或者調(diào)用OSIntEnter(),或者將變量OSIntNesting加1。在這個(gè)時(shí)候,被中斷任務(wù)的堆棧中只包含了被中斷任務(wù)的寄存器內(nèi)容。現(xiàn)在,ISR可以執(zhí)行中斷服務(wù)了。并且如果ISR發(fā)消息給任務(wù)(通過調(diào)用OSMboxPost()或OSQPost()),恢復(fù)任務(wù)(通過調(diào)用OSTaskResume()),或者調(diào)用OSTimeTick()或OSTimeDlyResume()的話,有可能使更高優(yōu)先級的任務(wù)處于就緒狀態(tài)。

假設(shè)有一個(gè)更高優(yōu)先級的任務(wù)處于就緒狀態(tài)。μC/OS-Ⅱ要求用戶的ISR在完成中斷服務(wù)的時(shí)候調(diào)用OSIntExit()。OSIntExit()會(huì)告訴μC/OS-Ⅱ到了返回任務(wù)級代碼的時(shí)間了。調(diào)用OSIntExit()會(huì)導(dǎo)致調(diào)用者的返回地址被保存到被中斷的任務(wù)的堆棧中[F8.2(3)]。

OSIntExit()剛開始時(shí)會(huì)禁止中斷,因?yàn)樗枰獔?zhí)行臨界段的代碼。根據(jù)OS_ENTER_CRITICAL()的不同執(zhí)行過程(參看8.03.02),處理器的狀態(tài)寄存器會(huì)被保存到被中斷的任務(wù)的堆棧中[F8.2(4)]。OSIntExit()注意到由于有更高優(yōu)先級的任務(wù)處于就緒狀態(tài),被中斷的任務(wù)已經(jīng)不再是要繼續(xù)執(zhí)行的任務(wù)了。在這種情況下,指針OSTCBHighRdy會(huì)被指向新任務(wù)的OS_TCB,并且OSIntExit()會(huì)調(diào)用OSIntCtxSw()來執(zhí)行任務(wù)切換。調(diào)用OSIntCtxSw()也同樣使返回地址被保存到被中斷的任務(wù)的堆棧中[F8.2(5)]。

在用戶切換任務(wù)的時(shí)候,用戶只想將某些項(xiàng)([F8.2(1)]和[F8.2(2)])保留在堆棧中,并忽略其它項(xiàng)(F8.2(3),(4)和(5))。這是通過調(diào)整堆棧指針(加一個(gè)數(shù)在堆棧指針上)來完成的[F8.2(6)]。加在堆棧指針上的數(shù)必須是明確的,而這個(gè)數(shù)主要依賴于移植的目標(biāo)處理器(地址空間可能是16,32或64位),所用的編譯器,編譯器選項(xiàng),內(nèi)存模式等等。另外,處理器狀態(tài)字可能是8,16,32甚至64位寬,并且OSIntExit()可能會(huì)分配局部變量。有些處理器允許用戶直接增加常量到堆棧指針中,而有些則不允許。在后一種情況下,可以通過簡單的執(zhí)行一定數(shù)量的pop(出棧)指令來實(shí)現(xiàn)相同的功能。一旦堆棧指針完成調(diào)整,新的堆棧指針會(huì)被保存到被切換出去的任務(wù)的OS_TCB中[F8.2(7)]。

OSIntCtxSw()的原型如程序清單 L8.3所示。這些代碼必須寫在匯編語言中,因?yàn)橛脩舨荒苤苯訌腃語言中訪問CPU寄存器。如果用戶的編譯器支持插入?yún)R編語言代碼的話,用戶就可以將OSIntCtxSw()代碼放到OS_CPU_C.C文件中,而不放到OS_CPU_A.ASM文件中。正如用戶所看到的那樣,除了第一行以外,OSIntCtxSw()的代碼與OSCtxSw()是一樣的。這樣在移植實(shí)例中,用戶可以通過“跳轉(zhuǎn)”到OSCtxSw()中來減少OSIntCtxSw()代碼量。


程序清單 L 8.3 OSIntCtxSw()的原型

void OSIntCtxSw(void)

{

    調(diào)整堆棧指針來去掉在調(diào)用:

        OSIntExit(),

        OSIntCtxSw()過程中壓入堆棧的多余內(nèi)容;

    將當(dāng)前任務(wù)堆棧指針保存到當(dāng)前任務(wù)的OS_TCB中:

        OSTCBCur->OSTCBStkPtr = 堆棧指針;

    調(diào)用用戶定義的OSTaskSwHook();

    OSTCBCur   = OSTCBHighRdy;

    OSPrioCur = OSPrioHighRdy;

    得到需要恢復(fù)的任務(wù)的堆棧指針:

        堆棧指針 = OSTCBHighRdy->OSTCBStkPtr;

    將所有處理器寄存器從新任務(wù)的堆棧中恢復(fù)出來;

    執(zhí)行中斷返回指令;

}


8.04.04 OSTickISR()

μC/OS-Ⅱ要求用戶提供一個(gè)時(shí)鐘資源來實(shí)現(xiàn)時(shí)間的延時(shí)和期滿功能。時(shí)鐘節(jié)拍應(yīng)該每秒鐘發(fā)生10-100次。為了完成該任務(wù),可以使用硬件時(shí)鐘,也可以從交流電中獲得50/60Hz的時(shí)鐘頻率。

用戶必須在開始多任務(wù)調(diào)度后(即調(diào)用OSStart()后)允許時(shí)鐘節(jié)拍中斷。換句話說,就是用戶應(yīng)該在OSStart()運(yùn)行后,μC/OS-Ⅱ啟動(dòng)運(yùn)行的第一個(gè)任務(wù)中初始化節(jié)拍中斷。通常所犯的錯(cuò)誤是在調(diào)用OSInit()和OSStart()之間允許時(shí)鐘節(jié)拍中斷(如程序清單 L8.4所示)。


程序清單 L 8.4 在不正確的位置啟動(dòng)時(shí)鐘節(jié)拍中斷

void main(void)

{

    .

    .

    OSInit();               /* 初始化 ?μC/OS-II                 */

    .

    .

    /* 應(yīng)用程序初始化代碼 ...                         */

    /* ... 調(diào)用OSTaskCreate()建立至少一個(gè)任務(wù)       */

    .

    .

    允許時(shí)鐘節(jié)拍中斷; /* 千萬不要在這里允許!!!            */

    .

    .

    OSStart();              /* 開始多任務(wù)調(diào)度                  */

}


有可能在μC/OS-Ⅱ開始執(zhí)行第一個(gè)任務(wù)前時(shí)鐘節(jié)拍中斷就發(fā)生了。在這種情況下,μC/OS-Ⅱ的運(yùn)行狀態(tài)不確定,用戶的應(yīng)用程序也可能會(huì)崩潰。

時(shí)鐘節(jié)拍ISR的原型如程序清單 L8.5所示。這些代碼必須寫在匯編語言中,因?yàn)橛脩舨荒苤苯訌腃語言中訪問CPU寄存器。如果用戶的處理器可以通過單條指令來增加OSIntNesting,那么用戶就沒必要調(diào)用OSIntEnter()了。增加OSIntNesting要比通過函數(shù)調(diào)用和返回快得多。OSIntEnter()只增加OSIntNesting,并且作為臨界段代碼中受到保護(hù)。


程序清單 L 8.5 時(shí)鐘節(jié)拍ISR的原型

void OSTickISR(void)

{

   保存處理器寄存器;

   調(diào)用OSIntEnter()或者直接將 OSIntNesting加1;


   調(diào)用OSTimeTick();


   調(diào)用OSIntExit();

   恢復(fù)處理器寄存器;

   執(zhí)行中斷返回指令;

}


8.05 OS_CPU_C.C

μC/OS-Ⅱ的移植實(shí)例要求用戶編寫六個(gè)簡單的C函數(shù):

OSTaskStkInit()

OSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

唯一必要的函數(shù)是OSTaskStkInit(),其它五個(gè)函數(shù)必須得聲明但沒必要包含代碼。

8.05.01 OSTaskStkInt()

OSTaskCreate()和OSTaskCreateExt()通過調(diào)用OSTaskStkInt()來初始化任務(wù)的堆棧結(jié)構(gòu),因此,堆棧看起來就像剛發(fā)生過中斷并將所有的寄存器保存到堆棧中的情形一樣。圖8.3顯示了OSTaskStkInt()放到正被建立的任務(wù)堆棧中的東西。注意,在這里我假定了堆棧是從上往下長的。下面的討論同樣適用于從下往上長的堆棧。

在用戶建立任務(wù)的時(shí)候,用戶會(huì)傳遞任務(wù)的地址,pdata指針,任務(wù)的堆棧棧頂和任務(wù)的優(yōu)先級給OSTaskCreate()和OSTaskCreateExt()。雖然OSTaskCreateExt()還要求有其它的參數(shù),但這些參數(shù)在討論OSTaskStkInt()的時(shí)候是無關(guān)緊要的。為了正確初始化堆棧結(jié)構(gòu),OSTaskStkInt()只要求剛才提到的前三個(gè)參數(shù)和一個(gè)附加的選項(xiàng),這個(gè)選項(xiàng)只能在OSTaskCreateExt()中得到。

圖 8.3 堆棧初始化(pdata通過堆棧傳遞)




回顧一下,在μC/OS-Ⅱ中,無限循環(huán)的任務(wù)看起來就像其它的C函數(shù)一樣。當(dāng)任務(wù)開始被μC/OS-Ⅱ執(zhí)行時(shí),任務(wù)就會(huì)收到一個(gè)參數(shù),好像它被其它的任務(wù)調(diào)用一樣。


void MyTask (void *pdata)

{

    /* 對'pdata'做某些操作 */

    for (;;) {

        /* 任務(wù)代碼                      */

    }

}


如果我想從其它的函數(shù)中調(diào)用MyTask(),C編譯器就會(huì)先將調(diào)用MyTask()的函數(shù)的返回地址保存到堆棧中,再將參數(shù)保存到堆棧中。實(shí)際上有些編譯器會(huì)將pdata參數(shù)傳至一個(gè)或多個(gè)寄存器中。在后面我會(huì)討論這類情況。假定pdata會(huì)被編譯器保存到堆棧中,OSTaskStkInit()就會(huì)簡單的模仿編譯器的這種動(dòng)作,將pdata保存到堆棧中[F8.3(1)]。但是結(jié)果表明,與C函數(shù)調(diào)用不一樣,調(diào)用者的返回地址是未知的。用戶所擁有的是任務(wù)的開始地址,而不是調(diào)用該函數(shù)(任務(wù))的函數(shù)的返回地址!事實(shí)上用戶不必太在意這點(diǎn),因?yàn)槿蝿?wù)并不希望返回到其它函數(shù)中。

這時(shí),用戶需要將寄存器保存到堆棧中,當(dāng)處理器發(fā)現(xiàn)并開始執(zhí)行中斷的時(shí)候,它會(huì)自動(dòng)地完成該過程的。一些處理器會(huì)將所有的寄存器存入堆棧,而其它一些處理器只將部分寄存器存入堆棧。一般而言,處理器至少得將程序計(jì)數(shù)器的值(中斷返回地址)和處理器的狀態(tài)字存入堆棧[F8.3(2)]。很明顯,處理器是按一定的順序?qū)⒓拇嫫鞔嫒攵褩5模脩粼趯⒓拇嫫鞔嫒攵褩5臅r(shí)候也就必須依照這一順序。

接著,用戶需要將剩下的處理器寄存器保存到堆棧中[F8.3(3)]。保存的命令依賴于用戶的處理器是否允許用戶保存它們。有些處理器用一個(gè)或多個(gè)指令就可以馬上將許多寄存器都保存起來。用戶必須用特定的指令來完成這一過程。例如,Intel 80x86使用PUSHA 指令將8個(gè)寄存器保存到堆棧中。對Motorola 68HC11處理器而言,在中斷響應(yīng)期間,所有的寄存器都會(huì)按一定順序自動(dòng)的保存到堆棧中,所以在用戶將寄存器存入堆棧的時(shí)候,也必須依照這一順序。

現(xiàn)在是時(shí)候討論這個(gè)問題了:如果用戶的C編譯器將pdata參數(shù)傳遞到寄存器中而不是堆棧中該作些什么?用戶需要從編譯器的文檔中找到pdata儲(chǔ)存在哪個(gè)寄存器中。pdata的內(nèi)容就會(huì)隨著這個(gè)寄存器的儲(chǔ)存被放置在堆棧中。

圖 8.4 堆棧初始化(pdata通過寄存器傳遞)



一旦用戶初始化了堆棧,OSTaskStkInit()就需要返回堆棧指針?biāo)傅牡刂穂F8.3(4)]。OSTaskCreate()和OSTaskCreateExt()會(huì)獲得該地址并將它保存到任務(wù)控制塊(OS_TCB)中。處理器文檔會(huì)告訴用戶堆棧指針會(huì)指向下一個(gè)堆棧空閑位置,還是會(huì)指向最后存入數(shù)據(jù)的堆棧單元位置。例如,對Intel 80x86處理器而言,堆棧指針會(huì)指向最后存入數(shù)據(jù)的堆棧單元位置,而對Motorola 68HC11處理器而言,堆棧指針會(huì)指向下一個(gè)空閑的位置。

8.05.02 OSTaskCreateHook()

當(dāng)用OSTaskCreate()或OSTaskCreateExt()建立任務(wù)的時(shí)候就會(huì)調(diào)用OSTaskCreateHook()。該函數(shù)允許用戶或使用用戶的移植實(shí)例的用戶擴(kuò)展μC/OS-Ⅱ的功能。當(dāng)μC/OS-Ⅱ設(shè)置完了自己的內(nèi)部結(jié)構(gòu)后,會(huì)在調(diào)用任務(wù)調(diào)度程序之前調(diào)用OSTaskCreateHook()。該函數(shù)被調(diào)用的時(shí)候中斷是禁止的。因此用戶應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時(shí)間。

當(dāng)OSTaskCreateHook()被調(diào)用的時(shí)候,它會(huì)收到指向已建立任務(wù)的OS_TCB的指針,這樣它就可以訪問所有的結(jié)構(gòu)成員了。當(dāng)使用OSTaskCreate()建立任務(wù)時(shí),OSTaskCreateHook()的功能是有限的。但當(dāng)用戶使用OSTaskCreateExt()建立任務(wù)時(shí),用戶會(huì)得到OS_TCB中的擴(kuò)展指針(OSTCBExtPtr),該指針可用來訪問任務(wù)的附加數(shù)據(jù),如浮點(diǎn)寄存器,MMU寄存器,任務(wù)計(jì)數(shù)器的內(nèi)容,以及調(diào)試信息。

只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生OSTaskCreateHook()的代碼。這樣,使用用戶的移植實(shí)例的用戶可以在其它的文件中重新定義hook函數(shù)。

8.05.03 OSTaskDelHook()

當(dāng)任務(wù)被刪除的時(shí)候就會(huì)調(diào)用OSTaskDelHook()。該函數(shù)在把任務(wù)從μC/OS-Ⅱ的內(nèi)部任務(wù)鏈表中解開之前被調(diào)用。當(dāng)OSTaskDelHook()被調(diào)用的時(shí)候,它會(huì)收到指向正被刪除任務(wù)的OS_TCB的指針,這樣它就可以訪問所有的結(jié)構(gòu)成員了。OSTaskDelHook()可以用來檢驗(yàn)TCB擴(kuò)展是否被建立了(一個(gè)非空指針)并進(jìn)行一些清除操作。OSTaskDelHook()不返回任何值。

只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生OSTaskDelHook()的代碼。

8.05.04 OSTaskSwHook()

當(dāng)發(fā)生任務(wù)切換的時(shí)候調(diào)用OSTaskSwHook()。不管任務(wù)切換是通過OSCtxSw()還是OSIntCtxSw()來執(zhí)行的都會(huì)調(diào)用該函數(shù)。OSTaskSwHook()可以直接訪問OSTCBCur 和OSTCBHighRdy,因?yàn)樗鼈兪侨肿兞俊STCBCur指向被切換出去的任務(wù)的OS_TCB,而OSTCBHighRdy指向新任務(wù)的OS_TCB。注意在調(diào)用OSTaskSwHook()期間中斷一直是被禁止的。因?yàn)榇a的多少會(huì)影響到中斷的響應(yīng)時(shí)間,所以用戶應(yīng)盡量使代碼簡化。OSTaskSwHook()沒有任何參數(shù),也不返回任何值。

只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生 OSTaskSwHook()的代碼。

8.05.05 OSTaskStatHook()

OSTaskStatHook()每秒鐘都會(huì)被OSTaskStat()調(diào)用一次。用戶可以用OSTaskStatHook()來擴(kuò)展統(tǒng)計(jì)功能。例如,用戶可以保持并顯示每個(gè)任務(wù)的執(zhí)行時(shí)間,每個(gè)任務(wù)所用的CPU份額,以及每個(gè)任務(wù)執(zhí)行的頻率等等。OSTaskStatHook()沒有任何參數(shù),也不返回任何值。

只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生OSTaskStatHook()的代碼。

8.05.06 OSTimeTickHook()

OSTaskTimeHook()在每個(gè)時(shí)鐘節(jié)拍都會(huì)被OSTaskTick()調(diào)用。實(shí)際上,OSTaskTimeHook()是在節(jié)拍被μC/OS-Ⅱ真正處理,并通知用戶的移植實(shí)例或應(yīng)用程序之前被調(diào)用的。OSTaskTimeHook()沒有任何參數(shù),也不返回任何值。

只用當(dāng)OS_CFG.H中的OS_CPU_HOOKS_EN被置為1時(shí)才會(huì)產(chǎn)生OSTaskTimeHook()的代碼。




OSTaskCreateHook()

void OSTaskCreateHook(OS_TCB *ptcb)


File

Called from

Code enabled by

OS_CPU_C.C

OSTaskCreate() and OSTaskCreateExt()

OS_CPU_HOOKS_EN



無論何時(shí)建立任務(wù),在分配好和初始化TCB后就會(huì)調(diào)用該函數(shù),當(dāng)然任務(wù)的堆棧結(jié)構(gòu)也已經(jīng)初始化好了。OSTaskCreateHook()允許用戶用自己的方式來擴(kuò)展任務(wù)建立函數(shù)的功能。例如用戶可以初始化和存儲(chǔ)與任務(wù)相關(guān)的浮點(diǎn)寄存器,MMU寄存器以及其它寄存器的內(nèi)容。通常,用戶可以存儲(chǔ)用戶的應(yīng)用程序所分配的附加的內(nèi)存信息。用戶還可以通過使用OSTaskCreateHook()來觸發(fā)示波器或邏輯分析儀,以及設(shè)置斷點(diǎn)。

參數(shù)

ptcb是指向所創(chuàng)建任務(wù)的任務(wù)控制塊的指針。

返回值

注意事項(xiàng)

該函數(shù)在被調(diào)用的時(shí)候中斷是禁止的。因此用戶應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時(shí)間。

范例

該例子假定了用戶是用OSTaskCreateExt()建立任務(wù)的,因?yàn)樗M谌蝿?wù)OS_TCB中有.OSTCBExtPtr域,該域包含了指向浮點(diǎn)寄存器的指針。


Void OSTaskCreateHook (OS_TCB *ptcb)

{

    if (ptcb->OSTCBExtPtr != (void *)0) {

        /* 儲(chǔ)存浮點(diǎn)寄存器的內(nèi)容到.. */

        /* ..TCB擴(kuò)展域中                           */

      }

}




OSTaskDelHook()

void OSTaskDelHook(OS_TCB *ptcb)


File

Called from

Code enabled by

OS_CPU_C.C

OSTaskDel()

OS_CPU_HOOKS_EN


當(dāng)用戶通過調(diào)用OSTaskDel()來刪除任務(wù)時(shí)都會(huì)調(diào)用該函數(shù)。這樣用戶就可以處理OSTaskCreateHook()所分配的內(nèi)存。OSTaskDelHook()就在TCB從TCB鏈中被移除前被調(diào)用。用戶還可以通過使用OSTaskDelHook()來觸發(fā)示波器或邏輯分析儀,以及設(shè)置斷點(diǎn)。

參數(shù)

ptcb是指向所創(chuàng)建任務(wù)的任務(wù)控制塊的指針。

返回值

注意事項(xiàng)

該函數(shù)在被調(diào)用的時(shí)候中斷是禁止的。因此用戶應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時(shí)間。

范例


void OSTaskDelHook (OS_TCB *ptcb)

{

    /* 輸出信號觸發(fā)示波器           */

}




OSTaskSwHook()

void OSTaskSwHook(void)


File

Called from

Code enabled by

OS_CPU_C.C

OSCtxSw() and
OSIntCtxSw()

OS_CPU_HOOKS_EN



當(dāng)執(zhí)行任務(wù)切換時(shí)都會(huì)調(diào)用該函數(shù)。全局變量OSTCBHighRdy指向得到CPU的任務(wù)的TCB,而OSTCBCur指向被切換出去的任務(wù)的TCB。OSTaskSwHook()在保存好了任務(wù)的寄存器和保存好了指向當(dāng)前任務(wù)TCB的堆棧指針后馬上被調(diào)用。用戶可以用該函數(shù)來保存或恢復(fù)浮點(diǎn)寄存器或MMU寄存器的內(nèi)容,來得到任務(wù)執(zhí)行時(shí)間的軌跡以及任務(wù)被切換進(jìn)來的次數(shù)等等。

參數(shù)

返回值

注意事項(xiàng)

該函數(shù)在被調(diào)用的時(shí)候中斷是禁止的。因此用戶應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時(shí)間。

范例


void OSTaskSwHook (void)

{

    /* 將浮點(diǎn)寄存器的內(nèi)容儲(chǔ)存在當(dāng)前任務(wù)的TCB擴(kuò)展域中。 */

    /* 用新任務(wù)的TCB擴(kuò)展域中的值更新浮點(diǎn)寄存器的內(nèi)容。   */

}





OSTaskStatHook()

void OSTaskStatHook(void)


File

Called from

Code enabled by

OS_CPU_C.C

OSTaskStat()

OS_CPU_HOOKS_EN


該函數(shù)每秒鐘都會(huì)被μC/OS-Ⅱ的統(tǒng)計(jì)任務(wù)調(diào)用。OSTaskStatHook()允許用戶加入自己的統(tǒng)計(jì)功能。

參數(shù)

返回值

注意事項(xiàng)

統(tǒng)計(jì)任務(wù)大概在調(diào)用OSStart()后再過5秒開始執(zhí)行。注意,當(dāng)OS_TASK_STAT_EN或者OS_TASK_CREATE_EXT_EN被置為0時(shí),該函數(shù)不會(huì)被調(diào)用。

范例


void OSTaskStatHook (void)

{

    /* 計(jì)算所有任務(wù)執(zhí)行的總時(shí)間     */

    /* 計(jì)算每個(gè)任務(wù)的執(zhí)行時(shí)間在總時(shí)間內(nèi)所占的百分比      */

}




OSTimeTickHook()

void OSTimeTickHook(void)


File

Called from

Code enabled by

OS_CPU_C.C

OSTimeTick()

OS_CPU_HOOKS_EN


只要發(fā)生時(shí)鐘節(jié)拍,該函數(shù)就會(huì)被OSTimeTick()調(diào)用。一旦進(jìn)入OSTimeTick()就會(huì)馬上調(diào)用OSTimeTickHook()以允許執(zhí)行用戶的應(yīng)用程序中的與時(shí)間密切相關(guān)的代碼。用戶還可以通過使用該函數(shù)觸發(fā)示波器或邏輯分析儀來調(diào)試,或者為仿真器設(shè)置斷點(diǎn)。

參數(shù)

返回值

注意事項(xiàng)

OSTimeTick()通常是被ISR調(diào)用的,所以時(shí)鐘節(jié)拍ISR的執(zhí)行時(shí)間會(huì)因?yàn)橛脩粼谠摵瘮?shù)中提供的代碼而增加。當(dāng)OSTimeTick()被調(diào)用的時(shí)候,中斷可以是禁止的也可以是允許的,這主要取決于該處理器上的移植是怎樣進(jìn)行的。如果中斷是禁止的,該函數(shù)將會(huì)影響到中斷響應(yīng)時(shí)間。

范例


void OSTimeTickHook (void)

{

    /* 觸發(fā)示波器                               */

}