嵌入式實時作業系統ucos/ii 原理與應用(三)
三、uC/OS-II的中斷和時鐘
3.1 uC/OS-II的中斷
3.1.1 uC/OS-II的中斷過程
uC/OS-II系統響應中斷的過程是:系統接收到中斷請求後,如果這是CPU處於中斷允許狀態(即中斷時開放的),系統就會中止正在執行的當前任務,而按照中斷向量的指向轉而去執行中斷服務子程式;當中斷服務子程式的執行結束後,系統會根據情況返回到被中止的任務去繼續執行,或者轉向執行另一個具有更高優先級別的就緒任務。
uC/OS-II中斷響應的過程示意圖:
函式OSIntEnter()的作用就是把全域性變數OSIntNesting
加1,從而用它記錄中斷巢狀的層數。OSIntEnter()的程式碼如下:
void OSIntEnter(void)
{
if(OSRunning == TRUE)
{
if(OSIntNesting < 255)
{
OSIntNesting++; //中斷巢狀層數計數器加1
}
}
}
函式OSIntExit()的流程圖:
退出中斷服務函式OSIntExit()的原始碼如下:
void OSIntExit(void)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
if(OSRunning == TRUE)
{
OS_ENTER_CRITICAL();
if(OSIntNesting > 0)
{
OSIntNesting--; //中斷巢狀層數計數器減1
}
if((OSIntNesting == 0) && (OSLockNesting == 0))
{
OSIntExitY = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
if(OSPrioHighRdy != OSPrioCur)
{
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++;
OSIntCtxSw();
}
}
OS_EXIT_CRITICAL();
}
}
一箇中斷服務子程式的流程圖如下:
在uC/OS-II中,通常用一個任務來進行非同步事件的處理,而在中斷服務程式只是通過向任務發訊息的方法去啟用這個任務。
3.1.2 中斷級任務切換函式
中斷級任務切換函式OSIntCtxSw()示意程式碼如下:
OSIntCtxSw()
{
OSTCBCur = OSTCBHighRdy; //任務控制塊的切換
OSPrioCur = OSPrioHighRdy;
SP = OSTCBHighRdy -> OSTCBStkPtr; //使SP指向待執行任務堆疊
用出棧指令把R1、R2、…彈入CPU的通用暫存器中;
RETI; //中斷返回,使PC指向待執行任務
}
3.1.3 應用程式中的臨界段
1.臨界段的基本概念
在uC/OS-II中,那些不希望被中斷的程式碼段就是臨界段;
從程式碼上來看,處在關中斷和開中斷之間的程式碼段就是臨界段。
2.巨集 OS_ENTER_CRITICAL() 和 OS_EXIT_CRITICAL()的實現方法
方式1:
#define OS_ENTER_CRITICAL() \
asm(“DI”) //關中斷
#define OS_EXIT_CRITICAL() \
asm(“EI”) //開中斷
方式2:
#define OS_ENTER_CRITICAL() \
asm(“PUSH PSW”) \ //通過儲存程式狀態字來儲存中斷允許標誌
asm(“DI”) //關中斷
#define OS_EXIT_CRITICAL() \
asm(“POP PSW”) //恢復中斷允許標誌
方式3:
#define OS_ENTER_CRITICAL() \
cpu_sr = get_processor_psw(); \ //通過儲存程式狀態字在全域性變數sr中
disable_interrupts(); //關中斷
#define OS_EXIT_CRITICAL() \
set_processor_psw(cpu_sr); //用sr恢復程式狀態字
3.2 uC/OS-II的時鐘
任何作業系統都要提供一個週期性的訊號源,以供系統處理諸如延時、超時等與時間有關的事件,這個週期性的訊號源叫做時鐘。
硬體定時器以時鐘節拍為週期定時的產生中斷,該中斷的中斷服務程式叫做 OSTickISR().中斷服務程式通過呼叫函式OSTimeTick()來完成系統在每個時鐘節拍時需要做的工作。
OSTickISR()示意性程式碼如下:
void OSTickISR(void)
{
儲存CPU暫存器;
呼叫OSIntEnter(); //記錄中斷巢狀層數
if(OSIntNesting == 1)
{
OSTCBCur -> OSTCBStkPtr = SP; //在任務TCB中儲存堆疊指標
}
呼叫OSTimeTick(); //節拍處理
清除中斷;
開中斷;
呼叫OSIntExit(); //中斷巢狀層數減1
恢復CPU暫存器;
中斷返回;
}
OSTimeTick)示意性程式碼如下:
uC/OS-II在每次響應定時中斷時調OSTimeTick()做了兩件事情:一是給計數器OSTime加1;二是遍歷任務控制塊連結串列中的所有任務控制塊,把各個任務控制塊中用來存放任務延時時限的OSTCBDly變數減1,並使該項為0,同時又不使被掛起的任務進入就緒狀態。
3.3 時間管理
3.3.1 任務的延時
uC/OS-II規定:除了空閒任務之外的所有任務必須在任務中合適的位置呼叫系統提供的函式OSTimeDly(),使當前任務的執行延時(暫停)一段時間並進行一次任務排程,以讓出CPU的使用權。
函式OSTimeDly()的程式碼如下:
void OSTimeDly()
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
if(ticks > 0)
{
OS_ENTER_CRITICAL();
if((OSRdyTbl[OSTCBCur -> OSTCBY] &= ~OSTBCur -> OSTCBitX) == 0)
{
OSRdyGrp &= ~OSTCBCur -> OSTCBBitY; //取消當前任務的就緒狀態
}
OSTCBCur -> OSTCBDly = ticks; //延時節拍數存入任務控制塊
OST_EXIT_CRITICAL();
OS_Sched(); //呼叫排程函式
}
}
延時函式OSTimeDlyHMSM()的程式碼如下:
INT8U OSTimeDlyHMSM(
INT8U hours, //小時
INT8U minutes, //分
INT8U seconds, //秒
INT16U milli //毫秒
);
3.3.2 取消任務的延時
函式OSTimeDlyResume()的原型如下:
INT8U OSTimeDlyResume(INT8U prio);
引數prio為被取消延時任務的優先級別
函式OSTimeDlyResume()原始碼如下:
小結:
1.在uC/OS-II中,中斷服務子程式執行結束之後,系統將會根據情況進行一次中斷級的任務排程去執行優先級別最高的就緒任務,而並不一定要接續執行被中斷的任務。
2.uC/OS-II的中斷允許巢狀,用全域性變數OSIntNesting來記錄巢狀數。
3.uC/OS-II的中斷服務程式的工作通常是由中斷啟用的一個任務來完成的。
4.在任務中可以設定臨界區的方法來遮蔽中斷。設定臨界區的巨集有三種方式來實現。
5.uC/OS-II的時鐘通常是一個由硬體計數器定時產生週期性中斷訊號來實現的,每一次中斷叫做一個節拍,其中斷服務程式叫做節拍服務程式。
6.uC/OS-II在每一個節拍服務裡都要遍歷系統中全部任務的任務控制塊,把其中記錄任務延時時間的成員OSTCBDly減1,並使延時時間到的任務進入就緒狀態。
7.uC/OS-II有10個函式提供了鉤子函式,應用程式設計人員可以再鉤子函式中編寫寫自己的程式碼。
8.uC/OS-II進行時間管理的函式中,最重要的是延時函式OSTimeDly()和OSTimeDlyHMSM().