天天看点

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运行权,从而完成任务切换。