FreeRTOS调度器开启和任务相关函数详解

调度器开启过程分析

在这里插入图片描述

任务调度器开启函数

void vTaskStartScheduler( void )
{
    BaseType_t xReturn;

    #if ( configSUPPORT_STATIC_ALLOCATION == 1 )//静态内存
    {
        StaticTask_t * pxIdleTaskTCBBuffer = NULL;
        StackType_t * pxIdleTaskStackBuffer = NULL;
        uint32_t ulIdleTaskStackSize;
        vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
        xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
                                             configIDLE_TASK_NAME,
                                             ulIdleTaskStackSize,
                                             ( void * ) NULL,      
                                             portPRIVILEGE_BIT,     
                                             pxIdleTaskStackBuffer,
                                             pxIdleTaskTCBBuffer ); 

        if( xIdleTaskHandle != NULL )
        {
            xReturn = pdPASS;
        }
        else
        {
            xReturn = pdFAIL;
        }
    }
    #else 
    {
        xReturn = xTaskCreate( prvIdleTask,
                               configIDLE_TASK_NAME,
                               configMINIMAL_STACK_SIZE,
                               ( void * ) NULL,
                               portPRIVILEGE_BIT,  
                               &xIdleTaskHandle ); 
    }//创建空闲任务
    #endif 

    #if ( configUSE_TIMERS == 1 )//使用软件定时器使能
    {
        if( xReturn == pdPASS )
        {
            xReturn = xTimerCreateTimerTask();//创建定时器服务任务
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif 

    if( xReturn == pdPASS )
    {
      
        #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
        {
            freertos_tasks_c_additions_init();
        }
        #endif

        portDISABLE_INTERRUPTS();//关闭中断

        #if ( ( configUSE_NEWLIB_REENTRANT == 1 ) || ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) )
        {
            
            configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock );
        }
        #endif

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;//调度器开始运行
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();//配置定时器

        traceTASK_SWITCHED_IN();

        xPortStartScheduler();//初始化跟调度器有关的硬件

    }
    else
    {
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }

    ( void ) xIdleTaskHandle;

    ( void ) uxTopUsedPriority;
}

内核相关硬件初始化函数

BaseType_t xPortStartScheduler( void )
{
    #if ( configASSERT_DEFINED == 1 )
    {
        volatile uint32_t ulOriginalPriority;
        volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
        volatile uint8_t ucMaxPriorityValue;
        ulOriginalPriority = *pucFirstUserPriorityRegister;

        *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;

        ucMaxPriorityValue = *pucFirstUserPriorityRegister;

        configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) );
  
        ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;

        ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;

        while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
        {
            ulMaxPRIGROUPValue--;
            ucMaxPriorityValue <<= ( uint8_t ) 0x01;
        }

        #ifdef __NVIC_PRIO_BITS
        {
          
            configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS );
        }
        #endif

        #ifdef configPRIO_BITS
        {
           
            configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );
        }
        #endif

        ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
        ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;

        *pucFirstUserPriorityRegister = ulOriginalPriority;
    }
    #endif 

    portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;//设置PendSV的中断优先级,为最低优先级

    portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;//设置滴答定时器的中断优先级,为最低优先级

    vPortSetupTimerInterrupt();//设置滴答定时器的定时周期,并且使能滴答定时器的中断

    uxCriticalNesting = 0;//初始化临界区嵌套计数器

    prvStartFirstTask();//开启第一个任务

    return 0;
}

启动第一个任务

__asm void prvStartFirstTask( void )
{
    PRESERVE8

    ldr r0, =0xE000ED08//将0xE000ED08保存在寄存器r0中。是向量表偏移寄存器的地址。
    ldr r0, [ r0 ]//取R0所保存的地址处的值赋给R0(在此之前R0保存的是地址),也就是从向量表偏移寄存器存储的初始地址的值0x08000000
    ldr r0, [ r0 ]//取R0 地址的值,偏移向量表的起始地址保存的是主栈指针MSP初始值

    msr msp, r0//复位MSP,将获得的MSP初始值赋值给msp
    cpsie i//使能中断
    cpsie f//使能中断
    dsb//数据同步屏障
    isb//指令同步屏障
    svc 0//触发SVC中断,使用它来启动第一个任务
    nop
    nop
}

SVC中断服务函数

__asm void xPortPendSVHandler( void )
{
    extern uxCriticalNesting;
    extern pxCurrentTCB;
    extern vTaskSwitchContext;

    PRESERVE8

    mrs r0, psp//PSP内容存入R0
    isb//指令同步屏障

    ldr r3, =pxCurrentTCB //R3=pxCurrentTCB的地址,这个指针永远指向正在运行的任务
    ldr r2, [ r3 ]//R3所保存地址的值赋值给R2,获取了当前任务的任务控制块的存储地址

    stmdb r0 !, { r4 - r11 } //保存剩余的寄存器,异常处理程序执行前,硬件自动将xPSR、PC、LR、R12、R0-R3入栈
    str r0, [ r2 ] //将新的栈顶保存到任务TCB的第一个成员中

    stmdb sp !, { r3, r14 }//将R3和R14临时压入堆栈,因为即将调用函数vTaskSwitchContext,调用函数时,返回地址自动保存到R14中,所以一旦调用发生,R14的值会被覆盖,因此需要入栈保护; R3保存的当前激活的任务TCB指针(pxCurrentTCB)地址,函数调用后会用到,因此也要入栈保护

    mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY//进入临界区
    msr basepri, r0//关闭中断
    dsb//数据同步屏障
    isb//指令同步屏障
    bl vTaskSwitchContext//调用函数,寻找新的任务运行,通过使变量pxCurrentTCB指向新的任务来实现任务切换
    mov r0, #0//退出临界区
    msr basepri, r0//开启中断
    ldmia sp !, { r3, r14 }//恢复R3和R14

    ldr r1, [ r3 ]
    ldr r0, [ r1 ] //当前激活的任务TCB第一项保存了任务堆栈的栈顶,现在栈顶值存入R0
    ldmia r0 !, { r4 - r11 } //出栈,R4-R11
    msr psp, r0
    isb
    bx r14//异常发生时,R14中保存异常返回标志,包括返回后进入线程模式还是处理器模式、使用PSP堆栈指针还是MSP堆栈指针,当调用 bx r14指令后,硬件会知道要从异常返回,然后出栈,这个时候堆栈指针PSP已经指向了新任务堆栈的正确位置,当新任务的运行地址被出栈到PC寄存器后,新的任务也会被执行
    nop
}

在这里插入图片描述
FreeRTOS高级篇4—FreeRTOS任务切换分析

空闲任务

空闲任务就是空闲的适合运行的任务。任务调度器启动后必须有一个任务运行。空闲任务的任务优先级是最小的,为0.任务函数为prvIdleTask()。
在这里插入图片描述

任务创建过程分析

任务创建函数

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
 

在这里插入图片描述

任务初始化函数

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
                                  const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                  const uint32_t ulStackDepth,
                                  void * const pvParameters,
                                  UBaseType_t uxPriority,
                                  TaskHandle_t * const pxCreatedTask,
                                  TCB_t * pxNewTCB,
                                  const MemoryRegion_t * const xRegions )

在这里插入图片描述
在这里插入图片描述

任务堆栈初始化函数

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
                                     TaskFunction_t pxCode,
                                     void * pvParameters )
{
    /* Simulate the stack frame as it would be created by a context switch
     * interrupt. */
    pxTopOfStack--;                                                      /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
    *pxTopOfStack = portINITIAL_XPSR;                                    /* xPSR */
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) prvTaskExitError;                    /* 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;
}

在这里插入图片描述

添加任务到就绪列表

任务删除过程分析

void vTaskDelete( TaskHandle_t xTaskToDelete )

在这里插入图片描述

任务挂起过程分析

void vTaskSuspend( TaskHandle_t xTaskToSuspend )

在这里插入图片描述

任务恢复过程分析

 void vTaskResume( TaskHandle_t xTaskToResume )

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值