主要包含在C源文件OS_CORE.C中。
1. uC/OS-II任務調度
(1) uC/OS-II調度算法
uC/OS-II採用基於優先級的調度算法,總是選擇當前處於就緒狀態的優先級最高的任務進行調度。uC/OS-II是可搶佔性的強實時性OS,在完成中斷後允許進行新的任務調度。
uC/OS-II有兩種調度方式:任務級任務調度、中斷級任務調度。
(2) 任務就緒表
INT8U const OSUnMapTbl[256] = {…};
OS_EXT INT8U OSRdyGrp;
OS_EXT INT8U OSRdyTbl[OS_RDY_TBL_SIZE];
添加就緒任務至就緒表;
從就緒表刪除就緒任務;
查找最高優先級就緒任務OS_SchedNew();
(3) 任務級任務調度
指在非中斷返回時進行任務調度,一般發生在當前任務因時間延遲或等待某事件而阻塞或被掛起,或有更高優先級的任務處於就緒狀態。
任務的基本信息:
CPU的PC寄存器:任務當前執行的位置;
CPU的通用寄存器:任務當前執行涉及的臨時數據;
CPU的狀態寄存器:存儲當前CPU的狀態。
任務級任務切換:從一個任務直接切換至另一個任務,不涉及CPU狀態的切換,OS_TASK_SW()既保存當前任務上下文,又恢復新任務上下文。
過程:OS_Sched() -> OS_SchedNew() -> OS_TASK_SW()
(4) 中斷級任務調度
中斷級任務切換:在中斷處理完成後,通過OSIntExit()判斷是否有更高優先級就緒任務。如果有,調用OSIntCtxSW()恢復新任務上下文。注意:在中斷處理中,已經保存了被中斷任務的上下文,所以這裏僅僅恢復。
過程:OSIntExt() –> OSIntEnter() -> ISR –> OSIntExit() -> OSIntCtxSW()
(5) 調度器上鎖與解鎖
uC/OS-II提供調度器鎖定功能,在鎖定期間不能進行任務調度。uC/OS-II使用全局變量OSLockNesting標識是否鎖定了任務調度器。
OS_EXT INT8U OSLockNesting;
void OSSchedLock(void);
void OSSchedUnlock(void);
(6) 中斷管理函數
在中斷處理中,不允許進行任務管理、事件管理及任務調度等操作。uC/OS-II通過全局變量OSIntNesting標識當前是否處於中斷狀態。在所有任務及事件管理的程序中,都有對OSIntNesting進行判斷的語句。
void OSIntEnter(void);
void OSIntExit(void);
(7) 中斷相關問題
OS_ENTER_CRITICAL()
OS_EXIT_CRITICAL()
關中斷使得uC/OS-II能夠同時避免有其他任務或中斷服務進入臨界代碼段。調用uC/OS-II功能函數時,中斷總應當是開着的。
uC/OS-II如何禁止調度?
在中斷中允許調度嗎?爲什麼?
uC/OS-II如何屏蔽中斷?
2. uC/OS-II系統啓動
uC/OS-II首先調用OSInit()進行初始化,然後創建任務(此時還未啓動系統,僅僅爲其分配資源),然後調用OSStart()啓動系統,將CPU控制權交給uC/OS-II,OS根據任務優先級選擇由哪個任務開始執行,或創建新的任務。
(1) 初始化函數OSInit()
OSInit()主要完成初始化操作,包括初始化全局變量(在OS_InitMisc()中)、任務就緒表、TCB、ECB、FCB、內存單元、消息隊列,並創建空閒任務。如果有必要,創建統計任務。
OS_InitMisc(); //初始化部分全局變量
OS_InitRdyList(); //初始化任務就緒表
OS_InitTCBList(); //初始化空閒TCB鏈表
OS_InitEventList(); //初始化ECB鏈表
OS_FlagInit(); //初始化事件組標誌結構
OS_MemInit(); //初始化內存管理
OS_QInit(); //初始化消息隊列
OS_InitTaskIdle(); //創建空閒任務
OS_InitTaskStat(); //創建統計任務
uC/OS-II初始化了5個空的數據結構緩衝區,每個緩衝區都是單向鏈表,允許uC/OS-II從緩衝區中迅速取得或釋放一個緩衝區中的元素。
uC/OS-II調用OSInit()後的變量與數據結構如下圖所示:
(2) 啓動函數OSStart()
OSStart()在一切準備就緒且需要首先創建的任務都被創建後,啓動uC/OS-II。它從就緒表中查找最高優先級就緒任務,並恢復其上下文開始執行。
過程:OSStart() -> OS_SchedNew() -> OSStartHighRdy()
問題:任務第一次被調用時,哪來的上下文供其恢復呢?創建任務時,調用了OSTaskStkInit()初始化任務堆棧,可此函數中沒有涉及任務的上下文呀?
uC/OS-II調用OSStart()後的變量和數據結構如下圖所示:
(3) 統計任務OSTaskStat
OSTaskStat用於計算CPU利用率。設置OS_CFG.H中的OS_TASK_STAT_EN爲1,創建統計任務,在系統啓動後一直處於就緒狀態。剛開始時,空閒任務運行1S,爲計算CPU利用率提供一個基準值,並保存在統計任務的堆棧中,這個值不會改變除非重新啓動CPU。此後空閒任每次被其它任務搶去CPU時,它裏面的計數器就會直接記錄下CPU空閒的時間。
3. uC/OS-II系統時鐘
任何實時系統的時鐘硬件設備每隔一段時間(一個系統tick)產生一個硬件中斷,OS接收到該中斷後,更新時間計數器,更新所有對時鐘依賴的程序代碼,從而維持系統有序穩定的運行。
主要包含在C源文件OS_TIME.C中。
#define OS_TICKS_PER_SEC 100 //系統時鐘中斷間隔
OS_EXT volatile INT32U OSTime; //系統運行的時間值
void OSTimeTick(void); //時鐘中斷服務程序
void OSTimeDly(INT16U ticks); //延遲指定時鐘節拍
INT8U OSTimeDlyHMSM(…); //延遲指定時間長度
INT8U OSTimeDlyResume(prio); //恢復等待(時延/阻塞)任務
INT32U OSTimeGet(void); //讀取當前時間
void OSTimeSet(INT32U ticks); //設置當前時間
4. uC/OS-II事件管理
(1) 事件控制塊
INT8U OSEventType; //事件類型
void *OSEventPtr; //指向MBox或Queue
INT16U OSEventCnt; //信號量計數器(注:Mutex)
INT8U OSEventGrp; //事件等待組標誌
INT8U OSEventTbl[]; //時間任務等待表
INT8U OSEventName[]; //時間名稱
(2) ECB管理機制
OS_EXT OS_EVENT *OSEventFreeList; //空閒ECB鏈表指針
OS_EXT OS_EVENT OSEventTbl[]; //ECB結構體數組
(3) ECB管理函數
OS_InitEventList():初始化ECB;
OS_EventWaitListInit():在創建事件時調用,初始化ECB任務等待表;
OS_EventTaskRdy():在事件發生時調用,修改此事件中最高優先級任務的TCB成員變量,在任務就緒表中添加此任務,將相應信息傳遞給該任務TCB,並將此任務從事件的任務等代表中刪除;
OS_EventTaskWait():在申請資源失敗而暫停當前任務時被調用,將任務從任務就緒表中刪除,並添加到事件的任務等代表中;
OS_EventTO():在事件等待超時時調用,將此任務從事件的任務等代表中刪除,並修改該任務的TCB成員變量;