FreeRTOS---任務切換

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運行權,從而完成任務切換。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章