當(dāng)前位置:首頁(yè) > 嵌入式 > 嵌入式軟件
[導(dǎo)讀] 這一章介紹如何將μC/OS-Ⅱ移植到不同的處理器上。所謂移植,就是使一個(gè)實(shí)時(shí)內(nèi)核能在某個(gè)微處理器或微控制器上運(yùn)行。為了方便移植,大部分的μC/OS-Ⅱ代碼是用C語(yǔ)言寫(xiě)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

z 用C語(yǔ)言編寫(xiě)六個(gè)簡(jiǎn)單的函數(shù)(OS_CPU_C.C)

z 編寫(xiě)四個(gè)匯編語(yǔ)言函數(shù)(OS_CPU_A.ASM)

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

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

8.00 開(kāi)發(fā)工具

如前所述,移植μC/OS-Ⅱ需要一個(gè)C編譯器,并且是針對(duì)用戶(hù)用的CPU的。因?yàn)?mu;C/OS-Ⅱ是一個(gè)可剝奪型內(nèi)核,用戶(hù)只有通過(guò)C編譯器來(lái)產(chǎn)生可重入代碼;C編譯器還要支持匯編語(yǔ)言程序。 絕大部分的C編譯器都是為嵌入式系統(tǒng)設(shè)計(jì)的, 它包括匯編器、 連接器和定位器。

連接器用來(lái)將不同的模塊(編譯過(guò)和匯編過(guò)的文件)連接成目標(biāo)文件。 定位器則允許用戶(hù)將代碼和數(shù)據(jù)放置在目標(biāo)處理器的指定內(nèi)存映射空間中。 所用的C編譯器還必須提供一個(gè)機(jī)制來(lái)從C中打開(kāi)和關(guān)閉中斷。一些編譯器允許用戶(hù)在C源代碼中插入?yún)R編語(yǔ)言。這就使得插入合適的處理器指令來(lái)允許和禁止中斷變得非常容易了。 還有一些編譯器實(shí)際上包括了語(yǔ)言擴(kuò)展功能,可以直接從C中允許和禁止中斷。

8.01 目錄和文件

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

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

8.02INCLUDES.H

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

#include"includes.h"

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

8.03OS_CPU.H

OS_CPU.H包括了用#defines定義的與處理器相關(guān)的常量,宏和類(lèi)型定義。OS_CPU.H的

大體結(jié)構(gòu)如程序清單L8.1所示。

程序清單 L8.1 OS_CPU.H.

#ifdefOS_CPU_GLOBALS

#defineOS_CPU_EXT

#else

#defineOS_CPU_EXTextern

#endif

/*

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

* 數(shù)據(jù)類(lèi)型

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

[!--empirenews.page--]

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

*/

typedefunsignedcharBOOLEAN;

typedefunsignedcharINT8U;/* 無(wú)符號(hào)8位整數(shù) */(1)

typedefsignedcharINT8S;/* 有符號(hào)8位整數(shù) */

typedefunsignedinTINT16U;/* 無(wú)符號(hào)16位整數(shù) */

typedefsignedintINT16S;/* 有符號(hào)16位整數(shù) */

typedefunsignedlONgINT32U;/* 無(wú)符號(hào)32位整數(shù) */

typedefsignedlongINT32S;/* 有符號(hào)32位整數(shù) */

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

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

typedefunsignedintOS_STK;/* 堆棧入口寬度為16位 */

/*

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

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

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

*/

#defineOS_ENTER_CRITICAL()???/* 禁止中斷 */(3)

#defineOS_EXIT_CRITICAL()???/* 允許中斷 */

#defineOS_STK_GROWTH1/* 定義堆棧的增長(zhǎng)方向: 1=向下,0=向上 */(4)

#defineOS_TASK_SW()???(5)

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

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

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

用戶(hù)必須將任務(wù)堆棧的數(shù)據(jù)類(lèi)型告訴給μC/OS-Ⅱ。這個(gè)過(guò)程是通過(guò)為OS_STK聲明正確的C數(shù)據(jù)類(lèi)型來(lái)完成的。如果用戶(hù)的處理器上的堆棧成員是32位的,并且用戶(hù)的編譯文件指定整型為32位數(shù),那么就應(yīng)該將OS_STK聲明位無(wú)符號(hào)整型數(shù)據(jù)類(lèi)型。所有的任務(wù)堆棧都必須用OS_STK來(lái)聲明數(shù)據(jù)類(lèi)型。用戶(hù)所必須要做的就是查看編譯器手冊(cè),并找到對(duì)應(yīng)于μC/OS-Ⅱ的標(biāo)準(zhǔn)C數(shù)據(jù)類(lèi)型。

8.03.02OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()

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

OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()[L8.1(3)]。

{

OS_ENTER_CRITICAL();

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

OS_EXIT_CRITICAL();

}

方法1

執(zhí)行這兩個(gè)宏的第一個(gè)也是最簡(jiǎn)單的方法是在OS_ENTER_CRITICAL()中調(diào)用處理器指令來(lái)禁止中斷,以及在OS_EXIT_CRITICAL()中調(diào)用允許中斷指令。但是,在這個(gè)過(guò)程中還存在著小小的問(wèn)題。如果用戶(hù)在禁止中斷的情況下調(diào)用μC/OS-Ⅱ函數(shù),在從μC/OS-Ⅱ返回的時(shí)候,中斷可能會(huì)變成是允許的了!如果用戶(hù)禁止中斷就表明用戶(hù)想在從μ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è)方法的話(huà),不管用戶(hù)是在中斷禁止還是允許的情況下調(diào)用μC/OS-Ⅱ服務(wù),在整個(gè)調(diào)用過(guò)程中都不會(huì)改變中斷狀態(tài)。如果用戶(hù)在中斷禁止的時(shí)候調(diào)用μC/OS-Ⅱ服務(wù),其實(shí)用戶(hù)是在延長(zhǎng)應(yīng)用程序的中斷響應(yīng)時(shí)間。用戶(hù)的應(yīng)用程序還可以用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()來(lái)保護(hù)代碼的臨界段。 但是, 用戶(hù)在使用這種方法的時(shí)候還得十分小心,因?yàn)槿绻脩?hù)在調(diào)用象OSTimeDly()之類(lèi)的服務(wù)之前就禁止中斷,很有可能用戶(hù)的應(yīng)用程序會(huì)崩潰。發(fā)生這種情況的原因是任務(wù)被掛起直到時(shí)間期滿(mǎn),而中斷是禁止的,因而用戶(hù)不可能獲得節(jié)拍中斷!很明顯,所有的PEND調(diào)用都會(huì)涉及到這個(gè)問(wèn)題,用戶(hù)得十分小心。一個(gè)通用的辦法是用戶(hù)應(yīng)該在中斷允許的情況下調(diào)用μC/OS-Ⅱ的系統(tǒng)服務(wù)!

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

如果用戶(hù)想在調(diào)用μC/OS-Ⅱ服務(wù)過(guò)程中保持中斷禁止?fàn)顟B(tài),那么很明顯用戶(hù)應(yīng)該選擇第二種方法。

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

#defineOS_ENTER_CRITICAL()asmCLI

#defineOS_EXIT_CRITICAL()asmSTI

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

#defineOS_ENTER_CRITICAL()asmPUSHF;CLI

#defineOS_EXIT_CRITICAL()asmPOPF

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

8.03.03OS_STK_GROWTH

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

置OS_STK_GROWTH為0表示堆棧從下往上長(zhǎng)。

置OS_STK_GROWTH為1表示堆棧從上往下長(zhǎng)。

8.03.04OS_TASK_SW()

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

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

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

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

8.04OS_CPU_A.ASM

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

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

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

8.04.01OSStartHighRdy()

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

voidOSStartHighRdy(void)

{

CalluserdefinableOSTaskSwHook();

Getthestackpointerofthetasktoresume:

Stackpointer=OSTCBHighRdy->OSTCBStkPtr;

OSRunning=TRUE;

Restoreallprocessorregistersfromthenewtask‘sstack;

Executeareturnfrominterruptinstruction;

}

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

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

8.04.02OSCtxSw()

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

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

程序清單 L8.2 OSCtxSw()的原型

voidOSCtxSw(void)

{

保存處理器寄存器;

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

OSTCBCur->OSTCBStkPtr=Stackpointer;

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

OSTCBCur=OSTCBHighRdy;

OSPrioCur=OSPrioHighRdy;

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

Stackpointer=OSTCBHighRdy->OSTCBStkPtr;

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

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

}

8.04.03OSIntCtxSw()

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

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

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

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

假設(shè)有一個(gè)更高優(yōu)先級(jí)的任務(wù)處于就緒狀態(tài)。μC/OS-Ⅱ要求用戶(hù)的ISR在完成中斷服務(wù)的時(shí)候調(diào)用OSIntExit()。OSIntExit()會(huì)告訴μC/OS-Ⅱ到了返回任務(wù)級(jí)代碼的時(shí)間了。

調(diào)用OSIntExit()會(huì)導(dǎo)致調(diào)用者的返回地址被保存到被中斷的任務(wù)的堆棧中[F8.2(3)]。

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

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

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

程序清單 L8.3 OSIntCtxSw()的原型

voidOSIntCtxSw(void)

{

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

OSIntExit(),

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

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

OSTCBCur->OSTCBStkPtr= 堆棧指針;

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

OSTCBCur=OSTCBHighRdy;

OSPrioCur=OSPrioHighRdy;

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

堆棧指針 =OSTCBHighRdy->OSTCBStkPtr;

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

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

}

OSIntCtxSw()是μC/OS-Ⅱ(和μC/OS)中唯一的與編譯器相關(guān)的函數(shù);在我收到的e-mail中,關(guān)于該函數(shù)的e-mail明顯多于關(guān)于μC/OS其它方面的。如果在多次任務(wù)切換后用戶(hù)的系統(tǒng)崩潰了,用戶(hù)應(yīng)該懷疑堆棧指針在OSIntCtxSw()中是否被正確地調(diào)整了。

8.04.04OSTickISR()

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

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

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

voidmain(void)

{

.

.

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

.

.

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

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

.

.

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

.

.

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

}

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

時(shí)鐘節(jié)拍ISR的原型如程序清單L8.5所示。這些代碼必須寫(xiě)在匯編語(yǔ)言中,因?yàn)橛脩?hù)不能直接從C語(yǔ)言中訪(fǎng)問(wèn)CPU寄存器。如果用戶(hù)的處理器可以通過(guò)單條指令來(lái)增加OSIntNesting,那么用戶(hù)就沒(méi)必要調(diào)用 OSIntEnter()了。增加OSIntNesting要比通過(guò)函數(shù)調(diào)用和返回快得多。OSIntEnter()只增加OSIntNesting,并且作為臨界段代碼中受到保護(hù)。

程序清單 L8.5 時(shí)鐘節(jié)拍ISR的原型

voidOSTickISR(void)

{

保存處理器寄存器;

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

調(diào)用OSTimeTick();

調(diào)用OSIntExit();

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

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

}

8.05OS_CPU_C.C

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

OSTaskStkInit()

OSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

唯一必要的函數(shù)是OSTaskStkInit(),其它五個(gè)函數(shù)必須得聲明但沒(méi)必要包含代碼。[!--empirenews.page--]

8.05.01OSTaskStkInt()

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

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

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

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

voidMyTask(void*pdata)

{

/* 對(duì)‘pdata‘做某些操作 */

for(;;){

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

}

}

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

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

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

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

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

一旦用戶(hù)初始化了堆棧,OSTaskStkInit()就需要返回堆棧指針?biāo)傅牡刂穂F8.3(4)]。

OSTaskCreate()和OSTaskCreateExt()會(huì)獲得該地址并將它保存到任務(wù)控制塊(OS_TCB)中。

處理器文檔會(huì)告訴用戶(hù)堆棧指針會(huì)指向下一個(gè)堆??臻e位置, 還是會(huì)指向最后存入數(shù)據(jù)的堆棧單元位置。例如,對(duì)Intel80x86處理器而言,堆棧指針會(huì)指向最后存入數(shù)據(jù)的堆棧單元位置,而對(duì)Motorola68HC11處理器而言,堆棧指針會(huì)指向下一個(gè)空閑的位置。

8.05.02OSTaskCreateHook()

當(dāng)用OSTaskCreate()或OSTaskCreateExt()建立任務(wù)的時(shí)候就會(huì)調(diào)用OSTaskCreateHook()。該函數(shù)允許用戶(hù)或使用用戶(hù)的移植實(shí)例的用戶(hù)擴(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í)候中斷是禁止的。因此用戶(hù)應(yīng)盡量減少該函數(shù)中的代碼以縮短中斷的響應(yīng)時(shí)間。

當(dāng)OSTaskCreateHook()被調(diào)用的時(shí)候,它會(huì)收到指向已建立任務(wù)的OS_TCB的指針,這樣它就可以訪(fǎng)問(wèn)所有的結(jié)構(gòu)成員了。當(dāng)使用OSTaskCreate()建立任務(wù)時(shí),OSTaskCreateHook()的功能是有限的。但當(dāng)用戶(hù)使用OSTaskCreateExt()建立任務(wù)時(shí),用戶(hù)會(huì)得到OS_TCB中的擴(kuò)展指針OSTCBExtPtr),該指針可用來(lái)訪(fǎng)問(wèn)任務(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()的代碼。這樣,使用用戶(hù)的移植實(shí)例的用戶(hù)可以在其它的文件中重新定義hook函數(shù)。

8.05.03OSTaskDelHook()

當(dāng)任務(wù)被刪除的時(shí)候就會(huì)調(diào)用OSTaskDelHook()。 該函數(shù)在把任務(wù)從μC/OS-Ⅱ的內(nèi)部任務(wù)鏈表中解開(kāi)之前被調(diào)用。當(dāng)OSTaskDelHook()被調(diào)用的時(shí)候,它會(huì)收到指向正被刪除任務(wù)的OS_TCB的指針, 這樣它就可以訪(fǎng)問(wèn)所有的結(jié)構(gòu)成員了。 OSTaskDelHook()可以用來(lái)檢驗(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.04OSTaskSwHook()

當(dāng)發(fā)生任務(wù)切換的時(shí)候調(diào)用OSTaskSwHook()。不管任務(wù)切換是通過(guò)OSCtxSw()還是OSIntCtxSw()來(lái)執(zhí)行的都會(huì)調(diào)用該函數(shù)。OSTaskSwHook()可以直接訪(fǎng)問(wèn)OSTCBCur和OSTCBHighRdy,因?yàn)樗鼈兪侨肿兞?。OSTCBCur指向被切換出去的任務(wù)的OS_TCB,而OSTCBHighRdy指向新任務(wù)的OS_TCB。 注意在調(diào)用OSTaskSwHook()期間中斷一直是被禁止的。

因?yàn)榇a的多少會(huì)影響到中斷的響應(yīng)時(shí)間,所以用戶(hù)應(yīng)盡量使代碼簡(jiǎn)化。OSTaskSwHook()沒(méi)有任何參數(shù),也不返回任何值。

[!--empirenews.page--]

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

8.05.05OSTaskStatHook()

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

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

8.05.06OSTimeTickHook()

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

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

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

參數(shù)

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

返回值

無(wú)

注意事項(xiàng)

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

的響應(yīng)時(shí)間。

范例

該例子假定了用戶(hù)是用OSTaskCreateExt()建立任務(wù)的,因?yàn)樗M谌蝿?wù)OS_TCB中

有.OSTCBExtPtr域,該域包含了指向浮點(diǎn)寄存器的指針。

VoidOSTaskCreateHook(OS_TCB*ptcb)

{

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

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

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

}

}

當(dāng)用戶(hù)通過(guò)調(diào)用OSTaskDel()來(lái)刪除任務(wù)時(shí)都會(huì)調(diào)用該函數(shù)。這樣用戶(hù)就可以處理OSTaskCreateHook()所分配的內(nèi)存。 OSTaskDelHook()就在TCB從TCB鏈中被移除前被調(diào)用。

用戶(hù)還可以通過(guò)使用OSTaskDelHook()來(lái)觸發(fā)示波器或邏輯分析儀,以及設(shè)置斷點(diǎn)。

參數(shù)

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

返回值

無(wú)

注意事項(xiàng)

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

的響應(yīng)時(shí)間。

范例

voidOSTaskDelHook(OS_TCB*ptcb)

{

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

}

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

參數(shù)

無(wú)

返回值

無(wú)

注意事項(xiàng)

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

范例

voidOSTaskSwHook(void)

{

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

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

}

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

參數(shù)

無(wú)

返回值

無(wú)

注意事項(xiàng)

統(tǒng)計(jì)任務(wù)大概在調(diào)用OSStart()后再過(guò)5秒開(kāi)始執(zhí)行。注意,當(dāng)OS_TASK_STAT_EN或者

OS_TASK_CREATE_EXT_EN被置為0時(shí),該函數(shù)不會(huì)被調(diào)用。

范例

voidOSTaskStatHook(void)

{

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

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

}

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

參數(shù)

無(wú)

返回值

無(wú)

注意事項(xiàng)

OSTimeTick()通常是被ISR調(diào)用的, 所以時(shí)鐘節(jié)拍ISR的執(zhí)行時(shí)間會(huì)因?yàn)橛脩?hù)在該函數(shù)

中提供的代碼而增加。當(dāng)OSTimeTick()被調(diào)用的時(shí)候,中斷可以是禁止的也可以是允許的,

這主要取決于該處理器上的移植是怎樣進(jìn)行的。如果中斷是禁止的,該函數(shù)將會(huì)影響到中斷

響應(yīng)時(shí)間。

范例

voidOSTimeTickHook(void)

{

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

}

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀(guān)點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶(hù)希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話(huà)語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉