Detailed explanation of CPU utilization calculation tutorial based on FreeRTOS (STM32 version)

Preface: FreeRTOS is an RTOS- like embedded real-time operating system. As a real-time operating system, it must have the attribute of task scheduling. Safe and efficient switching of tasks is achieved through the system's own task scheduling algorithm, which inevitably leads to the  problem of CPU utilization of each task ( CPU utilization defaults to 100% in the case of bare metal ). In actual engineering projects, through real-time monitoring of CPU utilization, we can clearly understand the current operating status of the system , clearly analyze whether each task needs to be optimized and whether the currently selected CPU model meets the objective requirements of the project chip performance .

This project tutorial uses STM32F103ZET6 as the core MCU, and realizes simple engineering projects through the FreeRTOS system. Take this project as an example to teach the method and code of CPU utilization calculation ( code open source! ).

Experimental hardware: STM32F103ZET6; 7-pin 1.3-inch TFT-LCD (240×240);

Hardware physical map:

Renderings:

1. Basic concepts

CPU utilization in an operating system  is an important performance indicator that must be considered in software architecture design . It directly affects the execution time of the program and the real-time response of higher priority tasks. The CPU utilization is actually the CPU resources occupied by the programs running on the system , which means that the program is running on the machine for a certain period of time. If the program has been occupying the right to use the CPU during this period of time, it can be artificially assumed that the CPU utilization is 100%. . The higher the CPU utilization, it means that the machine is running a lot of programs at this time, and vice versa.

Supplementary Note: The CPU utilization rate is directly related to the performance of the CPU . Just like an identical program, if a CPU with a slow operation speed is used, it may take 1000ms to run, while a CPU with a very fast operation speed may only It takes 100ms, so during the period of 1000ms, the CPU utilization rate of the former is 100%, while the CPU utilization rate of the latter is only 10%, because the former is using the CPU for calculations within 1000ms, while the latter only uses 10ms for calculations , the rest of the time the CPU can do other things.

2. CPU utilization statistics method

The FreeRTOS operating system uses the cumulative running time of tasks to count the CPU usage time of each task from the start of the system to the current moment, that is, the CPU utilization of the task . For example: the system has run for a total of 100s from power-on to the current moment, among which task A has run for 10s, task B has run for 20s, and the remaining 70s are run by idle tasks, then within 10s, the CPU usage of task A The rate is 10%, the CPU usage of task B is 20%, and the CPU usage of the idle task (IDLE) is 70%. The following figure is a schematic diagram of the CPU usage statistics of the FreeRTOS system:

CPU utilization calculation principle: use an external variable (CPU_RunTime) in FreeRTOS to count time, and consume a high-precision timer (TIM3 in the project) , whose timing accuracy is 10-20 of the system clock beat times (for example, the current system clock beat is 1000HZ, then the counting beat of the timer must be 10000-20000HZ).

The defect of the method: there is no overflow protection for the variable of the CPU utilization statistics time, and the 32-bit variable is used to count the time value of the system operation, and calculated according to the interrupt frequency of 20000HZ, every time an interrupt is entered, it is 50us, and the variable is incremented by one , the maximum supported counting time: 2^32 * 50us / 3600s = 59.6 minutes, the statistical results will be inaccurate after the running time exceeds 59.6 minutes, in addition, the whole system has been responding to the timer interrupt once every 50us will affect the system performance.

3. CubeMX configuration

★The project project has been successfully ported to the FreeRTOS system by default (CubeMX configuration only retains the CPU utilization calculation requirement part)

1. RCC is configured with an external high-speed crystal oscillator (higher precision) - HSE;

2. SYS configuration: Debug is set to Serial Wire ; Timebase Source configuration: set TIM2 as FreeRTOS system heartbeat ;

3. TIM3 configuration: count CPU running time by setting a 50us interrupt once (enable TIM3 timer interrupt);

4. UART1 configuration: MCU communicates with the computer via serial port, and prints out CPU utilization information;

5. Clock tree configuration 

6. Engineering configuration

4. Code and Analysis

4.1 FreeRTOSConfig.h file configuration

The CPU utilization statistics function in the FreeRTOS system is a tailorable function, which can be tailored through macro definitions. In the FreeRTOSConfig.h file, add the following code:

/********************************************************************
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 Interrupt System Timing

Implement a timer with an interrupt frequency period of 50us . In the interrupt callback function of the timer, you only need to add the CPU_RunTime variable. This variable is used to record the system running time. See the interrupt service function below (in 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 Task information and CPU utilization

vTaskGetRunTimeStats() After the setting is complete, you can call and  function in the task  vTaskList() to obtain the relevant information of the task and the CPU utilization, and then print it out:

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 Item Code

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 */
}

The above code creates 4 system tasks in total: 2 tasks for timing flashing of LED lights; 1 task for displaying GIF animation of TFTLCD; 1 task for printing out CPU utilization information. Readers and friends can modify the specific content that needs to be implemented in their task function according to their actual situation.

5. Source code analysis

5.1 Source code analysis 1

Through the configuration process of the above  FreeRTOSConfig.h  file, we can see that FreeRTOS itself provides the CPU supervision function, but we need to modify the configuration file.

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

And if the CPU usage statistics function is enabled , in the task control block of each task, a variable ulRunTimeCounter is defined to count the running time of the task, the code is as follows

/*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
    // ...
}

When performing task context switching, the running time of the current task (from the last task context switching to the current moment) will be accumulated to the ulRunTimeCounter in the task control block of the current task. The code for task context switching is as follows :

//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 */
    }
}

As can be seen from the above code, to realize the CPU usage statistics function of the task, it is also necessary to implement the macro function portGET_RUN_TIME_COUNTER_VALUE() to obtain the current time. In order to improve the calculation accuracy of CPU utilization, a high-precision timer is generally used to count the running time. The FreeRTOSConfig.h code is as follows:

/* 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

Finally, you only need to accumulate the CPU running time in the timer interrupt callback function:

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 Source Code Analysis 2

Next, let me analyze the core functions of CPU utilization calculation: vTaskList() and  vTaskGetRunTimeStats() 

vTaskList() function:  Get information about tasks

vTaskGetRunTimeStats() function: Get information about PU usage

#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 ) ) */

 In short, the function of the vTaskGetRunTimeStats() function is to  calculate the CPU usage of the current task through the running time ulRunTimeCounter of the current task and the total running time of the system ulTotalTime , and format the output ( the calculation method is consistent with the principle described in this article ).

5.3 Source Code Analysis 3

How to get the CPU utilization in the project:

When FreeRTOS starts task scheduling, it will create an IDLE idle task by default. When the CPU is running idle, it will run the idle task (IDLE) by default to ensure the normal operation of the system operation scheduling. Therefore, when the CPU is loaded with heavier tasks, the CPU utilization of IDEA idle tasks will be lower. To sum up, the author uses the CPU utilization of 100-idle tasks to indirectly reflect the current CPU load.

The CPU calculation formula in the project:

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

Modify the vTaskGetRunTimeStats code:

#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
                    }

Use the global variable to extract the value of the CPU, and display the value through TCTLCD.

6. Project effect

CPU utilization

7. Project Code

Code Address: FreeRTOS CPU Utilization Calculation Engineering Code Resources - CSDN Library

If you don't have enough points, click Bo to follow , leave an email in the comment area , and the author will provide the source code and follow-up questions for free . Please pay attention to a wave! ! !

Guess you like

Origin blog.csdn.net/black_sneak/article/details/130474124