FreeRTOS--CPU利用率
1.简介
FreeRTOS 原生支持运行时间统计 ,开启后可获取整体 CPU 使用率 和每个任务的 CPU 占用率 ,核心原理:CPU 使用率 = 100% − 空闲任务占用率 ,内核在任务上下文切换时自动累计每个任务运行时间。
2.核心原理
- 开启运行时间统计后,每个任务TCB会记录
ulRunTimeCounter()(任务累计运行时间)。 - 空闲任务(idle)的运行时间 = CPU空闲时间;
- 整体CPU使用率公式:
plaintext
CPU使用率(%) = 100 - (空闲任务运行时间 ÷ 系统总运行时间) × 100
- 单个任务使用率:
(任务运行时间 / 总运行时间)*100
3.具体实现
1.开启相关配置
- 将
configUSE_TRACE_FACILITY设置为1。 - 加入这两个宏定义。

- 把这两行程序给注释掉

- 打开获取空闲任务句柄函数(可选)
这个打开时可以使用方式二进行获取CPU利用率

- 开启定时器提供一个稳定的时钟
该定时每0.1秒,CPU_RunTime相加一次。
在步骤2定义这两个宏定义时,已经把这个CPU_RunTime值赋给了宏,FreeRTOS会调用这个宏,从而使用CPU_RunTime这个值。
2.编写获取任务
方式一:封装好的函数
c
vTaskGetRunTimeStats((char *)&CPU_RunInfo);
c
void CPU_task(void *pvParameters)
{
uint8_t CPU_RunInfo[400];//保存任务运行时间信息
while(1)
{
memset(CPU_RunInfo,0,400);//信息缓冲区清零
vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息
printf("---------------------------------------------\r\n");
printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
memset(CPU_RunInfo,0,400); //信息缓冲区清零
vTaskGetRunTimeStats((char *)&CPU_RunInfo);
printf("任务名 运行计数 利用率\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n\n");
vTaskDelay(3000); /* 延时1000个tick */
}
}
运行结果:

方式二:自己封装
使用公式自己进行计算:
CPU使用率(%) = 100 - (空闲任务运行时间 ÷ 系统总运行时间) × 100
- 获取空闲任务句柄。
- 通过句柄获取任务信息(运行时间)
- 代入公式进行计算
c
void CPU_task(void *pvParameters)
{
while(1)
{
TaskStatus_t xTaskDetails;
// 获取空闲任务句柄
TaskHandle_t xIdleHandle = xTaskGetIdleTaskHandle();
// 获取任务信息,其中包含运行时间
vTaskGetInfo(xIdleHandle, &xTaskDetails, pdTRUE, eInvalid);
// 2. 获取 系统高精度总运行时间(必须用高精度定时器!)
uint32_t total_time = portGET_RUN_TIME_COUNTER_VALUE();
// 3. 直接套用你的公式
float cpu_usage = 100.0f - ((float)xTaskDetails.ulRunTimeCounter / total_time) * 100.0f;
printf("CPU利用率: %0.1f \n", cpu_usage);
vTaskDelay(3000); /* 延时1000个tick */
}
}
运行结果:

方案共性和区别
其实这两个方案用的原理是一样的,都是使用这个公式:
c
任务使用率(%) = (该任务运行时间 ÷ 系统总运行时间) × 100
而当其他任务运行完成,轮到空闲任务运行时,则表示此时系统空闲当前没有任务运行。所以用1减去(空闲任务运行时间÷ 系统总运行时间) × 100。就是系统CPU利用率。
这个系统封装好的函数本质也是遍历所有任务的运算时间/系统总时间*100.
vTaskGetRunTimeStats((char *)&CPU_RunInfo);
源码:
c
void vTaskGetRunTimeStats( char *pcWriteBuffer )
{
TaskStatus_t *pxTaskStatusArray;
volatile UBaseType_t uxArraySize, x;
uint32_t ulTotalTime, ulStatsAsPercentage;
// 1. 获取系统总运行时间(你定义的定时器计数)
ulTotalTime = portGET_RUN_TIME_COUNTER_VALUE();
// 2. 获取当前有多少个任务
uxArraySize = uxTaskGetNumberOfTasks();
// 3. 申请一块内存,保存所有任务状态
pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
// 4. 把所有任务信息读取到数组里
uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime );
// 5. 遍历每一个任务
for( x = 0; x < uxArraySize; x++ )
{
// 6. 计算百分比(核心!)
ulStatsAsPercentage = ( pxTaskStatusArray[ x ].ulRunTimeCounter * 100UL ) / ulTotalTime;
// 7. 把名字、时间、百分比 写入字符串
sprintf( pcWriteBuffer, "%s\t%lu\t%lu%%\r\n",
pxTaskStatusArray[ x ].pcTaskName,
pxTaskStatusArray[ x ].ulRunTimeCounter,
ulStatsAsPercentage );
}
// 8. 释放内存
vPortFree( pxTaskStatusArray );
}
3.完整程序
创建3个任务,其中两个为任务为普通任务,另一个任务用于获取CPU利用率与任务相关信息并通过串口打印出来。
c
#include "system.h"
#include "SysTick.h"
#include "FreeRTOS.h"
#include "task.h"
#include "string.h"
#include "usart.h"
#include "oled.h"
#include "time.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 70
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define OLED1_TASK_PRIO 2
//任务堆栈大小
#define OLED1_STK_SIZE 150
//任务句柄
TaskHandle_t OLED1Task_Handler;
//任务函数
void OLED1_task(void *pvParameters);
//任务优先级
#define OLED2_TASK_PRIO 3
//任务堆栈大小
#define OLED2_STK_SIZE 150
//任务句柄
TaskHandle_t OLED2Task_Handler;
//任务函数
void OLED2_task(void *pvParameters);
//任务优先级
#define CPU_TASK_PRIO 4
//任务堆栈大小
#define CPU_STK_SIZE 500
//任务句柄
TaskHandle_t CPUTask_Handler;
//任务函数
void CPU_task(void *pvParameters);
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
OLED_Init();
delay_ms(400);
Serial_Init();
TIM4_Init(100-1,72-1);//定时0.1ms
printf("FreeRTOS CPU利用率统计\r\n");
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
OLED_ShowString(1,5,"succeed");
//创建按键任务
xTaskCreate( OLED1_task,"OLED1_task", OLED1_STK_SIZE, NULL,OLED1_TASK_PRIO,&OLED1Task_Handler);
xTaskCreate( OLED2_task,"OLED2_task", OLED2_STK_SIZE, NULL,OLED2_TASK_PRIO,&OLED2Task_Handler);
xTaskCreate( CPU_task,"CPU_task", CPU_STK_SIZE, NULL,CPU_TASK_PRIO,&CPUTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//oled1显示
void OLED1_task(void *pvParameters)
{
static u8 k=0;
while(1)
{
OLED_ShowNum(2,1,10,2);
vTaskDelay(600);
// printf("OLED1_Task Running,LED2_ON\r\n");
OLED_ShowNum(2,1,11,2);
vTaskDelay(2400);
// printf("OLED1_Task Running,LED2_OFF\r\n");
}
}
//oled2显示
void OLED2_task(void *pvParameters)
{
while(1)
{
OLED_ShowNum(2,5,20,2);
vTaskDelay(2400);
// printf("OLED2_Task Running,LED2_ON\r\n");
OLED_ShowNum(2,5,21,2);
vTaskDelay(600);
// printf("OLED2_Task Running,LED2_OFF\r\n");
}
}
//CPU任务函数
void CPU_task(void *pvParameters)
{
uint8_t CPU_RunInfo[400];//保存任务运行时间信息
while(1)
{
//方式一 :封装好的函数
memset(CPU_RunInfo,0,400);//信息缓冲区清零
vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息
printf("---------------------------------------------\r\n");
printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n");
memset(CPU_RunInfo,0,400); //信息缓冲区清零
vTaskGetRunTimeStats((char *)&CPU_RunInfo);
printf("任务名 运行计数 利用率\r\n");
printf("%s", CPU_RunInfo);
printf("---------------------------------------------\r\n\n");
// 方式二:自己进行封装运算
TaskStatus_t xTaskDetails;
// 获取空闲任务句柄
TaskHandle_t xIdleHandle = xTaskGetIdleTaskHandle();
// 获取任务信息,其中包含运行时间
vTaskGetInfo(xIdleHandle, &xTaskDetails, pdTRUE, eInvalid);
// 2. 获取 系统高精度总运行时间(必须用高精度定时器!)
uint32_t total_time = portGET_RUN_TIME_COUNTER_VALUE();
// 3. 直接套用你的公式
float cpu_usage = 100.0f - ((float)xTaskDetails.ulRunTimeCounter / total_time) * 100.0f;
printf("CPU利用率: %0.1f \n", cpu_usage);
vTaskDelay(3000); /* 延时1000个tick */
}
}
