freertos 源码分析六 任务调度二

任务调度由SysTick_Handler和 PendSV_Handler两个中断接管
系统时钟计数器按给定频率倒计时,至0触发SysTick_Handler中断,此中断函数为宏中对xPortSysTickHandler的重命名。
增加计数,置位可挂起中断。

void xPortSysTickHandler( void )
{
    portDISABLE_INTERRUPTS();
    {
        if( xTaskIncrementTick() != pdFALSE )
        {
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portENABLE_INTERRUPTS();
}

xTaskIncrementTick 以设定频率增加计数,处理延时任务。

BaseType_t xTaskIncrementTick( void )
{
    TCB_t * pxTCB;                                                                                                                                 
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;

    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
    {
        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;
        xTickCount = xConstTickCount;
        if( xConstTickCount == ( TickType_t ) 0U )
        {
            taskSWITCH_DELAYED_LISTS();  //计数溢出 交换延时链表与溢出链表
        }

        if( xConstTickCount >= xNextTaskUnblockTime )   //处理延时链表
        {
            for( ; ; )
            {
                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )  //空
                {
                    xNextTaskUnblockTime = portMAX_DELAY;
                    break;
                }
                else  //非空
                {
                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
                    if( xConstTickCount < xItemValue )
                    {                                                                                                                              
                        xNextTaskUnblockTime = xItemValue;
                        break;
                    }

                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );  //延时已过
                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
                    {
                        listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
                    }
                    prvAddTaskToReadyList( pxTCB );  //添至就绪链表
                    #if ( configUSE_PREEMPTION == 1 )
                    {
                        if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                        {
                            xSwitchRequired = pdTRUE;  //优先级大于现运行任务需切换
                        }
                    }
                    #endif
                }
            }
        }
        #if ( configUSE_PREEMPTION == 1 )
        {
            if( xYieldPending != pdFALSE )
            {
                xSwitchRequired = pdTRUE;
            }
        }
        #endif

    }
    else
    {
        ++xPendedTicks;  // 增加计数
    }
    return xSwitchRequired;
}

listLIST_IS_EMPTY宏定义

#define listLIST_IS_EMPTY( pxList )   ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) 

可挂起中断,此中断函数在宏中重命名为PendSV_Handler。系统时钟中断中对其置位,以低优先级运行,无高于其优先级时会调入对任务进行切换。

void xPortPendSVHandler( void )
{
    __asm volatile
    (
        "   mrs r0, psp                         \n"
        "   isb                                 \n"
        "   ldr r3, pxCurrentTCBConst           \n" //当前运行任务
        "   ldr r2, [r3]                        \n" 
        "   stmdb r0!, {r4-r11}                 \n"  //保存r4-r11 psp
        "   str r0, [r2]                        \n"  //保存当前栈顶pxTopOfStack 
        "   stmdb sp!, {r3, r14}                \n"  //保存r3 r14至msp
        "   mov r0, %0                          \n"  // 优先级
        "   msr basepri, r0                     \n"  
        "   bl vTaskSwitchContext               \n" // 在就绪链表寻找最高优先级设为当前切换任务
        "   mov r0, #0                          \n"
        "   msr basepri, r0                     \n"
        "   ldmia sp!, {r3, r14}                \n"  // 恢复 r3 lr
        "   ldr r1, [r3]                        \n"  //当前TCB指针 TCB已切换
        "   ldr r0, [r1]                        \n"  //当前TCB栈顶pxTopOfStack
        "   ldmia r0!, {r4-r11}                 \n"  //从TCB栈恢复 r4-r11 变动后指向任务参数
        "   msr psp, r0                         \n"  //psp栈指向tcb栈 
        "   isb                                 \n"  // 
        "   bx r14                              \n"  // 中断返回从PSP栈恢复寄存器值,任务回调函数指针载入PC,完成任务切换。
        "                                       \n"
        "   .align 4                            \n"
        "pxCurrentTCBConst: .word pxCurrentTCB  \n"
        ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
    );
}

参考创建任务时,任务堆栈设置

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
                                     TaskFunction_t pxCode,
                                     void * pvParameters )
{
    pxTopOfStack--;                                                      
    *pxTopOfStack = portINITIAL_XPSR;   //thumb                                 
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; //task回调函数指针
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS;             /* LR */
    pxTopOfStack -= 5;                                                   /* R12, R3, R2 and R1. */
    *pxTopOfStack = ( StackType_t ) pvParameters;                        /* R0 */
    pxTopOfStack -= 8;                                                   /* R11, R10, R9, R8, R7, R6, R5 and R4. */

    return pxTopOfStack;
}

你可能感兴趣的:(嵌入式硬件)