一、基本概念
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教程
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/245277.html
標籤:其他
上一篇:如何使用 Gitee 快速搭建 ESP-IDF 開發環境(Windows 版本)
下一篇:洛谷P1115最大子段和
