FreeRTOS cpu利用率简单介绍

  • Post author:
  • Post category:其他

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.实验现象


版权声明:本文为weixin_63889667原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。