FreeRTOS學習筆記(11)——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;

二、CPU利用率統計

在調試的時候很有必要得到當前系統的 CPU 利用率相關信息,但是在產品發佈的時候,就可以把 CPU 利用率統計這個功能去掉,因爲使用任何功能的時候,都是需要消耗系統資源的,FreeRTOS 是使用一個外部的變量進行統計時間的,並且消耗一個高精度的定時器,其用於定時的精度是系統時鐘節拍的 10-20 倍,比如當前系統時鐘節拍是 1000HZ,那麼定時器的計數節拍就要是 10000-20000HZ。而且 FreeRTOS 進行 CPU 利用率統計的時候,也有一定缺陷,因爲它沒有對進行 CPU 利用率統計時間的變量做溢出保護,我們使用的是 32 位變量來系統運行的時間計數值,而按 20000HZ 的中斷頻率計算,每進入一中斷就是 50us,變量加一,最大支持計數時間:2^32 * 50us / 3600s = 59.6 分鐘,運行時間超過了 59.6 分鐘後統計的結果將不準確,除此之外整個系統一直響應定時器 50us 一次的中斷會比較影響系統的性能。

三、配置選項

FreeRTOSConfig.h 配置與系統運行時間和任務狀態收集有關的配置選項,並且實現
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()portGET_RUN_TIME_COUNTER_VALUE() 這兩個宏定義

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

然後需要實現一箇中斷頻率爲 20000HZ 定時器,用於系統運行時間統計,其實很簡單,只需將 CPU_RunTime 變量自加即可,這個變量是用於記錄系統運行時間的,中斷服務函數見下

/* 用於統計運行時間 */
volatile uint32_t CPU_RunTime = 0UL;

void BASIC_TIM_IRQHandler (void)
{
   
   
    if(TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) 
    {
   
   
        CPU_RunTime++; 
        TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);
    }
}

然後我們就可以在任務中調用 vTaskGetRunTimeStats()vTaskList() 函數獲得任務的相關信息與 CPU 使用率的相關信息,然後打印出來即可

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");

四、示例

/* FreeRTOS頭文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 開發板硬件bsp頭文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_TiMbase.h"
#include "string.h"

/**************************** 任務句柄 ********************************/
/* 
 * 任務句柄是一個指針,用於指向一個任務,當任務創建好之後,它就具有了一個任務句柄
 * 以後我們要想操作這個任務都需要通過這個任務句柄,如果是自身的任務操作自己,那麼
 * 這個句柄可以爲NULL。
 */
 /* 創建任務句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED任務句柄 */
static TaskHandle_t LED1_Task_Handle = NULL;
static TaskHandle_t LED2_Task_Handle = NULL;
static TaskHandle_t CPU_Task_Handle = NULL;

/*
*************************************************************************
*                             函數聲明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用於創建任務 */

static void LED1_Task(void* pvParameters);/* LED1_Task任務實現 */
static void LED2_Task(void* pvParameters);/* LED2_Task任務實現 */
static void CPU_Task(void* pvParameters);/* CPU_Task任務實現 */
static void BSP_Init(void);/* 用於初始化板載相關資源 */

/*****************************************************************
  * @brief  主函數
  * @param  無
  * @retval 無
  * @note   第一步:開發板硬件初始化 
            第二步:創建APP應用任務
            第三步:啓動FreeRTOS,開始多任務調度
  ****************************************************************/
int main(void)
{
   
   	
    BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認爲pdPASS */

    /* 開發板硬件初始化 */
    BSP_Init();
    /* 創建AppTaskCreate任務 */
    xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任務入口函數 */
                          (const char*    )"AppTaskCreate",/* 任務名字 */
                          (uint16_t       )512,  /* 任務棧大小 */
                          (void*          )NULL,/* 任務入口函數參數 */
                          (UBaseType_t    )1, /* 任務的優先級 */
                          (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任務控制塊指針 */ 
    /* 啓動任務調度 */           
    if(pdPASS == xReturn)
    {
   
   
        vTaskStartScheduler();   /* 啓動任務,開啓調度 */
    }
    else
    {
   
   
        return -1;  
    }
  
    while(1);   /* 正常不會執行到這裏 */    
}

/***********************************************************************
  * @ 函數名  : AppTaskCreate
  * @ 功能說明: 爲了方便管理,所有的任務創建函數都放在這個函數裏面
  * @ 參數    : 無  
  * @ 返回值  : 無
  **********************************************************************/
static void AppTaskCreate(void)
{
   
   
    BaseType_t xReturn = pdPASS;/* 定義一個創建信息返回值,默認爲pdPASS */
  
    taskENTER_CRITICAL();           //進入臨界區
  
    /* 創建LED_Task任務 */
    xReturn = xTaskCreate((TaskFunction_t )LED1_Task, /* 任務入口函數 */
                          (const char*    )"LED1_Task",/* 任務名字 */
                          (uint16_t       )512,   /* 任務棧大小 */
                          (void*          )NULL,	/* 任務入口函數參數 */
                          (UBaseType_t    )2,	    /* 任務的優先級 */
                          (TaskHandle_t*  )&LED1_Task_Handle);/* 任務控制塊指針 */
    if(pdPASS == xReturn)
    {
   
   
        printf("創建LED1_Task任務成功!\r\n");
    }
  
    /* 創建LED_Task任務 */
    xReturn = xTaskCreate((TaskFunction_t )LED2_Task, /* 任務入口函數 */
                          (const char*    )"LED2_Task",/* 任務名字 */
                          (uint16_t       )512,   /* 任務棧大小 */
                          (void*          )NULL,	/* 任務入口函數參數 */
                          (UBaseType_t    )3,	    /* 任務的優先級 */
                          (TaskHandle_t*  )&LED2_Task_Handle);/* 任務控制塊指針 */
    if(pdPASS == xReturn)
    {
   
   
        printf("創建LED2_Task任務成功!\r\n");
    }

    /* 創建LED_Task任務 */
    xReturn = xTaskCreate((TaskFunction_t )CPU_Task, /* 任務入口函數 */
                          (const char*    )"CPU_Task",/* 任務名字 */
                          (uint16_t       )512,   /* 任務棧大小 */
                          (void*          )NULL,	/* 任務入口函數參數 */
                          (UBaseType_t    )4,	    /* 任務的優先級 */
                          (TaskHandle_t*  )&CPU_Task_Handle);/* 任務控制塊指針 */
    if(pdPASS == xReturn)
    {
   
   
        printf("創建CPU_Task任務成功!\r\n");
    }
  
    vTaskDelete(AppTaskCreate_Handle); //刪除AppTaskCreate任務
  
    taskEXIT_CRITICAL();            //退出臨界區
}

/**********************************************************************
  * @ 函數名  : LED_Task
  * @ 功能說明: LED_Task任務主體
  * @ 參數    :   
  * @ 返回值  : 無
  ********************************************************************/
static void LED1_Task(void* parameter)
{
   
   	
    while (1)
    {
   
   
        LED1_ON;
        vTaskDelay(500);   /* 延時500個tick */
        printf("LED1_Task Running,LED1_ON\r\n");
        LED1_OFF;     
        vTaskDelay(500);   /* 延時500個tick */		 		
        printf("LED1_Task Running,LED1_OFF\r\n");
    }
}

static void LED2_Task(void* parameter)
{
   
   	
    while (1)
    {
   
   
        LED2_ON;
        vTaskDelay(300);   /* 延時500個tick */
        printf("LED2_Task Running,LED2_ON\r\n");
    
        LED2_OFF;     
        vTaskDelay(300);   /* 延時500個tick */		 		
        printf("LED2_Task Running,LED2_OFF\r\n");
    }
}

static void CPU_Task(void* parameter)
{
   
   	
    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);   /* 延時500個tick */		
    }
}

/***********************************************************************
  * @ 函數名  : BSP_Init
  * @ 功能說明: 板級外設初始化,所有板子上的初始化均可放在這個函數裏面
  * @ 參數    :   
  * @ 返回值  : 無
  *********************************************************************/
static void BSP_Init(void)
{
   
   
	/*
	 * STM32中斷優先級分組爲4,即4bit都用來表示搶佔優先級,範圍爲:0~15
	 * 優先級分組只需要分組一次即可,以後如果有其他的任務需要用到中斷,
	 * 都統一用這個優先級分組,千萬不要再分組,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
    /* 基本定時器初始化	*/
	BASIC_TIM_Init();
}


• 由 Leung 寫於 2021 年 1 月 5 日

• 參考:野火FreeRTOS視頻與PDF教程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章