久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計實例 >> 瀏覽文章

uC/OS II 學(xué)習(xí)筆記

作者:佚名   來源:本站原創(chuàng)   點擊數(shù):  更新時間:2014年08月18日   【字體:

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

 
INT8U  const  OSUnMapTbl[256] = {
    0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u 
}
【這個表的通俗用法就是 給出 給任意一個長度不超過8位的二進(jìn)制值a, 將這個a做為該數(shù)組的索引, OSUnMapTb[a]就表示二進(jìn)制值a中第一個位1出現(xiàn)的位置】
 
這個數(shù)組 索引值  0~255
即通過這個數(shù)組 可以直接獲取0~255各值的二進(jìn)制形式從右到左第一次出現(xiàn)1的位置
 
Ucosii 中任務(wù)的優(yōu)先級管理方法(假設(shè)最多64個任務(wù)):
因為OSUnMapTbl256個元素,也就是索引是8位的二進(jìn)制值,這個索引的每一位的0 1 值代表的是對應(yīng)的任務(wù)是否就緒;
因此管理任務(wù)的最小單位為INT8U(也就是8個任務(wù)用8個位來組成一個INT8U);
依次類推:可用一個INT8U來表示某個最小單元中是否有就緒態(tài)的任務(wù)(即該INT8U是否為0
這樣就是 8X8 = 64個任務(wù);
依次往上類推,8X8X….X8,理論上在空間足夠的情況下可以管理無限個任務(wù);
具體的優(yōu)先級值的算法就是:
  y = OSUnMapTbl[OSRdyGrp];
  OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
其實Ucosii 任務(wù)數(shù)大于64個時,他是16個任務(wù)為一組,組成INT16U來管理的
同樣往上推,也是16組來管理的
16X16
if ((*ptbl & 0xFFu) != 0u) {
     //如果低8位不為0,都不用考慮高8位了,直接在低8位中去尋找第一次出現(xiàn)1的地方
        OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]);
} else {
//如果低8位為0,直接在高8位中去尋找第一次出現(xiàn)1的地方,然后加8 就表示整個16位的INT16U中第一次出現(xiàn)1的地方
        OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u);
    }

一個任意長度的二進(jìn)制值(n位),我們都可以將這個n位化成m個段,用一個2的m次方個成員的數(shù)組 去存放他的特征值。代碼中可以以m位為單位求得特征值。
編程技巧,比如在用一個int(32位)的整數(shù)的30位的各個位的置位來表示對于的消息到來,且0-30位,優(yōu)先級一次降低...
假如某個時刻同時來了很多個消息,很多位置1,那么怎么來獲取這時優(yōu)先級最高的捏?
就用上面的方法舉一反三
我們可以一次處理 用一個16個單元的數(shù)組,
a[16] = {}; 來存放 4位 二進(jìn)制能表示的16個數(shù)字 中 各數(shù)第一位出現(xiàn)1的位置,
每4位 4位依次處理, 就可以利用該數(shù)組快速獲取最高優(yōu)先級的消息....
 
//這里用了一個很小的數(shù)組,來處理
//這里GetOnePos 的算法 對于 很多位的大數(shù)值來說,可能效率不高,但是對于 只有4位的值來說,就是很快了
//其實這里只是一個簡單的例子而已,ucos中用的是8 位  也就是256個值的數(shù)組,
//這個就看你的需求,你來選擇了具體的數(shù)組的大小了, 看具體的情況來 用時間 換 空間, 或用空間換時間
unsigned int g_unHighPrio[16] = 
{
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u,
};
 
char GetOnePos(int data)
{
if(data & 0x0000000F)
{
return(0 + g_unHighPrio[(data & 0x0000000F)>>0]);
else if(data & 0x000000F0)
{
return(4 + g_unHighPrio[(data & 0x000000F0)>>4]);
}
else if(data & 0x00000F00)
{
return(8 + g_unHighPrio[(data & 0x00000F00)>>8]);
}
else if(data & 0x0000F000)
{
return(12 + g_unHighPrio[(data & 0x0000F000)>>12]);
}
else if(data & 0x000F0000)
{
return(16 + g_unHighPrio[(data & 0x000F0000)>>16]);
}
else if(data & 0x00F00000)
{
return(20 + g_unHighPrio[(data & 0x00F00000)>>20]);
}
else if(data & 0x0F000000)
{
return(24 + g_unHighPrio[(data & 0x0F000000)>>24]);
}
else if(data & 0xF0000000)
{
return(28 + g_unHighPrio[(data & 0xF0000000)>>28]);
}
else
{
return 0;
}
}
 
int main(void)
{
unsigned int nTst = 0;
int npos = 0; 
while(1)
{
nTst = 0;
printf("Input Tst = ");
scanf("%u",&nTst);
printf("The First 1 position is %d\n",GetOnePos(nTst));
getchar();
}
}
 
 
 
實時操作系統(tǒng)RTOS
實時操作系統(tǒng)包括軟實時與硬實時,軟實時要求各個任務(wù)盡快地運(yùn)行,而不要求限定某一個任務(wù)在多長的時間內(nèi)完成,而硬實時系統(tǒng)不僅須執(zhí)行無誤而且要做到準(zhǔn)時,需要在規(guī)定的時間內(nèi)完成相關(guān)操作
大多數(shù)實時系統(tǒng)是兩者的綜合;
非實時操作系統(tǒng)指操作系統(tǒng)無法保證哪怕是最高優(yōu)先級任務(wù)開始執(zhí)行的最后時限。軟實時操作系統(tǒng)指的是操作系統(tǒng)只能保證在xx時間內(nèi)執(zhí)行最高優(yōu)先級的用戶代碼,但用戶軟件是否能及時完成操作,操作系統(tǒng)不管!
RTlinux就是linux的硬實時操作系統(tǒng),它能夠創(chuàng)建精確運(yùn)行的符合POSIX.1B標(biāo)準(zhǔn)的實時進(jìn)程;
 
并非所有的嵌入式系統(tǒng)都需要實時操作系統(tǒng),只有在一些特定的場合,對時間比較敏感的應(yīng)用才會使用實時操作系統(tǒng)。實時操作系統(tǒng)必須及時響應(yīng)所要求的任務(wù),在限定時間內(nèi)完成任務(wù)。非實時的操作系統(tǒng),多時間不是很敏感,對所要求的任務(wù)只是會保證完成,但在什么時候完成,或用多長的時間完成就不一定了。例如:手機(jī)它不需要實時性。我們發(fā)短信時,系統(tǒng)對它的處理早1秒或者晚1秒都不會影響到我們的使用。而對于導(dǎo)彈這樣的應(yīng)用必須具有實時性。導(dǎo)彈被發(fā)射出去鎖定目標(biāo)后要不斷修正飛行方向,以保證擊中目標(biāo),如果它的實時性不好的話,從傳感器傳來的信號沒有及時響應(yīng),即使完了1毫秒的時間,那誤差就會很大。用這樣的導(dǎo)彈攻打敵方目標(biāo)的話,目標(biāo)很可能沒有擊中,美國大使館倒是有可能被炸掉。
另外一般linux不具有實時性,它是分時操作系統(tǒng)一般是面向用戶的,但是因為它的源代碼是公開的,它是可以改造成實時系統(tǒng)的,但即使是這樣它的實時性也不會很好,畢竟它最初的設(shè)計并不是為了實時性。我們在Linux上面同時運(yùn)行好幾個程序,它們會被并發(fā)的執(zhí)行。我們會發(fā)現(xiàn)同時多運(yùn)行幾個程序可能會比只允許一個程序慢,這是因為操作系統(tǒng)把處理器按時間片分給了每一個程序。自然會慢一些。而實時操作系統(tǒng),一般不同的任務(wù)會有不同的優(yōu)先級,他會把擁有最高的優(yōu)先級的程序一次性執(zhí)行完畢。然后再執(zhí)行次一級的程序。這樣的系統(tǒng)只適用于控制,不適合一般的應(yīng)用。
 
任務(wù)劃分
目標(biāo):
    滿足實時性要求;
    任務(wù)數(shù)目合理;-->合理使用系統(tǒng)的軟硬件資源
    簡化軟件系統(tǒng);-->合理規(guī)劃任務(wù),降低對操作系統(tǒng)的服務(wù)要求,使操作系統(tǒng)功能得到裁剪,簡化系統(tǒng)
    降低資源需求;
   目前,各功能單獨成立為一個任務(wù),比如顯示功能成為顯示任務(wù),文件系統(tǒng)通過一個任務(wù)來管理,這樣文件系統(tǒng)任務(wù)要顯示時,就向顯示任務(wù)的消息隊列里面發(fā)送顯示消息,而不是直接在本任務(wù)中調(diào)用顯示函數(shù)。
0:設(shè)備依賴性任務(wù)的劃分
從系統(tǒng)的結(jié)構(gòu)框圖中,我們可以看到系統(tǒng)的輸入輸出各個設(shè)備。并發(fā)性是任務(wù)的基本特性,而控制輸出、輸出的設(shè)備的程序具有先天的并發(fā)性,把他們分別封裝為不同的任務(wù)是合理的,這樣就可以劃分出第一批任務(wù),鍵盤任務(wù),顯示任務(wù),數(shù)據(jù)采集任務(wù),控制輸出任務(wù)和通信任務(wù)。
1:系統(tǒng)關(guān)鍵任務(wù)劃分:關(guān)鍵是指某種功能在應(yīng)用系統(tǒng)中的重要性,如果該功能不能正常實現(xiàn),則將造成重大影響,甚至引發(fā)災(zāi)難性的后果;包含該關(guān)鍵功能的任務(wù)為關(guān)鍵任務(wù),關(guān)鍵任務(wù)必須得到運(yùn)行機(jī)會即使遺漏一次也不行; 對于關(guān)鍵功能,必須盡可能與其他的功能剝離,獨立成為一個任務(wù),通過通信的方式再觸發(fā)其他的任務(wù),完成后續(xù)操作。例如:火災(zāi)檢測系統(tǒng)中,煙霧傳感器的檢測功能就是一個關(guān)鍵功能,必須將其與其他的報警、滅火等功能剝離。OSMboxPostOpt()消息發(fā)送函數(shù),具有廣播功能:  O在uC/OS II的較新版本中,消息發(fā)送函數(shù)OSMboxPostOpt()具有廣播功能,發(fā)送一條消息就可以使所有等待該消息的任務(wù)進(jìn)入就緒狀態(tài)。
2:緊迫任務(wù)劃分:某種功能必須在規(guī)定的時間內(nèi)得到運(yùn)行權(quán)(及時運(yùn)行),并在規(guī)定的時刻前執(zhí)行完畢(按時完成),這類功能有嚴(yán)格的實時性要求。大多數(shù)的緊迫任務(wù)是通過異步事件來觸發(fā),這些異步事件一般能夠引發(fā)某種中斷。在這種情況下,將緊迫任務(wù)安排在相應(yīng)的ISR中是最有效的辦法,如果不能安排在中斷任務(wù)中,那么可竟盡可能提高優(yōu)先級來解決“及時性”。對于按時完成,需要對緊迫任務(wù)進(jìn)行瘦身,盡可能剝離不太緊迫的操作,只剩下必須立刻做的操作,被剝離的不太緊迫的操作另外封裝成一個任務(wù)。例如:能譜分析儀,緊迫任務(wù)放在外部中斷服務(wù)程序中,完成對脈沖峰值的采樣,并將采樣結(jié)果放入消息隊列中。緊迫任務(wù)不一定是關(guān)鍵任務(wù),所以遺漏一兩次執(zhí)行會導(dǎo)致工作品質(zhì)下降,但是不會造成嚴(yán)重后果。
3:數(shù)據(jù)處理任務(wù)劃分:用戶程序中消耗時間最多的是各種數(shù)據(jù)處理程序單元,這些單元不止一個,且分別為不同的功能服務(wù);所以需要將他們劃分出來,分別包裝為不同的任務(wù),因為他們的處理較耗時,所以他們的優(yōu)先級須安排低,這樣讓他們使用其他任務(wù)的剩余時間來進(jìn)行數(shù)據(jù)處理(如果有時間片輪轉(zhuǎn),可以安排他們?yōu)橥粌?yōu)先級,利用時間片來輪轉(zhuǎn)他們)。模擬時間片輪轉(zhuǎn):假如有3個數(shù)據(jù)處理任務(wù)A、B、C;我們可以將他們細(xì)分為A1、A2、A3,B1、B2、B3,C1、C2、C3,再交叉安排優(yōu)先級就可以了。A1->1,B1->2,C1->3,A2->4,B2->5...
4:功能聚合任務(wù)劃分:將關(guān)系密切的若干功能組合為一個任務(wù),達(dá)到功能聚合的效果,關(guān)系密切:數(shù)據(jù)關(guān)聯(lián)和時序關(guān)聯(lián),如果他們分開,有可能會使用大量的通信資源,造成較大的負(fù)擔(dān)。
5:觸發(fā)條件相同的任務(wù)劃分
如果若干個功能由相同的事件觸發(fā),則可以將這些功能組合成為一個任務(wù),從而免除將事件分發(fā)給多個任務(wù)的工作量。這樣做的條件是:當(dāng)以某順序執(zhí)行這些功能時,各個功能的實時性要求仍然可以得到滿足,且各個功能在執(zhí)行過程中不會出現(xiàn)問題,例如:火警檢測系統(tǒng)中,撥打電話、啟動噴淋滅火系統(tǒng)、保持火警記錄,這些任務(wù)是不能合在一起做為一個任務(wù)的,否則其中一些功能有誤其他的任務(wù)就會被耽擱。
符合本類任務(wù)的通常是內(nèi)部事件,例如通過運(yùn)算處理產(chǎn)生某個結(jié)果,根據(jù)這個結(jié)果需要執(zhí)行若干功能,這些功能可組合為一個任務(wù)。
6:運(yùn)行周期相同的任務(wù)劃分
絕大多數(shù)功能都需要不停的重復(fù)執(zhí)行,如果重復(fù)執(zhí)行的條件是固定的時間間隔,則這個功能具有周期性。將周期相同的功能組合在一起封裝為一個任務(wù),就可以避免一個時間事件觸發(fā)幾個任務(wù),省去事件分發(fā)操作和他們的之間的通信。
7:順序操作任務(wù)劃分
如果若干個功能按固定的順序運(yùn)行流水作業(yè),相互之間完全沒有并發(fā)現(xiàn),則應(yīng)該將這個功能組合為一個任務(wù)。

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


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



 
 
相關(guān)函數(shù)使用說明:
創(chuàng)建任務(wù):
INT8U  OSTaskCreateExt (void   (*task)(void *p_arg),//被創(chuàng)建的任務(wù)函數(shù)指針
                        void    *p_arg,               //傳遞給任務(wù)的參數(shù)的指針
                        OS_STK  *ptos,                //分配給任務(wù)的堆棧的棧頂指針
                        INT8U    prio,                //被創(chuàng)建任務(wù)的優(yōu)先級
                        INT16U   id,                  //為創(chuàng)建的任務(wù)創(chuàng)建一個特殊的標(biāo)識符,暫沒用
                        OS_STK  *pbos,                //指向任務(wù)的堆棧棧底的指針(用于堆棧檢驗)
                        INT32U   stk_size,            //堆棧的容量
                        void    *pext,                //指向用戶附件的數(shù)據(jù)域的指針
                        INT16U   opt)                 //指定是否卞堆棧檢驗,是否將堆棧清0,任務(wù)是否需要進(jìn)行浮點操作
OSTaskCreate中還調(diào)用了OSTaskStkInit函數(shù),調(diào)用該函數(shù)的目標(biāo)是初始化任務(wù)的堆棧,使其看起來像發(fā)生過中斷一樣。OSTaskStkInit是需要移植的函數(shù)。
 
修改任務(wù)屬性:
調(diào)用OSTaskChangePrio()函數(shù)可以動態(tài)地改變某一個任務(wù)的優(yōu)先級,調(diào)用OSTaskNameGet()函數(shù)可以獲取某一個任務(wù)的名稱,調(diào)用OSTaskNameSet()函數(shù)可以設(shè)置一個任務(wù)的名稱。
INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio);
INT8U OSTaskNameGet INT8U   prio,INT8U  *pname,INT8U  *perr);
void  OSTaskNameSet (INT8U prio, INT8U *pname, INT8U *perr)
任務(wù)的名稱保存在任務(wù)對應(yīng)的任務(wù)控制塊中的成員:OSTCBTaskName[OS_TASK_NAME_SIZE]中;
直接掛起任務(wù)的函數(shù):
有時將任務(wù)掛起是很有用的,掛起任務(wù)的函數(shù)可以通過INT8U  OSTaskSuspend(INT8U prio)來實現(xiàn),且被掛起的任務(wù)只能通過調(diào)用INT8U  OSTaskResume (INT8U prio)來恢復(fù)。任務(wù)可以掛起自己或者其他的任務(wù)。
舉例:在一些設(shè)計中,在main函數(shù)中只有簡單的幾行,在其中創(chuàng)建了一個啟動任務(wù):用于啟動其他的任務(wù)與初始化系統(tǒng),啟動任務(wù)的優(yōu)先級比它要創(chuàng)建的任務(wù)都要高,初始化成功與創(chuàng)建完相關(guān)任務(wù)之后,一般自己將自己掛起。
堆棧檢驗函數(shù):
如果要使用堆棧檢驗函數(shù),那么在任務(wù)的建立需要使用OSTaskCreateExt()來建立任務(wù),且需要指定入口參數(shù):opt(OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
OS_TASK_OPT_STK_CHK:指示任務(wù)需要使用堆棧校驗功能
OS_TASK_OPT_STK_CLR:將任務(wù)的堆棧RAM清0(因為堆棧檢驗是從棧底開始計算為0(空閑)的空間大小)
在任務(wù)創(chuàng)建時指定棧頂、棧底,在任務(wù)運(yùn)行一段時間后,一般需在堆棧使用最充分后才去檢驗較為準(zhǔn)確;任務(wù)可以檢查自己或者其他任務(wù)的堆棧使用情況,堆棧檢驗函數(shù)確定堆棧的實際空間字節(jié)數(shù)和已被占用的字節(jié)數(shù),放在入口參數(shù)OS_STK_DATA數(shù)據(jù)結(jié)構(gòu)中;
[可以閱讀 嵌入式ARM系統(tǒng)原理與實例開發(fā)(楊宗德)的第八章,里面對該系統(tǒng)包含的大部分函數(shù)簡要說明以及移植時的相關(guān)函數(shù)說明]

任務(wù)切換過程分析
1:任務(wù)之間搶占式切換(高優(yōu)先級的任務(wù)就緒后立即搶占正在運(yùn)行的低優(yōu)先級的任務(wù))
系統(tǒng)在任何響應(yīng)后,都需要進(jìn)行任務(wù)調(diào)度,確保系統(tǒng)中優(yōu)先級最高的任務(wù)被執(zhí)行,那么系統(tǒng)采用的任務(wù)調(diào)度切換函數(shù)就是void  OS_Sched(void);
void  OS_Sched(void)
    OS_ENTER_CRITICAL();   //進(jìn)入臨界區(qū) 關(guān)中斷 
    if(OSIntNesting==0) {  //目前系統(tǒng)不處于中斷態(tài)                         
        if(OSLockNesting==0) { //系統(tǒng)調(diào)度功能使能                    
            OS_SchedNew();   //計算出目前系統(tǒng)中最高的優(yōu)先級,保存在INT8U OSPrioHighRdy中
            if(OSPrioHighRdy!=OSPrioCur) { //判斷當(dāng)前正運(yùn)行的任務(wù)是不是最高優(yōu)先級 否則就不用切換
                 OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//將最高優(yōu)先級任務(wù)的控制塊的指針放                                                             //入任務(wù)控制塊指針變量OSTCBHighRdy
                 #if OS_TASK_PROFILE_EN > 0
                    OSTCBHighRdy->OSTCBCtxSwCtr++; //目前優(yōu)先級最高的任務(wù)切換次數(shù)統(tǒng)計        
                 #endif
                 OSCtxSwCtr++; //系統(tǒng)任務(wù)切換的總次數(shù)統(tǒng)計
                 OS_TASK_SW(); //實現(xiàn)任務(wù)切換
            }
        }
     }
     OS_EXIT_CRITICAL(); //恢復(fù)全局中斷標(biāo)志 退出臨界區(qū)
 
在任務(wù)調(diào)度前,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 為設(shè)置ICSR的值(0x10000000
    STR     R1, [R0]            //設(shè)置ICSR = NVIC_PENDSVSET  即將觸發(fā)PendSV中斷
    BX      LR                  //因為在OS_Sched中調(diào)用OSCtxSw時,是在臨界區(qū),中斷是關(guān)閉的,所以這里通過BX LR返回OS_Sched,等退出臨界區(qū)后,觸發(fā)PendSV中斷【任務(wù)切換時必須關(guān)中斷”的原則
PendSV中斷響應(yīng)函數(shù)OSPendSV
//Cortex-M3進(jìn)入異常服務(wù)例程時,使用的是MSP指向的堆棧空間,且自動壓入了R0-R3,R12,LR(R14,連接寄存器),PSR(程序狀態(tài)寄存器)和PC(R15),這些寄存器是任務(wù)被中斷時的現(xiàn)場記錄此時任務(wù)所使用的PSP的值沒有變化。
//M3處理器的控制寄存器的CONTROL[1] = 1(系統(tǒng)復(fù)位后默認(rèn)是0),表明M3的線程模式的堆棧指針SP選用PSP【handler模式只允許使用MSP】,這樣兩個模式下SP使用不同的堆棧指針,且訪問SP時,訪問到的是當(dāng)前被使用的堆棧指針,這時另一個堆棧指針要通過MRS、MSR命令來訪問,即R13(PSP)或R13(MSP),R13是PSP還是MSP由系統(tǒng)當(dāng)前的狀態(tài)與CONTROL[1]決定,PSP是某個任務(wù)在RAM中的堆棧指針,MSP是異常模式下在RAM中的堆棧指針。
//一個任務(wù)占用CPU運(yùn)行,它的運(yùn)行現(xiàn)場是R0-R3,R4-R11,R12,R13(PSP/MSP),R14(LR),PSR和R15(PC),如果該任務(wù)被中斷,那么在進(jìn)中斷過程中,硬件會自動對CPU的部分現(xiàn)場寄存器(R0-R3,R12,R14,PSR和R15)進(jìn)行壓棧,其他寄存器硬件不壓棧,如果CONTROL[1] = 1,那么硬件就會將相關(guān)寄存器壓入PSP(R13)指向的堆棧中(線程模式時SP為PSP),CONTROL[1] = 0,硬件就會將相關(guān)寄存器壓入MSP(R13)指向的堆棧中;CONTROL[1] = 1時如下圖所示,線程模式與handler模式的SP不一樣。
 
OSPendSV   【在進(jìn)入中斷前 R0-R3,R12,R14,PSR和R15已被壓入任務(wù)所使用的堆棧中,下面是將其他的寄            存器再壓入任務(wù)的堆棧中,以及保存該任務(wù)的堆棧棧頂?shù)闹档絇SP指向的堆棧中去,任務(wù)使用            的是PSP,異常模式下使用的是MSP
    MRS     R0, PSP   //R0<=PSP 異常模式下使用的是MSP,所以只能通過MRS讀取被中斷的任務(wù)的堆棧指針,
    CBZ     R0, OSPendSV_nosave //如果PSP==0,則跳轉(zhuǎn)到 OSPendSV_nosave ,否則往下執(zhí)行【在系統(tǒng)剛開                                //始啟動時,OSStart()會調(diào)用OS_SchedNew()與OSStartHighRdy()來使                                 //系統(tǒng)中最高優(yōu)先級的任務(wù)運(yùn)行,在OSStartHighRdy中,PSP設(shè)置為0,因為                                 //在OSStart前還沒有任務(wù)運(yùn)行,所以進(jìn)入軟中斷后不用保存此時CPU的值,                                 //只要將最高優(yōu)先級任務(wù)的現(xiàn)場恢復(fù)到CPU的各寄存器就好了
    SUBS    R0, R0, #0x20   //R0(PSP)!=0,那么R0的值減去0X20,32個字節(jié)(下面保持8個32位的寄存器)
    STM     R0, {R4-R11}    //將R4~R11這8個寄存器存儲到PSP對應(yīng)的空間中去,這些寄存器是當(dāng)前任務(wù)被中                             //斷時的現(xiàn)場
    LDR     R1, __OS_TCBCur //__OS_TCBCur  DCD  OSTCBCur
                            //當(dāng)前任務(wù)(被中斷的任務(wù))的任務(wù)控制塊OSTCBCur, OSTCBCur是當(dāng)前任務(wù)控制                             //塊的首地址,也代表了它的第一個成員的地址,所以下面其實是對他的第一個                             //成員OS_STK *OSTCBStkPtr(指向任務(wù)的堆棧棧頂?shù)闹羔槪┻M(jìn)行賦值
    LDR     R1, [R1]        //將OSTCBStkPtr中保持的值賦給R1,該值指向任務(wù)的堆棧棧頂?shù)闹羔?/span>
                            //即R1存儲被中斷的當(dāng)前任務(wù)的堆棧棧頂?shù)闹羔?/span>
    STR     R0, [R1]        //將被中斷的當(dāng)前任務(wù)的堆棧棧頂?shù)闹羔槾娴絇SP指向的堆棧中去,即保存被中斷                             //任務(wù)所使用的堆棧棧頂?shù)闹羔?上面其他的寄存器是任務(wù)被中斷的線場
OSPendSV_nosave  【上面的程序執(zhí)行完后,接著繼續(xù)執(zhí)行下面的程序】
    PUSH    {R14}           //保存R14寄存器的值 壓入到MSP指向的堆棧中,該寄存器在中斷返回時大有作用
    LDR     R0, __OS_TaskSwHook //調(diào)用回調(diào)函數(shù)  鉤子函數(shù)        
    BLX     R0
    POP     {R14}           
    -------------------------------------------
    //相當(dāng)于定義指針變量:__OS_PrioCur DCD   OSPrioCur;  __OS_PrioHighRdy  DCD  OSPrioHighRdy
    LDR     R0, __OS_PrioCur     //INT8U OSPrioCur當(dāng)前任務(wù)的優(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ùn)行最高優(yōu)先級任務(wù))
                                 //即實現(xiàn):OSPrioCur = OSPrioHighRdy;
    -------------------------------------------------
    //相當(dāng)于定義指針變量:__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)先級最高的任務(wù)的任務(wù)控制塊
    STR     R2, [R0]             //*R0 = R2即實現(xiàn):OSTCBCur = OSTCBHighRdy;
                                 //系統(tǒng)中用OSPrioCur   OSTCBCur 來表示正在運(yùn)行的任務(wù)
    ----------------------------------------------
   通過R2(OSTCBHighRdy)將OSTCBHighRdy->OSTCBStkPtr的值賦給R0,再通過LDM將最高優(yōu)先級任務(wù)的堆棧中保存好的R4-R11恢復(fù)到當(dāng)前CPU的R4-R11寄存器中(這個過程與上面的保存過程是相逆的),因為R0-R3,R12,R14,PSR和R15是硬件自動壓入任務(wù)的堆棧的,在中斷中后來再保存R4-R11的(是先對SP減了32后再保存,相當(dāng)于SP的值沒有變化),且中斷退出時會自動彈出R0-R3,R12,R14,PSR,所以下面的代碼只要恢復(fù)最高優(yōu)先級任務(wù)的R4-R11(OSTCBHighRdy->OSTCBStkPtr通過LDM自減了32,),然后將加上32, OSTCBHighRdy->OSTCBStkPtr 相當(dāng)于沒有變化,再將OSTCBHighRdy->OSTCBStkPtr賦給PSP,這樣硬件在中斷退出時會自動恢復(fù) R0-R3,R12,R14,PSR ,這樣就完全恢復(fù)了,一運(yùn)行就是最高優(yōu)先級的任務(wù)了
    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的值自減,后綴為!表示自增),因為堆                                   //棧保持時是自減的,這里相當(dāng)于恢復(fù)最高優(yōu)先級任務(wù)的現(xiàn)場)               
    ADDS    R0, R0, #0x20        //將R0的值加上0x20后賦給R0
    MSR     PSP, R0              //PSP <= R0                              
    ORR     LR, LR, #0x04    //按位或 將LR的第二位置1,這樣中斷返回時,就將從進(jìn)程的堆棧中做                              //出棧操作,返回后使用PSP(否則將從主堆棧中做出棧操作,返回后                              //使用MSP)                                
    BX      LR                   //中斷返回
 
xPSR、PC、LR、R12、R3、R2、R1、R0會被硬件按一定的次序壓入PSP所指向的堆棧中(同時被中斷的任務(wù)后面要恢復(fù)執(zhí)行,還需保存R4-R11,保存后一定要保證堆棧指針指向硬件自動壓棧后的原位置,這樣硬件才能在恢復(fù)任務(wù)時硬件自動正確出棧)
 
 
2:在中斷中實現(xiàn)任務(wù)的切換
uC/OS II  的中斷服務(wù)函數(shù)必須遵循一定的架構(gòu)來實現(xiàn),
void SysTickHandler(void)
{
    OS_CPU_SR  cpu_sr;
    OS_ENTER_CRITICAL();  //保存全局中斷標(biāo)志,關(guān)總中斷
    OSIntNesting++;
    OS_EXIT_CRITICAL();  //恢復(fù)全局中斷標(biāo)志

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

OSInit函數(shù)
系統(tǒng)初始化函數(shù)OSInit()初始化所有的變量和數(shù)據(jù)結(jié)構(gòu),同時也會建立空閑任務(wù)OS_TaskIdle(),該任務(wù)永遠(yuǎn)處于就緒態(tài),如果統(tǒng)計任務(wù)使能,那么他還要建立統(tǒng)計任務(wù)OS_TaskStat(),并使其進(jìn)入就緒態(tài)。
空閑任務(wù)
  OS_TaskIdle
uC/OS II總要建立一個空閑任務(wù),idle task,這個任務(wù)在沒有其他任務(wù)進(jìn)入就緒態(tài)時投入運(yùn)行,它的優(yōu)先級永遠(yuǎn)設(shè)為最低優(yōu)先級,即OS_LOWEST_PRIO,且空閑任務(wù)是不能被應(yīng)用軟件刪除的
空閑任務(wù)不停的給一個32位的OSIdleCtr的變量加1,統(tǒng)計任務(wù)用這個計數(shù)器變量確定當(dāng)前應(yīng)用軟件實際消耗CPU的時間,計數(shù)器加1前后分別關(guān)閉打開中斷,OS_TaskIdle可以借助OSTaskIdleHook()做CPU的睡眠等;OS_TaskIdle總是處于就緒態(tài)
統(tǒng)計任務(wù)OS_TaskStat
只要將OS_TASK_STAT_EN宏使能,那么統(tǒng)計任務(wù)就會建立,一旦運(yùn)行,它將每秒運(yùn)行一次,計算當(dāng)前CPU的利用率,將值放在OSCPUsage這個8位的變量中,用百分比表示,精度為1%.如果應(yīng)用程序打算使用統(tǒng)計任務(wù),那么必須在初始化時建立的第一個也是唯一的任務(wù)中調(diào)用統(tǒng)計任務(wù)初始化函數(shù)OSStatInit(),也就是在調(diào)用系統(tǒng)啟動函數(shù)OSStart前,用戶初始化代碼中必須先建立一任務(wù),在這個任務(wù)中調(diào)用系統(tǒng)統(tǒng)計初始化函數(shù)OSStatInit,然后再建立應(yīng)用程序中的其他任務(wù)。它的優(yōu)先級是OS_LOWEST_PRIO-1
OSStart操作系統(tǒng)啟動函數(shù)
uC/OS II啟動之前,至少須建立一個應(yīng)用程序,因為如果使能統(tǒng)計任務(wù),那么統(tǒng)計任務(wù)要求必須先建立一個也是唯一一個用戶任務(wù)后,再啟動系統(tǒng),再創(chuàng)建其他的任務(wù),如果什么任務(wù)都沒有建立,只要空閑任務(wù),那系統(tǒng)也就一直空閑,所以需要先創(chuàng)建至少一個任務(wù)。

關(guān)閉窗口
主站蜘蛛池模板: 欧美一区二区在线观看 | 久久精品免费观看 | 97久久精品午夜一区二区 | 喷潮网站 | 一区二区三区免费 | 欧美精品1区 | 国产黄色大片网站 | 美女一级毛片 | 亚洲一二三区不卡 | 中文字幕精品一区二区三区精品 | 亚洲精品欧美 | 久亚州在线播放 | 中文字幕在线一区 | 国产在线一区观看 | 黄色在线免费观看视频 | 在线播放中文字幕 | 久久久久九九九女人毛片 | 欧美在线精品一区 | 精品一区二区三区在线观看 | 99久久久久 | 91香蕉| 国产传媒在线观看 | 国产日韩一区二区三区 | 九九热国产精品视频 | 日韩视频在线免费观看 | 成人高潮片免费视频欧美 | 久久国产精品久久久久久 | 亚洲一区在线播放 | 欧美日韩三级在线观看 | 久久这里只有精品首页 | 最新午夜综合福利视频 | 国产一区二区三区免费 | av在线播放一区二区 | 国产日韩欧美一区二区在线播放 | 毛片网络 | 一区二区免费高清视频 | 在线观看亚洲专区 | 亚洲欧美中文日韩在线v日本 | 特黄视频| 日韩欧美手机在线 | 日韩字幕|