免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
uC/OS II 學習筆記

uC/OS II 學習筆記

(2012-09-13 10:57:48)
轉載▼
標簽:

雜談

uC/OS II 提供給用戶通用接口函數(shù)都在Ucos_ii.h中【uC/GUI 提供給用戶通用接口函數(shù)都在INC包含的各個頭文件中,使用時參考官方的手冊用就好了,有中文版的】;

實時操作系統(tǒng)RTOS
實時操作系統(tǒng)包括軟實時與硬實時,軟實時要求各個任務盡快地運行,而不要求限定某一個任務在多長的時間內完成,而硬實時系統(tǒng)不僅須執(zhí)行無誤而且要做到準時,需要在規(guī)定的時間內完成相關操作;
大多數(shù)實時系統(tǒng)是兩者的綜合;
非實時操作系統(tǒng)指操作系統(tǒng)無法保證哪怕是最高優(yōu)先級任務開始執(zhí)行的最后時限。軟實時操作系統(tǒng)指的是操作系統(tǒng)只能保證在xx時間內執(zhí)行最高優(yōu)先級的用戶代碼,但用戶軟件是否能及時完成操作,操作系統(tǒng)不管!
RTlinux就是linux的硬實時操作系統(tǒng),它能夠創(chuàng)建精確運行的符合POSIX.1B標準的實時進程;

并非所有的嵌入式系統(tǒng)都需要實時操作系統(tǒng),只有在一些特定的場合,對時間比較敏感的應用才會使用實時操作系統(tǒng)。實時操作系統(tǒng)必須及時響應所要求的任務,在限定時間內完成任務。非實時的操作系統(tǒng),多時間不是很敏感,對所要求的任務只是會保證完成,但在什么時候完成,或用多長的時間完成就不一定了。例如:手機它不需要實時性。我們發(fā)短信時,系統(tǒng)對它的處理早1秒或者晚1秒都不會影響到我們的使用。而對于導彈這樣的應用必須具有實時性。導彈被發(fā)射出去鎖定目標后要不斷修正飛行方向,以保證擊中目標,如果它的實時性不好的話,從傳感器傳來的信號沒有及時響應,即使完了1毫秒的時間,那誤差就會很大。用這樣的導彈攻打敵方目標的話,目標很可能沒有擊中,美國大使館倒是有可能被炸掉。
另外一般linux不具有實時性,它是分時操作系統(tǒng)一般是面向用戶的,但是因為它的源代碼是公開的,它是可以改造成實時系統(tǒng)的,但即使是這樣它的實時性也不會很好,畢竟它最初的設計并不是為了實時性。我們在Linux上面同時運行好幾個程序,它們會被并發(fā)的執(zhí)行。我們會發(fā)現(xiàn)同時多運行幾個程序可能會比只允許一個程序慢,這是因為操作系統(tǒng)把處理器按時間片分給了每一個程序。自然會慢一些。而實時操作系統(tǒng),一般不同的任務會有不同的優(yōu)先級,他會把擁有最高的優(yōu)先級的程序一次性執(zhí)行完畢。然后再執(zhí)行次一級的程序。這樣的系統(tǒng)只適用于控制,不適合一般的應用。

任務劃分
目標:
    滿足實時性要求;
    任務數(shù)目合理;-->合理使用系統(tǒng)的軟硬件資源
    簡化軟件系統(tǒng);-->合理規(guī)劃任務,降低對操作系統(tǒng)的服務要求,使操作系統(tǒng)功能得到裁剪,簡化系統(tǒng)
    降低資源需求;
   目前,各功能單獨成立為一個任務,比如顯示功能成為顯示任務,文件系統(tǒng)通過一個任務來管理,這樣文件系統(tǒng)任務要顯示時,就向顯示任務的消息隊列里面發(fā)送顯示消息,而不是直接在本任務中調用顯示函數(shù)。
0:設備依賴性任務的劃分
從系統(tǒng)的結構框圖中,我們可以看到系統(tǒng)的輸入輸出各個設備。并發(fā)性是任務的基本特性,而控制輸出、輸出的設備的程序具有先天的并發(fā)性,把他們分別封裝為不同的任務是合理的,這樣就可以劃分出第一批任務,鍵盤任務,顯示任務,數(shù)據(jù)采集任務,控制輸出任務和通信任務。
1:系統(tǒng)關鍵任務劃分:關鍵是指某種功能在應用系統(tǒng)中的重要性,如果該功能不能正常實現(xiàn),則將造成重大影響,甚至引發(fā)災難性的后果;包含該關鍵功能的任務為關鍵任務,關鍵任務必須得到運行機會即使遺漏一次也不行; 對于關鍵功能,必須盡可能與其他的功能剝離,獨立成為一個任務,通過通信的方式再觸發(fā)其他的任務,完成后續(xù)操作。例如:火災檢測系統(tǒng)中,煙霧傳感器的檢測功能就是一個關鍵功能,必須將其與其他的報警、滅火等功能剝離。OSMboxPostOpt()消息發(fā)送函數(shù),具有廣播功能:  O在uC/OS II的較新版本中,消息發(fā)送函數(shù)OSMboxPostOpt()具有廣播功能,發(fā)送一條消息就可以使所有等待該消息的任務進入就緒狀態(tài)。
2:緊迫任務劃分:某種功能必須在規(guī)定的時間內得到運行權(及時運行),并在規(guī)定的時刻前執(zhí)行完畢(按時完成),這類功能有嚴格的實時性要求。大多數(shù)的緊迫任務是通過異步事件來觸發(fā),這些異步事件一般能夠引發(fā)某種中斷。在這種情況下,將緊迫任務安排在相應的ISR中是最有效的辦法,如果不能安排在中斷任務中,那么可竟盡可能提高優(yōu)先級來解決“及時性”。對于按時完成,需要對緊迫任務進行瘦身,盡可能剝離不太緊迫的操作,只剩下必須立刻做的操作,被剝離的不太緊迫的操作另外封裝成一個任務。例如:能譜分析儀,緊迫任務放在外部中斷服務程序中,完成對脈沖峰值的采樣,并將采樣結果放入消息隊列中。緊迫任務不一定是關鍵任務,所以遺漏一兩次執(zhí)行會導致工作品質下降,但是不會造成嚴重后果。
3:數(shù)據(jù)處理任務劃分:用戶程序中消耗時間最多的是各種數(shù)據(jù)處理程序單元,這些單元不止一個,且分別為不同的功能服務;所以需要將他們劃分出來,分別包裝為不同的任務,因為他們的處理較耗時,所以他們的優(yōu)先級須安排低,這樣讓他們使用其他任務的剩余時間來進行數(shù)據(jù)處理(如果有時間片輪轉,可以安排他們?yōu)橥粌?yōu)先級,利用時間片來輪轉他們)。模擬時間片輪轉:假如有3個數(shù)據(jù)處理任務A、B、C;我們可以將他們細分為A1、A2、A3,B1、B2、B3,C1、C2、C3,再交叉安排優(yōu)先級就可以了。A1->1,B1->2,C1->3,A2->4,B2->5...
4:功能聚合任務劃分:將關系密切的若干功能組合為一個任務,達到功能聚合的效果,關系密切:數(shù)據(jù)關聯(lián)和時序關聯(lián),如果他們分開,有可能會使用大量的通信資源,造成較大的負擔。
5:觸發(fā)條件相同的任務劃分
如果若干個功能由相同的事件觸發(fā),則可以將這些功能組合成為一個任務,從而免除將事件分發(fā)給多個任務的工作量。這樣做的條件是:當以某順序執(zhí)行這些功能時,各個功能的實時性要求仍然可以得到滿足,且各個功能在執(zhí)行過程中不會出現(xiàn)問題,例如:火警檢測系統(tǒng)中,撥打電話、啟動噴淋滅火系統(tǒng)、保持火警記錄,這些任務是不能合在一起做為一個任務的,否則其中一些功能有誤其他的任務就會被耽擱。
符合本類任務的通常是內部事件,例如通過運算處理產(chǎn)生某個結果,根據(jù)這個結果需要執(zhí)行若干功能,這些功能可組合為一個任務。
6:運行周期相同的任務劃分
絕大多數(shù)功能都需要不停的重復執(zhí)行,如果重復執(zhí)行的條件是固定的時間間隔,則這個功能具有周期性。將周期相同的功能組合在一起封裝為一個任務,就可以避免一個時間事件觸發(fā)幾個任務,省去事件分發(fā)操作和他們的之間的通信。
7:順序操作任務劃分
如果若干個功能按固定的順序運行流水作業(yè),相互之間完全沒有并發(fā)現(xiàn),則應該將這個功能組合為一個任務。

任務設計
任務函數(shù)結構
所設計的任務函數(shù)至少有一次對操作系統(tǒng)服務函數(shù)的調用,這樣才能讓低優(yōu)先級的任務得到執(zhí)行。
1:單次執(zhí)行的任務
任務創(chuàng)建后,得到執(zhí)行,執(zhí)行完畢后自我刪除;任務基本分為三大部分:1:準備工作代碼(定義變量以及初始化工作)2:任務實體代碼(完成具體的功能,一般都可以被中斷)3:調用刪除函數(shù)。例如啟動任務(如果采用啟動任務去啟動各個任務,那啟動任務的優(yōu)先級需要比它創(chuàng)建的任務的優(yōu)先級高,一般系統(tǒng)中通常將啟動任務所做的事情交給系統(tǒng)的一個實質任務去做,節(jié)省資源);
采用“創(chuàng)建任務”的方式來啟動任務,不僅可以省去通常的通信手段激活任務的麻煩,還可以通過*pdata來傳遞參數(shù),是沒有啟動具有不同的工作狀態(tài)(比如串口波特率),但是這樣的話實時性會比較差,每一次任務的啟動都需要創(chuàng)建,發(fā)費較多的時間,還有可能在刪除時引起不必要的后遺癥(如共享資源釋放、任務關聯(lián));
所以通過“創(chuàng)建任務”來啟動的任務一般是孤立的任務,他們不和其他的任務進行通信(ISR除外),只使用共享資源來獲取信息和輸出信息。
2:周期性執(zhí)行的任務 
周期性執(zhí)行的任務,通常在代碼中調用系統(tǒng)延時函數(shù),OSTimeDly或OSTimeDlyHMSM來調整執(zhí)行周期。但是這兩個函數(shù)有延時誤差,至少有一個或小于一個時鐘節(jié)拍的誤差,如需精確的定時需采用獨立的定時器。
3:事件觸發(fā)執(zhí)行的任務
任務的實體代碼的執(zhí)行需要等待某種事件的發(fā)生,在相關事件發(fā)生之前,任務被掛起,相關事件發(fā)生一次,任務執(zhí)行一次。當觸發(fā)條件是“時間間隔”(定時器觸發(fā))時,它既是周期任務。
如觸發(fā)條件是某個信號(信號量等),那么這個觸發(fā)條件僅僅是觸發(fā)任務的執(zhí)行。
如觸發(fā)條件是某個信息(郵箱等),那么這個觸發(fā)條件除了啟動該任務外,還為任務提供原始數(shù)據(jù)和資料。
任務優(yōu)先級安排
uC/OS II共有64個優(yōu)先級:0~63。在OS_CFG.h中設置OS_LOWEST_PRIO來確定系統(tǒng)實際使用的優(yōu)先級范圍,#define OS_LOWEST_PRIO  18 ->系統(tǒng)裁剪到只有19個優(yōu)先級,節(jié)省資源開銷。
OS_LOWEST_PRIO-->空閑任務;     OS_LOWEST_PRIO-1-->統(tǒng)計任務;
OS_LOWEST_PRIO-2-->系統(tǒng)保留;   OS_LOWEST_PRIO-3-->系統(tǒng)保留;
系統(tǒng)最高的4個優(yōu)先級(0、1、2、3)保留。
任務優(yōu)先級安排原則
中斷關聯(lián)性、緊迫性、關鍵性、頻繁性、快捷性、傳遞性

uC/OS II通信機制
uC/OS II通信機制包括有信號量(計數(shù))、互斥信號量(可以高低優(yōu)先級翻轉)、事件標志組、郵箱、消息隊列;
所有的通信機制都有5種功能函數(shù):創(chuàng)建、刪除、查詢、發(fā)送、(掛起式)獲取、(不掛起、不等待式)獲取;
且一旦某個通信制作被使用,其創(chuàng)建、發(fā)送、(掛起式)獲取功能是不能被裁剪的;
中斷函數(shù)需要盡可能短,實時性高,在中斷中,可以發(fā)送信號,其他的不要用。
除了事件標志組用OS_FLAG_GRP結構體表示外,其余的通信方式都使用OS_EVENT結構體表示;
typedef struct os_event {
    INT8U    OSEventType;                    //通信事件的類型
    void    *OSEventPtr;                     //郵箱或消息隊列中指向消息實體的指針
    INT16U   OSEventCnt;                     //計數(shù)單元
    INT8U    OSEventGrp;                     //等待該通信事件的任務所在的組
    INT8U    OSEventTbl[OS_EVENT_TBL_SIZE];  //等待該通信事件的任務列表
} OS_EVENT;
1:信號量(計數(shù)型)sem
信號量是一個可被多個進程共享的數(shù)據(jù)結構,主要用于任
務間少量的信息通信。信號量通常是在多個任務訪問一個共同的但
非共享的資源的情況下,用于同步各個任務之間的操作。
OS_EVENT *pevnt;
pevnt = OSSemCreate(int cnt);//創(chuàng)建并賦初值
OSsemPost(pevnt );
OSEventCnt>0表示該信號有效(且表示事件發(fā)生的次數(shù)),OSEventCnt==0表示該信號無效;
任務調用OSsemPost(pevnt),表示計數(shù)型信號量事件的OS_EVENT結構體中的OSEventCnt++;
任務調用OSSemPend(pevnt),如果此時OSEventCnt>0,則OSEventCnt--,且該任務接著繼續(xù)往下執(zhí)行;否則該任務掛起(再進行一次任務切換),直到該事件發(fā)生(OSEventCnt>0)且此時系統(tǒng)中該任務的優(yōu)先級最高方可得到運行;
任務調用OSSemAccept(pevnt),如果此時OSEventCnt>0,則OSEventCnt--,且不論OSEventCnt的值是多少,該函數(shù)直接返回OSEventCnt的值,return(cnt);
任務調用OSSemQuery(OS_EVENT *, OS_SEM_DATA),OS_SEM_DATA是一個精簡的OS_EVENT結構體,用來記錄被查詢信號量的計數(shù)值、任務等待列表等;
任務調用OSSemDel(pevnt )刪除信號量;起初分配的OS_EVENT結構體被釋放到空閑事件鏈表中;
2:互斥信號量mutex
在訪問比較耗時的共享資源時,如果采用關中斷的方法(系統(tǒng)此時不能被中斷、任務不能被切換)來實現(xiàn)訪問沖突,這對中斷的響應是很不好的,所以這時采用互斥型信號量就可以很好的解決問題,同時可以響應中斷。
互斥鎖用來實現(xiàn)任務之間的簡單同步,一個互斥鎖是一個
二元信號量,它的狀態(tài)只能是0(允許,開鎖)和1(禁止,上鎖)。
在互斥鎖范圍內,任何一個任務都可以對互斥負上鎖,但只有鎖住
該信號的任務才能開鎖從而實現(xiàn)了任務同步。
mutex不同于sem,mutex是一個互斥型信號量,它可以通過在應用程序中翻轉任務的優(yōu)先級來解決資源互鎖的問題;
舉例:首先給一塊共享資源配備一個互斥型信號量Mutex,系統(tǒng)中有兩個任務A、B,他們的優(yōu)先級分別為5,6;
假設B先運行,并通過OSMutexPend申請到了Mutex,那么它就會使用該共享資源繼續(xù)運行,在某個時刻,任務A搶占了任務B,同時也通過OSMutexPend申請Mutex,由于資源已經(jīng)被B占用,那么OSMutexPend會將B任務的優(yōu)先級調高到4(假設),這時任務B得意繼續(xù)運行,資源使用完畢后釋放mutex。OSMutexPost注意到原來占有這個mutex的任務的優(yōu)先級被調高了,于是將B的優(yōu)先級調低,同時注意到A在申請,于是將mutex給A,做任務切換后A得以執(zhí)行。
由于互斥型信號量的特性,互斥型信號量只能由于任務中(包括發(fā)送功能);
OS_EVENT *ResourceMutex;
ResourceMutex= OSMutexCreate(INT8U prio,&err);//在上述情況中 任務可以被調高到prio指定的優(yōu)先級
OS_EVENT中,pevent->OSEventCnt = (INT16U)((INT16U)prio << 8) | OS_MUTEX_AVAILABLE(0xff);
高八位:PIP;低八位:占用該互斥信號量的任務的優(yōu)先級(若為0xff表示無任務占用該信號量);
OSMutexPend(ResourceMutex,..,..):在訪問共享資源時,先通過該函數(shù)獲取互斥型信號量,如果互斥型信號量是有效的(沒有被占用),則該Mutex的OSEventCnt的低八位為0xff,如果已被占用,則低八位為占用該信號量的任務的優(yōu)先級;如果占用該信號量的任務的優(yōu)先級比調用該申請函數(shù)的任務的信號量的優(yōu)先級低,此時低優(yōu)先級的任務占用共享資源,且已被高優(yōu)先級的任務搶占了CPU,此時高優(yōu)先級的任務再調用OSMutexPend申請互斥量,OSMutexPend內部會將占用mutex的低優(yōu)先級的任務的優(yōu)先級調高到信號量指定的PIP,這樣來讓低優(yōu)先級的任務變?yōu)楦邇?yōu)先級,盡快釋放資源;
3:事件標志組event flag
typedef struct os_flag_grp {                
    INT8U         OSFlagType;               //事件類型
    void         *OSFlagWaitList;           //等待該事件標志組的任務列表
    OS_FLAGS      OSFlagFlags;              //事件標志組標志位
} OS_FLAG_GRP;
事件標志組用于實現(xiàn)多個任務(包括ISR)協(xié)同控制一個任務,當各個相關任務(ISR)先后發(fā)出自己的信號后(使事件標志組的對應標志位有效),預定的邏輯運算結果有效,這時將觸發(fā)被控制的任務(使其進去就緒態(tài))。
事件標志組可以選擇標志位1有效或0有效,邏輯關系可以為“邏輯與”或“邏輯或”,這樣有效定義與邏輯定義有4種組合,同時也可以設定只選擇使用所有標志位中的其中幾位;
OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags,INT8U *err);
flags為事件標志組中各個標志的初始值(1有效時,初始值為0);
發(fā)送標志到事件標志組:
OS_FLAGS  OSFlagPost (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *perr)
pgrp:事件標志組指針;flags:指明待發(fā)送標志在事件標志組中的位置(0x1表示bit0);opt:選擇操作的方式,OS_FLAG_SET(對標志位置1),OS_FLAG_CLR(對標志位置0);perr:執(zhí)行結果;
等待事件標志組:
OS_FLAGS  OSFlagPend (OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *perr)
pgrp:事件標志組指針;flags:指明哪些標志位用來等待,0x03(bit0、1),0x1d(bit0、2、3、4);opt:指明對這些等待的位采用的邏輯運算;perr:執(zhí)行結果;
opt如下:
OS_FLAG_WAIT_CLR_ALL:所以標志位為0時,將等待任務就緒;
OS_FLAG_WAIT_CLR_ANY任何一個標志位清0時,將等待任務就緒;
OS_FLAG_WAIT_SET_ALL:所以標志位為1時,將等待任務就緒;
OS_FLAG_WAIT_SET_ANY:任何一個標志位為1時,將等待任務就緒;
OS_FLAG_CONSUME:清除標志位(OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME
4:消息郵箱box
郵箱是μC/OS-II中另一種通訊機制,它可以使一個任務或者中斷服務子程序向另一個任務發(fā)送一個指針型的變量。該指針指向一個包含了特定“消息”的數(shù)據(jù)結構。
用信號量進行行為同步時,只能提供同步的時刻信息,不能提供內容信息;
當控制方在對被控制方進行控制的同時,還需要向被控制方提供內容信息時,消息郵箱是一個有效的方案;
由于消息郵箱中只能存儲一條消息,在用消息郵箱進行同步控制時,必須滿足一個前提:任何時候消息的生產(chǎn)速度都比消息的消費速度慢,即被控制的任務總是在等待消息;否則就會有消息丟失;
消息郵箱中可以放入任何類型的信息,通常用空郵箱(void * 0)表示事件沒有發(fā)生;用非空郵箱(void * 1)表示事件已發(fā)生;因此郵箱也可以用來做二值信號量(注意與互斥信號量的區(qū)別);
郵箱用OS_EVENT結構體中的OSEventPtr指向消息實體;
OS_EVENT *pbox;
pbox= OSMBoxCreate(void *msg);//void * 0表示空郵箱
INT8U OSMboxPost(pbox, void * msg);//向郵箱中發(fā)送一條消息
INT8U OSMboxPostOpt(pbox,void * msg, INT8U opt)//分發(fā)消息,將消息分發(fā)給所有正在等待該消息的任務,讓他們都處于就緒態(tài)
void *OSMboxPend(pbox,timeout,INT8U *err)//等待消息 當郵箱中的指針為空時,調用等待消息函數(shù)后就會被系統(tǒng)掛起。
5:消息隊列
將要通信的信息放置在一個預定義的消息結構中,任務生成的消息指定了消息的類型,并把它放在一個由系統(tǒng)負責維護的消息隊列中,而訪問消息隊列的任務可以根據(jù)消息類型,有選擇地從隊列中按FIFO的方式讀取特定類型的消息。消息隊列為用戶提供了從多個生產(chǎn)者中獲得多元信息的一種手段。
相當于一個郵箱隊列,消息隊列可以存放多個消息,能夠有效解決消息的臨時堆積問題。和計數(shù)信號量的情況類似,消息隊列的使用仍然需要滿足:消費速度比生產(chǎn)速度快,否則再大的隊列也會滿,從而溢出;
void *MyArrayOfMsg[SIZE];
OS_EVNT *pQ;
pQ=OSQCreate(MyArrayOfMsg,SIZE); 
INT8U OSQPost(pQ,void *Msg)//發(fā)送一條消息
void *OSQPend(pQ,0,&err)//當消息隊列為空時 掛起


【各個通信機制都要無等待式獲取相關的信號,均可用在中斷中,中斷中決不能用等待的方式】







相關函數(shù)使用說明:
創(chuàng)建任務:
INT8U  OSTaskCreateExt (void   (*task)(void *p_arg),//被創(chuàng)建的任務函數(shù)指針
                        void    *p_arg,               //傳遞給任務的參數(shù)的指針
                        OS_STK  *ptos,                //分配給任務的堆棧的棧頂指針
                        INT8U    prio,                //被創(chuàng)建任務的優(yōu)先級
                        INT16U   id,                  //為創(chuàng)建的任務創(chuàng)建一個特殊的標識符,暫沒用
                        OS_STK  *pbos,                //指向任務的堆棧棧底的指針(用于堆棧檢驗)
                        INT32U   stk_size,            //堆棧的容量
                        void    *pext,                //指向用戶附件的數(shù)據(jù)域的指針
                        INT16U   opt)                 //指定是否卞堆棧檢驗,是否將堆棧清0,任務是否需要進行浮點操作
OSTaskCreate中還調用了OSTaskStkInit函數(shù),調用該函數(shù)的目標是初始化任務的堆棧,使其看起來像發(fā)生過中斷一樣。OSTaskStkInit是需要移植的函數(shù)。

修改任務屬性:
調用OSTaskChangePrio()函數(shù)可以動態(tài)地改變某一個任務的優(yōu)先級,調用OSTaskNameGet()函數(shù)可以獲取某一個任務的名稱,調用OSTaskNameSet()函數(shù)可以設置一個任務的名稱。
INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio);
INT8U OSTaskNameGet INT8U   prio,INT8U  *pname,INT8U  *perr);
void  OSTaskNameSet (INT8U prio, INT8U *pname, INT8U *perr)
任務的名稱保存在任務對應的任務控制塊中的成員:OSTCBTaskName[OS_TASK_NAME_SIZE]中;
直接掛起任務的函數(shù):
有時將任務掛起是很有用的,掛起任務的函數(shù)可以通過INT8U  OSTaskSuspend(INT8U prio)來實現(xiàn),且被掛起的任務只能通過調用INT8U  OSTaskResume (INT8U prio)來恢復。任務可以掛起自己或者其他的任務。
舉例:在一些設計中,在main函數(shù)中只有簡單的幾行,在其中創(chuàng)建了一個啟動任務:用于啟動其他的任務與初始化系統(tǒng),啟動任務的優(yōu)先級比它要創(chuàng)建的任務都要高,初始化成功與創(chuàng)建完相關任務之后,一般自己將自己掛起。
堆棧檢驗函數(shù):
如果要使用堆棧檢驗函數(shù),那么在任務的建立需要使用OSTaskCreateExt()來建立任務,且需要指定入口參數(shù):opt(OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
OS_TASK_OPT_STK_CHK:指示任務需要使用堆棧校驗功能
OS_TASK_OPT_STK_CLR:將任務的堆棧RAM清0(因為堆棧檢驗是從棧底開始計算為0(空閑)的空間大?。?/div>
在任務創(chuàng)建時指定棧頂、棧底,在任務運行一段時間后,一般需在堆棧使用最充分后才去檢驗較為準確;任務可以檢查自己或者其他任務的堆棧使用情況,堆棧檢驗函數(shù)確定堆棧的實際空間字節(jié)數(shù)和已被占用的字節(jié)數(shù),放在入口參數(shù)OS_STK_DATA數(shù)據(jù)結構中;
[可以閱讀 嵌入式ARM系統(tǒng)原理與實例開發(fā)(楊宗德)的第八章,里面對該系統(tǒng)包含的大部分函數(shù)簡要說明以及移植時的相關函數(shù)說明]

任務切換過程分析
1:任務之間搶占式切換(高優(yōu)先級的任務就緒后立即搶占正在運行的低優(yōu)先級的任務)
系統(tǒng)在任何響應后,都需要進行任務調度,確保系統(tǒng)中優(yōu)先級最高的任務被執(zhí)行,那么系統(tǒng)采用的任務調度切換函數(shù)就是void  OS_Sched(void);
void  OS_Sched(void)
    OS_ENTER_CRITICAL();   //進入臨界區(qū) 關中斷 
    if(OSIntNesting==0) {  //目前系統(tǒng)不處于中斷態(tài)                         
        if(OSLockNesting==0) { //系統(tǒng)調度功能使能                    
            OS_SchedNew();   //計算出目前系統(tǒng)中最高的優(yōu)先級,保存在INT8U OSPrioHighRdy中
            if(OSPrioHighRdy!=OSPrioCur) { //判斷當前正運行的任務是不是最高優(yōu)先級 否則就不用切換
                 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//將最高優(yōu)先級任務的控制塊的指針放                                                              //入任務控制塊指針變量OSTCBHighRdy
                 #if OS_TASK_PROFILE_EN > 0
                    OSTCBHighRdy->OSTCBCtxSwCtr++; //目前優(yōu)先級最高的任務切換次數(shù)統(tǒng)計        
                 #endif
                 OSCtxSwCtr++; //系統(tǒng)任務切換的總次數(shù)統(tǒng)計
                 OS_TASK_SW(); //實現(xiàn)任務切換
            }
        }
     }
     OS_EXIT_CRITICAL(); //恢復全局中斷標志 退出臨界區(qū)


在任務調度前,OS_SchedNew()計算出目前系統(tǒng)中最高的優(yōu)先級,保存在INT8U OSPrioHighRdy中
#define  OS_TASK_SW()         OSCtxSw()
OSCtxSw //懸起PSV異常
    LDR     R0, =NVIC_INT_CTRL  //NVIC_INT_CTRL為SCB中的中斷控制寄存器(ICSR)的地址數(shù)值 0xE000ED04
    LDR     R1, =NVIC_PENDSVSET //NVIC_PENDSVSET 為設置ICSR的值(0x10000000
    STR     R1, [R0]            //設置ICSR = NVIC_PENDSVSET  即將觸發(fā)PendSV中斷
    BX      LR                  //因為在OS_Sched中調用OSCtxSw時,是在臨界區(qū),中斷是關閉的,所以這里通過BX LR返回OS_Sched,等退出臨界區(qū)后,觸發(fā)PendSV中斷【任務切換時必須關中斷”的原則
PendSV中斷響應函數(shù)OSPendSV
//Cortex-M3進入異常服務例程時,使用的是MSP指向的堆??臻g,且自動壓入了R0-R3,R12,LR(R14,連接寄存器),PSR(程序狀態(tài)寄存器)和PC(R15),這些寄存器是任務被中斷時的現(xiàn)場記錄,此時任務所使用的PSP的值沒有變化。
//M3處理器的控制寄存器的CONTROL[1] = 1(系統(tǒng)復位后默認是0),表明M3的線程模式的堆棧指針SP選用PSP【handler模式只允許使用MSP】,這樣兩個模式下SP使用不同的堆棧指針,且訪問SP時,訪問到的是當前被使用的堆棧指針,這時另一個堆棧指針要通過MRS、MSR命令來訪問,即R13(PSP)或R13(MSP),R13是PSP還是MSP由系統(tǒng)當前的狀態(tài)與CONTROL[1]決定,PSP是某個任務在RAM中的堆棧指針,MSP是異常模式下在RAM中的堆棧指針。
//一個任務占用CPU運行,它的運行現(xiàn)場是R0-R3,R4-R11,R12,R13(PSP/MSP),R14(LR),PSR和R15(PC),如果該任務被中斷,那么在進中斷過程中,硬件會自動對CPU的部分現(xiàn)場寄存器(R0-R3,R12,R14,PSR和R15)進行壓棧,其他寄存器硬件不壓棧,如果CONTROL[1] = 1,那么硬件就會將相關寄存器壓入PSP(R13)指向的堆棧中(線程模式時SP為PSP),CONTROL[1] = 0,硬件就會將相關寄存器壓入MSP(R13)指向的堆棧中;CONTROL[1] = 1時如下圖所示,線程模式與handler模式的SP不一樣。

OSPendSV  在進入中斷前 R0-R3,R12,R14,PSR和R15已被壓入任務所使用的堆棧中,下面是將其他的寄            存器再壓入任務的堆棧中,以及保存該任務的堆棧棧頂?shù)闹档絇SP指向的堆棧中去,任務使用            的是PSP,異常模式下使用的是MSP
    MRS     R0, PSP   //R0<=PSP 異常模式下使用的是MSP,所以只能通過MRS讀取被中斷的任務的堆棧指針,
    CBZ     R0, OSPendSV_nosave //如果PSP==0,則跳轉到 OSPendSV_nosave ,否則往下執(zhí)行【在系統(tǒng)剛開                                 //始啟動時,OSStart()會調用OS_SchedNew()與OSStartHighRdy()來使                                 //系統(tǒng)中最高優(yōu)先級的任務運行,在OSStartHighRdy中,PSP設置為0,因為                                 //在OSStart前還沒有任務運行,所以進入軟中斷后不用保存此時CPU的值,                                 //只要將最高優(yōu)先級任務的現(xiàn)場恢復到CPU的各寄存器就好了
    SUBS    R0, R0, #0x20   //R0(PSP)!=0,那么R0的值減去0X20,32個字節(jié)(下面保持8個32位的寄存器)
    STM     R0, {R4-R11}    //將R4~R11這8個寄存器存儲到PSP對應的空間中去,這些寄存器是當前任務被中                             //斷時的現(xiàn)場
    LDR     R1, __OS_TCBCur //__OS_TCBCur  DCD  OSTCBCur
                            //當前任務(被中斷的任務)的任務控制塊OSTCBCur, OSTCBCur是當前任務控制                             //塊的首地址,也代表了它的第一個成員的地址,所以下面其實是對他的第一個                             //成員OS_STK *OSTCBStkPtr(指向任務的堆棧棧頂?shù)闹羔槪┻M行賦值
    LDR     R1, [R1]        //將OSTCBStkPtr中保持的值賦給R1,該值指向任務的堆棧棧頂?shù)闹羔?/span>
                            //即R1存儲被中斷的當前任務的堆棧棧頂?shù)闹羔?/span>
    STR     R0, [R1]        //將被中斷的當前任務的堆棧棧頂?shù)闹羔槾娴絇SP指向的堆棧中去,即保存被中斷                             //任務所使用的堆棧棧頂?shù)闹羔?上面其他的寄存器是任務被中斷的線場
OSPendSV_nosave  【上面的程序執(zhí)行完后,接著繼續(xù)執(zhí)行下面的程序】
    PUSH    {R14}           //保存R14寄存器的值 壓入到MSP指向的堆棧中,該寄存器在中斷返回時大有作用
    LDR     R0, __OS_TaskSwHook //調用回調函數(shù)  鉤子函數(shù)        
    BLX     R0
    POP     {R14}           
    -------------------------------------------
    //相當于定義指針變量:__OS_PrioCur DCD   OSPrioCur;  __OS_PrioHighRdy  DCD  OSPrioHighRdy
    LDR     R0, __OS_PrioCur     //INT8U OSPrioCur當前任務的優(yōu)先級   
    LDR     R1, __OS_PrioHighRdy //OSPrioHighRdy系統(tǒng)中最高的優(yōu)先級  
    LDRB    R2, [R1]             //上面兩句是將變量的地址傳給了R0與R1,[R1]最高優(yōu)先級的值存入R2
    STRB    R2, [R0]             //將R2中的值存入到OSPrioCur變量中去(即接下來運行最高優(yōu)先級任務)
                                 //即實現(xiàn):OSPrioCur = OSPrioHighRdy;
    -------------------------------------------------
    //相當于定義指針變量:__OS_TCBCur DCD   OSTCBCur;  __OS_TCBHighRdy  DCD  OSTCBHighRdy     
    LDR     R0, __OS_TCBCur      //R0=&OSTCBCur;  OS_TCB  *OSTCBCur; OS_TCB *OSTCBHighRdy;
    LDR     R1, __OS_TCBHighRdy  //R1=&OSTCBHighRdy;這四句同理上面,OSTCBCur = OSTCBHighRdy
    LDR     R2, [R1]             //R2 = *R1;  R2=OSTCBHighRdy 指向優(yōu)先級最高的任務的任務控制塊
    STR     R2, [R0]             //*R0 = R2即實現(xiàn):OSTCBCur = OSTCBHighRdy;
                                 //系統(tǒng)中用OSPrioCur   OSTCBCur 來表示正在運行的任務
    ----------------------------------------------
   通過R2(OSTCBHighRdy)將OSTCBHighRdy->OSTCBStkPtr的值賦給R0,再通過LDM將最高優(yōu)先級任務的堆棧中保存好的R4-R11恢復到當前CPU的R4-R11寄存器中(這個過程與上面的保存過程是相逆的),因為R0-R3,R12,R14,PSR和R15是硬件自動壓入任務的堆棧的,在中斷中后來再保存R4-R11的(是先對SP減了32后再保存,相當于SP的值沒有變化),且中斷退出時會自動彈出R0-R3,R12,R14,PSR,所以下面的代碼只要恢復最高優(yōu)先級任務的R4-R11(OSTCBHighRdy->OSTCBStkPtr通過LDM自減了32,),然后將加上32, OSTCBHighRdy->OSTCBStkPtr 相當于沒有變化,再將OSTCBHighRdy->OSTCBStkPtr賦給PSP,這樣硬件在中斷退出時會自動恢復 R0-R3,R12,R14,PSR ,這樣就完全恢復了,一運行就是最高優(yōu)先級的任務了
    LDR     R0, [R2]             //[R2]為*OSTCBHighRdy,R2是4字節(jié)的寄存器,所以[R2]是OSTCBHighRdy                                  //指向地址的后4個字節(jié)的值,即OSTCBHighRdy->OSTCBStkPtr,  
                                 //即 R0 = OSTCBHighRdy->OSTCBStkPtr
    LDM     R0, {R4-R11}         //將 OSTCBHighRdy->OSTCBStkPtr(R0)指向的堆棧(向下生長)棧頂后                                  //的32個字節(jié)依次存入R4-R11(R0的值自減,后綴為!表示自增),因為堆                                   //棧保持時是自減的,這里相當于恢復最高優(yōu)先級任務的現(xiàn)場)               
    ADDS    R0, R0, #0x20        //將R0的值加上0x20后賦給R0
    MSR     PSP, R0              //PSP <= R0                              
    ORR     LR, LR, #0x04    //按位或 將LR的第二位置1,這樣中斷返回時,就將從進程的堆棧中做                              //出棧操作,返回后使用PSP(否則將從主堆棧中做出棧操作,返回后                              //使用MSP)                                
    BX      LR                   //中斷返回


xPSR、PC、LR、R12、R3、R2、R1、R0會被硬件按一定的次序壓入PSP所指向的堆棧中(同時被中斷的任務后面要恢復執(zhí)行,還需保存R4-R11,保存后一定要保證堆棧指針指向硬件自動壓棧后的原位置,這樣硬件才能在恢復任務時硬件自動正確出棧)



2:在中斷中實現(xiàn)任務的切換
uC/OS II  的中斷服務函數(shù)必須遵循一定的架構來實現(xiàn),
void SysTickHandler(void)
{
    OS_CPU_SR  cpu_sr;
    OS_ENTER_CRITICAL();  //保存全局中斷標志,關總中斷
    OSIntNesting++;
    OS_EXIT_CRITICAL();  //恢復全局中斷標志

    OSTimeTick();    

    OSIntExit();  //在os_core.c文件里定義,如果有更高優(yōu)先級的任務就緒了,則執(zhí)行一次任務切換 
}


OSInit函數(shù)
系統(tǒng)初始化函數(shù)OSInit()初始化所有的變量和數(shù)據(jù)結構,同時也會建立空閑任務OS_TaskIdle(),該任務永遠處于就緒態(tài),如果統(tǒng)計任務使能,那么他還要建立統(tǒng)計任務OS_TaskStat(),并使其進入就緒態(tài)。
空閑任務
OS_TaskIdle
uC/OS II總要建立一個空閑任務,idle task,這個任務在沒有其他任務進入就緒態(tài)時投入運行,它的優(yōu)先級永遠設為最低優(yōu)先級,即OS_LOWEST_PRIO,且空閑任務是不能被應用軟件刪除的;
空閑任務不停的給一個32位的OSIdleCtr的變量加1,統(tǒng)計任務用這個計數(shù)器變量確定當前應用軟件實際消耗CPU的時間,計數(shù)器加1前后分別關閉打開中斷,OS_TaskIdle可以借助OSTaskIdleHook()做CPU的睡眠等;OS_TaskIdle總是處于就緒態(tài)
統(tǒng)計任務OS_TaskStat
只要將OS_TASK_STAT_EN宏使能,那么統(tǒng)計任務就會建立,一旦運行,它將每秒運行一次,計算當前CPU的利用率,將值放在OSCPUsage這個8位的變量中,用百分比表示,精度為1%.如果應用程序打算使用統(tǒng)計任務,那么必須在初始化時建立的第一個也是唯一的任務中調用統(tǒng)計任務初始化函數(shù)OSStatInit(),也就是在調用系統(tǒng)啟動函數(shù)OSStart前,用戶初始化代碼中必須先建立一任務,在這個任務中調用系統(tǒng)統(tǒng)計初始化函數(shù)OSStatInit,然后再建立應用程序中的其他任務。它的優(yōu)先級是OS_LOWEST_PRIO-1
OSStart操作系統(tǒng)啟動函數(shù)
uC/OS II啟動之前,至少須建立一個應用程序,因為如果使能統(tǒng)計任務,那么統(tǒng)計任務要求必須先建立一個也是唯一一個用戶任務后,再啟動系統(tǒng),再創(chuàng)建其他的任務,如果什么任務都沒有建立,只要空閑任務,那系統(tǒng)也就一直空閑,所以需要先創(chuàng)建至少一個任務。
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服