任務切換一共兩種方式:系統節拍器中斷、呼叫portyield。但是,最終都是產生pendsv中斷。
/* systick異常處理入口 */
void xportsystickhandler(void)
} /* 開啟中斷 */
vportclearbaseprifromisr();
}
/* 請求排程 */
#define portyield() \
系統節拍器產生中斷之後,呼叫xtaskincrementtick函式檢查下乙個節拍是否有任務需要切換
xtaskincrementtick函式主要功能:
在排程器沒有被掛起的情況下
系統節拍加一
系統節拍溢位時將溢位延時列表切換成延時列表
將延時列表中的超時任務清除,掛接到就緒列表,如果優先順序大於當前任務,請求切換
當前任務優先順序就緒列表中任務數大於1個,請求切換下乙個任務
在排程器被掛起的情況下
排程器掛起時間加一
如果前面有程式因為各種原因(如帶中斷保護的api解除某任務阻塞)要求延遲到現在切換,請求切換
/* 系統節拍加一 */
basetype_t xtaskincrementtick(void)
else
/* 處理延時列表中超時的任務 */
if(xconsttickcount >= xnexttaskunblocktime)
/* 延時列表不是空的 */
else
else
/* 將超時任務從延時列表中移除 */
(void)uxlistremove(&(pxtcb->xstatelistitem));
/* 如果該任務被掛接到某個事件列表,還需要從事件列表中移除 */
if(listlist_item_container(&(pxtcb->xeventlistitem)) != null)
else
/* 將任務加入就緒列表中 */
prvaddtasktoreadylist(pxtcb);
#if (configuse_preemption == 1)
else
}#endif}}
} #if ((configuse_preemption == 1) && (configuse_time_slicing == 1))
else
}#endif
#if (configuse_tick_hook == 1)
else
}#endif
} /* 排程器被掛起 */
else
#endif
} #if (configuse_preemption == 1)
else
}#endif
return xswitchrequired;
}
在看pendsv異常處理源**之前,先了解一下基礎知識,參考《cortex-m3 權威指南》的第九章(中斷的具體行為)
發生異常之後會自動進行三個步驟:
1.入棧 2.取向量 3.選擇堆疊指標msp/psp,更新堆疊指標sp,更新鏈結暫存器lr,更新程式計數器pc
異常返回之後會自動進行兩個步驟:
1.出棧 2.更新nvic暫存器
在進入異常服務程式後,lr的值被自動更新為特殊的ex_return
需要注意的是,有部分暫存器是自動入棧和出棧的,但是這些暫存器並不包括r4-r11,因此需要手動入棧
pendsv的主要工作步驟:保護老任務現場,切換到新任務,恢復新任務現場,跳轉到新任務
/* pendsv異常入口 */
__asm void xportpendsvhandler(void)
/* 更新任務棧頂指標 */
str r0, [r2]
/* 將&pxcurrenttcb和lr壓入msp中 */
s***b sp!,
/* 禁止不高於系統呼叫優先順序的中斷 */
mov r0, #configmax_syscall_interrupt_priority
msr basepri, r0
dsbisb
/* 切換上下文 */
bl vtaskswitchcontext
/* 開啟中斷 */
mov r0, #0
msr basepri, r0
/* 將&pxcurrenttcb和lr從msp中彈出 */
ldmia sp!,
/* r1=pxcurrenttcb 注意:現在pxcurrenttcb已經變成新任務 */
ldr r1, [r3]
/* r0=pxtopofstack */
ldr r0, [r1]
/* 將任務棧中的r4-r11彈出 */
ldmia r0!,
/* 將任務棧設定為psp(程序堆疊) */
msr psp, r0
isb/* 返回 */
bx r14
nop}
切換上下文,其實就是從最高優先順序的就緒任務列表中取出下乙個需要執行的任務,並將該任務設定為當前任務
/* 任務切換上下文 */
void vtaskswitchcontext(void)
/* 排程器沒有掛起 */
else
else
ultaskswitchedintime = ultotalruntime;
} #endif
taskcheck_for_stack_overflow();
#if (configuse_posix_errno == 1)
#endif
/* 從最高優先順序就緒任務列表中獲取下乙個任務並設定為當前任務 */
taskselect_highest_priority_task();
tracetask_switched_in();
#if (configuse_posix_errno == 1)
#endif
#if (configuse_newlib_reentrant == 1)
#endif
}}
FreeRTOS任務切換總結
1處理時間被劃分多個片段,到達一定時間就會觸發系統滴答定時器 systick 中斷進行上下文切換,但由於systick中斷是最高端的,會使其他事件中斷延時。故每到systick中斷是讓pendsv中斷掛起,由於pendsv優先順序最低,只有所有中斷都執行完畢了才會執行pendsv中斷,在此進行任務切...
freertos學習之任務排程切換
freertos 的任務具有如下幾種狀態 執行running 就緒ready 阻塞blocked 掛起suspended 除了執行狀態之外的狀態統稱為非執行狀態。因為 freertos 是為單cpu設計的系統,在任何時刻最多只能允許乙個任務處在執行狀態,哪怕看起來好像有多個任務同時在執行 這只是多個...
FreeRTOS 任務不切換可能的問題
1 時間片排程沒有開啟 freertos.h 檔案中巨集定義 configuse time slicing 沒有定義為1 解決方案 ifndef configuse time slicing define configuse time slicing 1 endif2 中斷函式中定義了,svc ha...