CPU工作的时候,各个任务运行会占用CPU的资源,在Windows系统中我们可以通过任务管理器来看各任务(进程)占用系统资源的情况。
那么,FreeRTOS怎么实现这个功能呢?
我们翻阅FreeRTOS官网,查询API文档,在内核控制函数部分找到了相关的函数。
文档指出实现运行时间功能需要配置外设定时器,即32板载定时器,计时器频率应为滴答计时器(1ms)的至少10倍。
传入参数为pcWriteBUffer,其实是一个char类型的数组用以存储相关信息。
我们现在工程上调用这个函数。
char informationbuff[400];
void Get_info(void * pvParameters)
{
//vTaskGetRunTimeStats(informationbuff);
while(1)
{
if(KEY_Scan(0)==1)//按下按键1
{
memset(informationbuff,0,400);//清空数组内容
vTaskGetRunTimeStats(informationbuff);//获得运行时间
printf("%srn",informationbuff);//打印运行时间
}
vTaskDelay(10);
}
}
上述任务的作用为检测按键,如果按键按下即尝试获得运行状态,并打印运行状态。
出现了如下错误,显示我们未定义该函数,我们利用Ctrl+F全局寻找这个函数定义在哪里。
FreeRTOSFreeRTOS.axf: Error: L6218E: Undefined symbol vTaskGetRunTimeStats (referred from main.o).
F:CodeSTM32CodeSTM32F407_FreeRtosFreeRTOSFreeRTOSSourcetasks.c(4539) : void vTaskGetRunTimeStats( char * pcWriteBuffer )
F:CodeSTM32CodeSTM32F407_FreeRtosFreeRTOSFreeRTOSSourcetasks.c(4552) : * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part
F:CodeSTM32CodeSTM32F407_FreeRtosFreeRTOSFreeRTOSSourcetasks.c(4557) : * vTaskGetRunTimeStats() has a dependency on the sprintf() C library
F:CodeSTM32CodeSTM32F407_FreeRtosFreeRTOSFreeRTOSSourcetasks.c(4567) : * through a call to vTaskGetRunTimeStats().
第一行内容,即为函数定义的位置,我们跳转过去查看其情况。
#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;
省略后续内容(防止说水字数)
我们看到了函数模型以及相关注释,从头中我们可以看出需要相关的宏定义,分别是configGENERATE_RUN_TIME_STATS、configUSE_STATS_FORMATTING_FUNCTIONS、configUSE_TRACE_FACILITY。
我们在FreeRTOSconfig.h文件(头文件都行,方便管理)中添加使能这三个宏。
再次运行,依旧报错,从报错内容来看,提醒我们如果将configGENERATE_RUN_TIME_STATS使能的话,我们也必须定义portCONFIGURE_TIMER_FOR_RUN_TIME_STATS这个启动函数,以及后面的一条报错,我们必须定义portGET_RUN_TIME_COUNTER_VALUE时间的返回值。
#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS
(import) #error 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.
#endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */
#ifndef portGET_RUN_TIME_COUNTER_VALUE
#ifndef portALT_GET_RUN_TIME_COUNTER_VALUE
(import) #error 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.
#endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */
#endif /* portGET_RUN_TIME_COUNTER_VALUE *
启动函数即为外部定时器启动函数,返回值则是一个数用以计算时间。
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() configTIM_START()//定时器1提供时间统计的时基,频率为10K,即周期为100us
#define portGET_RUN_TIME_COUNTER_VALUE() FreeRTOSRunTime//时基
extern volatile unsigned long long FreeRTOSRunTime;
我们定义这两个宏,本来这个括号是没加上去的,后来发现其调用的时候是代括号的,所以定义宏的时候不带括号就会出错
此外我们定义了long long 类型的变量用以存储我们的时间,加上extern表示这个变量的实际定义并不在头文件中,之所以加上volatile是因为我们的变量会在不同的文件以及中断中被修改(这种修改属于意外修改),加上volatile标志给系统提前吱会一声。
之后,我们去CUBEMX启动我们的定时器。
定时器我们选择定时器1,时钟源选择内部时钟,分频系数由于我们的单片机主频是168MHZ,因此我们选择168分频,这样子定时器频率即为1MHZ,溢出值我们选择为50,通过这样的设置我们定时器的频率就是20KHZ,是滴答定时器时钟的20倍。
FreeRTOSRunTime也可以定义在这里。
之后我们将刚才宏定义的启动函数进行定义,内容则是重置计数器并启动定时器。
完成这步之后,我们还需要在主函数中启用定时器1 的中断并且编写相应的中断服务函数,其内容为FreeRTOSRunTime递增。
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim1.Instance)
{
FreeRTOSRunTime++;
}
}
/* USER CODE END 4 */
之后我们运行程序观察串口的输出。
这样子我们就可以打印出各个程序运行时间以及占用系统资源的占比了。