FreeRTOS在启动任务调度时会自动创建一个空闲任务,空闲任务主要在系统没有其它任务或任务都处于挂起状态时执行,它被系统设置为最低优先级,不会去抢占其它高优先级的任务,从而既能保证系统总有至少一个任务可以运行又不干扰到其它任务。空闲任务里面可以执行一些辅助操作,比如任务删除自身时由于无法立马释放掉自己的内存,这时可以做个标记,在空闲任务里面去删除。还有个非常重要的功能就是实现低功耗,进入空闲任务一般意味着系统当前没事做了,此时如果关闭某些外设或时钟就能达到降低功率的效果。
从启动任务调度器的代码中可以看到系统创建了一个空闲任务:
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/* 静态创建空闲任务 */
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
.....
/* 静态内存方式创建空闲任务 */
xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
configIDLE_TASK_NAME,
ulIdleTaskStackSize,
( void * ) NULL,
portPRIVILEGE_BIT,
pxIdleTaskStackBuffer,
pxIdleTaskTCBBuffer );
.....
}
#else
{
//动态方式创建空闲任务
xReturn = xTaskCreate( prvIdleTask,
configIDLE_TASK_NAME,
configMINIMAL_STACK_SIZE,
( void * ) NULL,
portPRIVILEGE_BIT,
&xIdleTaskHandle );
}
#endif
其中空闲任务的函数指针 prvIdleTask 是一个宏定义:
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
static portTASK_FUNCTION( prvIdleTask, pvParameters )
可以看出 portTASK_FUNCTION 的形参为 prvIdleTask ,所以最终 portTASK_FUNCTION 就是空闲任务 prvIdleTask,这里分析一下 portTASK_FUNCTION 这个函数的源码:
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
( void ) pvParameters;//防止报错
portTASK_CALLS_SECURE_FUNCTIONS();
for( ;; )
{
/* 进入空闲任务后会不停的查看是否有任务需要删除,在这个函数内进行删除 */
prvCheckTasksWaitingTermination();
#if ( configUSE_PREEMPTION == 0 )
{
/* 如果没有使用抢占,会不停的强制一次任务切换查看是否有其他任务需要执行
使用了抢占的话就不需要这一步了,因为有高优先级任务就绪后会自动抢占 */
taskYIELD();
}
#endif
/* 如果使能了抢占并使能时间片调度的话执行这个分支 */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
{
/* 查看除了空闲任务以外同等优先级下有没有任务等待执行,有的话将时间片剩余的时间让给同优先级的就绪任务 */
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
{
taskYIELD();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
/* 定义了这个宏的话当进入空闲任务时就会执行下面这个钩子函数 */
#if ( configUSE_IDLE_HOOK