空闲任务
2025/8/22大约 3 分钟
空闲任务
做的事
接上一讲,空闲任务是没任务时执行的函数,优先级最低,在里面可以进行各种处理
// 在 tasks.c 文件中查找这个函数
void prvIdleTask( void *pvParameters )
{
for( ;; ) {
// 这里是空闲任务的循环体
// 1. 检查是否要删除已终止的任务
prvCheckTasksWaitingTermination();
// 2. 如果启用了钩子函数,就调用它
#if ( configUSE_IDLE_HOOK == 1 )
{
vApplicationIdleHook();
}
#endif
// 3. 如果启用了Tickless模式,可能进入低功耗
#if ( configUSE_TICKLESS_IDLE != 0 )
{
// Tickless低功耗相关处理
}
#endif
}
}1.在这个函数里面会回收 在函数里面调用vTaskDelete()删除自己 任务的内存,如果是别的任务的话就立刻释放掉
2.configIDLE_SHOULD_YILED为 1的话可以创建和空闲任务优先级相同的任务,这样的话就是时间片调度。但是,和普通的时间片调度不一样:当就序列表里面有空闲优先级任务就绪时,如果当前正在执行空闲任务,那么会打断空闲任务的执行,用剩下的时间片去执行这个任务。
/*
* FreeRTOS 空闲任务(Idle Task)
* 当没有其它任务就绪时,由内核自动创建并运行。
*/
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
(void) pvParameters; // 防止编译器告警
for( ;; )
{
/* ① 检查是否有任务自删(例如任务调用了 vTaskDelete(NULL))
* 若有,则由空闲任务回收它们的堆栈和TCB */
prvCheckTasksWaitingTermination();
/* ②(可选)如果系统是非抢占式调度,则主动让出CPU */
#if ( configUSE_PREEMPTION == 0 )
taskYIELD(); // 主动切换任务
#endif
/* ③(可选)如果是抢占式调度,但空闲优先级有多个任务,则轮流执行 */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 )
{
taskYIELD(); // 同优先级任务轮换
}
#endif
/* ④(可选)用户自定义钩子函数:vApplicationIdleHook()
* 可在其中执行后台工作(不能阻塞!) */
#if ( configUSE_IDLE_HOOK == 1 )
vApplicationIdleHook();
#endif
/* ⑤(Tickless Idle 模式)节拍抑制与低功耗休眠 */
#if ( configUSE_TICKLESS_IDLE != 0 )
{
TickType_t xExpectedIdleTime;
// 预估系统可以空闲多久(tick数)
xExpectedIdleTime = prvGetExpectedIdleTime();
// 如果预计空闲时间足够长,考虑进入低功耗
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
// 暂停调度器,防止任务调度干扰
vTaskSuspendAll();
{
// 再次确认可空闲时间
configASSERT( xNextTaskUnblockTime >= xTickCount );
xExpectedIdleTime = prvGetExpectedIdleTime();
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
// 进入低功耗模式前的钩子
traceLOW_POWER_IDLE_BEGIN();
// 真正进入低功耗模式(调用vPortSuppressTicksAndSleep),具体见上一节
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
// 退出低功耗后的钩子
traceLOW_POWER_IDLE_END();
}
}
// 恢复调度
( void ) xTaskResumeAll();
}
}
#endif
}
}flowchart TD
A[开始:进入空闲任务循环] --> B[检查是否有任务被删除]
B --> C[若有任务删除 → 释放TCB和堆栈]
C --> D[是否启用抢占式调度?]
D -->|否| E[调用 taskYIELD() 主动切换任务]
D -->|是| F[是否启用空闲任务让出?]
F -->|是| G[若空闲优先级队列中有多个任务 → taskYIELD()]
F -->|否| H[继续执行]
G --> H[执行用户空闲钩子函数 vApplicationIdleHook()]
H --> I{是否启用Tickless Idle模式?}
I -->|否| J[保持空闲循环(节拍继续运行)]
I -->|是| K[计算预计空闲时间 xExpectedIdleTime]
K --> L{是否长于阈值 configEXPECTED_IDLE_TIME_BEFORE_SLEEP?}
L -->|否| J
L -->|是| M[暂停调度器 vTaskSuspendAll()]
M --> N[再次确认空闲时间]
N --> O{仍然满足休眠条件?}
O -->|否| P[恢复调度 xTaskResumeAll()]
O -->|是| Q[traceLOW_POWER_IDLE_BEGIN()]
Q --> R[调用 portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime)\n进入低功耗模式(WFI)]
R --> S[traceLOW_POWER_IDLE_END()]
S --> P[恢复调度 xTaskResumeAll()]
P --> J[继续空闲循环]
J --> A[下一次循环]钩子函数
就是回调函数而已

在空闲任务钩子函数里面不能再主动触发任务切换(阻塞延时等),已经是最低优先级了
也可以关闭Tickless,在这里面实现低功耗进入和退出。进出低功耗函数是自定义的,和上面的
PreSleepProcessing等2个函数内容一样


