欢迎光临
我们一直在努力

FreeRTOS CPU使用率统计详解(STM32标准库版)

在 FreeRTOS 工程中,我们经常需要知道每个任务占用了多少 CPU 时间,以便找出性能瓶颈。FreeRTOS 自带运行时间统计功能,只要提供一个高精度计时器,就可以打印出各任务的运行时间和 CPU 占比。

本文以 STM32F103 + 标准外设库 为例,介绍配置方法和完整代码。


一、原理简介

FreeRTOS 的每个任务控制块(TCB)中都有一个 ulRunTimeCounter 成员,用来累计该任务的运行时间。

  • 在任务切换时,FreeRTOS 会读取高精度计时器的值;
  • 用本次读数和上次读数的差值,累加到当前任务的 ulRunTimeCounter 中;
  • 空闲任务运行时间占总时间的比例就是 CPU 空闲率;
  • CPU使用率 = 100% – 空闲任务占比。

因此我们需要一个比系统节拍 tick 快 10~100 倍的硬件计时器,通常用 STM32 的定时器实现。本文使用 TIM2 产生 100kHz 时基,统计精度为 10us,32位计数器溢出时间约 11.9 小时,比较适合长时间运行。


二、FreeRTOS 宏配置

在 FreeRTOSConfig.h 中开启统计功能,并指定计时器函数:

#define configUSE_TRACE_FACILITY 1
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define INCLUDE_xTaskGetIdleTaskHandle 1

extern void vConfigureTimerForRunTimeStats(void);
extern unsigned long ulGetRunTimeCounterValue(void);

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vConfigureTimerForRunTimeStats()
#define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue()

说明:

  • configUSE_TRACE_FACILITY:必须为 1;
  • configGENERATE_RUN_TIME_STATS:开启运行时间统计;
  • configUSE_STATS_FORMATTING_FUNCTIONS:允许使用 vTaskGetRunTimeStats();
  • portCONFIGURE_TIMER_FOR_RUN_TIME_STATS:配置统计计时器初始化函数;
  • portGET_RUN_TIME_COUNTER_VALUE:获取计数器当前值函数。

三、STM32标准库计时器代码

使用 TIM2 产生 100kHz 计数时基,代码如下:

#include "stm32f10x.h"

void vConfigureTimerForRunTimeStats(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

// 打开 TIM2 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;
// 系统时钟 / 100000 – 1,得到 100kHz 计数频率
TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 100000) 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
}

unsigned long ulGetRunTimeCounterValue(void)
{
return (unsigned long)TIM2->CNT;
}


四、任务与CPU使用率计算完整代码

下面给出一个完整的示例工程关键代码,包含任务创建、运行时间统计打印和 CPU 使用率计算。

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>

static void vTask1(void *pvParameters);
static void vTask2(void *pvParameters);
static void vStatsTask(void *pvParameters);
static float fGetCpuUsage(void);

int main(void)
{
// 初始化串口并重定向 printf,这里省略
// USART1_Config();

// 创建任务
xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE * 2, NULL, 1, NULL);
xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE * 2, NULL, 2, NULL);
xTaskCreate(vStatsTask, "Stats", configMINIMAL_STACK_SIZE * 4, NULL, 3, NULL);

// 启动调度器,会自动调用 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
vTaskStartScheduler();

while (1);
}

// 任务1:模拟持续工作
static void vTask1(void *pvParameters)
{
for (;;)
{
for (volatile int i = 0; i < 100000; i++);
vTaskDelay(pdMS_TO_TICKS(50));
}
}

// 任务2:模拟间歇工作
static void vTask2(void *pvParameters)
{
for (;;)
{
for (volatile int i = 0; i < 50000; i++);
vTaskDelay(pdMS_TO_TICKS(100));
}
}

// 统计任务:周期性打印运行时间和CPU使用率
static void vStatsTask(void *pvParameters)
{
char pcBuffer[1024];

for (;;)
{
vTaskDelay(pdMS_TO_TICKS(2000));

printf("———- CPU RunTime Stats ———-\\r\\n");
vTaskGetRunTimeStats(pcBuffer);
printf("%s", pcBuffer);
printf("Total CPU Usage: %.2f%%\\r\\n", fGetCpuUsage());
printf("—————————————\\r\\n");
}
}

// 计算总CPU使用率:100% – 空闲任务占比
static float fGetCpuUsage(void)
{
TaskStatus_t *pxTaskStatusArray = NULL;
UBaseType_t uxArraySize, uxIndex;
uint32_t ulTotalRunTime = 0;
uint32_t ulIdleRunTime = 0;
float fCpuUsage = 0.0f;
TaskHandle_t xIdleTaskHandle = NULL;

uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = (TaskStatus_t *)pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));

if (pxTaskStatusArray != NULL)
{
uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime);
xIdleTaskHandle = xTaskGetIdleTaskHandle();

for (uxIndex = 0; uxIndex < uxArraySize; uxIndex++)
{
if (pxTaskStatusArray[uxIndex].xHandle == xIdleTaskHandle)
{
ulIdleRunTime = pxTaskStatusArray[uxIndex].ulRunTimeCounter;
break;
}
}

vPortFree(pxTaskStatusArray);
}

if (ulTotalRunTime > 0)
{
fCpuUsage = 100.0f ((float)ulIdleRunTime * 100.0f / (float)ulTotalRunTime);
}

return fCpuUsage;
}


五、运行结果示例

串口输出类似如下:

———- CPU RunTime Stats ———-
Task Abs Time % Time
IDLE 1234567 38.50
Task1 987654 30.81
Task2 654321 20.41
Stats 326789 10.19
Tmr Svc 10234 0.09
Total CPU Usage: 61.50%
—————————————

可以看到:

  • IDLE 任务占比 38.50%,说明 CPU 空闲率为 38.50%;
  • Total CPU Usage 为 61.50%,与空闲率对应;
  • 每个任务都有对应的绝对运行时间和百分比。

六、注意事项

  • 计时器频率不要太高
    1MHz 的 32位计数器约 71 分钟溢出,100kHz 约 11.9 小时。长时间运行建议处理溢出,或使用 10kHz~100kHz 的时基。

  • 不要在高频率任务或中断中调用统计函数
    vTaskGetRunTimeStats() 内部会动态申请内存,建议在低优先级任务中周期性调用。

  • 任务栈要足够大
    尤其是统计任务中使用 printf 和 1024 字节缓冲区,需要适当增大任务栈。

  • 开启统计会增加任务切换开销
    对实时性要求极高的场景要谨慎使用。

  • 确保 SystemCoreClock 正确
    标准库工程中该变量通常在 system_stm32f10x.c 中定义,需与实际系统时钟一致。


  • 总结

    通过 FreeRTOS 自带的运行时间统计功能,配合 STM32 标准库定时器,可以方便地查看各任务的 CPU 占用率。核心就是:

    • 配置一个高精度计时器;
    • 开启 configGENERATE_RUN_TIME_STATS;
    • 调用 vTaskGetRunTimeStats() 打印统计表;
    • 通过空闲任务占比计算整体 CPU 使用率。

    这套方法简单实用,适合大部分 FreeRTOS 嵌入式项目。

    赞(0)
    未经允许不得转载:171主机测评 » FreeRTOS CPU使用率统计详解(STM32标准库版)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址