FreeRTOS-任务运行时间统计
引入
- 上一章节中我们讲述了任务信息获取,我们已经能够获取绝大部分任务信息了,但是任务还有一个很重要的信息,那就是运行时间。如果我们知道了每个任务的运行时间和占比我们就可以更合理的分配任务。在统计运行时间前,我们还需要做一些准备工作。
- 我们已经知道了FreeRTOS是有一个系统时钟的,但是这个系统时钟的时基是以1ms(个人配置)为周期的,对于一些耗时小于1ms的任务是不能很好地统计的,所以我们还需要额外提供一个更加精确地时基,一般是系统时钟的20-30倍。在单片机中,利用定时器中断可以很容易提供一个固定周期的时基,因此我们需要配置一个定时器产生33.3us-50us的时钟脉冲(这里我们配置周期为50us)。下面逐步讲述这些配置步骤。
配置步骤
FreeRTOSConfig.h文件配置
-
首先需要将configGENERATE_RUN_TIME_STATS = 1,启用运行时间统计功能。编译文件。
-
可见,报了很多错误。错误提示如下:
- If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base.
- If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information.
-
上面错误提示意思是,如果刚刚我们定义的configGENERATE_RUN_TIME_STATS 这个宏被置1后,那么portCONFIGURE_TIMER_FOR_RUN_TIME_STATS 这个宏也需要被定义,并且该宏能够调用一个函数去启动一个定时器来提供时基。另一个的意思是我们还需要定义一个portGET_RUN_TIME_COUNTER_VALUE,该宏是一个变量,用以记录定时器中断次数。
-
在了解上面的错误提示之后,接下来我们在FreeRTOS.h文件中补充相关定义如下:
-
其中FreeRTOSRunTimeTicks为定义的一个变量(在timer.c文件中定义,变量名可以自己定义),用以记录定时器中断次数,其定义如下:
unsigned long long FreeRTOSRunTimeTicks = 0; -
ConfigureTimerForRunTimeStats()为定义的定时器初始化函数,也被定义在timer.c文件中(函数名可以自己定义)。系统会自动调用该函数初始化时基。
精准时基配置
- 下面是定时器配置的代码。
volatile unsigned long long FreeRTOSRunTimeTicks = 0;
void ConfigureTimerForRunTimeStats(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2,ENABLE);
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInitStruct.TIM_Period = 36-1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 100-1;
TIM_TimeBaseInit (TIM2,&TIM_TimeBaseInitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init (&NVIC_InitStruct);
TIM_ITConfig (TIM2,TIM_IT_Update,ENABLE);
TIM_ClearITPendingBit (TIM2,TIM_IT_Update);
TIM_Cmd (TIM2,ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus (TIM2,TIM_IT_Update) == SET)
{
FreeRTOSRunTimeTicks++;
}
TIM_ClearITPendingBit (TIM2,TIM_IT_Update);
}
- 这里使用stm32f103的定时器2,将其周期配置为50us,中断优先级为1,不受FreeRTOS的管理。完成上述步骤后,函数声明和变量声明通过extern关键字引用到FreeRTOSConfig.h文件中。再次编译,无误。
测试
vTaskGetRunTimeStats()
- 该函数既可以获取每个任务运行时间,也可以自动计算每个任务的运行时间占比,函数声明如下:
void vTaskGetRunTimeStats( char *pcWriteBuffer )
- 该函数很简单,只有字符指针这一个输入参数,该输入参数需要用户自己定义,调用该函数后,只需要将字符指针中的值打印出来即可得到任务运行时间信息。用法示例:
char InfoBuffer[200];
void runTim_task(void* pvParameters)
{
while(1)
{
if ( (keyFlag&0x01) == 0x01)
{
vTaskGetRunTimeStats(InfoBuffer);
printf("%s\r\n",InfoBuffer);
}
vTaskDelay(1000);
}
}
运行结果如下:
第一列为任务名称,第二列为函数运行时间,第三列为运行时间占比。
vTaskGetInfo()
- 上一章节中我们使用vTaskGetInfo()获取了除ulRunTimeCounter以外所有的任务信息,这一章节因为补充了相关宏定义以及时基,我们同样可以使用该函数获取任务运行时间。用法示例如下:
void runTim_task(void* pvParameters)
{
TaskStatus_t TaskStatus;
while(1)
{
if ( (keyFlag&0x01) == 0x01)
{
vTaskGetInfo(KeyTask_Handler,&TaskStatus,pdTRUE,eInvalid);
printf("Key Task Run Time Counter = %d\r\n",TaskStatus.ulRunTimeCounter);
vTaskGetRunTimeStats(InfoBuffer);
printf("%s\r\n",InfoBuffer);
}
vTaskDelay(1000);
}
}
- 这里我们只获取了KeyTask任务运行时间,运行结果如下:
可见两个函数获取的计数值是一样的。
vGetSystemState()
- 与 vTaskGetInfo()可以获得任务运行时间,那么vGetSystemState()同样也可以,并且该函数还可以获得系统总运行时间。用法示例如下:
void runTim_task(void* pvParameters)
{
u8 i=0;
TaskStatus_t TaskStatus;
TaskStatus_t* TaskStatusArray;
UBaseType_t ArraySize;
uint32_t* TotalRunTime;
ArraySize = uxTaskGetNumberOfTasks();
TaskStatusArray = pvPortMalloc(sizeof(TaskStatus_t)* ArraySize);
TotalRunTime = pvPortMalloc(sizeof(uint32_t));
while(1)
{
if ( (keyFlag&0x01) == 0x01)
{
vTaskGetInfo(KeyTask_Handler,&TaskStatus,pdTRUE,eInvalid);
printf("Key Task Run Time Counter = %d\r\n",TaskStatus.ulRunTimeCounter);
uxTaskGetSystemState(TaskStatusArray,ArraySize,TotalRunTime);
printf("System Total Run Time=%d\r\n",*TotalRunTime);
for (i=0; i<ArraySize-1;i++)
{
printf("%s\t%d\r\n",TaskStatusArray[i].pcTaskName,TaskStatusArray[i].ulRunTimeCounter);
}
vTaskGetRunTimeStats(InfoBuffer);
printf("%s\r\n",InfoBuffer);
}
vTaskDelay(1000);
}
}
运行结果如下:
因为任务运行时间统计是在runTim_task函数中进行的,所以除了runTim_task对应的时间项不同外,其他均相同。
到这里我们就将FreeRTOS中所有获取任务状态信息的函数讲述完了,主要就是vTaskGetInfo()、uxTaskGetSystemState()、vTaskGetRunTimeStats()这三个函数。