4. 空闲任务与钩子函数

任务定义

在RTOS中,空闲任务(Idle Task)是系统创建的最低优先级任务,仅在无其他就绪任务时运行。本案例要求在FreeRTOS环境下,利用空闲任务的钩子函数(Idle Hook)统计系统CPU利用率,并通过串口周期性输出结果。

实现原理

CPU利用率定义为:利用率 = (1 - 空闲时间 / 总时间) × 100%。空闲任务钩子函数在每个空闲任务循环中被调用,通过累加空闲计数器来记录空闲时间占比。同时,系统Tick中断周期性累加总时间计数器。统计周期结束后,计算两者比值即可得到CPU利用率。

代码实现


#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "uart.h"   // 假设已有串口驱动,提供 UART_SendString()

/* 统计周期:5000个Tick(假设Tick周期1ms,即5秒) */
#define STATS_PERIOD_TICKS    5000

static volatile uint32_t ulIdleCount = 0;      // 空闲计数器
static volatile uint32_t ulTotalCount = 0;     // 总计数器
static volatile uint32_t ulLastIdleCount = 0;  // 上次统计时的空闲计数

/* 空闲任务钩子函数:必须在FreeRTOSConfig.h中配置
   configUSE_IDLE_HOOK 为 1 */
void vApplicationIdleHook(void)
{
    ulIdleCount++;
}

/* 统计任务:周期性计算并输出CPU利用率 */
static void vStatsTask(void *pvParameters)
{
    uint32_t ulIdleDelta;
    uint32_t ulCpuUsage;
    char cBuf[32];

    for (;;)
    {
        /* 等待一个统计周期 */
        vTaskDelay(STATS_PERIOD_TICKS);

        /* 计算周期内空闲Tick数 */
        ulIdleDelta = ulIdleCount - ulLastIdleCount;
        ulLastIdleCount = ulIdleCount;

        /* 计算CPU利用率(百分比) */
        ulCpuUsage = 100UL - (ulIdleDelta * 100UL) / STATS_PERIOD_TICKS;

        /* 格式化并输出 */
        snprintf(cBuf, sizeof(cBuf), "CPU: %lu%%\r\n", ulCpuUsage);
        UART_SendString(cBuf);
    }
}

/* 模拟工作任务:占用CPU以产生非零利用率 */
static void vWorkTask(void *pvParameters)
{
    volatile uint32_t ulWork;
    for (;;)
    {
        /* 模拟约30% CPU负载:忙等300个Tick后休眠700个Tick */
        ulWork = 0;
        TickType_t xStart = xTaskGetTickCount();
        while ((xTaskGetTickCount() - xStart) < 300)
        {
            ulWork++;
        }
        vTaskDelay(700);
    }
}

void main(void)
{
    /* 初始化硬件(时钟、串口等) */
    HardwareInit();

    /* 创建统计任务(优先级2) */
    xTaskCreate(vStatsTask, "Stats", 128, NULL, 2, NULL);

    /* 创建工作任务(优先级1) */
    xTaskCreate(vWorkTask, "Work", 128, NULL, 1, NULL);

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

    /* 正常不会执行到这里 */
    for (;;);
}

关键配置说明

本案例依赖以下FreeRTOS配置项(在FreeRTOSConfig.h中):

  • configUSE_IDLE_HOOK 必须设为 1,否则空闲钩子不会被调用。
  • configTICK_RATE_HZ 设为 1000(即1ms一个Tick),保证统计精度。
  • 空闲任务栈大小需满足钩子函数调用需求(默认即可)。

验证方法

将代码编译烧录至目标板(如STM32F407),打开串口助手(波特率与UART驱动一致,通常115200)。观察输出:

  • 工作任务忙等300ms、休眠700ms,理论CPU利用率为30%。
  • 串口每5秒输出一行 CPU: xx%,数值应在30%附近波动(±5%以内)。
  • 若注释掉工作任务创建代码,则输出应接近 CPU: 0% 或极小值(因中断处理占用少量CPU)。

判定标准:串口输出的CPU利用率与工作任务忙等比例一致(约30%),且无系统崩溃或死机,即验证通过。

空闲任务钩子统计CPU利用率 — 时序与数据流 统计周期 = 5000 Tick (5秒) 工作任务 忙等300 Tick 工作任务 vTaskDelay(700) 工作任务 忙等300 Tick 空闲任务运行区间(钩子函数每次循环执行 ulIdleCount++) 空闲区间1: 约200 Tick 统计任务:每5000 Tick唤醒一次,计算并输出CPU利用率 读取 ulIdleCount CPU利用率 = (1 - (ulIdleDelta / 5000)) × 100%

代码行为说明

上述代码中,vApplicationIdleHook在空闲任务每次循环时被调用,累加ulIdleCount。统计任务每5秒读取一次该值,计算与上次的差值ulIdleDelta,该差值即为5秒内空闲任务执行的Tick数。由于系统总Tick数为5000,空闲占比即为ulIdleDelta/5000,CPU利用率由此算出。工作任务通过忙等300 Tick再休眠700 Tick的方式,模拟约30%的CPU负载,用于验证统计结果的正确性。