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