学习资料链接
http://wiki.csie.ncku.edu.tw/embedded/freertos
https://freertos.blog.csdn.net/article/details/51190095
介绍
调度器是FreeRTOS操作系统的核心,主要负责任务切换,即找出最高优先级的就绪任务,并使之获得CPU运行权。调度器并非自动运行的,需要人为启动它。
API函数vTaskStartScheduler()用于启动调度器
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/* Add the idle task at the lowest priority. */
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
{
#ifdef RTL_PLACE_IDLE_STACK_IN_SRAM
// it's same function call as original FreeRTOS source except that it use stack in SRAM
xReturn = xTaskGenericCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle, (void *)&ucIdleTaskHeap, NULL);
#else
/* Create the idle task, storing its handle in xIdleTaskHandle so it can
be returned by the xTaskGetIdleTaskHandle() function. */
xReturn = xTaskCreate( prvIdleTask, "IDLE", tskIDLE_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
}
#else
{
#ifdef RTL_PLACE_IDLE_STACK_IN_SRAM
// it's same function call as original FreeRTOS source except that it use stack in SRAM
xReturn = xTaskGenericCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL, (void *)&ucIdleTaskHeap, NULL );
#else
/* Create the idle task without storing its handle. */
xReturn = xTaskCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
#endif
}
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
#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 */
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 );
}
}
这个API函数首先创建一个空闲任务,空闲任务使用最低优先级(0级),空闲任务的任务句柄存放在静态变量xIdleTaskHandle中,可以调用API函数xTaskGetIdleTaskHandle()获得空闲任务句柄。
如果任务创建成功,则关闭中断(调度器启动结束时会再次使能中断的),初始化一些静态变量,然后调用函数xPortStartScheduler()来启动系统节拍定时器并启动第一个任务。因为设置系统节拍定时器涉及到硬件特性,因此函数xPortStartScheduler()由移植层提供,不同的硬件架构,这个函数的代码也不相同。
xPortStartScheduler()的实现
任务控制块如何运行
所以,SVC中断服务函数一开始就使用全局指针pxCurrentTCB获得第一个要启动的任务TCB,从而获得任务的当前堆栈栈顶指针。先将人为入栈的寄存器R4~R11出栈,将最新的堆栈栈顶指针赋值给线程堆栈指针PSP,再取消中断掩蔽。到这里,只要发生中断,就都能够被响应了。
中断服务函数通过下面两句汇编返回。Cortex-M3架构中,r14的值决定了从异常返回的模式,这里r14最后四位按位或上0x0d,表示返回时从进程堆栈中做出栈操作、返回后进入线程模式、返回Thumb状态。
orr r14, #0xd
bx r14
执行bx r14指令后,硬件自动将寄存器xPSR、PC、LR、R12、R3~R0出栈,这时任务A的任务函数指针vTask_A会出栈到PC指针中,从而开始执行任务A。
至此,任务vTask_A获得CPU执行权,调度器正式开始工作。
任务切换
调度的时候从就绪表中寻找优先级最高的任务
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority = uxTopReadyPriority; \
\
/* Find the highest priority queue that contains ready tasks. */ \
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
{ \
configASSERT( uxTopPriority ); \
--uxTopPriority; \
} \
\
/* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \
the same priority get an equal share of the processor time. */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
uxTopReadyPriority = uxTopPriority; \
} /* taskSELECT_HIGHEST_PRIORITY_TASK */