FreeRTOS的第一个任务是怎么跑起来的

来源:互联网 发布:ug编程爬面刀轨不出去 编辑:程序博客网 时间:2024/04/29 19:51

一、一般在程序末尾会有一个vTaskStartSheduler();函数

int main(void){        BSP_INIT();       BinarySemaphore=xSemaphoreCreateBinary();       if(BinarySemaphore!=NULL)         {                printf("semaphore create successfully .\r\n");         }      xTaskCreate(vLED1Task,"led1",50,NULL,1,NULL);     //    xTaskCreate((TaskFunction_t )DataProcess_task,                     (const char*    )"DataProcess_task",                   (uint16_t       )DATAPROCESS_STK_SIZE,                (void*          )NULL,                (UBaseType_t    )DATAPROCESS_TASK_PRIO,                (TaskHandle_t*  )&DataProcess_Handler);                             //  (TaskHandle_t*  )NULL);       vTaskStartScheduler();//启动调度器,任务开始执行。      return 0;}

二、下面看一下vTaskStartScheduler的具体内容:

void vTaskStartScheduler( void ){BaseType_t xReturn;    /* Add the idle task at the lowest priority. */    #if( configSUPPORT_STATIC_ALLOCATION == 1 )    {        StaticTask_t *pxIdleTaskTCBBuffer = NULL;        StackType_t *pxIdleTaskStackBuffer = NULL;        uint32_t ulIdleTaskStackSize;        /* The Idle task is created using user provided RAM - obtain the        address of the RAM then create the idle task. */        vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );        xIdleTaskHandle = xTaskCreateStatic(    prvIdleTask,                                                "IDLE",                                                ulIdleTaskStackSize,                                                ( void * ) NULL,                                                ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),                                                pxIdleTaskStackBuffer,                                                pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */        if( xIdleTaskHandle != NULL )        {            xReturn = pdPASS;        }        else        {            xReturn = pdFAIL;        }    }    #else    {        /* The Idle task is being created using dynamically allocated RAM. */        xReturn = xTaskCreate(  prvIdleTask,                                "IDLE", configMINIMAL_STACK_SIZE,                                ( void * ) NULL,                                ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),                                &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */    }    #endif /* configSUPPORT_STATIC_ALLOCATION */    #if ( configUSE_TIMERS == 1 )    {        if( xReturn == pdPASS )        {            xReturn = xTimerCreateTimerTask();        }        else        {            mtCOVERAGE_TEST_MARKER();        }    }    #endif /* configUSE_TIMERS */    if( xReturn == pdPASS )    {        /* Interrupts are turned off here, to ensure a tick does not occur        before or during the call to xPortStartScheduler().  The stacks of        the created tasks contain a status word with interrupts switched on        so interrupts will automatically get re-enabled when the first task        starts to run. */        portDISABLE_INTERRUPTS();        #if ( configUSE_NEWLIB_REENTRANT == 1 )        {            /* Switch Newlib's _impure_ptr variable to point to the _reent            structure specific to the task that will run first. */            _impure_ptr = &( pxCurrentTCB->xNewLib_reent );        }        #endif /* configUSE_NEWLIB_REENTRANT */        xNextTaskUnblockTime = portMAX_DELAY;        xSchedulerRunning = pdTRUE;        xTickCount = ( TickType_t ) 0U;        /* If configGENERATE_RUN_TIME_STATS is defined then the following        macro must be defined to configure the timer/counter used to generate        the run time counter time base. */        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();        /* Setting up the timer tick is hardware specific and thus in the        portable interface. */        ***if( xPortStartScheduler() != pdFALSE )***        {            /* Should not reach here as if the scheduler is running the            function will not return. */        }        else        {            /* Should only reach here if a task calls xTaskEndScheduler(). */        }    }    else    {        /* This line will only be reached if the kernel could not be started,        because there was not enough FreeRTOS heap to create the idle task        or the timer task. */        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );    }    /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,    meaning xIdleTaskHandle is not used anywhere else. */    ( void ) xIdleTaskHandle;}

可以看到vTaskStartScheduler()函数调用了xPortStartScheduler()函数。

三、下面再看一下xPortStartScheduler()函数

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;        /* Determine the maximum priority from which ISR safe FreeRTOS API        functions can be called.  ISR safe functions are those that end in        "FromISR".  FreeRTOS maintains separate thread and ISR API functions to        ensure interrupt entry is as fast and simple as possible.        Save the interrupt priority value that is about to be clobbered. */        ulOriginalPriority = *pucFirstUserPriorityRegister;        /* Determine the number of priority bits available.  First write to all        possible bits. */        *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;        /* Read the value back to see how many bits stuck. */        ucMaxPriorityValue = *pucFirstUserPriorityRegister;        /* Use the same mask on the maximum system call priority. */        ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;        /* Calculate the maximum acceptable priority group value for the number        of bits read back. */        ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;        while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )        {            ulMaxPRIGROUPValue--;            ucMaxPriorityValue <<= ( uint8_t ) 0x01;        }        /* Shift the priority group value back to its position within the AIRCR        register. */        ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;        ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;        /* Restore the clobbered interrupt priority register to its original        value. */        *pucFirstUserPriorityRegister = ulOriginalPriority;    }    #endif /* conifgASSERT_DEFINED */    /* Make PendSV and SysTick the lowest priority interrupts. */    portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;    portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;    /* Start the timer that generates the tick ISR.  Interrupts are disabled    here already. */    vPortSetupTimerInterrupt();    /* Initialise the critical nesting count ready for the first task. */    uxCriticalNesting = 0;    /* Start the first task. */    prvStartFirstTask();    /* Should not get here! */    return 0;}

可以看到xPortStartScheduler()函数调用了prvSetupTimerInterrupt()函数。
四、下面再看一下prvSetupTimerInterrupt()函数。

#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0    void vPortSetupTimerInterrupt( void )    {        /* Calculate the constants required to configure the tick interrupt. */        #if configUSE_TICKLESS_IDLE == 1        {            ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );            xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;            ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );        }        #endif /* configUSE_TICKLESS_IDLE */        /* Configure SysTick to interrupt at the requested rate. */        portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;        portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );    }#endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */

注意,这里使用了条件编译。只有在configOVERRIDE_DEFAULT_TICK_CONFIGURATION=0的时候才会进行编译。从函数名称可以看出来,这个函数是对stm32系统滴答定时器进行配置的。
配置的参数主要是两个:configSYSTICK_CLOCK_HZ和configTICK_RATE_HZ 。滴答定时器时钟和滴答定时器的运行频率。
那么configOVERRIDE_DEFAULT_TICK_CONFIGURATION是在哪里定义的呢?我们可以在port.c的101行发现它的定义。

#ifndef configOVERRIDE_DEFAULT_TICK_CONFIGURATION    #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 0#endif

那么SysTick的中断处理函数在哪里呢?我们同样可以在port.c文件的第431行找到。

void xPortSysTickHandler( void ){    /* The SysTick runs at the lowest interrupt priority, so when this interrupt    executes all interrupts must be unmasked.  There is therefore no need to    save and then restore the interrupt mask value as its value is already    known - therefore the slightly faster vPortRaiseBASEPRI() function is used    in place of portSET_INTERRUPT_MASK_FROM_ISR(). */    vPortRaiseBASEPRI();    {        /* Increment the RTOS tick. */        if( xTaskIncrementTick() != pdFALSE )        {            /* A context switch is required.  Context switching is performed in            the PendSV interrupt.  Pend the PendSV interrupt. */            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;        }    }    vPortClearBASEPRIFromISR();}

可以看到,进入中断处理函数后,进行了三个动作。
1、关闭中断;
2、进行上下文切换;
3、开启中断。
其中,整个中断处理函数最重要的部分是在xTaskIncreamentTick()里。这里完成了许多功能。暂时不是很了解。
具体可以参考以下链接:
xTaskIncreamentTick()
这样整个系统就跑起来了。

五、总结。
从以上步骤我么可以看出:
1、stm32的滴答定时器给FreeRTOS提供基准心跳时钟;
2、SysTICK默认使用FreeRTOS系统配置。配置的参数由两个 因数决定。即configSYSTICK_CLOCK_HZ和configTICK_RATE_HZ 。

原创粉丝点击