4. 空闲任务钩子函数应用

任务目标

在SafeRTOS中启用并实现空闲任务钩子函数,利用空闲任务执行时机进入低功耗模式或处理后台任务。本案例以LED翻转模拟后台任务执行,并展示如何正确配置钩子函数开关。

核心原理

SafeRTOS创建空闲任务时自动注册钩子函数。钩子函数在空闲任务每次获得CPU时被调用,且必须满足两个约束:不能调用任何可能阻塞的API(如延迟、队列读取),不能长期占用CPU。典型用途包括:进入睡眠模式、喂看门狗、统计空闲占比。

实现代码

/* SafeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/* 硬件抽象(示例) */
#define LED_TOGGLE()        do { /* 实际硬件翻转 */ } while(0)
#define ENTER_LOW_POWER()   do { /* 进入MCU低功耗模式 */ } while(0)

/* 后台任务计数 */
static volatile uint32_t ulBackgroundCounter = 0;

/* 空闲任务钩子函数 */
void vApplicationIdleHook(void)
{
    /* 每次空闲任务执行时,翻转LED模拟后台任务 */
    LED_TOGGLE();
    ulBackgroundCounter++;

    /* 若系统允许,可在此进入低功耗模式 */
    /* ENTER_LOW_POWER(); */
}

/* 普通任务:验证钩子函数是否被调用 */
static void vMainTask(void *pvParameters)
{
    uint32_t ulLastCounter = 0;

    for (;;)
    {
        /* 等待1秒(1000个tick) */
        vTaskDelay(pdMS_TO_TICKS(1000));

        /* 检查钩子函数是否执行过 */
        if (ulBackgroundCounter != ulLastCounter)
        {
            ulLastCounter = ulBackgroundCounter;
            /* 钩子函数已运行,可在此输出调试信息 */
        }
    }
}

int main(void)
{
    /* 硬件初始化(时钟、GPIO等) */

    /* 创建主任务 */
    xTaskCreate(vMainTask, "Main", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

    /* 启动调度器 */
    vTaskStartScheduler();

    /* 正常不会到达此处 */
    for (;;);
}

关键配置

在FreeRTOSConfig.h中必须显式启用钩子函数:

#define configUSE_IDLE_HOOK    1   /* 使能空闲钩子 */
#define configUSE_TICK_HOOK    0   /* 本案例不使用tick钩子 */

运行与验证

  1. 将上述代码加入工程,确保FreeRTOSConfig.h中configUSE_IDLE_HOOK设为1。
  2. 编译并烧录到目标板。
  3. 观察现象:LED持续翻转,说明空闲钩子函数被周期性调用。
  4. 若使用调试器,可在vApplicationIdleHook入口设断点,程序会频繁触发断点,证明空闲任务确实在运行。
  5. 通过ulBackgroundCounter变量值持续增长,可确认钩子函数执行次数在增加。

通过条件:LED以肉眼可见频率翻转(约每毫秒数次),且计数器单调递增。若LED不翻转,检查钩子函数是否注册、空闲任务是否被阻塞(如某任务占满CPU)。

低功耗模式接入说明

实际低功耗应用时,将ENTER_LOW_POWER()宏展开为MCU的睡眠指令(如__WFI())。需注意:

  • 进入睡眠前应关闭不必要的外设时钟;
  • 唤醒后需在钩子函数返回前恢复系统时钟;
  • 若使用中断唤醒,确保中断优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY。

常见问题

  • 钩子函数不执行:确认configUSE_IDLE_HOOK为1,且函数名拼写与SafeRTOS期望一致(vApplicationIdleHook)。
  • 系统卡死:钩子函数内调用了阻塞API(如vTaskDelay),空闲任务无法让出CPU。
  • 低功耗无法唤醒:检查唤醒源配置,以及睡眠前是否禁用了必要的中断。
高优先级任务 空闲任务 H H H 任务执行 钩子调用点 时间 →

上图展示了高优先级任务与空闲任务交替执行的时间序列。绿色标记点(H)表示空闲任务每次获得CPU时,钩子函数被调用的位置。空闲任务在所有其他任务阻塞或延迟时运行,因此钩子函数天然适合处理低优先级后台工作。