基于STM32F407的FreeRTOS学习笔记(4)——获取各任务运行时间及占用情况

本文介绍了如何在FreeRTOS中实现类似Windows系统任务管理器的功能,通过配置定时器获取任务运行时间和系统资源占用情况,涉及宏定义、定时器设置和中断处理等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图片

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("%s\r\n",informationbuff);//打印运行时间    }    vTaskDelay(10);  }}

上述任务的作用为检测按键,如果按键按下即尝试获得运行状态,并打印运行状态。

出现了如下错误,显示我们未定义该函数,我们利用Ctrl+F全局寻找这个函数定义在哪里。
    

FreeRTOS\FreeRTOS.axf: Error: L6218E: Undefined symbol vTaskGetRunTimeStats (referred from main.o).
F:\Code\STM32Code\STM32F407_FreeRtos\FreeRTOS\FreeRTOS\Source\tasks.c(4539) :     void vTaskGetRunTimeStats( char * pcWriteBuffer )F:\Code\STM32Code\STM32F407_FreeRtos\FreeRTOS\FreeRTOS\Source\tasks.c(4552) :          * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats partF:\Code\STM32Code\STM32F407_FreeRtos\FreeRTOS\FreeRTOS\Source\tasks.c(4557) :          * vTaskGetRunTimeStats() has a dependency on the sprintf() C libraryF:\Code\STM32Code\STM32F407_FreeRtos\FreeRTOS\FreeRTOS\Source\tasks.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 */

之后我们运行程序观察串口的输出。

图片

这样子我们就可以打印出各个程序运行时间以及占用系统资源的占比了。

<think>嗯,用户遇到了一个问题,他的函数debugShowOSTasksRunTimeStats()中间没有显示任务的调用时间。我需要帮他分析可能的原因。首先,用户提供的代码片段使用了vTaskGetRunTimeStats来生成运行时统计信息,但结果中没有任务数据。这可能涉及到多个配置和实现的问题。 首先,我应该回顾之前的对话历史。之前用户询问了关于vTaskGetRunTimeStats函数的注释翻译和解释,其中提到了该函数依赖configGENERATE_RUN_TIME_STATS和configUSE_STATS_FORMATTING_FUNCTIONS的配置,以及可能的sprintf依赖和替代方案。现在用户的问题是具体在实现中无法获取数据,所以需要根据这些配置和实现细节来排查。 可能的第一个原因是相关的FreeRTOS配置宏是否启用。用户代码中使用了条件编译,检查configGENERATE_RUN_TIME_STATS是否为1,以及configUSE_STATS_FORMATTING_FUNCTIONS是否大于0。如果这两个宏没有正确配置,代码就不会编译进去,或者无法生成统计数据。需要确认在FreeRTOSConfig.h中这两个宏是否被正确设置。 第二个可能性是运行时统计的时钟源配置是否正确。根据FreeRTOS的文档,启用configGENERATE_RUN_TIME_STATS需要用户实现portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()和portGET_RUN_TIME_COUNTER_VALUE()这两个宏。如果这些宏没有正确实现,统计功能将无法正常工作,导致没有数据。用户可能需要检查他们的端口配置,确保定时器被正确初始化和读取。 接下来,考虑任务的时间统计是否被正确更新。如果定时器的分辨率不够高,或者计数器溢出处理不当,可能导致统计信息不准确或未更新。例如,如果使用的定时器频率太低,任务运行时间过短,可能无法被正确记录。 另外,输出缓冲区的处理可能存在问题。代码中使用了一个全局缓冲区g_tasks_buf,大小为512字节。如果任务数量较多,或者任务名称较长,可能导致缓冲区溢出,从而截断数据。需要检查缓冲区是否足够大,以及在strcat和vTaskGetRunTimeStats调用时是否有足够的空间,避免溢出。 还有可能是硬件或外设的问题。例如,用户代码最后通过HAL_UART_Transmit发送数据,如果串口配置不正确,或者发送过程中出现错误,可能导致输出不完整。不过用户提到中间没有任务数据,而不是输出不完全,所以这个问题可能性较低,但也不能完全排除。 此外,需要确认是否真的有任务在运行。如果系统中只有一个空闲任务,或者所有任务都没有足够的运行时间,统计信息可能显示为空。用户应检查系统中存在的任务及其运行状况。 还有可能是在编译时,条件编译的宏虽然被定义,但由于某些原因没有被正确包含,导致相关代码未被编译。需要检查编译器的预处理输出,确认相关代码块确实被包含在编译中。 最后,考虑vTaskGetRunTimeStats函数本身的实现是否有问题。例如,是否因为使用了自定义的sprintf函数(如printf-stdarg.c)导致格式化输出错误,从而未能正确填充缓冲区。需要检查该函数的实现,特别是字符串拼接部分是否有错误。 综合这些可能性,应该逐步引导用户检查配置宏、定时器实现、缓冲区大小、任务存在性以及外设配置等方面,以确定具体原因。</think>以下是该函数不显示任务调用时间的可能原因及排查步骤,按优先级排序: ===== 核心原因分析 ===== 1️⃣ **配置宏未启用** - 检查`FreeRTOSConfig.h`中关键宏定义: ```c #define configGENERATE_RUN_TIME_STATS 1 // 必须为1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 // 必须>0 ``` - 🔍 验证方法:在预处理后的代码中确认`#if`条件成立(通过IDE查看编译后的汇编或使用`-E`选项) 2️⃣ **运行时统计时钟源未配置** - 必须实现两个关键宏: ```c // 在FreeRTOSConfig.h中添加 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ConfigureTimerForRuntimeStats() #define portGET_RUN_TIME_COUNTER_VALUE() GetRuntimeCounterValue() ``` - 📌 硬件要求: * 使用≥10倍时钟频率的定时器(例如100MHz CPU建议用10MHz定时器) * 32位定时器建议启用自动重装载(防止溢出) 3️⃣ **缓冲区溢出** - 测试案例:插入调试语句验证缓冲区状态 ```c strcat(g_tasks_buf, "TEST"); // 添加测试字符串 HAL_UART_Transmit(...); // 观察是否显示 ``` - 🛠️ 优化方案:改用动态计算 ```c int used_len = strlen(g_tasks_buf); int remain_len = 512 - used_len - 1; // 保留结束符 vTaskGetRunTimeStats(g_tasks_buf + used_len); ``` ===== 进阶排查步骤 ===== 4️⃣ **定时器实现验证** - 推荐使用DWT周期计数器(Cortex-M3/M4/M7): ```c void ConfigureTimerForRuntimeStats(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; } uint32_t GetRuntimeCounterValue(void) { return DWT->CYCCNT; } ``` - 🔧 校准方法:通过已知延时任务验证统计值(例如1秒任务应显示≈100%) 5️⃣ **任务状态检查** - 插入调试代码验证任务列表: ```c UBaseType_t numTasks = uxTaskGetNumberOfTasks(); TaskStatus_t *pxTaskStatusArray = pvPortMalloc(numTasks * sizeof(TaskStatus_t)); if(uxTaskGetSystemState(pxTaskStatusArray, numTasks, NULL) != 0) { // 遍历pxTaskStatusArray检查任务数据 } ``` ===== 典型故障案例 ===== 1. **STM32CubeMX配置遗漏**:通过CubeMX生成代码时忘记勾选`USE_STATS_FORMATTING_FUNCTIONS` 2. **SysTick冲突**:当使用自定义定时器时未关闭SysTick中断导致计数器被篡改 3. **任务优先级问题**:所有任务处于阻塞状态,只有空闲任务运行 建议通过逻辑分析仪捕获任务调度波形(如使用SEGGER SystemView)与统计数据进行交叉验证。若仍无法解决,可提供以下信息进一步诊断: - FreeRTOSConfig.h完整配置 - 定时器初始化代码片段 - 系统任务列表(通过vTaskList()输出)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值