天天看點

FreeRTOS---任務切換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 ==  )
         /** 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 == 
  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運作權,進而完成任務切換。