当前位置: 代码迷 >> 综合 >> 从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(三十)CPU 使用率统计
  详细解决方案

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(三十)CPU 使用率统计

热度:31   发布时间:2023-12-16 14:06:00.0

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(三十)CPU 使用率统计

 

 

一、CPU 利用率的基本概念:

CPU使用率其实就是系统运行的程序占用的CPU资源,表示机器在某段时间程序运行的情况

二、CPU 利用率的作用:

一个系统设计的好坏,可以使用CPU使用率来衡量,一个好的系统必然是能完美响应急需的处理并且系统的资源不会浪费

三、CPU 利用率统计:

① FreeRTOS我们提供测量各个任务占用CPU时间的函数接口

② 消耗一个高精度的定时器精度是系统时钟节拍的10-20

③ 宏定义配置:

configGENERATE_RUN_TIME_STATS

configUSE_TRACE_FACILITY

④ 会编译下面3个函数:

vTaskList()

vTaskGetRunTimeStats() -> prvWriteNameToBuffer()

⑤ 需要实现一个中断频率为 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");

四、CPU 利用率统计实验:

       CPU利用率实验是是在FreeRTOS创建了三个任务,其中两个任务是普通任务,另一个任务用于获取CPU利用率与任务相关信息并通过串口打印出来

实验步骤:

  1. 打开宏定义
  2. 实现一个高精度定时器
  3. 在定时器中断中计数
  4. 创建任务
  5. 获取任务信息与CPU利用率——vTaskList()vTaskGetRunTimeStats()

实验代码:

/************************************************************************ @file    main.c* @version V1.0* @date    2018-xx-xx* @brief   FreeRTOS v9.0.0 + STM32 *********************************************************************************************************************************************/ /*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/ 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_debug_usart.h"
#include "./tim/bsp_basic_tim.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();printf("这是一个-STM32全系列开发板-FreeRTOS-CPU利用率统计实验!\r\n");/* 创建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();   /* 启动任务,开启调度 */elsereturn -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(500);   /* 延时500个tick */printf("LED2_Task Running,LED1_ON\r\n");LED2_OFF;     vTaskDelay(500);   /* 延时500个tick */		 		printf("LED2_Task Running,LED1_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();/* 串口初始化	*/Debug_USART_Config();/* 基本定时器初始化	*/TIMx_Configuration();}/********************************END OF FILE****************************/

#include "bsp_basic_tim.h"/*** @brief  基本定时器 TIMx,x[6,7]中断优先级配置* @param  无* @retval 无*/
static void TIMx_NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);		// 设置中断来源NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQn; 	// 设置抢占优先级NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	 // 设置子优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}/** 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有* TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,* 另外三个成员是通用定时器和高级定时器才有.*-----------------------------------------------------------------------------* TIM_Prescaler         都有* TIM_CounterMode			 TIMx,x[6,7]没有,其他都有(基本定时器)* TIM_Period            都有* TIM_ClockDivision     TIMx,x[6,7]没有,其他都有(基本定时器)* TIM_RepetitionCounter TIMx,x[1,8]才有(高级定时器)*-----------------------------------------------------------------------------*/
static void TIM_Mode_Config(void)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;// 开启TIMx_CLK,x[6,7] RCC_APB1PeriphClockCmd(BASIC_TIM_CLK, ENABLE); /* 累计 TIM_Period个后产生一个更新或者中断*/		//当定时器从0计数到4999,即为5000次,为一个定时周期TIM_TimeBaseStructure.TIM_Period = 1000-1;       //定时器时钟源TIMxCLK = 2 * PCLK1  //				PCLK1 = HCLK / 4 //				=> TIMxCLK=HCLK/2=SystemCoreClock/2=90MHz// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=10000HzTIM_TimeBaseStructure.TIM_Prescaler = 180-1;	// 初始化定时器TIMx, x[2,3,4,5]TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);// 清除定时器更新中断标志位TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);// 开启定时器更新中断TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);// 使能定时器TIM_Cmd(BASIC_TIM, ENABLE);	
}/*** @brief  初始化基本定时器定时,1ms产生一次中断* @param  无* @retval 无*/
void TIMx_Configuration(void)
{TIMx_NVIC_Configuration();	TIM_Mode_Config();
}/*********************************************END OF FILE**********************/
/********************************************************************************* @file    FMC_SDRAM/stm32f4xx_it.c * @author  MCD Application Team* @version V1.0.1* @date    11-November-2013* @brief   Main Interrupt Service Routines.*         This file provides template for all exceptions handler and*         peripherals interrupt service routine.******************************************************************************* @attention** <h2><center>&copy; COPYRIGHT 2013 STMicroelectronics</center></h2>** Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");* You may not use this file except in compliance with the License.* You may obtain a copy of the License at:**        http://www.st.com/software_license_agreement_liberty_v2** Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.********************************************************************************//* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_it.h"
#include "./tim/bsp_basic_tim.h"#include "FreeRTOS.h"					//FreeRTOS使用		  
#include "task.h" /** @addtogroup STM32F429I_DISCOVERY_Examples* @{*//** @addtogroup FMC_SDRAM* @{*/ /* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*//******************************************************************************/
/*            Cortex-M4 Processor Exceptions Handlers                         */
/******************************************************************************//*** @brief  This function handles NMI exception.* @param  None* @retval None*/
void NMI_Handler(void)
{
}/*** @brief  This function handles Hard Fault exception.* @param  None* @retval None*/
void HardFault_Handler(void)
{/* Go to infinite loop when Hard Fault exception occurs */while (1){}
}/*** @brief  This function handles Memory Manage exception.* @param  None* @retval None*/
void MemManage_Handler(void)
{/* Go to infinite loop when Memory Manage exception occurs */while (1){}
}/*** @brief  This function handles Bus Fault exception.* @param  None* @retval None*/
void BusFault_Handler(void)
{/* Go to infinite loop when Bus Fault exception occurs */while (1){}
}/*** @brief  This function handles Usage Fault exception.* @param  None* @retval None*/
void UsageFault_Handler(void)
{/* Go to infinite loop when Usage Fault exception occurs */while (1){}
}/*** @brief  This function handles Debug Monitor exception.* @param  None* @retval None*/
void DebugMon_Handler(void)
{}/*** @brief  This function handles SysTick Handler.* @param  None* @retval None*/
extern void xPortSysTickHandler(void);
//systick中断服务函数
void SysTick_Handler(void)
{	#if (INCLUDE_xTaskGetSchedulerState  == 1 )if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED){#endif  /* INCLUDE_xTaskGetSchedulerState */  xPortSysTickHandler();#if (INCLUDE_xTaskGetSchedulerState  == 1 )}#endif  /* INCLUDE_xTaskGetSchedulerState */
}/* 用于统计运行时间 */
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);  		 }		 	
}/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

实验现象:

 

 

 

  相关解决方案