1. CPU 利用率简介
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;如果在一段时间内都是如此,那么这段时间内的利用率 为 40%,因为整个系统中只有 40%的时间是 CPU 处理数据的时间。 FreeRTOS 是一个很完善很稳定的操作系统,当然也给我们提供测量各个任 务占用 CPU 时间的函数接口,我们可以知道系统中的每个任务占用 CPU 的时 间,从而得知系统设计的是否合理,出于性能方面的考虑,有的时候,我们希望 知道 CPU 的使用率为多少,进而判断此 CPU 的负载情况和对于当前运行环境是 否能够“胜任工作”。所以,在调试的时候很有必要得到当前系统的 CPU 利用 率相关信息,但是在产品发布的时候,就可以把 CPU 利用率统计这个功能去掉, 因为使用任何功能的时候,都是需要消耗系统资源的,FreeRTOS 是使用一个外 部的变量进行统计时间的,并且消耗一个高精度的定时器,其用于定时的精度是 系统时钟节拍的 10-20 倍,比如当前系统时钟节拍是 1000HZ,那么定时器的计 数节拍就要是 10000-20000HZ。而且 FreeRTOS 进行 CPU 利用率统计的时候, 也有一定缺陷,因为它没有对进行 CPU 利用率统计时间的变量做溢出保护,我 们使用的是 32 位变量来系统运行的时间计数值,而按 20000HZ 的中断频率计 算,每进入一中断就是 50us,变量加一,最大支持计数时间:2^32 * 50us / 3600s =59.6 分钟,运行时间超过了 59.6 分钟后统计的结果将不准确,除此之外整个 系统一直响应定时器 50us 一次的中断会比较影响系统的性能。 385 用户想要使用 CPU 利用率统计的话,需要自定义配置一下,首先在 FreeRTOSConfig.h 配置与系统运行时间和任务状态收集有关的配置选项,并且 实现 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 与 portGET_RUN_TIME_COUNTER_VALUE()这两个宏定义,具体如下。
然后需要实现一个中断频率为 10000HZ 定时器,用于系统运行时间统计, 其实很简单,只需将 CPU_RunTime 变量自加即可,这个变量是用于记录系统运 行时间的,中断服务函数具体如下。
volatile uint32_t CPU_RunTime = 0UL;
/********************************************************************
***********
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : TIM6 中断函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************
**********/
void TIM6_IRQHandler(void)
{
386
if(TIM_GetITStatus(TIM6,TIM_IT_Update))
{
CPU_RunTime++;
}
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
}
2.整体代码
1.time.c
#include "time.h"
#include "usart.h"
/*******************************************************************************
* 函 数 名 : TIM4_Init
* 函数功能 : TIM4初始化函数
* 输 入 : per:重装载值
psc:分频系数
* 输 出 : 无
*******************************************************************************/
void TIM4_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4时钟
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //开启定时器中断
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//定时器中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=4;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM4,ENABLE); //使能定时器
}
/*******************************************************************************
* 函 数 名 : TIM4_IRQHandler
* 函数功能 : TIM4中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_Update))
{
printf("TIM4输出.......\r\n");
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
}
/*******************************************************************************
* 函 数 名 : TIM3_Init
* 函数功能 : TIM3初始化函数
* 输 入 : per:重装载值
psc:分频系数
* 输 出 : 无
*******************************************************************************/
void TIM3_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3时钟
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //开启定时器中断
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;//定时器中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=5;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3,ENABLE); //使能定时器
}
/*******************************************************************************
* 函 数 名 : TIM3_IRQHandler
* 函数功能 : TIM3中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update))
{
printf("TIM3输出.......\r\n");
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
/*******************************************************************************
* 函 数 名 : TIM6_Init
* 函数功能 : TIM6初始化函数
* 输 入 : per:重装载值
psc:分频系数
* 输 出 : 无
*******************************************************************************/
void TIM6_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);//使能TIM3时钟
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); //开启定时器中断
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;//定时器中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=5;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM6,ENABLE); //使能定时器
}
volatile uint32_t CPU_RunTime = 0UL;
/*******************************************************************************
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : TIM6中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void TIM6_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6,TIM_IT_Update))
{
CPU_RunTime++;
}
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
}
2.main.c
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "time.h"
#include "string.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 50
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
//任务优先级
#define CPU_TASK_PRIO 4
//任务堆栈大小
#define CPU_STK_SIZE 512
//任务句柄
TaskHandle_t CPUTask_Handler;
//任务函数
void CPU_task(void *pvParameters);
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
KEY_Init();
USART1_Init(115200);
TIM6_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(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED2任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
//创建CPU任务
xTaskCreate((TaskFunction_t )CPU_task,
(const char* )"CPU_task",
(uint16_t )CPU_STK_SIZE,
(void* )NULL,
(UBaseType_t )CPU_TASK_PRIO,
(TaskHandle_t* )&CPUTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
printf("LED1_Task Running,LED2_ON\r\n");
LED1=1;
vTaskDelay(800);
printf("LED1_Task Running,LED2_OFF\r\n");
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
while(1)
{
LED2=0;
vTaskDelay(800);
printf("LED2_Task Running,LED2_ON\r\n");
LED2=1;
vTaskDelay(200);
printf("LED2_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");
vTaskDelay(1000); /* 延时1000个tick */
}
}
3.实验现象