FreeRTOS兩種任務切換觸發方式
執行系統調用
普通任務可以使用taskYIELD()強制任務切換
正在執行的任務讀取隊列失敗,再執行下去沒有意義,決定通知調度器切換到其他任務,自己進入阻塞
(比如,當前的任務執行xSemaphoreTake—>xQueueGenericReceive,從隊列中讀取信號量,發現爲空,xQueueGenericReceive就會進行返回errQUEUE_FULL,也就是得知讀取不成功 !=pdPASS,這是這個任務就可以執行taskYIELD來強制任務切換了)
xTaskCreate—>xTaskGenericCreate,創建任務時,如果創建的這個任務的優先級高於當前運行的任務,切換任務。
if( xReturn == pdPASS )
{
if( xSchedulerRunning != pdFALSE )
{
/** If the created task is of a higher priority than the current task
then it should run now. */
if( pxCurrentTCB->uxPriority < uxPriority )
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#if( configUSE_PREEMPTION == 0 )
/** If the cooperative scheduler is being used then a yield should not be
performed just because a higher priority task has been woken. */
#define taskYIELD_IF_USING_PREEMPTION()
#else
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
#endif
#ifndef portYIELD_WITHIN_API
#define portYIELD_WITHIN_API portYIELD
#endif
#define portYIELD() vPortYieldFromISR()
中斷服務程序中使用portYIELD_FROM_ISR()強制任務切換
系統節拍時鐘中斷
#if (configCOMPILER==configCOMPILER_ARM_KEIL)
#if configPEX_KINETIS_SDK /** the SDK expects different interrupt handler names */
void SysTick_Handler(void) {
#else
void vPortTickHandler(void) {
#endif
#if configUSE_TICKLESS_IDLE == 1
TICK_INTERRUPT_FLAG_SET();
#endif
portSET_INTERRUPT_MASK(); /** disable interrupts */
if (xTaskIncrementTick()!=pdFALSE) { /** increment tick count */
taskYIELD();
}
portCLEAR_INTERRUPT_MASK(); /** enable interrupts again */
}
#endif
taskYIELD()—>portYIELD() —>vPortYieldFromISR(void)
—>*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET_BIT;
void vPortYieldFromISR(void) {
/** Set a PendSV to request a context switch. */
*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET_BIT;
/** Barriers are normally not required but do ensure the code is completely
within the specified behavior for the architecture. */
__asm volatile("dsb");
__asm volatile("isb");
}
從上面的代碼中可以看出,PendSV中斷的產生是通過代碼:portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT實現的,它向中斷狀態寄存器bit28位寫入1,將PendSV中斷設置爲掛起狀態,等到優先級高於PendSV的中斷執行完成後,PendSV中斷服務程序將被執行,進行任務切換工作。
總結
對於Cortex-M3平臺,這兩種方法的實質是一樣的,都會使能一個PendSV中斷,在PendSV中斷服務程序中,找到最高優先級的就緒任務,然後讓這個任務獲得CPU運行權,從而完成任務切換。