μC/OS-III---I筆記13---中斷管理
中斷管理
先看一下最常用的臨界段進入的函數:進入臨界段 OS_CRITICAL_ENTER() 退出臨界段OS_CRITICAL_EXIT()他們兩個的宏是這樣的.
在使能中斷延遲提交時:
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u /* Deferred ISR Posts ------------------------------ */
/* Lock the scheduler */
#define OS_CRITICAL_ENTER() do { CPU_CRITICAL_ENTER(); OSSchedLockNestingCtr++; if (OSSchedLockNestingCtr == 1u ) { OS_SCHED_LOCK_TIME_MEAS_START(); } CPU_CRITICAL_EXIT(); } while (0)
/* Lock the scheduler but re-enable interrupts */
#define OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT() do { OSSchedLockNestingCtr++; if (OSSchedLockNestingCtr == 1u) { OS_SCHED_LOCK_TIME_MEAS_START(); } CPU_CRITICAL_EXIT(); } while (0)
/* Scheduling occurs only if an interrupt occurs */
#define OS_CRITICAL_EXIT() do { CPU_CRITICAL_ENTER(); OSSchedLockNestingCtr--; if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { OS_SCHED_LOCK_TIME_MEAS_STOP(); if (OSIntQNbrEntries > (OS_OBJ_QTY)0) { CPU_CRITICAL_EXIT(); OS_Sched0(); } else { CPU_CRITICAL_EXIT(); } } else { CPU_CRITICAL_EXIT(); } } while (0)
#define OS_CRITICAL_EXIT_NO_SCHED() do { CPU_CRITICAL_ENTER(); OSSchedLockNestingCtr--; if (OSSchedLockNestingCtr == (OS_NESTING_CTR)0) { OS_SCHED_LOCK_TIME_MEAS_STOP(); } CPU_CRITICAL_EXIT(); } while (0)
#else /* Direct ISR Posts -------------------------------- */
在沒有使能中斷延遲提交時:
#define OS_CRITICAL_ENTER() CPU_CRITICAL_ENTER()
#define OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT()
#define OS_CRITICAL_EXIT() CPU_CRITICAL_EXIT()
#define OS_CRITICAL_EXIT_NO_SCHED() CPU_CRITICAL_EXIT()
#endif
先看一下在沒有使能中斷延遲提交時的臨界段進入的函數:進入臨界段 CPU_CRITICAL_ENTER() 退出臨界段CPU_CRITICAL_EXIT()
他們兩個的宏是這樣的
#define CPU_CRITICAL_ENTER() do { CPU_INT_DIS(); } while (0)
#define CPU_CRITICAL_EXIT() do { CPU_INT_EN(); } while (0)
另一層宏是
#define CPU_INT_DIS() do { cpu_sr = CPU_SR_Save(); } while (0)
#define CPU_INT_EN() do { CPU_SR_Restore(cpu_sr); } while (0)
接著另一層宏是
CPU_SR_Save
MRS R0, PRIMASK ; Set prio int mask to mask all (except faults)
CPSID I
BX LR
這裏關於C語言調用匯編,規定32位的返回值存放到R0,64位的R0存放第32為,R1存放高32位。所以上邊的匯編代碼第二行實際就是將PRIMASK保存到R0最後返回給變量cpu_sr ,第三行是一個CM3關全局中斷的命令,在LR保存了調用CPU_SR_Save()之前的下一條指令PC指針,BX就返回了。
CPU_SR_Restore ;
MSR PRIMASK, R0
BX LR
這裏註意調用這個函數時傳入了cpu_sr,根據C調用匯編的規定這個變量被存放到R0裏,所以第二句就是將cpu_sr 恢復到PRIMASK,由CM3權威指南可以知道,這是一個中斷管理的寄存器,寫1屏蔽所有中斷,只剩下faul和NMI,寫0則開啟中斷;c語言調用匯編時規定,當傳遞參數少於4個時,從右往左依次存放到R0~R3中,當傳入參數多於4個時,多出來的參數會先入棧;
註意在調用進入臨界段時常常有這個函數CPU_SR_ALLOC()它的宏是
#define CPU_SR_ALLOC() CPU_SR cpu_sr = (CPU_SR)0
即定義的一個CPU_SR的變量初始化為0;
對於延遲提交使能下的進入臨界段代碼,就是加了一個調度器嵌套層數OSSchedLockNestingCtr++的操作,它代表了調度器被鎖住的嵌套層數,OS_CRITICAL_ENTER直接鎖住調度器,OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT()這個宏調用之前一般先關閉中斷,接著宏鎖住調度器然後鎖住調度器,再恢鎖調度器前的中斷狀態(這裏不是開中斷),在沒有使能中斷延遲時,會增加中斷關閉的時間。解開調度嵌套也有兩種
- OS_CRITICAL_EXIT()
- 解開調度時進行任務調度(延遲);
- OS_CRITICAL_EXIT_NO_SCHED()
- 解開調度時不進行任務調度;
這兩個函數後應該都根據選項選擇是否調用OSSchedule()進行任務調度。且進入調度嵌套和退出嵌套要成對使用負責會導致調度器被一直鎖住。鎖住調度器是用戶無法進行任務切換,也無法將當前任務掛起。
中斷在系統中相當於優先級最高的任務,因此對於中斷的處理是十分緊急的從CM3的內核中斷響應機制就能知道cpu對於中斷的相應是追求極致的快速,所以UCOS對於這裏也有對應的措施,系統級的一些處理時間比較長的任務不允許在中斷中調用,對於一些中斷中的提交函數會放到中斷延遲提交裏完成,把中斷級的任務放到任務級從而減少關中斷時間,減少對中斷的實時性的影響。
中斷延遲提交:
當用戶在把宏OS_CFG_ISR_POST_DEFERRED使能,使用延遲提交在系統初始化時會自動初始化延遲提交隊列然後創建一個延遲提交任務。
OSInit()函數內的延遲提交任務初始化
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
OS_IntQTaskInit(p_err); /* Initialize the Interrupt Queue Handler Task */
if (*p_err != OS_ERR_NONE) {
return;
}
#endif
中斷延遲提交的過程就是把要提交的內容先保存起來,然後把系統初始化時創建一個優先級 為0(最高)的延遲提交任務就緒,在中斷函數執行完成後就會執行延遲的提交。對於OS_IntQTaskInit(p_err)主要作用就是初始化延遲提交隊列,並創建任務。在之前的任務管理章節我們知道一個任務被創建就會加如就緒列表等待調用時就會執行.
void OS_IntQTaskInit (OS_ERR *p_err)
{
OS_INT_Q *p_int_q;
OS_INT_Q *p_int_q_next;
OS_OBJ_QTY i;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
//延遲提交溢出計數清零
OSIntQOvfCtr = (OS_QTY)0u; /* Clear the ISR queue overflow counter */
//延遲提交信息隊列的內存不能是空
if (OSCfg_IntQBasePtr == (OS_INT_Q *)0) {
*p_err = OS_ERR_INT_Q;
return;
}
//延遲信息隊列的長最小是1
if (OSCfg_IntQSize < (OS_OBJ_QTY)2u) {
*p_err = OS_ERR_INT_Q_SIZE;
return;
}
//延遲提交任務每次運行的最長時間
OSIntQTaskTimeMax = (CPU_TS)0;
p_int_q = OSCfg_IntQBasePtr; /* Initialize the circular ISR queue */
p_int_q_next = p_int_q;
p_int_q_next++;
//將信息對壘串成單項鏈表
for (i = 0u; i < OSCfg_IntQSize; i++) {
p_int_q->Type = OS_OBJ_TYPE_NONE;
p_int_q->ObjPtr = (void *)0;
p_int_q->MsgPtr = (void *)0;
p_int_q->MsgSize = (OS_MSG_SIZE)0u;
p_int_q->Flags = (OS_FLAGS )0u;
p_int_q->Opt = (OS_OPT )0u;
p_int_q->NextPtr = p_int_q_next;
p_int_q++;
p_int_q_next++;
}
//單項鏈表“圈”操作方便延時提交隊列內存的使用
p_int_q--;
p_int_q_next = OSCfg_IntQBasePtr;
p_int_q->NextPtr = p_int_q_next;
OSIntQInPtr = p_int_q_next;
OSIntQOutPtr = p_int_q_next;
//延遲提交隊列中要進行提交的內核對象個數
OSIntQNbrEntries = (OS_OBJ_QTY)0u;
//延遲提交隊列中要進行提交的內核對象的最大個數
OSIntQNbrEntriesMax = (OS_OBJ_QTY)0u;
/* -------------- CREATE THE ISR QUEUE TASK ------------- */
if (OSCfg_IntQTaskStkBasePtr == (CPU_STK *)0) {
*p_err = OS_ERR_INT_Q_STK_INVALID;
return;
}
if (OSCfg_IntQTaskStkSize < OSCfg_StkSizeMin) {
*p_err = OS_ERR_INT_Q_STK_SIZE_INVALID;
return;
}
//創建延遲提交任務
OSTaskCreate((OS_TCB *)&OSIntQTaskTCB,
(CPU_CHAR *)((void *)"uC/OS-III ISR Queue Task"),
(OS_TASK_PTR )OS_IntQTask,
(void *)0,
(OS_PRIO )0u, /* This task is ALWAYS at priority ‘0‘ (i.e. highest) */
(CPU_STK *)OSCfg_IntQTaskStkBasePtr,
(CPU_STK_SIZE)OSCfg_IntQTaskStkLimit,
(CPU_STK_SIZE)OSCfg_IntQTaskStkSize,
(OS_MSG_QTY )0u,
(OS_TICK )0u,
(void *)0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)p_err);
}
#endif
OS_IntQTaskInit ()
函數內創建的延遲提交任務
void OS_IntQTask (void *p_arg)
{
CPU_BOOLEAN done;
CPU_TS ts_start;
CPU_TS ts_end;
CPU_SR_ALLOC();
p_arg = p_arg; /* Not using ‘p_arg‘, prevent compiler warning */
while (DEF_ON) {
done = DEF_FALSE;
while (done == DEF_FALSE) {
CPU_CRITICAL_ENTER();
//還有內核對象等待提交?
//否
if (OSIntQNbrEntries == (OS_OBJ_QTY)0u) {
//任務刪除
OSRdyList[0].NbrEntries = (OS_OBJ_QTY)0u; /* Remove from ready list */
OSRdyList[0].HeadPtr = (OS_TCB *)0;
OSRdyList[0].TailPtr = (OS_TCB *)0;
OS_PrioRemove(0u); /* Remove from the priority table */
CPU_CRITICAL_EXIT();
OSSched();
done = DEF_TRUE; /* No more entries in the queue, we are done */
}
//是
else {
CPU_CRITICAL_EXIT();
ts_start = OS_TS_GET();
//提交信息
OS_IntQRePost();
ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */
if (OSIntQTaskTimeMax < ts_end) {
OSIntQTaskTimeMax = ts_end;
}
CPU_CRITICAL_ENTER();
//提交信息隊列出口指向更新
OSIntQOutPtr = OSIntQOutPtr->NextPtr; /* Point to next item in the ISR queue */
OSIntQNbrEntries--;
CPU_CRITICAL_EXIT();
}
}
}
}
函數內先檢查提交信息隊列是否為空了也就是提交完了,如果 提交完成了就將延遲提交任務從對應優先級的就緒任務列表移除,如果還有信息隊列等待提交就繼續調用OS_IntQRePost();進行內核對象提交。
void OS_IntQRePost (void)
{
CPU_TS ts;
OS_ERR err;
//根據提交對象的類型進行選擇內核對象提交函數
switch (OSIntQOutPtr->Type) { /* Re-post to task */
case OS_OBJ_TYPE_FLAG:
#if OS_CFG_FLAG_EN > 0u
(void)OS_FlagPost((OS_FLAG_GRP *) OSIntQOutPtr->ObjPtr,
(OS_FLAGS ) OSIntQOutPtr->Flags,
(OS_OPT ) OSIntQOutPtr->Opt,
(CPU_TS ) OSIntQOutPtr->TS,
(OS_ERR *)&err);
#endif
break;
case OS_OBJ_TYPE_Q:
#if OS_CFG_Q_EN > 0u
OS_QPost((OS_Q *) OSIntQOutPtr->ObjPtr,
(void *) OSIntQOutPtr->MsgPtr,
(OS_MSG_SIZE) OSIntQOutPtr->MsgSize,
(OS_OPT ) OSIntQOutPtr->Opt,
(CPU_TS ) OSIntQOutPtr->TS,
(OS_ERR *)&err);
#endif
break;
case OS_OBJ_TYPE_SEM:
#if OS_CFG_SEM_EN > 0u
(void)OS_SemPost((OS_SEM *) OSIntQOutPtr->ObjPtr,
(OS_OPT ) OSIntQOutPtr->Opt,
(CPU_TS ) OSIntQOutPtr->TS,
(OS_ERR *)&err);
#endif
break;
case OS_OBJ_TYPE_TASK_MSG:
#if OS_CFG_TASK_Q_EN > 0u
OS_TaskQPost((OS_TCB *) OSIntQOutPtr->ObjPtr,
(void *) OSIntQOutPtr->MsgPtr,
(OS_MSG_SIZE) OSIntQOutPtr->MsgSize,
(OS_OPT ) OSIntQOutPtr->Opt,
(CPU_TS ) OSIntQOutPtr->TS,
(OS_ERR *)&err);
#endif
break;
case OS_OBJ_TYPE_TASK_RESUME:
#if OS_CFG_TASK_SUSPEND_EN > 0u
(void)OS_TaskResume((OS_TCB *) OSIntQOutPtr->ObjPtr,
(OS_ERR *)&err);
#endif
break;
case OS_OBJ_TYPE_TASK_SIGNAL:
(void)OS_TaskSemPost((OS_TCB *) OSIntQOutPtr->ObjPtr,
(OS_OPT ) OSIntQOutPtr->Opt,
(CPU_TS ) OSIntQOutPtr->TS,
(OS_ERR *)&err);
break;
case OS_OBJ_TYPE_TASK_SUSPEND:
#if OS_CFG_TASK_SUSPEND_EN > 0u
(void)OS_TaskSuspend((OS_TCB *) OSIntQOutPtr->ObjPtr,
(OS_ERR *)&err);
#endif
break;
case OS_OBJ_TYPE_TICK:
#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
//任務時間片輪換調度任務
OS_SchedRoundRobin(&OSRdyList[OSPrioSaved]);
#endif
//時間任務管理
(void)OS_TaskSemPost((OS_TCB *)&OSTickTaskTCB, /* Signal tick task */
(OS_OPT ) OS_OPT_POST_NONE,
(CPU_TS ) OSIntQOutPtr->TS,
(OS_ERR *)&err);
#if OS_CFG_TMR_EN > 0u
//定時器任務提交
OSTmrUpdateCtr--;
if (OSTmrUpdateCtr == (OS_CTR)0u) {
OSTmrUpdateCtr = OSTmrUpdateCnt;
ts = OS_TS_GET(); /* Get timestamp */
(void)OS_TaskSemPost((OS_TCB *)&OSTmrTaskTCB, /* Signal timer task */
(OS_OPT ) OS_OPT_POST_NONE,
(CPU_TS ) ts,
(OS_ERR *)&err);
}
#endif
break;
default:
break;
}
}
OS_IntQRePost()
這個函數內就是根據延遲提交的內核對象的類型然後分別調用內核對應的Post函數對內核對象進行提交。從而完成內核對象的延遲提交。
μC/OS-III---I筆記13---中斷管理