獲取CPU時間的API:
vTaskGetRunTimeStats() 獲取任務運行時間信息,此函數會統計任務的運行時間,並且將統計到的運行時間信息按照表格的形式組織在一起並存放在用戶設置的緩沖區里面,緩沖區的首地址通過參數傳遞給函數 vTaskGetRunTimeStats()。
獲取前期准備:
(1)開啟宏configGENERATE_RUN_TIME_STATS 1
(2)實現兩個宏
① 配置一個高精度定時器/計數器提供時基,因為時間統計功能需要用戶提供一個高精度的時鍾,這里使用定時器 3。這個時鍾的精度要比 FreeRTOS 的系統時鍾高,大約 10~20 倍即可
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimeForRunTimeStats() //函數 ConfigureTimeForRunTimeStats()其實就是初始化定時器
② 讀取時基時間值
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTimeTicks
實驗代碼:
①時基的實現:
//FreeRTOS時間統計所用的節拍計數器 volatile unsigned long long FreeRTOSRunTimeTicks; //初始化TIM3使其為FreeRTOS的時間統計提供時基 void ConfigureTimeForRunTimeStats(void) { //為72M/72=1M,自動重裝載為50-1,那么定時器周期就是50us FreeRTOSRunTimeTicks=0; TIM6_Time_Init(50-1,72-1); //初始化TIM6 } //1/(72 000 000÷psc)×arr~5ms void TIM6_Time_Init(u16 arr,u16 psc)//如arr=49 psc=7199 ---10khz(0.1ms)--5ms { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE); //72M NVIC_InitStructure.NVIC_IRQChannel =TIM6_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 4; //搶占優先級 NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0; //響應優先級 NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_TimeBaseStructure. TIM_Period = arr;//自動重裝值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //時鍾預分頻數 TIM_TimeBaseInit(TIM6,&TIM_TimeBaseStructure); //初始化TIM6 TIM_ClearFlag(TIM6,TIM_FLAG_Update); //清除計數器中斷標志位 TIM_ITConfig(TIM6,TIM_IT_Update, ENABLE );//TIM中斷使能 TIM_Cmd(TIM6, ENABLE); //使能定時器 } void TIM6_IRQHandler(void) { if(TIM_GetITStatus(TIM6, TIM_IT_Update)) //是否產生中斷 { FreeRTOSRunTimeTicks++; //FreeRTOS時間統計所用的節拍計數器 TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清除中斷標志位 } }
②基本功能,按下按鍵串口打印出每個任務所占用的CPU時間
//----------------------------------------任務優先級 #define START_TASK_PRIO 1 #define TASK1_PRIO 2 #define TASK2_PRIO 3 //優先級高 //----------------------------------------任務堆棧大小 #define START_STK_SIZE 128 #define TASK1_STK_SIZE 128 #define TASK2_STK_SIZE 256 //----------------------------------------任務句柄 TaskHandle_t Task1_Handler; TaskHandle_t Task2_Handler; TaskHandle_t StartTask_Handler; //----------------------------------------任務函數 void start_task(void *pvParameters); void task1_task(void *pvParameters); void task2_task(void *pvParameters); char InfoBuffer[1000]; //保存信息的數組 int main(void) { BaseType_t OS; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); User_GPIO_Init(); Delay_init(); USART_Config(); OS= xTaskCreate( (TaskFunction_t ) start_task, //任務函數 (const char * ) "start_task", //任務名 (configSTACK_DEPTH_TYPE) START_STK_SIZE, //堆棧大小 (void * )NULL, //傳遞給任務函數的參數 (UBaseType_t ) START_TASK_PRIO, //任務優先級 (TaskHandle_t * ) &StartTask_Handler //任務句柄 ); if(OS==pdPASS) GPIO_SetBits(GPIOA, GPIO_Pin_8); vTaskStartScheduler(); //開啟任務調度 } void start_task(void *pvParameters) { taskENTER_CRITICAL(); //進入臨界區 //創建任務Task1 xTaskCreate((TaskFunction_t )task1_task, //任務函數 (const char* )"task1_task", //任務名稱 (uint16_t )TASK1_STK_SIZE, //任務堆棧大小 (void* )NULL, (UBaseType_t )TASK1_PRIO, //任務優先級 (TaskHandle_t* )&Task1_Handler); //任務句柄 xTaskCreate((TaskFunction_t )task2_task, //任務函數 (const char* )"task2_task", //任務名稱 (uint16_t )TASK2_STK_SIZE, //任務堆棧大小 (void* )NULL, (UBaseType_t )TASK2_PRIO, //任務優先級 (TaskHandle_t* )&Task2_Handler); //任務句柄 vTaskDelete(StartTask_Handler); //vTaskDelete(NULL)也可以 刪除開始任務 taskEXIT_CRITICAL(); //退出臨界區 } //任務1 void task1_task(void *pvParameters) { while(1) { vTaskDelay(5000); //vTaskDelay delay_xms不發生任務切換 } } //任務2 void task2_task(void *pvParameters) { uint8_t key=0; while(1) { key=KEY_Scan(); if(key==TASK2_SUSPEND) { printf("按下\n"); memset(InfoBuffer,0,1000); //信息緩沖區清零 vTaskGetRunTimeStats(InfoBuffer); //獲取任務運行時間信息 printf("任務名\t\t運行時間\t運行所占百分比\r\n"); printf("%s\r\n",InfoBuffer); } vTaskDelay(10); //延時10ms,也就是1000個時鍾節拍 } }
執行結果:
vTaskGetRunTimeStats()相對來說會很耗時間,所以不要太過於頻繁的調用此函數,測試階段可以使用此函數來分析任務的運行情況。 還有就是運行時間不是真正的運行時間,真正的時間值要乘以 100us
因為上面實現的時基為100us, FreeRTOS的時基為1ms,正好是其精度的10倍。