FreeRTOS cpu利用率简单介绍

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于STM32CPU利用率计算的代码实现: ```c #include "stm32f4xx.h" #include "stm32f4xx_hal.h" #include "cmsis_os.h" #define IDLE_TASK_STACK_SIZE 128 #define TASK_1_STACK_SIZE 128 #define TASK_2_STACK_SIZE 128 #define IDLE_TASK_PRIORITY osPriorityIdle #define TASK_1_PRIORITY osPriorityNormal #define TASK_2_PRIORITY osPriorityNormal #define IDLE_TASK_NAME "IdleTask" #define TASK_1_NAME "Task1" #define TASK_2_NAME "Task2" void idle_task(void const * argument); void task1(void const * argument); void task2(void const * argument); osThreadId idleTaskHandle; osThreadId task1Handle; osThreadId task2Handle; uint32_t idleTaskCpuTime; uint32_t task1CpuTime; uint32_t task2CpuTime; uint32_t totalCpuTime; int main(void) { HAL_Init(); SystemClock_Config(); osThreadDef(idleTask, idle_task, IDLE_TASK_PRIORITY, 0, IDLE_TASK_STACK_SIZE); idleTaskHandle = osThreadCreate(osThread(idleTask), NULL); osThreadSetName(idleTaskHandle, IDLE_TASK_NAME); osThreadDef(task1, task1, TASK_1_PRIORITY, 0, TASK_1_STACK_SIZE); task1Handle = osThreadCreate(osThread(task1), NULL); osThreadSetName(task1Handle, TASK_1_NAME); osThreadDef(task2, task2, TASK_2_PRIORITY, 0, TASK_2_STACK_SIZE); task2Handle = osThreadCreate(osThread(task2), NULL); osThreadSetName(task2Handle, TASK_2_NAME); osKernelStart(); } void idle_task(void const * argument) { for (;;) { idleTaskCpuTime++; } } void task1(void const * argument) { for (;;) { task1CpuTime++; osDelay(100); } } void task2(void const * argument) { for (;;) { task2CpuTime++; osDelay(50); } } void SysTick_Handler(void) { totalCpuTime++; } void HAL_MspInit(void) { __HAL_RCC_SYSCFG_CLK_ENABLE(); __HAL_RCC_PWR_CLK_ENABLE(); } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 360; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); } ``` 该代码利用了FreeRTOS操作系统来创建三个任务:一个空闲任务和两个周期性任务。空闲任务将会一直运行,计算CPU空闲时间。两个周期性任务分别每隔100ms和50ms运行一次,计算CPU占用时间。同时,SysTick中断被用来计算总CPU时间。最终,可以通过简单的计算来计算CPU利用率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值