从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利用率与任务相关信息并通过串口打印出来。
实验步骤:
- 打开宏定义
- 实现一个高精度定时器
- 在定时器中断中计数
- 创建任务
- 获取任务信息与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>© 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****/
实验现象: