FreeRTOS--开启任务调度器过程分析
vTaskStartScheduler();–代码分析
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/* 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. */
#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;
}
从上面代码可以看到,整个开启任务调度一共分为以下几步
1:创建空闲任务。(此处我默认的动态创建)
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
2:判断是否开启软件定时器,是,则创建软件定时器
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
关于定时器里面的具体实现,留到日后再写。
3:关闭中断,初始化一些全局变量
portDISABLE_INTERRUPTS();
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) 0U;
4:开启全局的一个时间计数的函数,函数具体需要自己实现,
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
5:执行xPortStartScheduler,下面进入分析该函数。
if( xPortStartScheduler() != pdFALSE )
{
/* Should not reach here as if the scheduler is running the
function will not return. */
}
5.1:设置PendSV中断和滴答定时器中断优先级为最低。
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
5.2 :设置滴答寄存器的寄存器。
vPortSetupTimerInterrupt();
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 );
}
5.3:初始化嵌套技术变量。
uxCriticalNesting = 0;
5.4:如果芯片有FPU,则使能FPU。(F103没有)
5.5:开始第一个任务,下面分析开始第一个任务函数
prvStartFirstTask();
5.5.1:先获取MSP初始值
ldr r0, =0xE000ED08
ldr r0, [r0]
ldr r0, [r0]
5.5.2:然后复位MSP
msr msp, r0
5.5.3:使能中断,数据同步屏障,指令同步屏障
cpsie i
cpsie f
dsb
isb
5.5.4:触发SVC中断,SVC中断处理函数在FreeRTOSConfig.h里面进行了定义。下面分析该韩式
svc 0
5.5.4.1:获取当前正在运行的任务的任务栈顶指针,
ldr r3, =pxCurrentTCB
ldr r1, [r3]
ldr r0, [r1]
5.5.4.2:R4-R11,R14 寄存器出栈,
ldmia r0!, {r4-r11}
5.5.4.3:将PSP设置为任务的堆栈。
msr psp, r0
5.5.4.4:指令同步屏障,然后开启中断,
isb
mov r0, #0
msr basepri, r0
5.5.4.5:操作R14寄存器与0X0D或,表示退出异常后CPU进入线程模式,并使用进程栈。
orr r14, #0xd
5.5.4.6:恢复寄存器R0-R3,R12,LR,PC,xPSR的值。堆栈使用进程栈PSP,然后执行PC中的任务函数。
bx r14