《uCOOS-II原理與ARM應(yīng)用程序設(shè)計》課件第4章_第1頁
《uCOOS-II原理與ARM應(yīng)用程序設(shè)計》課件第4章_第2頁
《uCOOS-II原理與ARM應(yīng)用程序設(shè)計》課件第4章_第3頁
《uCOOS-II原理與ARM應(yīng)用程序設(shè)計》課件第4章_第4頁
《uCOOS-II原理與ARM應(yīng)用程序設(shè)計》課件第4章_第5頁
已閱讀5頁,還剩129頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

第四章μC/OS-Ⅱ內(nèi)核與面向任務(wù)程序設(shè)計

4.1μC/OS-Ⅱ內(nèi)核OS_CORE.C

4.2任務(wù)OS_TASK.C

4.3時間OS_TIME.C

4.4本章小結(jié)

μC/OS-Ⅱ是多任務(wù)的實時操作系統(tǒng),它的工作原理很簡單,這里通過一個實例來說明。例如,一家公司有255個員工,所有這些員工按職位分為255級(即不存在職位相同的員工),公司里只有一個電話總機,每個員工都有一個分機,每個員工打外線電話,都需要通過同一個總機接外線。4.1μC/OS-Ⅱ內(nèi)核OS_CORE.C假定每個員工每次打電話的時間絕不會超過1分鐘??倷C處有一個接線員管理。公司總經(jīng)理的職位最高,他打電話的優(yōu)先級為0;第一副經(jīng)理的職位第二,他打電話的優(yōu)先級為1;依次類推,職位最低的員工打電話的優(yōu)先級為254。因此,公司運作過程中總機電話業(yè)務(wù)有以下情況(假設(shè)接線員8點0分上班后,每1分鐘視察一下總機,這里的1分鐘即時鐘節(jié)拍):

(1)接線員上班后,打開總機,檢查撥號的電話。如果此時所有員工同時撥號,當(dāng)然,接線員會使總經(jīng)理得到總機使用權(quán),其他員工進入隊列。等總經(jīng)理打完后,再按優(yōu)先級調(diào)度,依次接通。

(2)如果在某個時刻總機空閑,此時,任一員工撥號,等到接線員視察總機時進行調(diào)度,把總機分配給他。

(3)如果在某個時刻總機空閑,此時,有多個員工同時撥號,等到接線員視察總機時,進行調(diào)度,把總機分配給最高職位的員工,其他員工進入等待隊列,等高職位的員工打完后,再依次接通。

(4)如果某個時刻總機正在被某個員工使用,此時,比他職位高的人正在撥號,則等到接線員視察總機時,中斷當(dāng)前的通話,使職位高的人先接通,等職位高的人打完電話后,再恢復(fù)剛才被中斷的員工的通話。如果此時又有多個人撥號,此時,接線員將進行調(diào)度,讓被中斷的員工和所有新?lián)芴柕膯T工中職位最高的人先使用總機,其他員工進入等待隊列。從這一步可以看出,如果某個職位低的員工正在使用總機,此時,即使總經(jīng)理撥號,也必須等到時鐘節(jié)拍來到后(即接線員視察或系統(tǒng)調(diào)度時)才能使總經(jīng)理得到總機,最長等待時間為1分鐘。但是如果該職位低的員工使用完總機后,還沒有到接線員視察總機的時刻,那么總機會自動調(diào)度給新?lián)芴柕母呗毼粏T工。即空閑總機不會調(diào)度,但是每個員工打完電話后都會自動調(diào)度一次。

(5)如果某個時刻總機正在被使用,更高職位的員工緊急撥號,此時,可通過“中斷”通知接線員立即進行調(diào)度,使高職位的員工使用總機,中斷原來的通話,使職位低的員工進入中斷狀態(tài)。

從接線員的角度出發(fā)來看待這一問題,其運作過程是這樣的:接線員每隔1分鐘視察一下總機,如果總機空閑,則什么都不做;如果此時只有一人在撥號中,則幫他接通;如果此時有多人在撥號中,則幫職位最高的人接通,把其他人放入等待隊列中,等職位高的人打完電話后,依次按職位高低自動接通;如果接線員在間隔中受到“中斷”指示,則立即進行臨時調(diào)度,如果當(dāng)前有員工在使用總機,則中斷他,使正在撥號的優(yōu)先級最高的人接通電話,其他人進入隊列。每個員工打完電話后,都會自動調(diào)度一次,這次調(diào)度不需要接線員干預(yù)。顯然,接線員相當(dāng)于總機切換的時鐘節(jié)拍,如果沒有接線員,總機無法切換,只能保持空閑狀態(tài),整個公司的電話業(yè)務(wù)就會停止。如果把上述的員工視為任務(wù),接線員視為時鐘節(jié)拍,則μC/OS-Ⅱ的基本工作原理也是如此。在μC/OS-Ⅱ中,創(chuàng)建好若干任務(wù)后,一般地,系統(tǒng)按任務(wù)的優(yōu)先級依次執(zhí)行一次后,各任務(wù)均進入延時等待狀態(tài),時鐘節(jié)拍相當(dāng)于心臟的脈搏,在每個時鐘節(jié)拍處,將檢查處于就緒態(tài)的任務(wù),進行任務(wù)(切換)調(diào)度,使就緒態(tài)任務(wù)中最高優(yōu)先級的任務(wù)得到CPU使用權(quán),其他任務(wù)進入等待隊列。每個任務(wù)單次運行CPU占用的時間不能大于一個時鐘節(jié)拍。除了系統(tǒng)創(chuàng)建的空閑任務(wù)外,用戶創(chuàng)建的任務(wù)運行完后,系統(tǒng)將進行一次任務(wù)調(diào)度。時鐘節(jié)拍用于更新各個任務(wù)的延時。上述這種情況是各個任務(wù)都是獨立運行、互不通信的情況,μC/OS-Ⅱ允許多個任務(wù)間進行數(shù)據(jù)通信,此時,系統(tǒng)運行稍微復(fù)雜一點,即在任務(wù)調(diào)度時,會檢查用于任務(wù)間通信的信號量和郵箱。

所謂的任務(wù),本質(zhì)上是帶有一個void*指針參數(shù)、無返回值的死循環(huán)函數(shù)。在μC/OS-ⅡV2.86版本中,用戶最多可以創(chuàng)建252個任務(wù)(μC/OS-ⅡV2.86最多支持255個任務(wù),其中,系統(tǒng)創(chuàng)建的3個任務(wù)是空閑任務(wù)、統(tǒng)計任務(wù)和定時器任務(wù))。μC/OS-Ⅱ應(yīng)用程序就是由一個個的任務(wù)組成的,任務(wù)中再調(diào)用函數(shù)完成特定的功能。每個任務(wù)具有不同的優(yōu)先級,當(dāng)一個任務(wù)執(zhí)行完成后,把CPU的使用權(quán)交給μC/OS-Ⅱ內(nèi)核,進行任務(wù)調(diào)度,使處于就緒態(tài)的最高優(yōu)先級任務(wù)得到執(zhí)行權(quán)。當(dāng)μC/OS-Ⅱ內(nèi)核進行任務(wù)調(diào)度后,去執(zhí)行就緒態(tài)的具有最高優(yōu)先級的任務(wù)的過程,不是通常意義下的函數(shù)調(diào)用(可以說,任務(wù)在“創(chuàng)建”時就被調(diào)用了),而是μC/OS-Ⅱ意義下的函數(shù)“返回”(或稱任務(wù)調(diào)用),即返回到那個死循環(huán)函數(shù)(任務(wù))中執(zhí)行。通常意義下的函數(shù)調(diào)用是指:①保存當(dāng)前調(diào)用函數(shù)(程序)的環(huán)境,程序指針跳轉(zhuǎn)到被調(diào)用函數(shù)入口處,執(zhí)行完被調(diào)用函數(shù)后,從堆棧中恢復(fù)調(diào)用函數(shù)的環(huán)境,繼續(xù)執(zhí)行原程序;②調(diào)用函數(shù)和被調(diào)用函數(shù)共用相同的堆棧,實際上被調(diào)用函數(shù)沒有堆棧;③被調(diào)用函數(shù)的調(diào)用執(zhí)行是由調(diào)用函數(shù)發(fā)出的;④被調(diào)用函數(shù)被調(diào)用后立即執(zhí)行。而μC/OS-Ⅱ意義下的任務(wù)調(diào)用是指:①每個任務(wù)都有獨立的堆??臻g,μC/OS-Ⅱ下的任務(wù)調(diào)用是先把當(dāng)前任務(wù)的執(zhí)行環(huán)境保存在它自己的堆棧中,然后從被調(diào)用任務(wù)的堆棧恢復(fù)被調(diào)用任務(wù)的環(huán)境,這兩個任務(wù)占用不同的堆??臻g;②被調(diào)用任務(wù)的入口地址來自其堆棧,而不是函數(shù)標(biāo)號;③被調(diào)用函數(shù)的調(diào)用執(zhí)行是由μC/OS-Ⅱ調(diào)度器發(fā)出的,即由μC/OS-Ⅱ內(nèi)核調(diào)用的,而不是某個任務(wù);④被調(diào)用任務(wù)進入就緒態(tài),即可以執(zhí)行,但有可能不會立即執(zhí)行。所以,一定意義上,任務(wù)的調(diào)用可以理解為返回到那個函數(shù)去執(zhí)行,而不是調(diào)用那個函數(shù)來執(zhí)行。任務(wù)永遠(yuǎn)不會返回,當(dāng)前任務(wù)完成特定的功能后,釋放CPU占用權(quán),進入等待態(tài),等待下一個“該函數(shù)返回”。CPU空閑時,μC/OS-Ⅱ執(zhí)行系統(tǒng)定義的優(yōu)先級最低的空閑任務(wù),這時,每個時鐘節(jié)拍到達(dá)時進行任務(wù)就緒態(tài)檢查和調(diào)度管理。

μC/OS-Ⅱ下的任務(wù)可以有5種狀態(tài),即就緒態(tài)、執(zhí)行態(tài)、被中斷態(tài)、等待態(tài)和休眠態(tài)。就緒態(tài)表示該任務(wù)已經(jīng)準(zhǔn)備好,隨時可以執(zhí)行,但由于其優(yōu)先級比正在運行的任務(wù)優(yōu)先級低,暫時得不到CPU執(zhí)行權(quán),而在就緒列表中排隊;執(zhí)行態(tài)表示該任務(wù)獲得了CPU執(zhí)行權(quán),正在運行;被中斷態(tài)表示該任務(wù)被中斷服務(wù)程序中斷了;等待態(tài)表示該任務(wù)在等待運行(一般地,等待某一事件的發(fā)生或超時信號,處于等待態(tài)的任務(wù)位于等待列表隊列中,與就緒態(tài)任務(wù)的區(qū)別在于,即使獲得了CPU使用權(quán),等待態(tài)任務(wù)也不能得到執(zhí)行);休眠態(tài)專指一個任務(wù)被刪除后的狀態(tài)(任務(wù)不能被從內(nèi)存中刪除,只是從任務(wù)列表中刪除,仍然位于內(nèi)存中,這種任務(wù)狀態(tài)稱為休眠態(tài)),可以調(diào)用創(chuàng)建任務(wù)函數(shù),使任務(wù)轉(zhuǎn)到就緒態(tài)。任務(wù)中可以調(diào)用各種函數(shù),被任務(wù)調(diào)用的函數(shù)是通常意義上的函數(shù),但是,在μC/OS-Ⅱ中,要求這些被調(diào)用函數(shù)為可重入函數(shù)。所謂的可重入函數(shù),是指只使用局部變量而不使用全局變量的函數(shù),這樣被多個任務(wù)調(diào)用時,每個任務(wù)下可重入函數(shù)都有自己獨立的數(shù)據(jù),不會造成數(shù)據(jù)變量使用的混亂。如果一個函數(shù)使用了全局變量,而這個全局變量被某個任務(wù)修改后,后繼調(diào)用這個函數(shù)的任務(wù)都將使用被修改了的全局變量,從而得到意想不到的結(jié)果。所以,在μC/OS-Ⅱ下編寫的函數(shù)都應(yīng)該是可重入函數(shù),即不使用全局變量、只使用局部變量的函數(shù)。4.1.1任務(wù)控制塊

每個任務(wù)都與一個任務(wù)控制塊結(jié)構(gòu)體變量相關(guān)聯(lián),用于保存該任務(wù)的工作狀態(tài)。任務(wù)控制塊結(jié)構(gòu)體類型在ucos_ii.h中定義,代碼如下:上述代碼中預(yù)編譯常量均在os_cfg.h中定義(實際上,所有的預(yù)編譯常量均在os_cfg.h中定義,通過修改這些常量的值為0,可以大幅度地裁剪μC/OS-Ⅱ代碼長度)。第2行OSTCBStkPtr表示指向當(dāng)前任務(wù)的堆棧指針,每個任務(wù)都具有獨立的堆??臻g,且空間大小隨意指定。第4~10行表示如果使用OSTaskCreateExt函數(shù)創(chuàng)建任務(wù),其任務(wù)控制塊中需要有第5~9行的成員,即OSTCBExtPtr指向用戶定義的數(shù)據(jù)結(jié)構(gòu),用于擴展任務(wù)控制塊數(shù)據(jù);OSTCBStkBottom是指向該任務(wù)堆棧棧底的指針;OSTCBStkSize為堆棧大小;

OSTCBOpt為選項,一般只能取四個值或其組合,即OS_TASK_OPT_NONE(在ucos_ii.h中宏定義為0)、OS_TASK_OPT_STK_CHK(值為1)、OS_TASK_OPT_STK_

CLR(值為2)或OS_TASK_OPT_SAVE_FP(值為4),其中,OS_TASK_OPT_STK_CHK表示使能堆棧檢查,OS_

TASK_OPT_STK_CLR表示創(chuàng)建堆棧時將堆棧空間清為0,這二者常組合在一起使用;OSTCBId表示任務(wù)ID號,取值為0~65535,用戶可以使用這個ID號作為數(shù)組索引,在μC/OS-Ⅱ內(nèi)核中并沒有意義。第12~13行為任務(wù)控制塊結(jié)構(gòu)體指針,OSTCBNext指向下一個任務(wù)控制塊,OSTCBPrev指向前一個任務(wù)控制塊,這兩個指針把所有任務(wù)的任務(wù)控制塊連成一個雙向鏈表,每個任務(wù)在創(chuàng)建時,將其任務(wù)控制塊插入鏈表中。第16行OSTCBEventPtr為指向事件控制塊的指針;第20行為指向多事件控制塊的指針,多事件請求處理機制是μC/OS-ⅡV2.86新添加的特性。第24行表示指向傳遞給任務(wù)的消息或消息隊列的指針。第29行為指向事件標(biāo)志節(jié)點的指針;第31行為事件標(biāo)志就緒狀態(tài)變量。第34行OSTCBDly表示任務(wù)的延時節(jié)拍數(shù);第35行OSTCBStat保存任務(wù)狀態(tài);第36行OSTCBStatPend保存任務(wù)掛起狀態(tài);第37行OSTCBPrio為任務(wù)優(yōu)先級,0級最高。第39~47行與任務(wù)就緒表有關(guān),OSTCBX是優(yōu)先級分組的位索引,OSTCBY是優(yōu)先級就緒表的索引,OSTCBBitX是訪問就緒表位的位屏蔽,OSTCBBitY是訪問優(yōu)先級分組位的位屏蔽。第41行判斷OS_LOWEST_PRIO是否小于等于63,如果是的話,OSTCBBitX和OSTCBBitY均為8位無符號整型變量,否則為16位無符號整型變量。

第50行表示任務(wù)是否需要刪除自身。第53~59行為5個任務(wù)調(diào)度變量,分別記錄任務(wù)切換次數(shù)、任務(wù)執(zhí)行總時鐘周期、任務(wù)循環(huán)計數(shù)、任務(wù)堆棧首地址和堆棧使用量等調(diào)試信息。第62行為任務(wù)名稱,主要用作調(diào)試信息。在第4.2節(jié)的實例中,可以單步執(zhí)行查看每個任務(wù)的任務(wù)控制塊內(nèi)容,在調(diào)試模式下,通過菜單“View|Locals”查看。4.1.2事件控制塊

事件控制塊結(jié)構(gòu)體用于記錄任務(wù)間通信的信息,該結(jié)構(gòu)體類型將在第五章中使用,其在ucos_ii.h中的定義如下:上述代碼第3行OSEventType表示事件類型,在ucos_ii.h中定義的事件類型有7種,即

#defineOS_EVENT_TYPE_UNUSED 0u

#defineOS_EVENT_TYPE_MBOX 1u

#defineOS_EVENT_TYPE_Q 2u

#defineOS_EVENT_TYPE_SEM 3u

#defineOS_EVENT_TYPE_MUTEX 4u

#defineOS_EVENT_TYPE_FLAG 5u

#defineOS_TMR_TYPE

100u分別表示空類型、郵箱、隊列、信號量、互斥量、標(biāo)志和定時器類型。第4行OSEventPtr為指向郵箱或隊列的指針。第5行OSEventCnt為信號量計數(shù)值,如果為其他事件類型則該成員不使用。第6~12行的OSEventGrp和OSEventTble分別為等待事件的任務(wù)分組和任務(wù)列表。第15行為事件名,一般用作調(diào)試。

在第5章實例ex5_1中可以查看事件控制塊變量的內(nèi)容。

在ucos_ii.h中還定義了事件標(biāo)志控制塊、消息郵箱數(shù)據(jù)結(jié)構(gòu)、內(nèi)存控制塊、互斥量數(shù)據(jù)結(jié)構(gòu)、消息列隊數(shù)據(jù)結(jié)構(gòu)、信號量數(shù)據(jù)結(jié)構(gòu)、定時器數(shù)據(jù)結(jié)構(gòu)等數(shù)據(jù)類型,在使用這些數(shù)據(jù)類型時再作介紹,這里不詳細(xì)說明了,讀者可直接參考ucos_ii.h文件。任務(wù)堆棧數(shù)據(jù)結(jié)構(gòu)定義如下:

1#ifOS_TASK_CREATE_EXT_EN>0

2typedefstructos_stk_data{

3INT32UOSFree; /*Numberoffreebytesonthestack*/

4INT32UOSUsed;/*Numberofbytesusedonthestack*/

5}OS_STK_DATA;

6#endif

上述代碼第3行OSFree表示堆棧中沒有使用的字節(jié)數(shù);第4行表示堆棧中已使用的字節(jié)數(shù)。4.1.3就緒表

μC/OS-Ⅱ是多任務(wù)操作系統(tǒng),系統(tǒng)的核心功能是任務(wù)調(diào)度,即當(dāng)一個任務(wù)發(fā)生切換時,μC/OS-Ⅱ需要在眾多已經(jīng)就緒的任務(wù)中找到優(yōu)先級最高的任務(wù)。為了實現(xiàn)快速查找,J.J.Labrosse使用查表的方法。他設(shè)計了一個表,表中的元素為位,可以根據(jù)任務(wù)的優(yōu)先級將就緒任務(wù)填入表格的某位,同樣,由表格中的一些位可直接算得任務(wù)的優(yōu)先級。這樣,任務(wù)一旦就緒,其表格中相應(yīng)的某位就按既定算法置位;任務(wù)調(diào)度時,根據(jù)表格中所有已置位的位,直接算出優(yōu)先級最高的就緒任務(wù)。設(shè)ptcb為某任務(wù)的任務(wù)控制塊變量,則使用以下語句將就緒任務(wù)填入就緒表中,這里的OSRdyGrp和OSRdyTbl即為Labrosse設(shè)計的表。

OSRdyGrp|=ptcb->OSTCBBitY; /*No,Makeready*/

OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;

而OSTCBBitY和OSTCBBitX由以下語句算出(位于os_core.c中第1960行):

結(jié)合圖4-1來理解任務(wù)就緒表的賦值操作。圖4-1中HPT表示最高優(yōu)先級任務(wù),LPT表示最低優(yōu)先級任務(wù),HPT(0)表示0為最高優(yōu)先級任務(wù)。OSRdyGrp和OSRdyTbl的大小依賴于OS_LOWEST_PRIO(位于os_cfg.h中,在ex4_1實例中修改為254)。圖4-1任務(wù)就緒表根據(jù)上述代碼和圖4-1,如果任務(wù)的優(yōu)先級為190(大于63),則OSTCBY=11,OSTCBX=14,OSTCBBitY=0x0800,OSTCBBitX=0x4000;如果該任務(wù)就緒了,且OSRdyGrp和OSRdyTbl都為0,則OSRdyGrp=0x0800,OSRdyTbl[11]為0x4000。在這個基礎(chǔ)上,另一個優(yōu)先級為28的任務(wù)就緒了,則它的OSTCBY=1,OSTCBX=12,OSTCBBitY=0x0002,OSTCBBitX=0x1000。此時任務(wù)就緒表的OSRdyGrp=0x0802,OSRdyTbl[1]=0x1000,OSRdyTbl[11]=0x4000。然后,優(yōu)先級為0x31的任務(wù)也就緒了,且上述兩個任務(wù)還沒有進入執(zhí)行狀態(tài),可以算得OSRdyGrp=0x0802,OSRdyTbl[1]=0x9000,OSRdyTbl[11]=0x4000,如圖4-2所示。圖4-2表格中的空格均為0。圖4-2三個任務(wù)的就緒表從圖4-2及上述推算過程知,有幾個任務(wù)就緒,OSRdyTbl中就有多少個1;如果OSRdyTbl中的某一行至少有一個1,則OSRdyGrp中對應(yīng)的位就為1。

由上述過程,可以依據(jù)一個任務(wù)的優(yōu)先級得到它在就緒表中的位置。相反地,可以由就緒表的位,直接算得就緒表中的最高優(yōu)先級的任務(wù)的優(yōu)先級號。

假設(shè)就緒表如圖4-2所示(這里只有三個就緒任務(wù),讀者可以自己演算更復(fù)雜的),在os_core.c中定義常量數(shù)組OSUnMapTbl如下:1INT8UconstOSUnMapTbl[256]={

20,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x00to0x0F*/

34,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x10to0x1F*/

45,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x20to0x2F*/

54,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x30to0x3F*/

66,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x40to0x4F*/

74,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x50to0x5F*/

85,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x60to0x6F*/

94,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x70to0x7F*/107,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x80to0x8F*/

114,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x90to0x9F*/

125,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xA0to0xAF*/

134,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xB0to0xBF*/

146,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xC0to0xCF*/

154,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xD0to0xDF*/

165,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xE0to0xEF*/

174,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 /*0xF0to0xFF*/

18};

由圖4-2推算就緒任務(wù)的最高優(yōu)先級號的算法(來自os_core.c)為按上述代碼和圖4-2易推算得,y=1,*ptbl=0x9000,就緒任務(wù)中的最高優(yōu)先級為OSPrioHighRdy=16+4+8=28。μC/OS-Ⅱ最多支持255個優(yōu)先級,使用查表方法速度很快,占用RAM空間也不多,而最新的μC/OS-Ⅲ支持無限多個優(yōu)先級,顯然不能用查表方法,作者可能使用了一種基于鏈表的新算法。4.1.4空閑任務(wù)和統(tǒng)計任務(wù)

在os_cfg.h中定義了OS_LOWEST_PRIO后,系統(tǒng)將自動把該優(yōu)先級分配給空閑任務(wù)(即沒有用戶任務(wù)執(zhí)行的狀態(tài)下運行的任務(wù),位于os_core.c中),其代碼如下:上述代碼是一個簡單的死循環(huán)體,空閑記數(shù)器OSIdleCtr不斷累加(第12行)。用戶可以添加自定義代碼(添加到OSTaskIdleHook鉤子函數(shù)中),使進入空閑狀態(tài)的任務(wù)點亮LED燈提示或使CPU工作于省電模式。

統(tǒng)計任務(wù)(位于os_core.c中)的代碼如下:由上述代碼可知,統(tǒng)計任務(wù)開始工作(即進入死循環(huán))后,要計算CPU的使用率OSCPUUsage(第24行),然后進行任務(wù)堆棧的檢查,最后,延時0.1秒進入任務(wù)切換。用戶可以通過鉤子函數(shù)OSTaskStatHook插入自定義的代碼。統(tǒng)計任務(wù)需要一個初始化函數(shù)OSStatInit,這個函數(shù)需在調(diào)用OSInit后和進入多任務(wù)前調(diào)用。

μC/OS-Ⅱ代碼中有大量的預(yù)編譯指令代碼,這些預(yù)編譯常量一般在os_cfg.h中定義,用于對μC/OS-Ⅱ內(nèi)核代碼進行裁剪。對于UP-Star實驗板來說,由于其具有32MB的RAM空間,無需進行裁剪。對于RAM空間較小的單片機系統(tǒng)板,則往往需要裁剪。4.1.5時鐘節(jié)拍

μC/OS-Ⅱ系統(tǒng)借助一個時鐘節(jié)拍實現(xiàn)任務(wù)的延時管理和超時調(diào)度,時鐘節(jié)拍由定時器中斷服務(wù)程序調(diào)用,必須在調(diào)用了OSStart后才能開啟時鐘節(jié)拍!

在后面的實例ex4_1中,使用S3C2410A的內(nèi)部定時器4實現(xiàn)時鐘節(jié)拍功能,即在其中斷服務(wù)函數(shù)中調(diào)用OSTimeTick,完整代碼可參閱bsp.c文件,這里不再列出。時鐘節(jié)拍的任務(wù)調(diào)度均由匯編語言編寫,位于os_cpu_a.asm(或os_cpu_a.s)中,這里也不再羅列。

為了使時鐘節(jié)拍的頻率為OS_TICKS_PER_SEC(位于os_cfg.h中第51行)所宏定義的值,必須設(shè)置定時器4的中斷信號頻率為OS_TICKS_PER_SEC,一般為100Hz。

由上述內(nèi)容可知,本小節(jié)內(nèi)容與μC/OS-Ⅱ的移植關(guān)系密切。4.1.6μC/OS-Ⅱ初始化

將第4.2節(jié)的實例ex4_1的主程序代碼羅列如下:由上述代碼可知,μC/OS-Ⅱ系統(tǒng)的啟動分為三步,即調(diào)用OSInit初始化系統(tǒng)、創(chuàng)建任務(wù)(這里使用了OSTaskCreateExt)、調(diào)用OSStart啟動多任務(wù)。一般地,在第一個創(chuàng)建的任務(wù)代碼中添加創(chuàng)建其他任務(wù)的代碼,這個在主函數(shù)(main)中創(chuàng)建的任務(wù)稱為“引導(dǎo)”任務(wù),而在其中創(chuàng)建的任務(wù)稱為“工作”任務(wù)。引導(dǎo)任務(wù)只用來創(chuàng)建一些工作任務(wù),本身沒有太大意義;工作任務(wù)是用來完成特定功能的任務(wù)。

進入到os_core.c中可查得OSInit的代碼如下:上述代碼完成了μC/OS-Ⅱ系統(tǒng)的初始化,包括初始化全局變量、任務(wù)控制塊、事件控制塊、內(nèi)存控制塊、創(chuàng)建統(tǒng)計任務(wù)、初始化堆棧、定時器管理任務(wù)等內(nèi)容,讀者可通過單步調(diào)試工程ex4_1獲得更多的信息。其實,Labrosse先生完全可以把OSStatInit函數(shù)通過預(yù)編譯指令加入到OSInit中(第29行以后)。

在os_core.c中可查得OSStart函數(shù)的代碼如下:第3行OSRunning為真(OS_TRUE)時表示多任務(wù)工作開始了;如果為OS_FALSE,則表示系統(tǒng)還沒有工作,此時第4~8行查找最高優(yōu)先級的任務(wù),然后開始多任務(wù)調(diào)度工作。在OSStartHighRdy中將OSRunning置為OS_TURE(即1)。

μC/OS-Ⅱ意義下的任務(wù)是一個無限循環(huán)的函數(shù),即任務(wù)被創(chuàng)建時即相當(dāng)于被“執(zhí)行”了,這里的“執(zhí)行”只是完成了任務(wù)的入棧操作,并沒有執(zhí)行任務(wù)代碼;當(dāng)任務(wù)被“調(diào)用”時,即程序切換到該任務(wù)執(zhí)行時,相當(dāng)于程序返回到該任務(wù)去執(zhí)行,即有一個出棧過程。任務(wù)中可以加入一些初始化代碼,但其主體代碼位于while(1)或for(;;)死循環(huán)中。如果只想讓任務(wù)執(zhí)行一次就退出,則需在死循環(huán)中加入OSTaskDel(OS_PRIO_SELF)語句。

本節(jié)將介紹第一章表1-4中一些函數(shù)的使用方法。首先建立一個工程ex4_1。4.2任務(wù)OS_TASK.C4.2.1工程ex4_1

在工程ex3_2的基礎(chǔ)上新建工程ex4_1,保存目錄為D:\ZYUCOSII\ex4_1,此時的工程ex4_1與ex3_2完全相同,只是工程文件名更改為ex4_1,如圖4-3所示。圖4-3工程ex4_1的最初版本在圖4-3的基礎(chǔ)上,需要修改的文件有app.c、appfun.c、app.h、includes.h、bsp.c、os_cfg.h等。其中,os_cfg.h中僅是把其第39行中的OS_LOWEST_PRIO宏定義值由63更改為254;includes.h文件中添加了一行“#include"math.h"”,用于計算數(shù)學(xué)函數(shù);bsp.c文件中把函數(shù)myInitUART0中的串口0初始化波特率值設(shè)為115200bps,這個函數(shù)的代碼如下:

1//InitializeUART0

2voidmyInitUART0(void)

3{

4//SetGPH3:2asRXD0:TXD0

5GPHCON=0xA0;6

7//UBRDIV0=0x270,SetUART0Baudrate:4800bps,8-bit,1-bitstop

8//UBRDIV0=0x19,SetUART0Baudrate:115200bps,8-bit,1-bitstop

9UFCON0=0x0;

10UMCON0=0x0;

11ULCON0=0x03;

12UCON0=0x05;

13UBRDIV0=0x19;

14}

app.h文件中添加了一些任務(wù)(或函數(shù))的原型定義,完整的代碼如下:

1/*FileName:app.h

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#ifdefMY_APP_GLOBALS

8#defineMY_APP

9#else

10#defineMY_APPextern

11#endif上述代碼中,第42~45行宏定義了4個任務(wù)的優(yōu)先級,為5~8;第47行宏定義了AppTaskStart任務(wù)的堆棧大??;第48行宏定義了其他任務(wù)的堆棧大小。第34行N_TASKS表示用戶創(chuàng)建任務(wù)的個數(shù),其ID號在第36~39行宏定義,堆棧在第50~54行定義,任務(wù)的原型在第65、67~69行聲明。

app.c和appfun.c的內(nèi)容將在第4.2.2和第4.2.3節(jié)介紹。

工程ex4_1運行時,可以看到三個LED燈每隔1秒閃爍,同時,串口調(diào)試助手中顯示一些信息,如圖4-4和圖4-5所示。圖4-4工程ex4_1運行情況圖4-5串口調(diào)試助手顯示情況比較圖4-4和圖4-5可以看出,使用C-SPY顯示的系統(tǒng)堆棧信息和串口調(diào)試助手顯示的信息完全一致,工程ex4_1中共有7個任務(wù),4個用戶創(chuàng)建任務(wù),CPU利用率約1%,每秒任務(wù)切換約為42(圖4-5由于運行時間太短,顯示的切換統(tǒng)計數(shù)33不太準(zhǔn)確,請讀者自己分析原因,并根據(jù)圖4-4推算準(zhǔn)確任務(wù)切換數(shù))。圖4-5中還顯示了當(dāng)前μC/OS-Ⅱ的版本號為V2.86,并且顯示了30度角的正弦值。4.2.2主程序app.c

app.c的完整代碼如下:

1/*FileName:app.c

2**Byzhnyong@21

3**@2009-4-4

4**MainRoutine

5**CopyrightReserved

6*/

7

8#include"includes.h"

9

10voidmain(void)

11{基于μC/OS-Ⅱ的主函數(shù)main,首先調(diào)用OSInit進行系統(tǒng)初始化(第12行),然后調(diào)用OSTaskCreateExt創(chuàng)建一個用戶任務(wù)AppTaskStart(第13行),之后調(diào)用OSStart函數(shù)開始多任務(wù)調(diào)度(第22行)。

μC/OS-Ⅱ中有兩個創(chuàng)建任務(wù)的函數(shù),即OSTaskCreate和OSTaskCreateExt,這兩個函數(shù)的原型位于ucos_ii.h中,如下所示:

OSTaskCreate有4個參數(shù),依次傳遞任務(wù)名(函數(shù)名)、任務(wù)參數(shù)、任務(wù)堆棧頂?shù)刂?、任?wù)優(yōu)先級(第2~5行)。OSTaskCreateExt是OSTaskCreate的擴展,有9個參數(shù),依次傳遞任務(wù)名、任務(wù)參數(shù)、任務(wù)堆棧頂?shù)刂?、任?wù)優(yōu)先級、任務(wù)ID號、任務(wù)堆棧底地址、任務(wù)堆棧大小、用戶自定義數(shù)據(jù)地址、指定是否允許堆棧檢查和清零等的選項值。

Labrosse建議用戶使用OSTaskCreateExt創(chuàng)建任務(wù),這類任務(wù)可以用C-SPY調(diào)試器查看堆棧使用情況。例如,在文件app.c中的第13行,創(chuàng)建的任務(wù)名為AppTaskStart;任務(wù)參數(shù)為空;棧頂?shù)刂窞锳ppTaskStartStk[TASK_START_STK_SIZE-1];優(yōu)先級為AppTaskStartPrio;ID號為AppTaskStartID;棧底地址為AppTaskStartStk[0];棧大小為TASK_START_STK_SIZE;用戶數(shù)據(jù)為空;選項為OS_TASK_OPT_STK_CHK|OS_TASK_

OPT_STK_CLR,表示任務(wù)堆棧檢查和清零。4.2.3任務(wù)程序appfun.c

文件appfun.c的完整代碼如下:

1/*FileName:appfun.c

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#defineMY_APP_GLOBALS

8#include"includes.h"

9

上述代碼第10~60行為LED相關(guān)的代碼,只需對第41~60行的代碼作一說明即可。參考第一章圖1-31,三個LED燈接在GPC5~7引腳上,第44行讀取這三個引腳的值;第48行為讀出值與0x0020異或,這樣,只有LED1燈的狀態(tài)翻轉(zhuǎn),其他兩個燈的狀態(tài)不變,這表現(xiàn)為LED1會閃爍;同理,第51、54和57行分別實現(xiàn)LED2、LED3以及LED1~3的狀態(tài)變化,即實現(xiàn)LED燈的閃爍。

第61~69行為開定時器4中斷。第70~113行為串口0相關(guān)的函數(shù),實現(xiàn)了串口0的字節(jié)讀/寫以及字符串的輸出功能。第117~156行為AppTaskStart任務(wù)的代碼。第129行調(diào)用OSVersion函數(shù)獲得μC/OS-Ⅱ的版本號,這里返回的值為286;第130行將os_version變量的值格式化賦給字符串sOsVer;第131行通過串口0輸出;第134~136行計算30度角的正弦值,并通過串口輸出。

第138~140行開定時器4。第138行為關(guān)中斷操作,這樣第139行代碼執(zhí)行過程中不受干擾;第140行恢復(fù)CPU的狀態(tài)(注意,這里只是恢復(fù),并沒有開中斷。如果中斷原來是關(guān)閉的,則第140行執(zhí)行完后,中斷仍然是關(guān)閉的)。

第142行調(diào)用OSStatInit初始化統(tǒng)計任務(wù)用到的一些全局量。第144行調(diào)用自定義函數(shù)TaskStartCreateTasks創(chuàng)建一些任務(wù),后面會介紹。

第146行將當(dāng)前任務(wù)命名為AppTaskStart。

第148~155為死循環(huán)體。第150行將CPU利用率和任務(wù)切換數(shù)格式化輸出到字符串s;第151行將s通過串口輸出;第153行清除任務(wù)切換數(shù);第154行延時3秒。

第157~190行借助OSTaskCreateExt創(chuàng)建了三個任務(wù)。與第4.2.2節(jié)app.c中第20行不同的地方是,這里的第168、178、188行均使用一個自定義結(jié)構(gòu)體類型變量,用于傳遞用戶數(shù)據(jù),這里的TaskUserData變量在app.h中定義,有任務(wù)名、任務(wù)運行次數(shù)、任務(wù)單次運行時間等三個成員。第191~256行為三個任務(wù)的函數(shù)體,這里只解釋第191~224行的任務(wù)1。第200行為任務(wù)1命名,OSTaskNameSet函數(shù)有三個參數(shù),分別為任務(wù)優(yōu)先級(OS_PRIO_SELF表示任務(wù)本身)、任務(wù)名字符串和調(diào)用成功與否的提示信息。第205行調(diào)用OSTaskStkChk函數(shù)檢查任務(wù)堆棧,OSTaskStkChk有兩個參數(shù),即任務(wù)的優(yōu)先級和指向記錄堆棧信息的結(jié)構(gòu)體變量指針,該結(jié)構(gòu)體類型OS_STK_DATA參見第4.1.2節(jié)。第209~215行將堆棧使用情況通過串口0輸出。第218行調(diào)用OSTimeGet獲得系統(tǒng)時鐘節(jié)拍的總計數(shù)值。對比第222、242和254行,可知任務(wù)1、2、3的延時分別為10秒、5秒和1秒,因此它們的執(zhí)行次數(shù)的比值為1:2:10,在圖4-5中向下拉動窗口滾動條會觀察到這種情況。第258~263行定義了一個函數(shù)DispTaskStat,輸入?yún)?shù)為任務(wù)的優(yōu)先級,該函數(shù)輸出自定義結(jié)構(gòu)體數(shù)組變量TaskUserData的內(nèi)容。

綜上所述,AppTaskStart任務(wù)的功能為:當(dāng)?shù)谝淮螆?zhí)行時,輸出μC/OS-Ⅱ版本號,計算并輸出sin(30°)的值;然后,打開定時器4中斷,初始化統(tǒng)計任務(wù)全局變量,創(chuàng)建三個任務(wù),設(shè)置任務(wù)名為AppTaskStart;進入任務(wù)循環(huán)體后,每隔3秒輸出任務(wù)數(shù)、CPU利用率和任務(wù)每秒切換次數(shù)。AppTask_1任務(wù)的功能為:第一次執(zhí)行時,設(shè)置任務(wù)名為AppTask_1;進入任務(wù)循環(huán)體后,每隔10秒,輸出三個用戶任務(wù)使用堆棧的情況以及總的時鐘節(jié)拍計數(shù)。

AppTask_2任務(wù)的功能為:第一次執(zhí)行時,初始化TaskUserData的成員TaskCtr,將任務(wù)命名為AppTask_2;進入任務(wù)循環(huán)體后,每隔5秒調(diào)用函數(shù)DispTaskStat顯示各個任務(wù)的用戶信息,即任務(wù)名和執(zhí)行次數(shù)。AppTask_3任務(wù)的功能為:第一次執(zhí)行時,關(guān)閉所有LED燈,將任務(wù)命名為AppTask_3;進入任務(wù)循環(huán)體后,每隔1秒,三個LED燈閃爍一次。

本小節(jié)中使用的OSVersion、OSTaskNameSet、OSTimeDly、OSTaskCreateExt、OSTimeDlyHMSM等函數(shù)的詳細(xì)說明可以參考“μC/OS-ⅡReferenceManual”手冊(位于第1.1節(jié)中的Micrium-uCOS-Ⅱ-V286.ZIP壓縮包中)。這個手冊中包含了幾乎所有μC/OS-Ⅱ的常用函數(shù)。4.2.4工程ex4_2

第一章表1-4中函數(shù)OSTaskCreate在第三章已用到了,工程ex4_1中沒有出現(xiàn)的函數(shù)有OSTaskDel、OSTaskDelReq、OSTaskSuspend、OSTaskResume、OSTaskNameGet、OSTaskQuery和OSTaskChangePrio。為了介紹這些函數(shù)的使用方法,在工程ex4_1的基礎(chǔ)上,新建工程ex4_2,保存在目錄D:\ZYUCOSII\ex4_2中(新建的工程與ex4_1完全相同,只是工程文件名改為ex4_2)。然后,僅修改appfun.c文件的AppTask_1和AppTask_3函數(shù)即得工程ex4_2。

任務(wù)AppTask_1和AppTask_3的內(nèi)容如下:

1voidAppTask_1(void*pdata)

2{

3OS_STK_DATAstkData;

4INT8Uerr;

5INT8Ui;

6INT8Us[80];

7INT32UstkSize;修改的內(nèi)容從第36行開始,判斷時鐘節(jié)拍計數(shù)值是否大于4000而小于6000,即經(jīng)過40秒而不超過60秒時,第38~39行判斷優(yōu)先級為AppTask_3_Prio的任務(wù)(即任務(wù)3)是否存在,如果存在,則第41行調(diào)用OSTaskDelReq函數(shù)請求任務(wù)3刪除自己。這里的OSTaskDelReq函數(shù)只有一個參數(shù),即任務(wù)優(yōu)先級,該函數(shù)向?qū)⒈粍h除的任務(wù)發(fā)出刪除請求,它本身不刪除任務(wù)。在第107行任務(wù)3判斷有沒有任務(wù)請求它刪除它自身,如果有,則第109行調(diào)用OSTaskDel刪除任務(wù)3。當(dāng)tickCur>4000時,即經(jīng)過40秒后,任務(wù)3將被刪除,這里,三個LED燈不再閃爍??山Y(jié)合串口調(diào)試助手觀察時間的變化。

從第45行開始,將判斷時鐘節(jié)拍是否大于6000而小于8000,如果是,則進一步判斷任務(wù)3是否存在(第47~48行),如果不存在,則調(diào)用OSTaskCreateExt重新創(chuàng)建任務(wù)3。也就是說,到60秒的時候,三個LED燈間隔了20秒后重新開始閃爍了。

第62行,即時鐘節(jié)拍到了8000次以上而不超過10000次時,將調(diào)用OSTaskSuspend掛起任務(wù)3。OSTaskSuspend只有一個參數(shù),即任務(wù)的優(yōu)先級,與OSTaskResume的作用相反,被掛起的任務(wù)只能調(diào)用OSTaskResume恢復(fù)運行。因此,到80秒的時候,三個LED燈又不再閃爍了。間隔20秒后,到了第67~70行,調(diào)用OSTaskResume恢復(fù)任務(wù)3,這時三個LED燈繼續(xù)開始閃爍了。

然后,到了第72行,即到

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論