www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 嵌入式 > 嵌入式軟件
[導(dǎo)讀] 本章將介紹如何將μC/OS-II移植到INTEL80x86系列CPU上,本章所介紹的移植和代碼都是針對80x86的實(shí)模式的,且編譯器在大模式下編譯和連接。本章的內(nèi)容同樣適用于下述CPU:

 本章將介紹如何將μC/OS-II移植到INTEL80x86系列CPU上,本章所介紹的移植和代碼都是針對80x86的實(shí)模式的,且編譯器在大模式下編譯和連接。本章的內(nèi)容同樣適用于下述CPU:

80186

80286

80386

80486

Pentium

PentiumII

實(shí)際上,將要介紹的移植過程適用于所有與80x86兼容的CPU,如AMD,Cyrix,NEC(V-系列)等等。以INTEL的為例只是一種更典型的情況。80x86CPU每年的產(chǎn)量有數(shù)百萬,大部分用于個人計算機(jī),但用于嵌入式系統(tǒng)的數(shù)量也在不斷增加。最快的處理器(Pentium系列)將在2000年達(dá)到1G的工作頻率。

大部分支持80x86(實(shí)模式)的C編譯器都提供了不同的內(nèi)存使用模式,每一種都有不同的內(nèi)存組織方式,適用于不同規(guī)模的應(yīng)用程序。在大模式下,應(yīng)用程序和數(shù)據(jù)最大尋址空間為1Mb,程序指針為32位。下一節(jié)將介紹為什么32位指針只用到了其中的20位來尋址(1Mb)。

本章所介紹的內(nèi)容也適用于8086處理器,但由于8086沒有PUSHA指令,移植的時候要用幾條PUSH指令來代替。

圖F9.1顯示了工作在實(shí)模式下的80x86處理器的編程模式。所有的寄存器都是16位,在任務(wù)切換時需要保存寄存器內(nèi)容。

圖F9.180x86 實(shí)模式內(nèi)部寄存器圖.

80x86提供了一種特殊的機(jī)制,使得用16位寄存器可以尋址1Mb地址空間,這就是存儲器分段的方法。內(nèi)存的物理地址用段地址寄存器和偏移量寄存器共同表示。計算方法是:段地址寄存器的內(nèi)容左移4位(乘以16),再加上偏移量寄存器(其他6個寄存器中的一個,AX,BP,SP,SI,DI或IP)的內(nèi)容,產(chǎn)生可尋址1Mb的20位物理地址。圖F9.2表明了寄存器是如何組合的。段寄存器可以指向一個內(nèi)存塊,稱為一個段。一個16位的段寄存器可以表示65,536個不同的段,因此可以尋址1,048,576字節(jié)。由于偏移量寄存器也是16位的,所以單個段不能超過64K。實(shí)際操作中,應(yīng)用程序是由許多小于64K的段組成的。

圖F9.2 使用段寄存器和偏移量寄存器尋址.

代碼段寄存器(CS)指向當(dāng)前程序運(yùn)行的代碼段起始,堆棧段寄存器(SS)指向程序堆棧段的起始,數(shù)據(jù)段寄存器指向程序數(shù)據(jù)區(qū)的起始,附加段寄存器(ES)指向一個附加數(shù)據(jù)存儲區(qū)。每次CPU尋址的時候,段寄存器中的某一個會被自動選用,加上偏移量寄存器的內(nèi)容作為物理地址。文獻(xiàn)中會經(jīng)常發(fā)現(xiàn)用段地址—偏移量表示地址的方法,例如1000:00FF表示物理地址0x100FF。

9.00 開發(fā)工具

筆者采用的是BorlandC/C++V3.1和BorlandTurboAssembler匯編器完成程序的移植和測試,它可以產(chǎn)生可重入的代碼,同時支持在C程序中嵌入?yún)R編語句。編譯完成后,程序可在PC機(jī)上運(yùn)行。本書代碼的測試是在一臺Pentium-II計算機(jī)上完成的,操作系統(tǒng)是MicrosoftWindows95。實(shí)際上編譯器生成的是DOS可執(zhí)行文件,在Windows的DOS窗口中運(yùn)行。

只要您用的編譯器可以產(chǎn)生實(shí)模式下的代碼,移植工作就可以進(jìn)行。如果開發(fā)環(huán)境不同,就只能麻煩您更改一下編譯器和匯編器的設(shè)置了。

9.01 目錄和文件

在安裝μC/OS-II的時候,安裝程序?qū)押陀布嚓P(guān)的,針對INTEL80x86的代碼安裝到SOFTWAREuCOS-IIIx86L目錄下。代碼是80x86實(shí)模式,且在編譯器大模式下編譯的。移植部

分的代碼可在下述文件中找到:OS_CPU.H,OS_CPU_C.C,和OS_CPU_A.ASM。

9.02 INCLUDES.H文件

INCLUDES.H是主頭文件,在所有后綴名為.C的文件的開始都包含INCLUDES.H文件。使用INCLUDES.H的好處是所有的.C文件都只包含一個頭文件,程序簡潔,可讀性強(qiáng)。缺點(diǎn)是.C文件

可能會包含一些它并不需要的頭文件,額外的增加編譯時間。與優(yōu)點(diǎn)相比,多一些編譯時間還

是可以接受的。用戶可以改寫INCLUDES.H文件,增加自己的頭文件,但必須加在文件末尾。程

序清單L9.1是為80x86編寫的INCLUDES.H文件的內(nèi)容。

程序清單L 9.1 INCLUDES.H.

#include

#include

#include

#include

#include

#include

#include

#include"softwareucos-iiix86los_cpu.h"

#include"os_cfg.h"

#include"softwareblockspcsourcepc.h"

#include"softwareucos-iisourceucos_ii.h"

9.03 OS_CPU.H文件

OS_CPU.H文件中包含與處理器相關(guān)的常量,宏和結(jié)構(gòu)體的定義。程序清單L9.2是為80x86編寫的OS_CPU.H文件的內(nèi)容。

程序清單L 9.2 OS_CPU.H.

#ifdefOS_CPU_GLOBALS

#defineOS_CPU_EXT

#else

#defineOS_CPU_EXTextern

#endif

/*

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

******

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

*(與編譯器相關(guān)的內(nèi)容)

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

******

*/

typedefunsignedcharBOOLEAN;

typedefunsignedcharINT8U;/* 無符號8位數(shù) (1)*/

typedefsignedcharINT8S;/* 帶符號8位數(shù) */

typedefunsignedintINT16U;/* 無符號16位數(shù) */

typedefsignedintINT16S;/* 帶符號16位數(shù) */

typedefunsignedlongINT32U;/* 無符號32位數(shù) */

typedefsignedlongINT32S;/* 帶符號32位數(shù) */

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

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

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

#defineBYTEINT8S/* 以下定義的數(shù)據(jù)類型是為了與uC/OSV1.xx 兼容 */[!--empirenews.page--]

#defineUBYTEINT8U/*在uC/OS-II中并沒有實(shí)際的用處 */

#defineWORDINT16S

#defineUWORDINT16U

#defineLONGINT32S

#defineULONGINT32U

/*

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

******

*INTEL80x86(實(shí)模式, 大模式編譯)

*

*方法 #1: 用簡單指令開關(guān)中斷。

* 注意,用方法1關(guān)閉中斷,從調(diào)用函數(shù)返回后中斷會重新打開!

* 注意將文件OS_CPU_A.ASM中與OSIntCtxSw()相關(guān)的常量從10改到8。

*

* 方法 #2: 關(guān)中斷前保存中斷被關(guān)閉的狀態(tài).

* 注意將文件OS_CPU_A.ASM中與OSIntCtxSw()相關(guān)的常量從8改到10。

*

*

*

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

******

*/

#defineOS_CRITICAL_METHOD2

#ifOS_CRITICAL_METHOD==1

#defineOS_ENTER_CRITICAL()asmCLI/* 關(guān)閉中斷*/

#defineOS_EXIT_CRITICAL()asmSTI/* 打開中斷*/

#endif

#ifOS_CRITICAL_METHOD==2

#defineOS_ENTER_CRITICAL()asm{PUSHF;CLI}/* 關(guān)閉中斷 */

#defineOS_EXIT_CRITICAL()asmPOPF/* 打開中斷 */

#endif

/*

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

******

*INTEL80x86(實(shí)模式, 大模式編譯)

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

******

*/

#defineOS_STK_GROWTH1/* 堆棧由高地址向低地址增長 (3)*/

#defineuCOS0x80/* 中斷向量0x80用于任務(wù)切換 (4)*/

#defineOS_TASK_SW()asmINTuCOS(5)

/*

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

******

* 全局變量

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

******

*/

OS_CPU_EXTINT8UOSTickDOSCtr;/* 為調(diào)用DOS時鐘中斷而定義的計數(shù)器*/

(6)*/

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

由于不同的處理器有不同的字長,μC/OS-II的移植需要重新定義一系列的數(shù)據(jù)結(jié)構(gòu)。使用

BorlandC/C++編譯器,整數(shù)(int)類型數(shù)據(jù)為16位,長整形(long)為32位。為了讀者方便起見,盡管μC/OS-II中沒有用到浮點(diǎn)類型的數(shù),在源代碼中筆者還是提供了浮點(diǎn)類型的定義。

由于在80x86實(shí)模式中堆棧都是按字進(jìn)行操作的,沒有字節(jié)操作,所以BorlandC/C++編譯器中堆棧數(shù)據(jù)類型OS_STK聲明為16位。所有的堆棧都必須用OS_STK聲明。

9.03.02 代碼臨界區(qū)

與其他實(shí)時系統(tǒng)一樣,μC/OS-II在進(jìn)入系統(tǒng)臨界代碼區(qū)之前要關(guān)閉中斷,等到退出臨界區(qū)后再打開。從而保護(hù)核心數(shù)據(jù)不被多任務(wù)環(huán)境下的其他任務(wù)或中斷破壞。BorlandC/C++支持嵌入?yún)R編語句,所以加入關(guān)閉/打開中斷的語句是很方便的。μC/OS-II定義了兩個宏用來關(guān)閉/打開中斷:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。此處,筆者為用戶提供兩種開關(guān)中斷的方法,如下所述的方法1和方法2。作為一種測試,本書采用了方法1。當(dāng)然,您可以自由決定采用那種方法。

方法1

第一種方法,也是最簡單的方法,是直接將OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()定義為處理器的關(guān)閉(CLI)和打開(STI)中斷指令。但這種方法有一個隱患,如果在關(guān)閉中斷后調(diào)用μC/OS-II函數(shù),當(dāng)函數(shù)返回后,中斷將被打開!嚴(yán)格意義上的關(guān)閉中斷應(yīng)該是執(zhí)行OS_ENTER_CRITICAL()后中斷始終是關(guān)閉的, 方法1顯然不滿足要求。 但方法1的最大優(yōu)點(diǎn)是簡單,執(zhí)行速度快(只有一條指令),在此類操作頻繁的時候更為突出。如果在任務(wù)中并不在意調(diào)用函數(shù)返回后是否被中斷,推薦用戶采用方法1。此時需要將OSIntCtxSw()中的常量由10改到8(見文件OS_CPU_A.ASM)。

方法2

執(zhí)行OS_ENTER_CRITICAL()的第二種方法是先將中斷關(guān)閉的狀態(tài)保存到堆棧中,然后關(guān)閉中斷。與之對應(yīng)的OS_EXIT_CRITICAL()的操作是從堆棧中恢復(fù)中斷狀態(tài)。采用此方法,不管用戶是在中斷關(guān)閉還是允許的情況下調(diào)用μC/OS-Ⅱ中的函數(shù),在調(diào)用過程中都不會改變中斷狀態(tài)。

如果用戶在中斷關(guān)閉的情況下調(diào)用μC/OS-Ⅱ函數(shù),其實(shí)是延長了中斷響應(yīng)時間。雖然OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()可以保護(hù)代碼的臨界段。但如此用法要小心,特別

是在調(diào)用OSTimeDly()一類函數(shù)之前關(guān)閉了中斷。 此時任務(wù)將處于延時掛起狀態(tài), 等待時鐘中斷,但此時時鐘中斷是禁止的!則系統(tǒng)可能會崩潰。很明顯,所有的PEND調(diào)用都會涉及到這個問題,必須十分小心。所以建議用戶調(diào)用μC/OS-Ⅱ的系統(tǒng)函數(shù)之前打開中斷。

9.03.03 堆棧增長方向

80x86處理器的堆棧是由高地址向低地址方向增長的, 所以常量OS_STK_GROWTH必須設(shè)置為1

[程序清單L9.2(3)]。

9.03.04 OS_TASK_SW()

在 μC/OS-II中,就緒任務(wù)的堆棧初始化應(yīng)該模擬一次中斷發(fā)生后的樣子,堆棧中應(yīng)該按進(jìn)

棧次序設(shè)置好各個寄存器的內(nèi)容。OS_TASK_SW()函數(shù)模擬一次中斷過程,在中斷返回的時候進(jìn)

行任務(wù)切換。80x86提供了256個軟中斷源可供選用,中斷服務(wù)程序(ISR)(也稱為例外處理過

程)的入口點(diǎn)必須指向匯編函數(shù)OSCtxSw()(請參看文件OS_CPU_A.ASM)。

由于筆者是在PC機(jī)上測試代碼的,本章的代碼用到了中斷號128(0x80),因為此中斷號是提供給用戶使用的[程序清單L9.2(4)](PC和操作系統(tǒng)會占用一部分中斷資源—譯者注),類似的用戶可用中斷號還有0x4B到0x5B,0x5D到0x66,或者0x68到0x6F。如果用戶用的不是PC,而是其他嵌入式系統(tǒng),如80186處理器,用戶可能有更多的中斷資源可供選用。

9.03.05 時鐘節(jié)拍的發(fā)生頻率

實(shí)時系統(tǒng)中時鐘節(jié)拍的發(fā)生頻率應(yīng)該設(shè)置為10到100Hz。通常(但不是必須的)為了方便計算設(shè)為整數(shù)。不幸的是,在PC中,系統(tǒng)缺省的時鐘節(jié)拍頻率是18.20648Hz,這對于我們的計算和設(shè)置都不方便。本章中,筆者將更改PC的時鐘節(jié)拍頻率到200Hz(間隔5ms)。一方面200Hz近似18.20648Hz的11倍,可以經(jīng)過11次延時再調(diào)用DOS中斷;另一方面,在DOS中,有些操作要求時鐘間隔為54.93ms,我們設(shè)定的間隔5ms也可以滿足要求。如果您的PC機(jī)處理器是80386,時鐘節(jié)拍最快也只能到200Hz,而如果是PentiumII處理器,則達(dá)到200Hz以上沒有問題。[!--empirenews.page--]

在文件OS_CPU.H的末尾聲明了一個8位變量OSTickDOSCtr,將保存時鐘節(jié)拍發(fā)生的次數(shù),每發(fā)生11次,調(diào)用DOS的時鐘節(jié)拍函數(shù)一次,從而實(shí)現(xiàn)與DOS時鐘的同步。OSTickDOSCtr是專門為PC環(huán)境而聲明的,如果在其他非PC的系統(tǒng)中運(yùn)行μC/OS-II,就不用這種同步方法,直接設(shè)定時鐘節(jié)拍發(fā)生頻率就行了。

9.04 OS_CPU_A.ASM

μC/OS-II的移植需要用戶改寫OS_CPU_A.ASM中的四個函數(shù):

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

9.04.01 OSStartHighRdy()

該函數(shù)由SStart()函數(shù)調(diào)用,功能是運(yùn)行優(yōu)先級最高的就緒任務(wù),在調(diào)用OSStart()之前,用戶必須先調(diào)用OSInit(),并且已經(jīng)至少創(chuàng)建了一個任務(wù)(請參考OSTaskCreate()和OSTaskCreateExt()函數(shù))。OSStartHighRdy()默認(rèn)指針OSTCBHighRdy指向優(yōu)先級最高就緒任務(wù)的任務(wù)控制塊(OS_TCB)(在這之前OSTCBHighRdy已由OSStart()設(shè)置好了)。圖F9.3給出了由函數(shù)OSTaskCreate()或OSTaskCreateExt()創(chuàng)建的任務(wù)的堆棧結(jié)構(gòu)。很明顯,OSTCBHighRdy-

>OSTCBStkPtr指向的是任務(wù)堆棧的頂端。

函數(shù)OSStartHighRdy()的代碼見程序清單L9.3。

圖F9.3 任務(wù)創(chuàng)立時的80x86堆棧結(jié)構(gòu).

為了啟動任務(wù),OSStartHighRdy()從任務(wù)控制塊(OS_TCB)[程序清單L9.3(1)]中找到指向堆棧的指針,然后運(yùn)行POPDS[程序清單L9.3(2)],POPES[程序清單L9.3(3)],POPA[程序清單L9.3(4)],和IRET[程序清單L9.3(5)]指令。此處筆者將任務(wù)堆棧指針保存在任務(wù)控制塊的開頭,這樣使得堆棧指針的存取在匯編語言中更容易操作。

當(dāng)執(zhí)行了IRET指令后,CPU會從(SS:SP)指向的堆棧中恢復(fù)各個寄存器的值并執(zhí)行中斷前的指令。SS:SP+4指向傳遞給任務(wù)的參數(shù)pdata。

程序清單L 9.3 OSStartHighRdy().

_OSStartHighRdyPROCFAR

MOVAX,SEG_OSTCBHighRdy; 載入 DS

MOVDS,AX;

LESBX,DWORDPTRDS:_OSTCBHighRdy;SS:SP=OSTCBHighRdy-

>OSTCBStkPtr (1)

MOVSS,ES:[BX+2];

MOVSP,ES:[BX+0];

;

POPDS; 恢復(fù)任務(wù)環(huán)境 (2)

POPES;(3)

POPA;(4)

;

IRET; 運(yùn)行任務(wù) (5)

_OSStartHighRdyENDP

9.04.02 OSCtxSw()

OSCtxSw()是一個任務(wù)級的任務(wù)切換函數(shù)(在任務(wù)中調(diào)用,區(qū)別于在中斷程序中調(diào)用的

OSIntCtxSw())。在80x86系統(tǒng)上,它通過執(zhí)行一條軟中斷的指令來實(shí)現(xiàn)任務(wù)切換。軟中斷向量

指向OSCtxSw()。在μC/OS-II中,如果任務(wù)調(diào)用了某個函數(shù),而該函數(shù)的執(zhí)行結(jié)果可能造成系統(tǒng)

任務(wù)重新調(diào)度(例如試圖喚醒了一個優(yōu)先級更高的任務(wù)),則在函數(shù)的末尾會調(diào)用OSSched(),

如果OSSched()判斷需要進(jìn)行任務(wù)調(diào)度,會找到該任務(wù)控制塊OS_TCB的地址,并將該地址拷貝到

OSTCBHighRdy,然后通過宏OS_TASK_SW()執(zhí)行軟中斷進(jìn)行任務(wù)切換。注意到在此過程中,變量

OSTCBCur始終包含一個指向當(dāng)前運(yùn)行任務(wù)OS_TCB的指針。程序清單L9.4為OSCtxSw()的代碼。

圖F9.4是任務(wù)被掛起或被喚醒時的堆棧結(jié)構(gòu)。在80x86處理器上,任務(wù)調(diào)用OS_TASK_SW()執(zhí)

行軟中斷指令后[圖F9.4/程序清單L9.4(1)],先向堆棧中壓入返回地址(段地址和偏移量),

然后是狀態(tài)字寄存器SW。緊接著用PUSHA[圖F9.4/程序清單L9.4(2)],PUSHES[圖F9.4/程序

清單L9.4(3)],和PUSHDS[圖F9.4/程序清單L9.4(4)]保存任務(wù)運(yùn)行環(huán)境。最后用OSCtxSw()在

任務(wù)OS_TCB中保存SS和SP寄存器。

任務(wù)環(huán)境保存完后,將調(diào)用用戶定義的對外接口函數(shù)OSTaskSwHook()[程序清單L9.4(6)]。

請注意,此時OSTCBCur指向當(dāng)前任務(wù)OS_TCB,OSTCBHighRdy指向新任務(wù)的OS_TCB。在

OSTaskSwHook()中,用戶可以訪問這兩個任務(wù)的OS_TCB。如果不使用對外接口函數(shù),請在頭文

件中把相應(yīng)的開關(guān)選項關(guān)閉,加快任務(wù)切換的速度。

程序清單L9.4 OSCtxSw().

_OSCtxSwPROCFAR(1)

;

PUSHA; 保存當(dāng)前任務(wù)環(huán)境 (2)

PUSHES (3)

PUSHDS (4)

;

MOVAX,SEG_OSTCBCur; 載入DS

MOVDS,AX

;

LESBX,DWORDPTRDS:_OSTCBCur;OSTCBCur->OSTCBStkPtr=SS:S(5)

MOVES:[BX+2],SS

MOVES:[BX+0],SP

;

CALLFARPTR_OSTaskSwHook(6)

;

MOVAX,WORDPTRDS:_OSTCBHighRdy+2;OSTCBCur=OSTCBHighRdy(7)

MOVDX,WORDPTRDS:_OSTCBHighRdy

MOVWORDPTRDS:_OSTCBCur+2,AX

MOVWORDPTRDS:_OSTCBCur,DX

;

MOVAL,BYTEPTRDS:_OSPrioHighRdy;OSPrioCur=OSPrioHighRdy(8)

MOVBYTEPTRDS:_OSPrioCur,AL

;

LESBX,DWORDPTRDS:_OSTCBHighRdy;SS:SP=OSTCBHighRdy-

>OSTCBStkPtr (9)

MOVSS,ES:[BX+2]

MOVSP,ES:[BX]

;

POPDS; 載入新任務(wù)的CPU環(huán)境 (10)

POPES (11)

POPA (12)

;

IRET; 返回新任務(wù) (13)

;

_OSCtxSwENDP

從對外接口函數(shù)OSTaskSwHook()返回后,由于任務(wù)的更替,變量OSTCBHighRdy被拷貝到

OSTCBCur中[程序清單L9.4(7)],同樣,OSPrioHighRdy被拷貝到OSPrioCur中[程序清單

L9.4(8)]。OSCtxSw()將載入新任務(wù)的CPU環(huán)境,首先從新任務(wù)OS_TCB中取出SS和SP寄存器的值

[圖F9.4(6)/程序清單L9.4(9)],然后運(yùn)行POPDS[圖F9.4(7)/程序清單L9.4(10)],POPES

[圖F9.4(8)/程序清單L9.4(11)],POPA[圖F9.4(9)/程序清單L9.4(12)]取出其他寄存器的值,

最后用中斷返回指令I(lǐng)RET[圖F9.4(10)/L9.4(13)]完成任務(wù)切換。

需要注意的是在運(yùn)行OSCtxSw()和OSTaskSwHook()函數(shù)期間,中斷是禁止的。

9.04.03 OSIntCtxSw()

在μC/OS-II中,由于中斷的產(chǎn)生可能會引起任務(wù)切換,在中斷服務(wù)程序的最后會調(diào)用[!--empirenews.page--]

OSIntExit()函數(shù)檢查任務(wù)就緒狀態(tài),如果需要進(jìn)行任務(wù)切換,將調(diào)用OSIntCtxSw()。所以

OSIntCtxSw()又稱為中斷級的任務(wù)切換函數(shù)。由于在調(diào)用OSIntCtxSw()之前已經(jīng)發(fā)生了中斷,

OSIntCtxSw()將默認(rèn)CPU寄存器已經(jīng)保存在被中斷任務(wù)的堆棧中了。

圖F 9.4 任務(wù)級任務(wù)切換時的80x86堆棧結(jié)構(gòu).

程序清單L9.5給出的代碼大部分與OSCtxSw()的代碼相同,不同之處是,第一,由于中斷已

經(jīng)發(fā)生, 此處不需要再保存CPU寄存器 (沒有PUSHA,PUSHES,或PUSHDS) ; 第二, OSIntCtxSw()需要調(diào)整堆棧指針,去掉堆棧中一些不需要的內(nèi)容,以使堆棧中只包含任務(wù)的運(yùn)行環(huán)境。圖F9.5可以幫助讀者理解這一過程。

程序清單L 9.5 OSIntCtxSw().

_OSIntCtxSwPROCFAR

;;IgnorecallstoOSIntExitandOSIntCtxSw

;ADDSP,8;(UncommentifOS_CRITICAL_METHODis1,seeOS_CPU.H)(1)

ADDSP,10;(UncommentifOS_CRITICAL_METHODis2,seeOS_CPU.H)

;

MOVAX,SEG_OSTCBCur; 載入DS

MOVDS,AX

;

LESBX,DWORDPTRDS:_OSTCBCur;OSTCBCur->OSTCBStkPtr=SS:SP(2)

MOVES:[BX+2],SS

MOVES:[BX+0],SP

;

CALLFARPTR_OSTaskSwHook(3)

;

MOVAX,WORDPTRDS:_OSTCBHighRdy+2;OSTCBCur=OSTCBHighRdy(4)

MOVDX,WORDPTRDS:_OSTCBHighRdy

MOVWORDPTRDS:_OSTCBCur+2,AX

MOVWORDPTRDS:_OSTCBCur,DX

;

MOVAL,BYTEPTRDS:_OSPrioHighRdy;OSPrioCur=OSPrioHighRdy(5)

MOVBYTEPTRDS:_OSPrioCur,AL

;

LESBX,DWORDPTRDS:_OSTCBHighRdy;SS:SP=OSTCBHighRdy-

>OSTCBStkPtr (6)

MOVSS,ES:[BX+2]

MOVSP,ES:[BX]

;

POPDS; 載入新任務(wù)的CPU環(huán)境 (7)

POPES (8)

POPA (9)

;

IRET; 返回新任務(wù) (10)

;

_OSIntCtxSwENDP

圖F 9.5 中斷級任務(wù)切換時的80x86堆棧結(jié)構(gòu)

當(dāng)中斷發(fā)生后,CPU在完成當(dāng)前指令后,進(jìn)入中斷處理過程。首先是保存現(xiàn)場,將返回地址

壓入當(dāng)前任務(wù)堆棧,然后保存狀態(tài)寄存器的內(nèi)容。接下來CPU從中斷向量處找到中斷服務(wù)程序的

入口地址,運(yùn)行中斷服務(wù)程序。在μC/OS-II中,要求用戶的中斷服務(wù)程序在開頭保存CPU其他寄

存器的內(nèi)容[圖F9.5(1)]。此后,用戶必須調(diào)用OSIntEnter()或著把全局變量OSIntNesting加1。

此時,被中斷任務(wù)的堆棧中保存了任務(wù)的全部運(yùn)行環(huán)境。在中斷服務(wù)程序中,有可能引起任務(wù)

就緒狀態(tài)的改變而需要任務(wù)切換,例如調(diào)用了OSMboxPost(),OSQPostFront(),OSQPost(),或試

圖喚醒一個優(yōu)先級更高的任務(wù)(調(diào)用OSTaskResume()),還可能調(diào)用OSTimeTick(),

OSTimeDlyResume()等等。

μC/OS-II要求用戶在中斷服務(wù)程序的末尾調(diào)用OSInt Exit(),以檢查任務(wù)就緒狀態(tài)。在調(diào)用

OSInt Exit()后,返回地址會壓入堆棧中[圖F9.5(2)]。

進(jìn)入OSIntExit()后,由于要訪問臨界代碼區(qū),首先關(guān)閉中斷。由于OS_ENTER_CRITICAL()可

能有不同的操作(見9.03.02節(jié)),狀態(tài)寄存器SW的內(nèi)容有可能被壓入堆棧[圖F9.5(3)]。如果

確實(shí)要進(jìn)行任務(wù)切換,指針OSTCBHighRdy將指向新的就緒任務(wù)的OS_TCB,OSIntExit()會調(diào)用

OSIntCtxSw()完成任務(wù)切換。注意,調(diào)用OSIntCtxSw()會在再一次在堆棧中保存返回地址[圖

F9.5(4)]。在進(jìn)行任務(wù)切換的時候,我們希望堆棧中只保留一次中斷發(fā)生的任務(wù)環(huán)境(如圖

F9.5(1)),而忽略掉由于函數(shù)嵌套調(diào)用而壓入的一系列返回地址(圖F9.5(2),(3),(4))。忽

略的方法也很簡單,只要把堆棧指針加一個固定的值就可以了[圖F9.5(5)/程序清單L9.5(1)]。

如果用方法2實(shí)現(xiàn)OS_ENTER_CRITICAL(),這個固定值是10;如果用方法1,則是8。實(shí)際操作中

還與編譯器以及編譯模式有關(guān)。例如,有些編譯器會為OSIntExit()在堆棧中分配臨時變量,這

都會影響具體占用堆棧的大小,這一點(diǎn)需要提醒用戶注意。

一但堆棧指針重新定位后,就被保存到將要被掛起的任務(wù)OS_TCB中[圖F9.5(6)/程序清單

L9.5(2)]。在μC/OS-II中(包括μC/OS),OSIntCtxSw()是唯一一個與編譯器相關(guān)的函數(shù),也是

用戶問的最多的。如果您的系統(tǒng)移植后運(yùn)行一段時間后就會死機(jī),就應(yīng)該懷疑是OSIntCtxSw()

中堆棧指針重新定位的問題。

當(dāng)當(dāng)前任務(wù)的現(xiàn)場保存完畢后,用戶定義的對外接口函數(shù)OSTaskSwHook()會被調(diào)用[程序清

單L9.5(3)]。注意到OSTCBCur指向當(dāng)前任務(wù)的OS_TCB,OSTCBHighRdy指向新任務(wù)的OS_TCB。在

函數(shù)OSTaskSwHook()中用戶可以訪問這兩個任務(wù)的OS_TCB。如果不用對外接口函數(shù),請在頭文

件中關(guān)閉相應(yīng)的開關(guān)選項,提高任務(wù)切換的速度。

從對外接口函數(shù)OSTaskSwHook()返回后,由于任務(wù)的更替,變量OSTCBHighRdy被拷貝到

OSTCBCur中[程序清單L9.5(4)],同樣,OSPrioHighRdy被拷貝到OSPrioCur中[程序清單

L9.5(5)]。此時,OSIntCtxSw()將載入新任務(wù)的CPU環(huán)境,首先從新任務(wù)OS_TCB中取出SS和SP寄

存器的值[圖F9.5(7)/程序清單L9.5(6)],然后運(yùn)行POPDS[圖F9.5(8)/程序清單L9.5(7)],

POPES[圖F9.5(9)/程序清單L9.5(8)],POPA[圖F9.5(10)/程序清單L9.5(9)]取出其他寄存器

的值,最后用中斷返回指令I(lǐng)RET[圖F9.5(11)/程序清單L9.5(10)]完成任務(wù)切換。

需要注意的是在運(yùn)行OSIntCtxSw()和用戶定義的OSTaskSwHook()函數(shù)期間,中斷是禁止的。

9.04.04 OSTickISR()

在9.03.05節(jié)中,我們已經(jīng)提到過實(shí)時系統(tǒng)中時鐘節(jié)拍發(fā)生頻率的問題,應(yīng)該在10到100Hz[!--empirenews.page--]

之間。但由于PC環(huán)境的特殊性,時鐘節(jié)拍由硬件產(chǎn)生,間隔54.93ms(18.20648Hz)。我們將時

鐘節(jié)拍頻率設(shè)為200Hz。PC時鐘節(jié)拍的中斷向量為0x08,μC/OS-II將此向量截取,指向了μC/OS

的中斷服務(wù)函數(shù)OSTickISR(),而原先的中斷向量保存在中斷129(0x81)中。為滿足DOS的需要,

原先的中斷服務(wù)還是每隔54.93ms(實(shí)際上還要短些)調(diào)用一次。圖F9.6為安裝μC/OS-II前后的

中斷向量表。

在μC/OS-II中, 當(dāng)調(diào)用OSStart()啟動多任務(wù)環(huán)境后, 時鐘中斷的作用是非常重要的。 但在PC

環(huán)境下,啟動μC/OS-II之前就已經(jīng)有時鐘中斷發(fā)生了,實(shí)際上我們希望在μC/OS-II初始化完成之后再發(fā)生時鐘中斷,調(diào)用OSTickISR()。與此相關(guān)的有下述過程:

PC_DOSSaveReturn()函數(shù)(參看PC.C):該函數(shù)由main()調(diào)用,任務(wù)是取得DOS下時鐘中斷向量,并將其保存在0x81中。

main()函數(shù):

設(shè)定中斷向量0x80指向任務(wù)切換函數(shù)OSCtxSw()

至少創(chuàng)立一個任務(wù)

當(dāng)初始化工作完成后調(diào)用OSStart()啟動多任務(wù)環(huán)境

第一個運(yùn)行的任務(wù):

設(shè)定中斷向量0x08指向函數(shù)OSTickISR()

將時鐘節(jié)拍頻率從18.20648改為200Hz

圖F9.6 PC 中斷向量表(IVT).

在程序清單L9.6給出了函數(shù)OSTickISR()的偽碼。和μC/OS-II中的其他中斷服務(wù)程序一樣,OSTickISR()首先在被中斷任務(wù)堆棧中保存CPU寄存器的值,然后調(diào)用OSIntEnter()。

μC/OS-II要求在中斷服務(wù)程序開頭調(diào)用OSIntEnter(), 其作用是將記錄中斷嵌套層數(shù)的全局

變量OSIntNesting加1。如果不調(diào)用OSIntEnter(),直接將OSIntNesting加1也是允許的。接下來計數(shù)器OSTickDOSCtr減1[程序清單L9.6(3)],每發(fā)生11次中斷,OSTickDOSCtr減到0,則調(diào)用DOS的時鐘中斷處理函數(shù)[程序清單L9.6(4)],調(diào)用間隔大約是54.93ms。如果不調(diào)用DOS時鐘中斷函數(shù),則向中斷優(yōu)先級控制器(PIC)發(fā)送命令清除中斷標(biāo)志。如果調(diào)用了DOS中斷,則此項操作可免,因為在DOS的中斷程序中已經(jīng)完成了。隨后,OSTickISR()調(diào)用OSTimeTick(),檢查所有處于延時等待狀態(tài)的任務(wù),判斷是否有延時結(jié)束就緒的任務(wù)[程序清單L9.6(6)]。 在OSTickISR()的最后調(diào)用OSIntExit(), 如果在中斷中 (或其他嵌套的中斷)有更高優(yōu)先級的任務(wù)就緒,并且當(dāng)前中斷為中斷嵌套的最后一層。OSIntExit()將進(jìn)行任務(wù)調(diào)度。注意如果進(jìn)行了任務(wù)調(diào)度,OSIntExit()將不再返回調(diào)用者,而是用新任務(wù)的堆棧中的寄存器數(shù)值恢復(fù)CPU現(xiàn)場,然后用IRET實(shí)現(xiàn)任務(wù)切換。如果當(dāng)前中斷不是中斷嵌套的最后一層,或中斷中沒有改變?nèi)蝿?wù)的就緒狀態(tài),OSIntExit()將返回調(diào)用者OSTickISR(),最后OSTickISR()返回被中斷的任務(wù)。

程序清單L9.7給出了OSTickISR()的完整代碼。

程序清單L 9.6 OSTickISR()偽碼.

voidOSTickISR(void)

{

Saveprocessorregisters;(1)

OSIntNesting++;(2)

OSTickDOSCtr—-;(3)

if(OSTickDOSCtr==0){

ChainintoDOSbyexecutingan‘INT81H‘instruction;(4)

}else{

SendEOIcommandtoPIC(PriorityInterruptController);(5)

}

OSTimeTick();(6)

OSIntExit(); (7)

Restoreprocessorregisters;(8)

Executeareturnfrominterruptinstruction(IRET);(9)

}

程序清單L9.7 OSTickISR().

_OSTickISRPROCFAR

;

PUSHA; 保存被中斷任務(wù)的CPU環(huán)境

PUSHES

PUSHDS

;

MOVAX,SEG_OSTickDOSCtr; 載入 DS

MOVDS,AX

;

INCBYTEPTR_OSIntNesting; 標(biāo)示 uC/OS-II 進(jìn)入中斷

;

DECBYTEPTRDS:_OSTickDOSCtr

CMPBYTEPTRDS:_OSTickDOSCtr,0

JNESHORT_OSTickISR1; 每11個時鐘節(jié)拍(18.206Hz)調(diào)用DOS時鐘中斷

;

MOVBYTEPTRDS:_OSTickDOSCtr,11

INT081H; 調(diào)用DOS時鐘中斷處理過程

JMPSHORT_OSTickISR2

_OSTickISR1:

MOVAL,20H; 向中斷優(yōu)先級控制器發(fā)送命令,清除標(biāo)志位.

MOVDX,20H;

OUTDX,AL;

;

_OSTickISR2:

CALLFARPTR_OSTimeTick; 調(diào)用OSTimeTick()函數(shù)

;

CALLFARPTR_OSIntExit; 標(biāo)示uC/OS-II退出中斷

;

POPDS; 恢復(fù)被中斷任務(wù)的CPU環(huán)境

POPES

POPA

;

IRET; 返回被中斷任務(wù)

;

_OSTickISRENDP

如果不更改DOS下的時鐘中斷頻率(保持18.20648Hz),OSTickISR()函數(shù)還可以簡化。程序清單L9.8為18.2Hz的OSTickISR()函數(shù)的偽碼。同樣,函數(shù)開頭要保存所有的CPU寄存器[程序清單L9.8(1)],將OSIntNesting加1[程序清單L9.8(2)]。接下來調(diào)用DOS的時鐘中斷處理過程[程序清單L9.8(3)],此處就不需要清除中斷優(yōu)先級控制器的操作了,因為DOS的時鐘中斷處理中包含了這一過程。然后調(diào)用OSTimeTick()檢查任務(wù)的延時是否結(jié)束[程序清單L9.8(4)], 最后調(diào)用OSInt Exit()[程序清單L9.8(5)]。 結(jié)束部分是恢復(fù)CPU寄存器的內(nèi)容[程序清單L9.8(6)],執(zhí)行IRET指令返回被中斷的任務(wù)。如果采用8.2Hz的OSTickISR()函數(shù),系統(tǒng)初始化過程就不用調(diào)用PC_SetTickRate(),同時將文件OS_CFG.H中的常量OS_TICKS_PER_SEC由200改為18。

程序清單L9.9給出了18.2HzOSTickISR()的完整代碼。

程序清單L9.818.2Hz OSTickISR()偽碼.

voidOSTickISR(void)

{

Saveprocessorregisters;(1)

OSIntNesting++;(2)

ChainintoDOSbyexecutingan‘INT81H‘instruction;(3)

OSTimeTick();(4)

OSIntExit(); (5)

Restoreprocessorregisters;(6)

Executeareturnfrominterruptinstruction(IRET);(7)

}

9.05 OS_CPU_C.C

μC/OS-II的移植需要用戶改寫OS_CPU_C.C中的六個函數(shù):[!--empirenews.page--]

OSTaskStkInit()

OSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

實(shí)際需要修改的只有OSTaskStkInit()函數(shù),其他五個函數(shù)需要聲明,但不一定有實(shí)際內(nèi)容。這五個函數(shù)都是用戶定義的,所以O(shè)S_CPU_C.C中沒有給出代碼。如果用戶需要使用這些函數(shù),請將文件OS_CFG.H中的#define constant OS_CPU_HOOKS_EN設(shè)為1,設(shè)為0表示不使用這些函數(shù)。

程序清單L 9.9 18.2Hz 的OSTickISR()函數(shù).

_OSTickISRPROCFAR

;

PUSHA; 保存被中斷任務(wù)的CPU環(huán)境

PUSHES

PUSHDS

;

MOVAX,SEG_OSIntNesting;載入 DS

MOVDS,AX

;

INCBYTEPTR_OSIntNesting;標(biāo)示uC/OS-II進(jìn)入中斷

;

INT081H; 調(diào)用DOS的時鐘中斷處理函數(shù)

;

CALLFARPTR_OSTimeTick; 調(diào)用OSTimeTick()函數(shù)

;

CALLFARPTR_OSIntExit;標(biāo)示uC/OS-IIof中斷結(jié)束

;

POPDS; 恢復(fù)被中斷任務(wù)的CPU環(huán)境

POPES

POPA

;

IRET; 返回被中斷任務(wù)

;

_OSTickISRENDP

圖F9.7 傳遞參數(shù) pdata的堆棧初始化結(jié)構(gòu)

9.05.01 OSTaskStkInit()

該函數(shù)由OSTaskCreate()或OSTaskCreateExt()調(diào)用,用來初始化任務(wù)的堆棧。初始狀態(tài)的堆棧模擬發(fā)生一次中斷后的堆棧結(jié)構(gòu)。圖F9.7說明了OSTaskStkInit()初始化后的堆棧內(nèi)容。請注意,圖中的堆棧結(jié)構(gòu)不是調(diào)用OSTaskStkInit()任務(wù)的,而是新創(chuàng)建任務(wù)的。

當(dāng)調(diào)用OSTaskCreate()或OSTaskCreateExt()創(chuàng)建一個新任務(wù)時,需要傳遞的參數(shù)是:

任務(wù)代碼的起使地址,參數(shù)指針(pdata),任務(wù)堆棧頂端的地址,任務(wù)的優(yōu)先級。

OSTaskCreateExt()還需要一些其他參數(shù),但與OSTask StkInit()沒有關(guān)系。

OSTaskStkInit()(程序清單L9.10)只需要以上提到的3個參數(shù)(task,pdata,和ptos)。

程序清單L 9.10 OSTaskStkInit().

void*OSTaskStkInit(void(*task)(void*pd),void*pdata,void*ptos,INT16Uopt)

{

INT16U*stk;

opt=opt;/*‘opt‘未使用,此處可防止編譯器的警告 */

stk=(INT16U*)ptos;/* 載入堆棧指針 (1)*/

*stk--=(INT16U)FP_SEG(pdata);/* 放置向函數(shù)傳遞的參數(shù) (2)*/

*stk--=(INT16U)FP_OFF(pdata);

*stk--=(INT16U)FP_SEG(task);/* 函數(shù)返回地址(3)*/

*stk--=(INT16U)FP_OFF(task);

*stk--=(INT16U)0x0202;/*SW 設(shè)置為中斷開啟 (4)*/

*stk--=(INT16U)FP_SEG(task);/* 堆棧頂端放置指向任務(wù)代碼的指針*/

*stk--=(INT16U)FP_OFF(task);

*stk--=(INT16U)0xAAAA;/*AX=0xAAAA(5)*/

*stk--=(INT16U)0xCCCC;/*CX=0xCCCC*/

*stk--=(INT16U)0xDDDD;/*DX=0xDDDD*/

*stk--=(INT16U)0xBBBB;/*BX=0xBBBB*/

*stk--=(INT16U)0x0000;/*SP=0x0000*/

*stk--=(INT16U)0x1111;/*BP=0x1111*/

*stk--=(INT16U)0x2222;/*SI=0x2222*/

*stk--=(INT16U)0x3333;/*DI=0x3333*/

*stk--=(INT16U)0x4444;/*ES=0x4444*/

*stk=_DS;/*DS=當(dāng)前CPU的 DS寄存器 (6)*/

return((void*)stk);

}

由于80x86堆棧是16位寬的(以字為單位)[程序清單L9.10(1)],OSTaskStkInit()將創(chuàng)立一個指向以字為單位內(nèi)存區(qū)域的指針。同時要求堆棧指針指向空堆棧的頂端。

筆者使用的BorlandC/C++編譯器配置為用堆棧而不是寄存器來傳送參數(shù)pdata,此時參數(shù)pdata的段地址和偏移量都將被保存在堆棧中[程序清單L9.10(2)]。

堆棧中緊接著是任務(wù)函數(shù)的起始地址[程序清單L9.10(3)],理論上,此處應(yīng)該為任務(wù)的返回地址,但在μC/OS-II中,任務(wù)函數(shù)必須為無限循環(huán)結(jié)構(gòu),不能有返回點(diǎn)。

返回地址下面是狀態(tài)字(SW)[程序清單L9.10(4)], 設(shè)置狀態(tài)字也是為了模擬中斷發(fā)生后的堆棧結(jié)構(gòu)。堆棧中的SW初始化為0x0202,這將使任務(wù)啟動后允許中斷發(fā)生;如果設(shè)為0x0002,則任務(wù)啟動后將禁止中斷。需要注意的是,如果選擇任務(wù)啟動后允許中斷發(fā)生,則所有的任務(wù)運(yùn)行期間中斷都允許;同樣,如果選擇任務(wù)啟動后禁止中斷,則所有的任務(wù)都禁止中斷發(fā)生,而不能有所選擇。

如果確實(shí)需要突破上述限制,可以通過參數(shù)pdata向任務(wù)傳遞希望實(shí)現(xiàn)的中斷狀態(tài)。如果某個任務(wù)選擇啟動后禁止中斷,那么其他的任務(wù)在運(yùn)行的時候需要重新開啟中斷。同時還要修改OSTaskIdle()和OSTaskStat()函數(shù),在運(yùn)行時開啟中斷。如果以上任何一個環(huán)節(jié)出現(xiàn)問題,系統(tǒng)就會崩潰。所以筆者還是推薦用戶設(shè)置SW為0x0202,在任務(wù)啟動時開啟中斷。

堆棧中還要留出各個寄存器的空間,注意寄存器在堆棧中的位置要和運(yùn)行指令PUSHA,PUSHES,和PUSHDS和壓入堆棧的次序相同。 上述指令在每次進(jìn)入中斷服務(wù)程序時都會調(diào)用[程序清單L9.10(5)]。AX,BX,CX,DX,SP,BP,SI,和DI的次序是和指令PUSHA的壓棧次序相同的。如果使用沒有PUSHA指令的8086處理器,就要使用多個PUSH指令壓入上述寄存器,且順序要與PUSHA相同。 在程序清單L9.10中每個寄存器被初始化為不同的值, 這是為了調(diào)試方便。

Borland編譯器支持偽寄存器變量操作,可以用_DS關(guān)鍵字取得CPUDS寄存器的值,程序清單

L9.10中(6)標(biāo)記處用_DS直接把DS寄存器拷貝到堆棧中。

堆棧初始化工作結(jié)束后,OSTaskStkInit()返回新的堆棧棧頂指針,OSTaskCreate()或

OSTaskCreateExt()將指針保存在任務(wù)的OS_TCB中。

9.05.02 OSTaskCreateHook()

OS_CPU_C.C中未定義,此函數(shù)為用戶定義。

9.05.03 OSTaskDelHook()

OS_CPU_C.C中未定義,此函數(shù)為用戶定義。

9.05.04 OSTaskSwHook()

OS_CPU_C.C中未定義,此函數(shù)為用戶定義。其用法請參考例程3。

9.05.05 OSTaskStatHook()[!--empirenews.page--]

OS_CPU_C.C中未定義,此函數(shù)為用戶定義。其用法請參考例程3。

9.05.06 OSTimeTickHook()

OS_CPU_C.C中未定義,此函數(shù)為用戶定義。

9.06 內(nèi)存占用

表9.1列出了指定初始化常量的情況下,μC/OS-II占用內(nèi)存的情況,包括數(shù)據(jù)和程序代碼。如果μC/OS-II用于嵌入式系統(tǒng),則數(shù)據(jù)指RAM的占用,程序代碼指ROM的占用。內(nèi)存占用的說明清單隨磁盤一起提供給用戶,在安裝μC/OS-II后,查看SOFTWAREuCOS-

IIIx836LDOC目錄下的ROM-RAM.XLS文件。 該文件為MicrosoftExcel文件, 需要Office97

或更高版本的Excel打開。

表9.1中所列出的內(nèi)存占用大小都近似為25字節(jié)的倍數(shù)。筆者所用的BorlandC/C++V3.1設(shè)定為編譯產(chǎn)生運(yùn)行速度最快的目標(biāo)代碼,所以表中所列的數(shù)字并不是絕對的,但可以給讀者一個總的概念。例如,如果不使用消息隊列機(jī)制,在編譯前將OS_Q_EN設(shè)為0,則編譯后的目標(biāo)代碼長度6,875字節(jié),可減小大約1,475字節(jié)。

此外,空閑任務(wù)(idle)和統(tǒng)計任務(wù)(statistics)的堆棧都設(shè)為1,024字節(jié)(1Kb)。根據(jù)您自己的要求可以增減。μC/OS-II的數(shù)據(jù)結(jié)構(gòu)最少需要35字節(jié)的RAM。

表9.2說明了如何裁減μC/OS-II,應(yīng)用在更小規(guī)模的系統(tǒng)上。此處的小系統(tǒng)有16個任務(wù)。

并且不采用如下功能:

?郵箱功能(OS_MBOX_EN設(shè)為0)

?內(nèi)存管理機(jī)制(OS_MEM_EN設(shè)為0)

?動態(tài)改變?nèi)蝿?wù)優(yōu)先級(OS_TASK_CHANGE_PRIO_EN設(shè)為0)

?舊版本的任務(wù)創(chuàng)建函數(shù)OSTaskCreate()(OS_TASK_CREATE_EN設(shè)為0)

?任務(wù)刪除(OS_TASK_DEL_EN設(shè)為0)

?掛起和喚醒任務(wù)(OS_TASK_SUSPEND_EN設(shè)為0)

采取上述措施后, 程序代碼空間可以減小3Kb, 數(shù)據(jù)空間可以減小2,200字節(jié)。 由于只有16個任務(wù)運(yùn)行,節(jié)省了大量用于任務(wù)控制塊OS_TCB的空間。在80x86的大模式編譯條件下,每一個OS_TCB將占用45字節(jié)的RAM。

9.07 運(yùn)行時間

表9.3到9.5列出了大部分μC/OS-II函數(shù)在80186處理器上的運(yùn)行時間。 統(tǒng)計的方法是將C原程序編譯為匯編代碼,然后計算每條匯編指令所需的時鐘周期,根據(jù)處理器的時鐘頻率,最后算出運(yùn)行時間。表中的I 欄為函數(shù)包含有多少條指令,C 欄為函數(shù)運(yùn)行需要多少時鐘周期,μs為運(yùn)行所需的以微秒為單位的時間。表中有3類時間,分別是在函數(shù)中關(guān)閉中斷的時間、函數(shù)運(yùn)行的最小時間和最大時間。如果您不使用80186處理器,表中的數(shù)據(jù)就沒有什么實(shí)際意義,但可以使您理解每個函數(shù)運(yùn)行時間的相對大小。

表 9.1μC/OS-II 內(nèi)存占用( 80186).

表 9.2 壓縮后的μC/OS-II配置.

以上各表中的時間數(shù)據(jù)都是假設(shè)函數(shù)成功運(yùn)行,正常返回;同時假定處理器工作在最大總線速度。平均來說,80186處理器的每條指令需要10個時鐘周期。

對于80186處理器,μC/OS-II中的函數(shù)最大的關(guān)閉中斷時間是33.3μs,約1,100個時鐘周期。

N/A是指該函數(shù)的運(yùn)行時間長短并不重要,例如一些只執(zhí)行一次初始化函數(shù)。

如果您用的是x86系列的其他CPU,您可以根據(jù)表中每個函數(shù)的運(yùn)行時鐘周期項估計當(dāng)前CPU的執(zhí)行時間。例如,如果用80486,且知80486的指令平均用2個時鐘周期;或者知道80486總線頻率為66MHz(比80186的33MHz快2倍),都可以估計出函數(shù)在80486上的執(zhí)行時間。

表 9.3μC/OS-II函數(shù)在33MHz80186上的執(zhí)行時間.

表9.3μC/OS-II函數(shù)在33MHz80186上的執(zhí)行時間.(續(xù)表)內(nèi)存管理

表9.3μC/OS-II函數(shù)在33MHz80186上的執(zhí)行時間.(續(xù)表)

下面我們將討論每個函數(shù)的關(guān)閉中斷時間,最大、最小運(yùn)行時間是如何計算的,以及這樣計算的先決條件。

OSSchedUnlock()

最小運(yùn)行時間是當(dāng)變量OSLockNesting減為0,且系統(tǒng)中沒有更高優(yōu)先級的任務(wù)就緒,SSchedUnlock()正常結(jié)束返回調(diào)用者。

最大運(yùn)行時間是也是當(dāng)變量OSLockNesting減為0,但有更高優(yōu)先級的任務(wù)就緒,函數(shù)中需要進(jìn)行任務(wù)切換。

OSIntExit()

最小運(yùn)行時間是當(dāng)變量OSLockNesting減為0,且系統(tǒng)中沒有更高優(yōu)先級的任務(wù)就緒,OSIntExit()正常結(jié)束返回被中斷任務(wù)。

最大運(yùn)行時間是也是當(dāng)變量OSLockNesting減為0,但有更高優(yōu)先級的任務(wù)就緒,OSIntExit()將不返回調(diào)用者,經(jīng)過任務(wù)切換操作后,將直接返回就緒的任務(wù)。

OSTickISR()

此函數(shù)假定在當(dāng)前μC/OS-II中運(yùn)行有最大數(shù)目的任務(wù)(64個)。

最小運(yùn)行時間是當(dāng)64個任務(wù)都不在等待延時狀態(tài)。也就是說,所有的任務(wù)都不需要OSTickISR()處理。

最大運(yùn)行時間是當(dāng)63個任務(wù)(空閑進(jìn)程不會延時等待)都處于延時狀態(tài),此時OSTickISR()需要逐個檢查等待中的任務(wù),將計數(shù)器減1,并判斷是否延時結(jié)束。這種情況對于系統(tǒng)是一個很重的負(fù)荷。例如在最壞的情況,設(shè)時鐘節(jié)拍間隔10ms,OSTickISR()需要625μs,占了約6%的CPU利用率。但請注意,此時所有的任務(wù)都沒有執(zhí)行,只是內(nèi)核的開銷。

OSMboxPend()

最小運(yùn)行時間是當(dāng)郵箱中有消息需要處理的時候。

最大運(yùn)行時間是當(dāng)郵箱中沒有消息,任務(wù)需要等待的時候。此時調(diào)用OSMboxPend()的任務(wù)將被掛起,進(jìn)行任務(wù)切換。最大運(yùn)行時間是同一任務(wù)執(zhí)行OSMboxPend()的累計時間,這個過程包括OSMboxPend()查看郵箱,發(fā)現(xiàn)沒有消息,再調(diào)用任務(wù)切換函數(shù)OSSched(),切換到新任務(wù)。當(dāng)由于某種原因調(diào)用OSMboxPend()的任務(wù)又被喚醒執(zhí)行,從OSSched()中返回,發(fā)現(xiàn)返回的原因是由于延時結(jié)束(處理延時結(jié)束情況的代碼最長—譯者注),最后返回調(diào)用任務(wù)。OSMboxPend()的最大運(yùn)行時間是上述時間的總和。[!--empirenews.page--]

OSMboxPost()

最小運(yùn)行時間是當(dāng)郵箱是空的,沒有任務(wù)等待消息的時候。

最大運(yùn)行時間是當(dāng)消息郵箱中有一個或多個任務(wù)在等待消息。此時,消息將發(fā)往等待隊列中優(yōu)先級最高的任務(wù),將此任務(wù)喚醒執(zhí)行。最大運(yùn)行時間是同一任務(wù)執(zhí)行OSMboxPost()的累計時間,這個過程包括任務(wù)喚醒等待任務(wù),發(fā)送消息,調(diào)用任務(wù)切換函數(shù)OSSched(),切換到新任務(wù)。當(dāng)由于某種原因調(diào)用OSMboxPost()的任務(wù)又被喚醒執(zhí)行,從OSSched()中返回,發(fā)現(xiàn)返回的原因是由于延時結(jié)束(處理延時結(jié)束情況的代碼最長—譯者注),最后返回調(diào)用任務(wù)。OSMboxPost()的最大運(yùn)行時間是上述時間的總和。

OSMemGet()

最小運(yùn)行時間是當(dāng)系統(tǒng)中已經(jīng)沒有內(nèi)存塊,OSMemGet()返回錯誤碼。

最大運(yùn)行時間是OSMemGet()獲得了內(nèi)存塊,返回調(diào)用者。

OSMemPut()

最小運(yùn)行時間是當(dāng)向一個已經(jīng)排滿的內(nèi)存分區(qū)中返回內(nèi)存塊。

最大運(yùn)行時間是當(dāng)向一個未排滿的內(nèi)存分區(qū)中返回內(nèi)存塊

OSQPend()

最小運(yùn)行時間是當(dāng)消息隊列中有消息需要處理的時候。

最大運(yùn)行時間是當(dāng)消息隊列中沒有消息,任務(wù)需要等待的時候。此時調(diào)用OSQPend()的任務(wù)將被掛起,進(jìn)行任務(wù)切換。最大運(yùn)行時間是同一任務(wù)執(zhí)行OSQPend()的累計時間,這個過程包括OSQPend()查看消息隊列,發(fā)現(xiàn)沒有消息,再調(diào)用任務(wù)切換函數(shù)OSSched(),切換到新任務(wù)。當(dāng)由于某種原因調(diào)用OSQPend()的任務(wù)又被喚醒執(zhí)行,從OSSched()中返回,發(fā)現(xiàn)返回的原因是由于延時結(jié)束(處理延時結(jié)束情況的代碼最長—譯者注),最后返回調(diào)用任務(wù)。OSQ`Pend()的最大運(yùn)行時間是上述時間的總和。

OSQPost()

最小運(yùn)行時間是當(dāng)消息隊列是空的,沒有任務(wù)等待消息的時候。

最大運(yùn)行時間是當(dāng)消息隊列中有一個或多個任務(wù)在等待消息。此時,消息將發(fā)往等待隊列中優(yōu)先級最高的任務(wù),將此任務(wù)喚醒執(zhí)行。最大運(yùn)行時間是同一任務(wù)執(zhí)行OSQPost()的累計時間,這個過程包括任務(wù)喚醒等待任務(wù),發(fā)送消息,調(diào)用任務(wù)切換函數(shù)OSSched(),切換到新任務(wù)。當(dāng)由于某種原因調(diào)用OSQPost()的任務(wù)又被喚醒執(zhí)行,從OSSched()中返回,發(fā)現(xiàn)返回的原因是由于延時結(jié)束(處理延時結(jié)束情況的代碼最長—譯者注),最后返回調(diào)用任務(wù)。OSQPost()的最大運(yùn)行時間是上述時間的總和。

OSQPostFront()

此函數(shù)與OSQPost()的過程相同。

OSSemPend()

最小運(yùn)行時間是當(dāng)信號量可獲取的時候(信號量計數(shù)器大于0)。

最大運(yùn)行時間是當(dāng)信號量不可得,任務(wù)需要等待的時候。此時調(diào)用OSSemPend()的任務(wù)將被掛起,進(jìn)行任務(wù)切換。最大運(yùn)行時間是同一任務(wù)執(zhí)行OSQPend()的累計時間,這個過程包括OSSemPend()查看信號量計數(shù)器,發(fā)現(xiàn)是0,再調(diào)用任務(wù)切換函數(shù)OSSched(),切換到新任務(wù)。當(dāng)由于某種原因調(diào)用OSSemPend()的任務(wù)又被喚醒執(zhí)行,從OSSched()中返回,發(fā)現(xiàn)返回的原因是由于延時結(jié)束(處理延時結(jié)束情況的代碼最長—譯者注),最后返回調(diào)用任務(wù)。OSSemPend()的最大運(yùn)行時間是上述時間的總和。

OSSemPost()

最小運(yùn)行時間是當(dāng)沒有任務(wù)在等待信號量的時候。

最大運(yùn)行時間是當(dāng)有一個或多個任務(wù)在等待信號量。此時,等待隊列中優(yōu)先級最高的任務(wù)將被喚醒執(zhí)行。最大運(yùn)行時間是同一任務(wù)執(zhí)行OSSemPost()的累計時間,這個過程包括任務(wù)喚醒等待任務(wù),調(diào)用任務(wù)切換函數(shù)OSSched(),切換到新任務(wù)。當(dāng)由于某種原因調(diào)用OSSemPost()的任務(wù)又被喚醒執(zhí)行,從OSSched()中返回,發(fā)現(xiàn)返回的原因是由于延時結(jié)束(處理延時結(jié)束情況的代碼最長—譯者注),最后返回調(diào)用任務(wù)。OSSemPost()的最大運(yùn)行時間是上述時間的總和。

OSTaskChangePrio()

最小運(yùn)行時間是當(dāng)任務(wù)被改變的優(yōu)先級比當(dāng)前運(yùn)行任務(wù)的低,此時不進(jìn)行任務(wù)切換,直接返回調(diào)用任務(wù)。

最大運(yùn)行時間是當(dāng)任務(wù)被改變的優(yōu)先級比當(dāng)前運(yùn)行任務(wù)的高,此時將進(jìn)行任務(wù)切換。

OSTaskCreate()

最小運(yùn)行時間是當(dāng)調(diào)用OSTaskCreate()的任務(wù)創(chuàng)建了一個比自己優(yōu)先級低的任務(wù), 此時不進(jìn)行任務(wù)切換。

最大運(yùn)行時間是當(dāng)調(diào)用OSTaskCreate()的任務(wù)創(chuàng)建了一個比自己優(yōu)先級高的任務(wù), 此時將進(jìn)行任務(wù)切換。

上述兩種情況都是假定OSTaskCreateHook()不進(jìn)行任何操作。

OSTaskCreateExt()

最小運(yùn)行時間是當(dāng)OSTaskCreateExt()不對堆棧進(jìn)行清零操作(此項操作是為堆棧檢查函數(shù)做準(zhǔn)備的)。

最大運(yùn)行時間是當(dāng)OSTaskCreateExt()需要進(jìn)行堆棧清零操作。但此項操作的時間取決于堆棧的大小。如果設(shè)清除每個堆棧單元(堆棧操作以字為單位—譯者注)需要100個時鐘周期(3μs),1000字節(jié)的堆棧將需要1,500μs(1000字節(jié)除以2再乘以3μs/每字)。在清除堆棧過程中中斷是打開的,可以響應(yīng)中斷請求。

上述兩種情況都是假定OSTaskCreateHook()不進(jìn)行任何操作。

OSTaskDel()

最小運(yùn)行時間是當(dāng)被刪除的任務(wù)不是當(dāng)前任務(wù),此時不進(jìn)行任務(wù)切換。

最大運(yùn)行時間是當(dāng)被刪除的任務(wù)是當(dāng)前任務(wù),此時將進(jìn)行任務(wù)切換。

OSTaskDelReq()

該函數(shù)很短,幾乎沒有最小和最大運(yùn)行時間之分。

OSTaskResume()

最小運(yùn)行時間是當(dāng)OSTaskResume()喚醒了一個任務(wù),但該任務(wù)的優(yōu)先級比當(dāng)前任務(wù)低,此時不進(jìn)行任務(wù)切換。

最大運(yùn)行時間是OSTaskResume()喚醒了一個優(yōu)先級更高的任務(wù),此時將進(jìn)行任務(wù)切換。

OSTaskStkChk()

OSTaskStkChk()的執(zhí)行過程是從堆棧的底端開始檢查0的個數(shù),估計堆棧所剩的空間。

所以最小運(yùn)行時間是當(dāng)OSTaskStkChk()檢查一個全部占滿的堆棧。 但實(shí)際上這種情況是不允許發(fā)生的,這將使系統(tǒng)崩潰。

最大運(yùn)行時間是當(dāng)OSTaskStkChk()檢查一個全空堆棧,執(zhí)行時間取決于堆棧的大小。例如檢查每個堆棧單元(堆棧操作以字為單位—譯者注)需要80鐘周期(2.4μs),1000字節(jié)的堆棧將需要1,200μs(1000字節(jié)除以2再乘以2.4μs/每字)。再加上其他的一些操作,總共需要大約1,218μs。在檢查堆棧過程中中斷是打開的,可以響中斷請求。

OSTaskSuspend()

最小運(yùn)行時間是當(dāng)被掛起的任務(wù)不是當(dāng)前任務(wù),此時不進(jìn)行任務(wù)切換。[!--empirenews.page--]

最大運(yùn)行時間是當(dāng)前任務(wù)掛起自己,此時將進(jìn)行任務(wù)切換。

OSTaskQuery()

該函數(shù)的運(yùn)行時間總是一樣的。OSTaskQuery()執(zhí)行的操作是獲取任務(wù)的任務(wù)控制塊OS_TCB。如果OS_TCB中包含所有的操作項,需要占用45字節(jié)(大模式編譯)。

OSTimeDly()

如果延時時間不為0,則OSTimeDly()運(yùn)行時間總是相同的。此時將進(jìn)行任務(wù)切換。

如果延時時間為0,OSTimeDly()不清除OSRdyGrp中的任務(wù)就緒位,不進(jìn)行延時操作,直接返回。

OSTimeDlyHMSM()

如果延時時間不為0,則OSTimeDlyHMSM()運(yùn)行時間總是相同的。此時將進(jìn)行任務(wù)切換。

此外,OSTimeDlyHMSM()延時時間最好不要超過65,536個時鐘節(jié)拍。也就是說,如果時鐘節(jié)拍發(fā)生的間隔為10ms(頻率100Hz),延時時間應(yīng)該限定在10分55秒350毫秒內(nèi)。如果超過了上述數(shù)值,該任務(wù)就不能用OSTimeDlyResume()函數(shù)喚醒。

OSTimeDlyResume()

最小運(yùn)行時間是當(dāng)被喚醒的任務(wù)優(yōu)先級低于當(dāng)前任務(wù),此時不進(jìn)行任務(wù)切換。

最大運(yùn)行時間是當(dāng)喚醒了一個優(yōu)先級更高的任務(wù),此時將進(jìn)行任務(wù)切換。

OSTimeTick()

前面我們討論的OSTickISR()函數(shù)其實(shí)就是OSTimeTick()與OSIntEnter()、 OSIntExit()

的組合。OSTickISR()的時間占用情況就是OSTimeTick()的占用情況。以下討論假定系統(tǒng)中有μC/OS-II允許的最大數(shù)量的任務(wù)(64個)。

最小運(yùn)行時間是當(dāng)64個任務(wù)都不在等待延時狀態(tài)。也就是說,所有的任務(wù)都不需要OSTimeTick()處理。

最大運(yùn)行時間是當(dāng)63個任務(wù) (空閑進(jìn)程不會延時等待) 都處于延時狀態(tài), 此時OSTimeTick()

需要逐個檢查等待中的任務(wù),將計數(shù)器減1,并判斷是否延時結(jié)束。例如在最壞的情況,設(shè)時鐘節(jié)拍間隔10ms,OSTimeTick()需要約600μs,占了6%的CPU利用率

表 9.4 各函數(shù)的執(zhí)行時間(按關(guān)閉中斷時間排序).

表9.5 各函數(shù)的執(zhí)行時間(按最大運(yùn)行時間排序).

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

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

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

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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