FreeRTOS学习笔记(11)——CPU使用率统计
发布日期:2021-05-06 23:35:57 浏览次数:19 分类:技术文章

本文共 8573 字,大约阅读时间需要 28 分钟。

一、基本概念

CPU 使用率其实就是系统运行的程序占用的 CPU 资源,表示机器在某段时间程序运行的情况,如果这段时间中,程序一直在占用 CPU 的使用权,那么可以人为 CPU 的利用率是 100%。CPU 的利用率越高,说明机器在这个时间上运行了很多程序,反之较少。利用率的高低与 CPU 强弱有直接关系,就像一段一模一样的程序,如果使用运算速度很慢的 CPU,它可能要运行 1000ms,而使用很运算速度很快的 CPU 可能只需要 10ms,那么在 1000ms 这段时间中,前者的 CPU 利用率就是 100%,而后者的 CPU 利用率只有 1%,因为 1000ms 内前者都在使用 CPU 做运算,而后者只使用 10ms 的时间做运算,剩下的时间 CPU 可以做其他事情。

FreeRTOS 是多任务操作系统,对 CPU 都是分时使用的:比如 A 任务占用 10ms,然后 B 任务占用 30ms,然后空闲 60ms,再又是 A 任务占 10ms,B 任务占 30ms,空闲 60ms;

二、CPU利用率统计

在调试的时候很有必要得到当前系统的 CPU 利用率相关信息,但是在产品发布的时候,就可以把 CPU 利用率统计这个功能去掉,因为使用任何功能的时候,都是需要消耗系统资源的,FreeRTOS 是使用一个外部的变量进行统计时间的,并且消耗一个高精度的定时器,其用于定时的精度是系统时钟节拍的 10-20 倍,比如当前系统时钟节拍是 1000HZ,那么定时器的计数节拍就要是 10000-20000HZ。而且 FreeRTOS 进行 CPU 利用率统计的时候,也有一定缺陷,因为它没有对进行 CPU 利用率统计时间的变量做溢出保护,我们使用的是 32 位变量来系统运行的时间计数值,而按 20000HZ 的中断频率计算,每进入一中断就是 50us,变量加一,最大支持计数时间:2^32 * 50us / 3600s = 59.6 分钟,运行时间超过了 59.6 分钟后统计的结果将不准确,除此之外整个系统一直响应定时器 50us 一次的中断会比较影响系统的性能。

三、配置选项

FreeRTOSConfig.h 配置与系统运行时间和任务状态收集有关的配置选项,并且实现

portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()portGET_RUN_TIME_COUNTER_VALUE() 这两个宏定义

/********************************************************************FreeRTOS 与运行时间和任务状态收集有关的配置选项**********************************************************************///启用运行时间统计功能 #define configGENERATE_RUN_TIME_STATS 1 //启用可视化跟踪调试 #define configUSE_TRACE_FACILITY 1 /* 与宏 configUSE_TRACE_FACILITY 同时为 1 时会编译下面 3 个函数* prvWriteNameToBuffer()* vTaskList(),* vTaskGetRunTimeStats()*/#define configUSE_STATS_FORMATTING_FUNCTIONS 1extern volatile uint32_t CPU_RunTime; #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (CPU_RunTime = 0ul) #define portGET_RUN_TIME_COUNTER_VALUE() CPU_RunTime

然后需要实现一个中断频率为 20000HZ 定时器,用于系统运行时间统计,其实很简单,只需将 CPU_RunTime 变量自加即可,这个变量是用于记录系统运行时间的,中断服务函数见下

/* 用于统计运行时间 */volatile uint32_t CPU_RunTime = 0UL;void BASIC_TIM_IRQHandler (void){
if(TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) {
CPU_RunTime++; TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); }}

然后我们就可以在任务中调用 vTaskGetRunTimeStats()vTaskList() 函数获得任务的相关信息与 CPU 使用率的相关信息,然后打印出来即可

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");

四、示例

/* FreeRTOS头文件 */#include "FreeRTOS.h"#include "task.h"/* 开发板硬件bsp头文件 */#include "bsp_led.h"#include "bsp_usart.h"#include "bsp_TiMbase.h"#include "string.h"/**************************** 任务句柄 ********************************//*  * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么 * 这个句柄可以为NULL。 */ /* 创建任务句柄 */static TaskHandle_t AppTaskCreate_Handle = NULL;/* LED任务句柄 */static TaskHandle_t LED1_Task_Handle = NULL;static TaskHandle_t LED2_Task_Handle = NULL;static TaskHandle_t CPU_Task_Handle = NULL;/***************************************************************************                             函数声明**************************************************************************/static void AppTaskCreate(void);/* 用于创建任务 */static void LED1_Task(void* pvParameters);/* LED1_Task任务实现 */static void LED2_Task(void* pvParameters);/* LED2_Task任务实现 */static void CPU_Task(void* pvParameters);/* CPU_Task任务实现 */static void BSP_Init(void);/* 用于初始化板载相关资源 *//*****************************************************************  * @brief  主函数  * @param  无  * @retval 无  * @note   第一步:开发板硬件初始化             第二步:创建APP应用任务            第三步:启动FreeRTOS,开始多任务调度  ****************************************************************/int main(void){
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */ /* 开发板硬件初始化 */ BSP_Init(); /* 创建AppTaskCreate任务 */ xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */ (const char* )"AppTaskCreate",/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )1, /* 任务的优先级 */ (TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */ /* 启动任务调度 */ if(pdPASS == xReturn) {
vTaskStartScheduler(); /* 启动任务,开启调度 */ } else {
return -1; } while(1); /* 正常不会执行到这里 */ }/*********************************************************************** * @ 函数名 : AppTaskCreate * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面 * @ 参数 : 无 * @ 返回值 : 无 **********************************************************************/static void AppTaskCreate(void){
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */ taskENTER_CRITICAL(); //进入临界区 /* 创建LED_Task任务 */ xReturn = xTaskCreate((TaskFunction_t )LED1_Task, /* 任务入口函数 */ (const char* )"LED1_Task",/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL, /* 任务入口函数参数 */ (UBaseType_t )2, /* 任务的优先级 */ (TaskHandle_t* )&LED1_Task_Handle);/* 任务控制块指针 */ if(pdPASS == xReturn) {
printf("创建LED1_Task任务成功!\r\n"); } /* 创建LED_Task任务 */ xReturn = xTaskCreate((TaskFunction_t )LED2_Task, /* 任务入口函数 */ (const char* )"LED2_Task",/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL, /* 任务入口函数参数 */ (UBaseType_t )3, /* 任务的优先级 */ (TaskHandle_t* )&LED2_Task_Handle);/* 任务控制块指针 */ if(pdPASS == xReturn) {
printf("创建LED2_Task任务成功!\r\n"); } /* 创建LED_Task任务 */ xReturn = xTaskCreate((TaskFunction_t )CPU_Task, /* 任务入口函数 */ (const char* )"CPU_Task",/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL, /* 任务入口函数参数 */ (UBaseType_t )4, /* 任务的优先级 */ (TaskHandle_t* )&CPU_Task_Handle);/* 任务控制块指针 */ if(pdPASS == xReturn) {
printf("创建CPU_Task任务成功!\r\n"); } vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL(); //退出临界区}/********************************************************************** * @ 函数名 : LED_Task * @ 功能说明: LED_Task任务主体 * @ 参数 : * @ 返回值 : 无 ********************************************************************/static void LED1_Task(void* parameter){
while (1) {
LED1_ON; vTaskDelay(500); /* 延时500个tick */ printf("LED1_Task Running,LED1_ON\r\n"); LED1_OFF; vTaskDelay(500); /* 延时500个tick */ printf("LED1_Task Running,LED1_OFF\r\n"); }}static void LED2_Task(void* parameter){
while (1) {
LED2_ON; vTaskDelay(300); /* 延时500个tick */ printf("LED2_Task Running,LED2_ON\r\n"); LED2_OFF; vTaskDelay(300); /* 延时500个tick */ printf("LED2_Task Running,LED2_OFF\r\n"); }}static void CPU_Task(void* parameter){
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(1000); /* 延时500个tick */ }}/*********************************************************************** * @ 函数名 : BSP_Init * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面 * @ 参数 : * @ 返回值 : 无 *********************************************************************/static void BSP_Init(void){
/* * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断, * 都统一用这个优先级分组,千万不要再分组,切忌。 */ NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); /* LED 初始化 */ LED_GPIO_Config(); /* 串口初始化 */ USART_Config(); /* 基本定时器初始化 */ BASIC_TIM_Init();}


• 由 写于 2021 年 1 月 5 日

• 参考:

上一篇:NRF52832学习笔记(36)——iBeacon
下一篇:FreeRTOS学习笔记(10)——中断管理

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2025年03月25日 20时53分11秒