ucos ii system
文件結構
上層:
應用軟件,用戶代碼
中層:
- 與處理器無關代碼
- 與應用程序相關配置文件
- 與處理器有關代碼
下層:
硬件(cpu,interupt,timer,gpio,iis…)
內核結構
ucos的內核機構可以從以下的代碼可以看出,應用支持10個事件控制塊,5個事件標志組,5個內存區塊,4個隊列控制塊和20個任務,最低優先級為63,任務堆棧大小都為128等等,這些都是可以在OS_CFG.H中自行定義的.
臨界段
處理器處理臨界代碼都必須先關中斷,再處理臨界代碼,然后再開中斷。關中斷時間對實時系統的實時響應很重要。所以是實時系統的一個很重要的指標。uCOS使用兩個宏(在OS_CPU.h中定義。注:沒個CPU都有自己的OS_CPU.h)。這兩個宏分別為OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()關閉中斷和打開中斷. 列:
void function(void)
{
OS_ENTER_CRITICAL(); //關閉中斷
/*uCOS II 臨界代碼段*/
OS_EXIT_CRITICAL(); //打開中斷
}
注:在ODTimeDel()之類的函數調用的時候不能關閉中斷,不然應用程序會死機.
任務
通常任務是一個無限循環。函數沒有返回值。任務完成以后可以自我刪除。(注意:刪除不是任務代碼刪除了,只是這個任務不會再執行了;即使調用了OSTaskDel()這個任務也不會有返回值).
任務創建:
可以使用OSTaskCreat()
或者OSTaskCreatExt()
創建.uCOS II 可以管理的任務可以達到64個,但是建議不要使用前四個優先級的任務和后四個優先級的任務.
這里OSTaskCreate
創建了一個任務TeskTask1
,傳給任務的參數為空(void *)0
,棧頂地址為&TaskStartStk[TASK_STK_SIZE - 1],優先級為0,創建第一個任務前需要先初始化系統OSInit()
,創建完任務后就可以調用OSStart()開始多任務.
TeskTask1
首先初始化統計任務,然后依次創建三個任務:TestTask2,TestTask3,TestTask4.然后進入while循環.任務永遠不會退出,但可以通過OSTimeDly
或者OSTaskSuspend
掛起.
任務狀態:
1. 睡眠: 駐留在ROM或者RAM中,系統還沒有管理,只有通過`OSTaskCreat()`或`OSCreatExt()`創建之后才能使得系統管理任務.
2. 就緒: 任務一旦建立就進入了就緒態,等待運行.
3. 等待: 可以調用`OSTimeDel()`或者`OSTimeDlyHMSM()`使得任務進入等待狀態。一直等待函數中定義的延時時間到了,這兩個函數會強制執行任務轉換,讓下一個優先級更高的任務進入就緒態的任務運行.
4. 運行: 當前任務正在執行.
5. 中斷: 當前正在執行的任務被中斷,進入中斷服務態,響應中斷時該任務被掛起。中斷服務子程序占有了CPU的使用權.
任務控制塊(TCB):
重要的數據結構,一旦任務建立了,任務控制塊OS_TCB
將被賦值,ucosii
用它保存任務的狀態,用來恢復任務. 任務建立的時候,OS_TCBs
就被初始化了. 關鍵的結構體變量:
1. OSTCBStkPtr: 當前任務棧頂的指針.是OS_TCB數據結構中唯一的一個能用匯編語言來處置的變量(在任務切換段的代碼中),把OSTCBStkPtr
放在數據結構的最前面,使得從匯編語言中處理這個變量時較為容易.
2. OSTCBExtPtr: 指向用戶定義的任務控制塊擴展.用戶可以擴展任務控制塊而不必修改μC/OS-Ⅱ的源代碼.
3. OSTCBStkBottom: 指向任務棧底的指針.函數OSTaskStkChk()
(用於堆棧檢驗)要用到變量OSTCBStkBottom
,在運行中檢驗棧空間的使用情況。用戶可以用它來確定任務實際需要的棧空間.這個功能只有當用戶在任務建立時允許使用OSTaskCreateExt()
函數時才能實現。這就要求用戶將OS_TASK_CREATE_EXT_EN
設為1以便允許該功能.
4. OSTCBStkSize: 存有棧中可容納的指針元數目,而不是用字節表示的棧容量總數.
任務就緒表:
每個就緒的任務都放在任務就緒表中。就緒表中有兩個變量,OSRdyGrp
和OSRdyTbl[OS_RDY_TBL_SIZE]
,在OSRdyGrp
中任務按照優先級分組,8個任務為一組。OSRdyGrp
中的每位表示8組任務中每一組是否有進入就緒態的任務,任務就緒,OSRdyTbl[OS_RDY_TBL_SIZE]
中相應元素中的相應位也被置1.OSRdyTbl[OS_RDY_TBL_SIZE]
數組有多大取決於OS_LOWSET_PRIO
.當應用程序的數目比較少的時候可以降低OS_LOWSET_PRIO
,可以降低系統對RAM(數據空間)的需求.
就緒表的定義如下,大小由最低優先級確定.
系統通過OSRdyGrp和OSRdyTbl[OSUnMapTbl[OSRdyGrp]]來確定就緒表中的最高優先級任務.
優先級判定表OSUnMapTbl[]定義如下:
任務調度:
根據任務優先級,UCOS總是讓就緒表中任務優先級最高的任務先執行.
任務的調度由函數:OSSched()完成。中斷級的調用由另一個函數:OSIntExt()完成。
任務切換:
任務調度器決定哪個任務該運行了,然后由IS_TASK_SW()函數做任務切換.OS_TASK_SW()是一個宏調用。含有處理器的軟中斷指令.
注:UCOSII只有任務的概念,目前沒有進程的概念,那任務和進程的區別是什么,這也是UCOSII這么小的一個原因,還有任務之間的內存分配要划分好空間,否則某個任務如果寫了另外一個任務的空間,那么就有可能導致系統崩潰,但像linux有了進程概念,就不會出現這種情況,這些都是要逐漸了解的. 可信度?
調度
上鎖,開鎖
空閑任務
優先級最低,必須存在
統計任務
優先級次低,選擇
每秒運行一次計算CPU利用率,精度1%。
中斷
時鍾節拍
提供周期性信號源,用於時間延遲和確認超時。
流程
OSInit();//ucos的初始化,空閑任務,統計任務,系統變量及數據結構
OSTaskCreate();//任務創建
OSStart();//ucos的啟動,必須建立一個任務
時間管理
時間管理的內容在代碼os_time.c
中,包含操作系統時間的設置及獲取,對任務的延時,任務按分秒延時,取消任務的延時共5個系統調用。時間管理的最主要功能就是對任務進行延時。
時間管理中最重要的數據結構就是全局變量OSTime,OSTime的值就是操作系統的時間,它的定義在uC/OS-II的頭文件ucos_ii.h中:
其中關鍵字volatile總是與優化有關,volatile意味着禁止對變量進行優化.因為OSTime的值是易變的,加了關鍵字volatile后,不會被編譯器優化,每次取值都會直接在內存中對該變量的地址取值,從而保證不會因為編譯器優化而產生錯誤的結果.
OSTime在操作系統初始化時被設置為0。
時間管理中使用的另一個重要的數據結構就是任務控制塊,任務控制塊有一項是OSTCBDly,標志這個任務延時的時間。這個時間是以兩次時鍾中斷間隔的時間為單位的。另外,對任務的延時實際上阻塞了任務,因此要對就緒表和就緒組等數據結構進行相關的操作.
時間的設置和獲取都是關於OSTime的賦值,代碼比較簡單,如下所示:
需要注意的是,對OSTime的操作一定要使用臨界區。時間設置函數將參數ticks的值賦值給OSTime,這兩個函數並不常用.
任務延時函數OSTimeDly用於阻塞任務一定時間,這個時間以參數的形式給出。如果這個參數的值是N,那么在N個時間片(時鍾滴答)之后,任務才能回到就緒態獲得繼續運行的機會。如果參數的值是0,就不會阻塞任務。任務延時函數OSTimeDly的代碼如下所示:
代碼清晰,OSLockNesting是調度鎖,也就是說,如果OSLockNesting>0,那么不允許進行任務調度.因為任務延時的時候要中止當前任務的執行,所以要進行調度,因此在調度鎖有效的情況下是不能執行任務延時的.如果延時時間大於0,那么就要進行一次任務調度,將當前的任務的就緒標志取消,也就是對就緒表和就緒組的相關操作.之后延時時間賦值給任務塊的OSTCBDly項以對延時計數。操作系統在每個時鍾中斷都要對每個OSTCBDly大於0的任務的OSTCBDly進行減1操作和進行任務調度,那么當任務的延時時間到了的時候(OSTCBDly為0)就可以恢復到就緒態.
注:需要注意的是,如果將任務延時1個時間片,調用OSTimeDly(1),會不會產生正確的結果呢?回答是否定的。這是因為任務在調用時間延時函數的時候可能已經馬上就要發生時間中斷了,那么設置OSTCBDly的值為1,想延時10ms,然后系統切換到一個新的任務運行。在可能極短的時間,如0.5ms的時候就進入時鍾中斷服務程序,立刻將OSTCBDly的值減到0了。調度器在調度的時候就會恢復這個才延時了0.5ms的任務。可見,OSTimeDly的誤差最大應該是1個時間片的長度,OSTCBDly(1)不會剛好延時10ms,如果真的需要延時一個時間片,最好調用OSTCBDly(2).
任務延時函數OSTimeDly用於將任務阻塞一段時間,這個時間是以時間片為單位的。如果想以時、分、秒、毫秒為單位進行任務延時,需要調用以分秒作為單位的任務延時函數OSTimeDlyHMSM
.
任務在延時之后,進入阻塞態。當延時時間到了就從阻塞態恢復到就緒態,可以被操作系統調度執行。但是,並非回到就緒態就只有這么一種可能,因為即便任務的延時時間沒到,還是可以通過函數OSTimeDlyResume
恢復該任務到就緒態的.
另外,OSTimeDlyResume也不僅僅能恢復使用OSTimeDly
或OSTimeDlyHMSM
而延時的任務。對於因等待事件發生而阻塞的,並且設置了超時(timeout)時間的任務,也可以使用OSTimeDlyResume來恢復。對這些任務使用了OSTimeDlyResume
,就好像已經等待超時了一樣。
但是,對於,采用OSTaskSuspend
掛起的任務,是不允許采用OSTimeDlyResume
來恢復的。
代碼中一個非常重要的數據結構就是任務塊的OSTCBStat,如下所示:
宏定義如下:
因此,如果一個任務只是設置了延時,那么該任務塊的OSTCBStat的值應該是0,也就是OS_STAT_RDY.被設置了延時的任務和就緒任務的區別在於,就緒任務的控制塊的OSTCBDly的值一定是0,而設置了延時的任務的OSTCBDly的值一定不是0.
如果一個任務在等待一個或多個事件的發生,那么該任務的控制塊的0、1、2、4、5位必然有1位或多位不為0.也就是ptcb->OSTCBStat&OS_STAT_PEND_ANY
的值不為0.這是在判斷任務是不是在等待事件的發生。等待事件發生的任務可能設置了超時,也可能沒有設置超時,如果沒有設置超時那么就會在下面所示的代碼返回:
所以不會被恢復到就緒態。設置了超時的OSTCBDly的值大於0,先將OSTCBDly的值置位為0,然后使用ptcb->OSTCBStat&=~OS_STAT_PEND_ANY
,所以的5種事件等待標志全部強制清0,不再等待了。
另外,還需要判斷OSTCBStat的位3掛起標志。因為被掛起的任務必須用也只能用OSTaskResume來恢復。OS_STAT_SUSPEND
的值是0x08,ptcb->OSTCBStat&OS_STAT_SUSPEND
是將STCBStat
的位3掛起標志位單獨取出來了,判斷它是不是0,如果是0,那么就不是被掛起的任務,否則就是被掛起的任務。對於掛起的任務只能處理到這里,對於其他的任務就開始對就緒表和就緒組進行處理,恢復任務到就緒態,然后執行任務調度。
事件管理
事件控制塊結構體(UCOS_II.H)
信號量管理
信號量數據結構
互斥信號量數據結構
消息管理
消息郵箱數據結構
消息隊列數據結構
內存管理
內存塊結構體
ucos ii的移植
ucos ii給實時系統開發提供一個簡易的框架,其實現基本的系統管理功能。
開發者需要做的是根據自己的平台修改和剪裁。
需要設計中修改的如處理器的位數,開中斷和關中斷實現等。
調通基本的系統,就可以在上面根據需要開發更多的功能。