FreeRTOS--CPU利用率

FreeRTOS--CPU利用率

1.简介

FreeRTOS 原生支持运行时间统计 ,开启后可获取整体 CPU 使用率每个任务的 CPU 占用率 ,核心原理:CPU 使用率 = 100% − 空闲任务占用率 ,内核在任务上下文切换时自动累计每个任务运行时间

2.核心原理

  1. 开启运行时间统计后,每个任务TCB会记录ulRunTimeCounter()(任务累计运行时间)。
  2. 空闲任务(idle)的运行时间 = CPU空闲时间;
  3. 整体CPU使用率公式:
plaintext 复制代码
CPU使用率(%) = 100 - (空闲任务运行时间 ÷ 系统总运行时间) × 100
  1. 单个任务使用率:(任务运行时间 / 总运行时间)*100

3.具体实现

1.开启相关配置

  1. configUSE_TRACE_FACILITY设置为1。
  2. 加入这两个宏定义。
  3. 把这两行程序给注释掉
  4. 打开获取空闲任务句柄函数(可选)

这个打开时可以使用方式二进行获取CPU利用率

  1. 开启定时器提供一个稳定的时钟

该定时每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

  1. 获取空闲任务句柄。
  2. 通过句柄获取任务信息(运行时间)
  3. 代入公式进行计算
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 */		
  }
}
相关推荐
0南城逆流03 小时前
【网站分享】常用网站分享三:STM32常用模块链接
stm32·单片机·嵌入式硬件
星夜夏空993 小时前
STM32单片机学习(17) —— 串口外设中断
stm32·单片机·学习
hhcgchpspk3 小时前
easyx按键游戏
c++·stm32·单片机·游戏·easyx
0南城逆流03 小时前
【网站分享】常用网站分享四:STM32常用外设链接
stm32·单片机·嵌入式硬件
yu85939583 小时前
STM32 控制 W5500 以太网传输程序
stm32·单片机·嵌入式硬件
LCG元4 小时前
STM32实战:基于STM32F103的车内防窒息系统(红外检测+GSM报警)
stm32·单片机·嵌入式硬件
爱的si念4 小时前
Zephyr 在 Nucleo G474RE 的完整编译与模块提取指南
stm32·单片机·嵌入式硬件
m0_377108145 小时前
stm32--I2C
stm32·单片机·嵌入式硬件
秀秀更健康16 小时前
stm32: 系统时钟如何配置为72Mhz
stm32·单片机·嵌入式硬件