基于FreeRTOS的CPU利用率计算教程详解(STM32版)

news2024/12/23 13:00:11

前言:FreeRTOS 是一个 RTOS 类的嵌入式实时操作系统,作为实时操作系统其必定拥有任务调度的属性。通过系统自身的任务调度算法实现任务安全且高效的切换,这就不可避免的引出了各个任务对 CPU 的利用率问题(裸机情况下 CPU 利用率默认100%)。实际工程项目中,通过对 CPU 利用率的实时监控可以清楚地熟知系统目前的运行状态,清楚地分析出各任务是否存在需要优化以及当前选定的CPU型号是否满足项目芯片性能的客观要求

本项目教程以 STM32F103ZET6 为核心 MCU ,通过 FreeRTOS 系统实现简单地工程项目。以该项目为例进行教学CPU利用率计算的方法和代码(代码开源!)。

实验硬件:STM32F103ZET6;7针1.3寸TFT-LCD(240×240);

硬件实物图:

效果图:

一、基本概念

在操作系统中 CPU 利用率是在软件架构设计中必须要考虑的一个重要性能指标。它直接影响到程序的执行时间以及优先级更高的任务能否实时响应的问题。CPU 利用率其实就是系统运行的程序占用的 CPU 资源,表示机器在某段时间程序运行的情况,如果这段时间中,程序一直在占用 CPU 的使用权,那么可以人为 CPU 的利用率是 100%。CPU 的利用率越高,说明机器在这个时间上运行了很多程序,反之较少。

补充说明:CPU 利用率的高低与 CPU 性能强弱有直接关系,就像一段一模一样的程序,如果使用运算速度很慢的 CPU,它可能要运行 1000ms,而使用很运算速度很快的 CPU 可能只需要 100ms,那么在 1000ms 这段时间中,前者的 CPU 利用率就是 100%,而后者的 CPU 利用率只有 10%,因为 1000ms 内前者都在使用 CPU 做运算,而后者只使用 10ms 的时间做运算,剩下的时间 CPU 可以做其他事情。

二、CPU利用率统计方法

FreeRTOS操作系统是使用任务的累计运行时间来统计每一个任务自系统开始运行到当前时刻的CPU占用时间,即该任务的CPU利用率举个例子:系统上电到当前时刻一共运行了 100s,其中任务A运行了 10s,任务B运行了 20s,剩下的 70s 由空闲任务在运行,那么在 10s 的时间内,任务A的CPU使用率是 10%,任务B的CPU使用率是 20%,空闲任务(IDLE)的CPU使用率是 70%。如下图是FreeRTOS系统CPU使用率统计示意图:

CPU利用率计算原理:在FreeRTOS中使用一个外部的变量(CPU_RunTime)进行统计时间的,并且消耗一个高精度的定时器(项目中TIM3),其用于定时的精度是系统时钟节拍的 10-20 倍(比如当前系统时钟节拍是 1000HZ,那么定时器的计数节拍就要是 10000-20000HZ)。

方法的缺陷:没有对进行 CPU 利用率统计时间的变量做溢出保护,使用的是 32 位变量来系统运行的时间计数值,而按 20000HZ 的中断频率计算,每进入一中断就是 50us,变量加一,最大支持计数时间:2^32 * 50us / 3600s = 59.6 分钟,运行时间超过了 59.6 分钟后统计的结果将不准确,除此之外整个系统一直响应定时器 50us 一次的中断会比较影响系统的性能。

三、CubeMX配置

★项目工程默认已经成功移植FreeRTOS系统(CubeMX配置仅保留CPU利用率计算需求部分)

1、RCC配置外部高速晶振(精度更高)——HSE;

2、SYS配置:Debug设置成Serial WireTimebase Source配置:设置TIM2为FreeRTOS系统心跳

3、TIM3配置:通过设置50us中断一次计数CPU运行时间(开启TIM3定时器中断);

4、UART1配置:MCU与电脑进行串口通讯,打印出CPU利用率信息;

5、时钟树配置 

6、工程配置

四、代码与解析

4.1 FreeRTOSConfig.h文件配置

FreeRTOS系统中CPU利用率统计功能是一个可裁剪的功能,可通过宏定义来进行裁剪。在FreeRTOSConfig.h文件中,添加如下代码:

/********************************************************************
FreeRTOS 与运行时间和任务状态收集有关的配置选项
**********************************************************************/
//启用运行时间统计功能 
#define configGENERATE_RUN_TIME_STATS 1 
//启用可视化跟踪调试 
#define configUSE_TRACE_FACILITY 1 
/* 与宏 configUSE_TRACE_FACILITY 同时为 1 时会编译下面 3 个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

extern volatile uint32_t CPU_RunTime; 

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() (CPU_RunTime = 0UL) 
#define portGET_RUN_TIME_COUNTER_VALUE() CPU_RunTime

4.2 TIM3中断系统计时

实现一个中断频率周期为 50us 的定时器,在定时器的中断回调函数中只需将 CPU_RunTime 变量自加即可,这个变量是用于记录系统运行时间的,中断服务函数见下(在main.c):

HAL_TIM_Base_Start_IT(&htim3);    //开启TIM3的定时器中断

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM2) {            //心跳
       HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
    if(htim->Instance == TIM3)
    {
//		HAL_IncTick();
        CPU_RunTime++;
    }
  /* USER CODE END Callback 1 */
}

4.3 任务信息与CPU利用率

设置完成后,可以在任务中调用 vTaskGetRunTimeStats() 和 vTaskList() 函数获得任务的相关信息与 CPU 利用率的相关信息,然后打印出来即可:

void CPU_TRACE(void *pvParameters)
{
	uint8_t CPU_RunInfo[512];		//信息缓冲区清零
	while(1)
	{
	    memset(CPU_RunInfo,0,512); //信息缓冲区清零

        vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息 

        printf("---------------------------------------------\r\n");
        printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
        printf("%s", CPU_RunInfo);
        printf("---------------------------------------------\r\n");
 
        memset(CPU_RunInfo,0,512); //信息缓冲区清零

        vTaskGetRunTimeStats((char *)&CPU_RunInfo); 
 
        printf("任务名 运行计数 使用率\r\n");
        printf("%s", CPU_RunInfo);
        printf("---------------------------------------------\r\n\n");
	}
}

4.4 项目代码

volatile uint32_t CPU_RunTime = 0;
int cpu = 0;

#define TASK0_TASK_PRIO		1		    //任务优先级	
#define TASK0_STK_SIZE 		256  		//任务堆栈大小
#define TASK1_TASK_PRIO		2			//任务优先级	
#define TASK1_STK_SIZE 		256  		//任务堆栈大小	
#define TASK2_TASK_PRIO		1			//任务优先级	
#define TASK2_STK_SIZE 		256  		//任务堆栈大小		
#define TASK3_TASK_PRIO		4			//任务优先级	
#define TASK3_STK_SIZE 		256  		//任务堆栈大小

TaskHandle_t Task0Task_Handler;		//任务句柄
TaskHandle_t Task1Task_Handler;		//任务句柄
TaskHandle_t Task2Task_Handler;		//任务句柄
TaskHandle_t Task3Task_Handler;		//任务句柄
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//task0任务函数
void task0_task(void *pvParameters)
{
	while(1)
	{	
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,0); 
		vTaskDelay(600);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,1); 
		vTaskDelay(600);
	}
}

//task1任务函数
void task1_task(void *pvParameters)
{
	while(1)
	{
		for(int a=0;a<8;a++)
		{
			showimage4(gImage_1[a]); 
			if(a == 7)a=0;
			vTaskDelay(200);
		}
	}
}

//task2任务函数
void task2_task(void *pvParameters)
{	
	while(1)
	{
		HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,0); 
		vTaskDelay(1000);
		HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,1); 
		vTaskDelay(1000);		
	}
}

//task3_task任务函数
void task3_task(void *pvParameters)
{
	uint8_t CPU_RunInfo[512];		//信息缓冲区清零
	while(1)
	{
	    memset(CPU_RunInfo,0,512); //信息缓冲区清零

        vTaskList((char *)&CPU_RunInfo); //获取任务运行时间信息 

        printf("---------------------------------------------\r\n");
        printf("任务名 任务状态 优先级 剩余栈 任务序号\r\n");
        printf("%s", CPU_RunInfo);
        printf("---------------------------------------------\r\n");
 
        memset(CPU_RunInfo,0,512); //信息缓冲区清零

        vTaskGetRunTimeStats((char *)&CPU_RunInfo); 
 
        printf("任务名 运行计数 使用率\r\n");
        printf("%s", CPU_RunInfo);
        printf("---------------------------------------------\r\n\n");
        LCD_ShowNew(65,170,'C',24,0);
		LCD_ShowNew(80,170,'P',24,0);
		LCD_ShowNew(95,170,'U',24,0);
		LCD_ShowNew(110,170,':',24,0);
		LCD_ShowxNum2(125,170,cpu,2,24,0);
		LCD_ShowNew(155,170,'%',24,0);
        vTaskDelay(2000);   /* 2000tick */	
	}
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_RTC_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
	//硬件初始化
    HAL_TIM_Base_Start_IT(&htim3);
	Lcd_Init();			//初始化TFT	
	LCD_Clear(WHITE);   //清屏
	BACK_COLOR=WHITE;
	POINT_COLOR=RED;

	showhanzi(40,20,0);
	showhanzi(75,20,1);	
	showhanzi(110,20,2);
	showhanzi(145,20,3);
	showhanzi(180,20,4);
	
	BACK_COLOR=WHITE;
	POINT_COLOR=BLACK;	
	//创建任务0
    xTaskCreate((TaskFunction_t )task0_task,          //任务函数
              (const char*    )"task0_task",          //任务名称
              (uint16_t       )TASK0_STK_SIZE,        //任务堆栈大小
              (void*          )NULL,                  //传递给任务函数的参数
              (UBaseType_t    )TASK0_TASK_PRIO,       //任务优先级
              (TaskHandle_t*  )&Task0Task_Handler);   //任务句柄  
    //创建任务1
    xTaskCreate((TaskFunction_t )task1_task,             
                (const char*    )"task1_task",           
                (uint16_t       )TASK1_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )TASK1_TASK_PRIO,        
                (TaskHandle_t*  )&Task1Task_Handler);   
    //创建任务2
    xTaskCreate((TaskFunction_t )task2_task,     
                (const char*    )"task2_task",   
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_TASK_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler);
    //创建任务3
    xTaskCreate((TaskFunction_t )task3_task,     
                (const char*    )"task3_task",   
                (uint16_t       )TASK3_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK3_TASK_PRIO,
                (TaskHandle_t*  )&Task3Task_Handler); 								
	//启动任务调度器
	vTaskStartScheduler();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

上述代码共创建了4个系统任务:2个LED灯定时闪烁任务;1个TFTLCD的GIF动图显示任务;1个CPU利用率信息打印输出任务。读者朋友可以根据自己的实际情况去修改自己的任务函数中具体需要实现的内容。

五、源码分析

5.1 源码分析1

通过上述 FreeRTOSConfig.h 文件的配置过程可以看出,FreeRTOS本身就是提供 CPU 的监管功能的,只不过需要我们对配置文件进行修改。

/* FreeRTOSConfig.h */
/* 使能CPU使用率统计功能 */
#define configGENERATE_RUN_TIME_STATS   1

而如果使能了 CPU 使用率统计功能,则在每一个任务的任务控制块中,定义了一个变量 ulRunTimeCounter 用于统计该任务的运行时间,代码如下

/*task.c*/
/* 任务控制块接口体定义 */
typedef struct tskTaskControlBlock{
    volatile StackType_t   *pxTopOfStack;
    // ...
#if( configGENERATE_RUN_TIME_STATS == 1 )
    uint32_t        ulRunTimeCounter;   /*< Stores the amount of time the task has spent in the Running state. */
#endif
    // ...
}

在进行任务上下文切换时,会将当前任务的本次运行时间(从上一次任务上下文切换开始至当前时刻)累计到当前任务的任务控制块中的 ulRunTimeCounter 上,任务上下文切换的代码如下:

//task.c
void vTaskSwitchContext( void ){
    if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    {        
        /* The scheduler is currently suspended - do not allow a context        switch. */
        xYieldPending = pdTRUE;
    }
    else
    {
       xYieldPending = pdFALSE;
        traceTASK_SWITCHED_OUT();

       /* 条件编译选项:如果使能了CPU使用率统计功能,则下面的代码用于统计当前任务的CPU占用总时间 */
        #if ( configGENERATE_RUN_TIME_STATS == 1 )
        {
            /* **(1)获取当前时间ulTotalRunTime(CPU运行的总时间)** */
            #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
                portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
           #else
               ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
           #endif
               /* **(2)将当前任务的运行时间(当前时间(ulTotalRunTime) - 上一次任务切换的时间(ulTaskSwitchedInTime))累加到当前任务控制块** */
                if( ulTotalRunTime > ulTaskSwitchedInTime )
                {
                    pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                } 
                /* **(3)更新上一次任务切换的时间ulTaskSwitchedInTime,作为下一次任务切换时计算任务运行时间的基准时刻** */
                ulTaskSwitchedInTime = ulTotalRunTime; 
           } 
       #endif /* configGENERATE_RUN_TIME_STATS */

        /* Check for stack overflow, if configured. */
        taskCHECK_FOR_STACK_OVERFLOW();

        /* Select a new task to run using either the generic C or port        optimised asm code. */
        taskSELECT_HIGHEST_PRIORITY_TASK();
        traceTASK_SWITCHED_IN();
        #if ( configUSE_NEWLIB_REENTRANT == 1 ) 
       {
                  /* Switch Newlib's _impure_ptr variable to point to the _reent            structure specific to this task. */
                  _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
        }       
        #endif /* configUSE_NEWLIB_REENTRANT */
    }
}

由上面的代码可知,要实现任务的CPU使用率统计功能,还需要实现宏函数portGET_RUN_TIME_COUNTER_VALUE() 用于获取当前时间。为了提高 CPU 利用率的计算精度,一般使用一个高精度的定时器去计数运行时间,FreeRTOSConfig.h代码如下:

/* Run time stats gathering definitions. */
#define configGENERATE_RUN_TIME_STATS  1
extern volatile uint32_t ulHighFrequencyTimerCounts;    /* 在高精度定时器中断中累加 */
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()    (ulHighFrequencyTimerCounts = 0ul)
#define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerCounts

最后只需要在定时器中断回调函数中进行累加CPU运行时间即可:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM2) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
    if(htim->Instance == TIM3)
    {
        CPU_RunTime++;
    }
  /* USER CODE END Callback 1 */
}

5.2 源码分析2

接下来给大家分析一下,CPU 利用率计算的核心函数:vTaskList() 和 vTaskGetRunTimeStats() 

vTaskList()函数: 获取任务的相关信息

vTaskGetRunTimeStats() 函数:获取PU使用率的相关信息

#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) )
    void vTaskGetRunTimeStats( char *pcWriteBuffer )
    {
        TaskStatus_t *pxTaskStatusArray;
        volatile UBaseType_t uxArraySize, x;
        uint32_t ulTotalTime, ulStatsAsPercentage;
        #if( configUSE_TRACE_FACILITY != 1 )
        {
            #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats().
        }
        #endif
        
        /* 确保 write buffer中没有字符串(写入字符串结束符). */
        *pcWriteBuffer = 0x00;
        /* 获取当前系统任务个数 */
        uxArraySize = uxCurrentNumberOfTasks;
        /* 为每一个任务申请一块内存空间用于存储任务状态信息. */
        pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) );
        if( pxTaskStatusArray != NULL )
        {
            /* Generate the (binary) data. */
            uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime );
            /* ulTotalTime为系统运行总时间. */
            ulTotalTime /= 100UL;
            /* Avoid divide by zero errors. */
            if( ulTotalTime > 0 )
            {
                /* Create a human readable table from the binary data. */
                for( x = 0; x < uxArraySize; x++ )
                {
                    /* 计算当前任务的CPU占用率. */
                    ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime;
                    /* 将任务名写入到输出字符串. */
                    pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName );
                    /* 将当前任务的运行时间ulRunTimeCounter和CPU占用率写入到输出字符串 */
                    if( ulStatsAsPercentage > 0UL )
                    {
                    #ifdef portLU_PRINTF_SPECIFIER_REQUIRED
                    {
                        sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
                    }
                    #else
                    {
                        /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */
                        sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage );
                    }
                    #endif
                    }
                    else
                    {
                    /* 如果计算的CPU占用率为0, 则用"<1%"表示 */
                    #ifdef portLU_PRINTF_SPECIFIER_REQUIRED
                    {
                        sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter );
                    }
                    #else
                    {
                        /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */
                        sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter );
                    }
                    #endif
                }
                pcWriteBuffer += strlen( pcWriteBuffer );
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
        /* 释放内存块pxTaskStatusArray . */
        vPortFree( pxTaskStatusArray );
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}
#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */

 简而言之,vTaskGetRunTimeStats() 函数的作用就是通过当前任务的运行时间ulRunTimeCounter 和系统运行总时间 ulTotalTime 来计算当前任务的CPU占用率,并格式化输出(计算方法和本文所述的原理一致)。

5.3 源码分析3

项目中的CPU利用率如何来得:

FreeRTOS在启动任务调度时候,会默认创建一个IDLE空闲任务。当CPU运行闲置的时候就会默认去运行空闲任务(IDLE),保证系统运行调度的正常运行。所以,当CPU运载的任务越重,IDEA空闲任务的CPU利用率就越低。综上所述,作者采用100-空闲任务的CPU利用率就能间接反映出目前CPU的负载情况。

项目中CPU计算公式:

CPU = (100 - IDEA(CPU))*100%

修改vTaskGetRunTimeStats代码:

#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configUSE_TRACE_FACILITY == 1 ) )

    void vTaskGetRunTimeStats( char * pcWriteBuffer )
    {
        TaskStatus_t * pxTaskStatusArray;
        UBaseType_t uxArraySize, x;
        configRUN_TIME_COUNTER_TYPE ulTotalTime, ulStatsAsPercentage;

        /* Make sure the write buffer does not contain a string. */
        *pcWriteBuffer = ( char ) 0x00;

        /* Take a snapshot of the number of tasks in case it changes while this
         * function is executing. */
        uxArraySize = uxCurrentNumberOfTasks;

        /* Allocate an array index for each task.  NOTE!  If
         * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will
         * equate to NULL. */
        pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */

        if( pxTaskStatusArray != NULL )
        {
            /* Generate the (binary) data. */
            uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime );

            /* For percentage calculations. */
            ulTotalTime /= 100UL;

            /* Avoid divide by zero errors. */
            if( ulTotalTime > 0UL )
            {
                /* Create a human readable table from the binary data. */
                for( x = 0; x < uxArraySize; x++ )
                {
                    /* What percentage of the total run time has the task used?
                     * This will always be rounded down to the nearest integer.
                     * ulTotalRunTime has already been divided by 100. */
                    ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime;

										cpu = 100 - pxTaskStatusArray[ uxArraySize-4 ].ulRunTimeCounter / ulTotalTime;
									
                    /* Write the task name to the string, padding with
                     * spaces so it can be printed in tabular form more
                     * easily. */
                    pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName );

                    if( ulStatsAsPercentage > 0UL )
                    {
                        #ifdef portLU_PRINTF_SPECIFIER_REQUIRED
                        {
                            sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
                        }
                        #else
                        {
                            /* sizeof( int ) == sizeof( long ) so a smaller
                             * printf() library can be used. */
                            sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */
                        }
                        #endif
                    }

利用全局变量将 CPU 的数值提取出来,通过TCTLCD将数值显示出来。

六、项目效果

CPU利用率

七、项目代码

代码地址:FreeRTOS的CPU利用率计算工程代码资源-CSDN文库

如果积分不够的朋友,点波关注评论区留下邮箱,作者无偿提供源码和后续问题解答。求求啦关注一波吧 !!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自行车和电动自行车上亚马逊标准有什么区别?UL2849,16CFR1512

自行车 自行车是一种两轮的或三轮的交通工具&#xff0c;完全靠人力驱动后轮前进。本政策所涵盖的自行车包括当座位调整到最高位置时&#xff0c;座位离地面超过 25 英寸的自行车&#xff0c;以及座位高度为 25 英寸或以下的人行道自行车。本政策也适用于公路使用的卧式自行车…

有人看好、有人唱衰,无代码能不能开发复杂应用?

司马说 工欲善其事&#xff0c;必先利其器&#xff0c;软件开发也是如此。《人月神话》有一章的标题就是“巧匠因为他的工具而出名”。软件开发与编码工作是一项枯燥无味的任务&#xff0c;但现在这项工作很可能利用软件本身来完成。 无代码开发颠覆了传统编码的开发模式&#…

【工具使用】- git实现gitee托管代码以及检出代码

1. 下载Git工具 git下载地址1&#xff1a;https://git-scm.com/download/win git下载2&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/github-release/git-for-windows/git/Git%20for%20Windows%202.40.1/ 下载完成后安装 安装直接执行exe可执行程序&#xff0c;下一步…

晒出新高度?2023夏季小红书防晒趋势前瞻

夏日将临&#xff0c;防晒需求激增&#xff0c;进入市场旺季。今年防晒赛道朝着“防护升级&#xff0c;多面兼顾”大势发展。 哪些趋势值得关注&#xff1f;本期&#xff0c;千瓜将通过小红书数据分析和笔记内容洞察&#xff0c;为品牌提供数据支持和方向参考。 月增长高达501.…

分布式事务 [面试]

1. 基础概念 1.1 什么是事务 事务可以看做是一次大的活动&#xff0c;它由不同的小活动组成&#xff0c;这些活动要么全部成功&#xff0c;要么全部失败。 1.2 本地事务 在计算机系统中&#xff0c;更多的是通过关系型数据库来控制事务&#xff0c;这是利用数据库本身的事务…

TBSS和dpabifiber

以前用过TBSS&#xff0c;最近好久没用&#xff0c;又忘记了&#xff0c;现在重新捡起来。 首先是QSIprep和QSIrecon。 QSIPrep预处理后&#xff0c;文件夹包括anat和dwi。anat和fmriprep是一样的&#xff0c;就不介绍了。 dwi&#xff1a; 可以看出&#xff0c;这些就是在原始…

全景丨0基础学习VR全景制作,平台篇第16章:热点功能-图片

大家好&#xff0c;欢迎观看蛙色VR官方——后台使用系列课程&#xff01; 功能说明 应用场景 热点&#xff0c;指在全景作品中添加各种类型图标的按钮&#xff0c;引导用户通过按钮产生更多的交互&#xff0c;增加用户的多元化体验。 图片热点&#xff0c;即点击热点后弹出单张…

总结841

学习目标&#xff1a; 5月&#xff08;张宇强化前10讲&#xff0c;背诵25篇短文&#xff0c;熟词僻义300词基础词&#xff09; 每日必复习&#xff08;5分钟&#xff09; 重做了5道记录本上的极限题&#xff0c;值得庆幸的是&#xff0c;有一道极限题之前有三种解法&#xff…

【c语言】函数递归调用

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ…

98. 验证二叉搜索树【68】

难度等级&#xff1a;中等 上一篇算法&#xff1a; 剑指 Offer 54. 二叉搜索树的第k大节点【37】 力扣此题地址&#xff1a; 98. 验证二叉搜索树 - 力扣&#xff08;Leetcode&#xff09; 1.题目&#xff1a;98. 验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断…

阿里云服务器配置选择流程(2023新版教程)

阿里云服务器ECS选购指南&#xff0c;阿里云百科分享2023阿里云服务器新手选择流程&#xff0c;选购云服务器有两个入口&#xff0c;一个是选择活动机&#xff0c;只需要选择云服务器地域、系统、带宽即可&#xff1b;另一个是在云服务器页面&#xff0c;自定义选择云服务器配置…

Cartographer源码阅读---点云数据的预处理

上一节我们已经看到了, 传感器数据是通过CollatedTrajectoryBuilder类的HandleCollatedSensorData函数 传递给 GlobalTrajectoryBuilder类的相应函数. 从GlobalTrajectoryBuilder开始, 传感器数据才真正进入到Cartographer的前后端. Cartographer最重要的数据类型就是点云, 所以…

软件著作权审查时间、软件导刊审稿周期、计算机工程与应用审稿周期、计算机技术与发展审稿周期、电子测量与仪器学报审稿周期

目录 《软件著作权》审查时间《软件导刊》审稿周期《计算机工程与应用》审稿周期《计算机技术与发展》审稿周期《电子测量与仪器学报》审稿周期 《软件著作权》审查时间 2022年申请软著的时间节点&#xff1a; 11.15受理通知书 11.15审查中 12.3审批中 12.20审查中 12.24待发放…

如何利用ChatGPT API 搭建私人 AI会话

搭建私有ChatGPT 访问谷歌的方式自行解决一、Github域名和证书私有服务器开始搭建私有Open Ai 访问谷歌的方式自行解决 对不起&#xff0c;没有魔法者止步&#xff01;没有 API 止步&#xff01; 对不起&#xff0c;没有魔法者止步&#xff01;没有 API 止步&#xff01; 对不…

C#医院手术麻醉临床信息管理系统源码:操作指南(一)

手术麻醉系统操作指南&#xff1a; 1.麻醉管理 手术管理包括:手术申请、手术安排、查看手术申请单、手术通知单&#xff0c;填写病人术前会诊记录、谈话记录、麻醉记录、手术记录、附加手术、术后信息及手术回顾等功能。 &#xff08;1&#xff09;手术申请 【功能】&#…

亚马逊cpc常见产品测试合集

CPC认证就是儿童产品安全证书&#xff08;Children’s Product Certificate, CPC&#xff09;&#xff0c;适用于所有以12岁及以下儿童为主要目标使用对象的产品&#xff0c;如玩具、摇篮、儿童服装等。 如在美国本地生产则由制造商负责提供&#xff0c;如在其他国家生产则由进…

电脑文件夹拒绝访问,如何解决?

案例&#xff1a;打不开电脑文件夹怎么办&#xff1f; 【今天工作的时候&#xff0c;需要打开文件夹查找资料&#xff0c;但是这个文件无法打开&#xff0c;提示拒绝访问。有没有小伙伴也遇到过这种情况&#xff1f;最后是怎么解决的&#xff1f;】 使用电脑的过程中&#xf…

自定义构建docker镜像

创建dockerfile 我们新建一个目录docker_test&#xff0c;然后在这个目录下新建一个dockerfile文件&#xff0c;文件内容如下&#xff1a; FROM centos VOLUME ["volume01","volume02"] CMD echo "......end......" CMD /bin/bash这些是docker…

Vue.js核心概念简介:组件、数据绑定、指令和事件处理

本文介绍了Vue.js的四个核心概念&#xff1a;组件、数据绑定、指令和事件处理。每个概念都通过一个简单的示例进行了详细的解释。通过学习这些概念&#xff0c;您将能够充分利用Vue.js的强大功能&#xff0c;构建高效、灵活的Web应用程序。 1 组件 组件是Vue.js的核心概念之一…

Windows10本地搭建网站教程【内网穿透】

文章目录 概述1. 搭建一个静态Web站点2. 本地浏览测试站点是否正常3. 本地站点发布公网可访问3.1 安装cpolar内网穿透3.2 创建隧道映射公网地址3.3 获取公网URL地址 4. 公网远程访问内网web站点5. 配置固定二级子域名5.1 保留二级子域名5.2 配置二级子域名 6. 测试访问二级子域…