FreeRTOS 学习:(二十五)任务时间统计相关 API 函数

上一篇 下一篇
任务状态与信息查询 API 函数

目 录


2)任务时间统计相关 API 函数

函数有很多,但是常用的就一个。

2.1)vTaskGetRunTimeStats()

这个函数主要用于调试,实际产品中一般不用,因为占用时间还是比较大的

声明:Void vTaskGetRunTimeStats( char * pcWriteBuffer )

功能:此函数用于 统计任务的运行时间信息使用此函数需将宏 configGENERATE_RUN_TIME_STAT(默认为0) 、configUSE_STATS_FORMATTING_FUNCTIONS 置 1(默认为1)

形参

  • pcWriteBuffer:指向保存任务运行时间信息的存储区的指针(数组名调用时为指针)

    保存的内容为:

    c 复制代码
    Task		:	任务名称
    Abs Time	:	任务实际运行的总时间(绝对时间),其值乘以时基定时器的单位溢出时间即为真实时间
    % Time  	:	占总处理时间的百分比

    可直接使用 printf 打印(%s)

无返回值

注意

  • 空闲任务所占时间百分比越大,说明 CPU 越空闲,压力越小
  • vTaskGetRunTimeStats() 这个函数要放在 while(1){} 循环中

!宏定义 !:

FreeRTOSConfig.h 中,有关 configGENERATE_RUN_TIME_STATS 这个宏的部分如下:

c 复制代码
/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS                   0                       /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks
#endif

源码表示:如果将 configGENERATE_RUN_TIME_STATS 置 1 ,就还需要实现 2 个宏定义

  • portCONFIGURE_TIMER_FOR_RUNTIME_STATES() :用于初始化用于配置任务运行时间统计的时基定时器
    • 这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍
    • 这个时基定时器本质上是软件定时器 ,我们一般就用一个基本定时器来实现,一旦用作统计任务时间之后,这个基本定时器就不能用于别的任务了,具体代码看下面实验部分。
    • portCONFIGURE_TIMER_FOR_RUNTIME_STATES() 这个宏在任务调度器中默认调用了,但是只是个空壳,没有内容,我们在定义其内容的时候,宏名不可以更改 ,所以我们就将其进一步封装为 ConfigureTimeForRunTimeStats() 函数, 这个函数我们可以在自己的自定义定时器文件中定义 ,其函数内容就是定义一个高精度基本定时器,对应的 #include "./BSP/TIMER/btim.h" 就要改成自定义定时器的头文件名。
  • portGET_RUN_TIME_COUNTER_VALUE():用于获取该功能时基硬件定时器计数的计数值 。
    • 这个宏,我们将其封装为 FreeRTOSRunTimeTicks ,同样在自定义定时器文件中定义
    • 我们需要在时基定时器初始化函数 ConfigureTimeForRunTimeStats() 中将 FreeRTOSRunTimeTicks 清零,然后在对应基本定时器的中断服务函数中自加(一般定义为 32 位的。有必要的话,可以在主函数中当其过大时将其清零)

2.2)实验

① 实验目的: 学习 FreeRTOS 任务运行时间统计相关 API 函数的使用

② 实验设计: 将设计三个任务:start_task、task1、task2,

这三个任务的功能如下:

  • start_task:用来创建 task1/task2 任务。
  • task1:LED0 每 500ms 闪烁一次,提示程序正在运行。
  • task2:用于展示任务运行时间统计相关 API 函数的使用。

③ 参数预设:

我使用的是 F103ZET6 的 TIM6 充当时基定时器,这个板子的基本定时器 TIM6 的时钟源频率是 72MHz ,系统时钟频率在 FreeRTOSConfig.h 文件中有宏定义,一般默认是 1000Hz ,我们要想实现时基定时器的计时精度高于系统时钟计时精度的 100 倍,那么基本定时器 TIM6 的时钟频率应为 100000Hz ,即溢出时间为 10us 。按照公式 T_out=((arr+1)*(psc+1))/F_clk (us),F_clk=72000000Hz 计算的话,可以取预分频系数 PSC = 71,重装载值 ARR = 9

④ 代码:

timer.c 文件代码如下:

c 复制代码
#include "timer.h"
#include "led.h"
#include "usart.h" 


TIM_HandleTypeDef TIM6_Handler;      // 定时器句柄 
TIM_HandleTypeDef TIM7_Handler;      // 定时器句柄 


uint32_t FreeRTOSRunTimeTicks;
/* 时基定时器的初始化 */
void ConfigureTimeForRunTimeStats(void)
{
    TIM6_Init(10-1, 72-1);  /* 100倍的系统时钟节拍 */
    FreeRTOSRunTimeTicks = 0;
}


/**
  * @brief  基本定时器TIM6初始化(含中断)
  * @param  arr:自动重装值
            psc:时钟预分频数
  * @others 溢出时间:T_out=((arr+1)*(psc+1))/F_clk (us),F_clk=72000000Hz
  */
void TIM6_Init(u16 arr,u16 psc)
{
    TIM6_Handler.Instance=TIM6;                             // 基本定时器6
    TIM6_Handler.Init.Prescaler=psc;                        // 分频系数
    TIM6_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;       // 向上计数器, 这里不设置也行
    TIM6_Handler.Init.Period=arr;                           // 自动装载值
    HAL_TIM_Base_Init(&TIM6_Handler);
    
    HAL_TIM_Base_Start_IT(&TIM6_Handler); // 使能定时器6和定时器6更新中断:TIM_IT_UPDATE   
}


/**
  * @brief  基本定时器TIM7初始化(含中断)
  * @param  arr:自动重装值
            psc:时钟预分频数
  * @others 溢出时间:T_out=((arr+1)*(psc+1))/F_clk (us),F_clk=72000000Hz
  */
void TIM7_Init(u16 arr,u16 psc)
{
    TIM7_Handler.Instance=TIM7;                             // 基本定时器7
    TIM7_Handler.Init.Prescaler=psc;                        // 分频系数
    TIM7_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;       // 向上计数器, 这里不设置也行
    TIM7_Handler.Init.Period=arr;                           // 自动装载值
    HAL_TIM_Base_Init(&TIM7_Handler);
    
    HAL_TIM_Base_Start_IT(&TIM7_Handler); // 使能定时器7和定时器7更新中断:TIM_IT_UPDATE   
}


/**
  * @brief  定时器初始化回调函数,包括开启时钟、设置中断优先级
  * @param  htim:定时器句柄
  * @others 此函数会被TIMx_Init()调用
  */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM6)
    {
        __HAL_RCC_TIM6_CLK_ENABLE();            // 使能TIM6时钟
        HAL_NVIC_SetPriority(TIM6_IRQn,6,0);    // 设置中断优先级,抢占优先级1,子优先级3
        HAL_NVIC_EnableIRQ(TIM6_IRQn);          // 开启ITM6中断   
    }
    else if(htim->Instance==TIM7)
    {
        __HAL_RCC_TIM7_CLK_ENABLE();            // 使能TIM7时钟
        HAL_NVIC_SetPriority(TIM7_IRQn,4,0);    // 设置中断优先级,抢占优先级1,子优先级3
        HAL_NVIC_EnableIRQ(TIM7_IRQn);          // 开启ITM7中断   
    }
}


/**
  * @brief  TIM6中断服务函数
  */
void TIM6_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM6_Handler);
}


/**
  * @brief  TIM7中断服务函数
  */
void TIM7_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM7_Handler);
}


/**
  * @brief  中断服务函数底层回调函数
  * @others 此函数会被HAL_TIM_IRQHandler()调用,具体内容为TIM6溢出后翻转LED1
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM6_Handler))
    {
        FreeRTOSRunTimeTicks++;
    }
    else if(htim==(&TIM7_Handler))
    {
        //printf("TIM7正在运行(优先级为4)777\r\n");
        HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
    }
}

timer.h 文件代码如下:

c 复制代码
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

void TIM6_Init(u16 arr,u16 psc);  // 定时器6初始化函数
void TIM7_Init(u16 arr,u16 psc);  // 定时器7初始化函数
void ConfigureTimeForRunTimeStats(void);    // 时基定时器初始化函数

#endif

我的 FreeRTOSConfig.h 文件中需要修改的地方,修改结果为:

c 复制代码
/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS                   1     /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "timer.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks
#endif

FreeRTOSCode.c 文件代码如下(task2 的延时函数尽量短,不然检测不到按键):

c 复制代码
#include "sys.h" 
#include "delay.h" 
#include "usart.h" 
#include "led.h" 
#include "key.h"
#include "FreeRTOS.h" 
#include "task.h"
#include "FreeRTOS_Code.h"

/* ----------------------------------------------------------------------------------- */

#define START_TASK_PRIO  1           // 任务优先级 
#define START_STK_SIZE   128         // 任务堆栈大小  
TaskHandle_t StartTask_Handler;      // 任务句柄 
void start_task(void *pvParameters); // 任务函数 
 
#define TASK1_PRIO   2               // 任务优先级 
#define TSAK1_STK_SIZE    100        // 任务堆栈大小  
TaskHandle_t Task1_Handler;          // 任务句柄 
void task1(void *p_arg);             // 任务函数 
 
#define TASK2_PRIO   3               // 任务优先级 
#define TSAK2_STK_SIZE    100        // 任务堆栈大小  
TaskHandle_t Task2_Handler;          // 任务句柄 
void task2(void *p_arg);             // 任务函数 

char task_buff[500];                 // 保存信息存储区

/* ----------------------------------- 主函数 ---------------------------------------- */

void freertos_code(void)
{
    /* 创建开始任务 */
    xTaskCreate((TaskFunction_t  )start_task,           // 任务函数 
                (const char*     )"start_task",         // 任务名称 
                (uint16_t        )START_STK_SIZE,       // 任务堆栈大小 
                (void*           )NULL,                 // 传递给任务函数的参数 
                (UBaseType_t     )START_TASK_PRIO,      // 任务优先级 
                (TaskHandle_t*   )&StartTask_Handler);  // 任务句柄               
    vTaskStartScheduler();                              // 开启任务调度器 
}

/* ---------------------------------- 任务函数 --------------------------------------- */

/**
 * @brief       开始任务函数
 * @param       无
 * @retval      无
 */
void start_task(void *pvParameters) 
{ 
    taskENTER_CRITICAL();           // 进入临界区 -----------

    /* 创建 TASK1 任务 */
    xTaskCreate((TaskFunction_t  )task1,       
                (const char*     )"task1",     
                (uint16_t        )TSAK1_STK_SIZE,  
                (void*           )NULL,     
                (UBaseType_t     )TASK1_PRIO,  
                (TaskHandle_t*   )&Task1_Handler);

    /* 创建 TASK2 任务 */
    xTaskCreate((TaskFunction_t  )task2,       
                (const char*     )"task2",     
                (uint16_t        )TSAK2_STK_SIZE,  
                (void*           )NULL,     
                (UBaseType_t     )TASK2_PRIO,  
                (TaskHandle_t*   )&Task2_Handler);

    vTaskDelete(StartTask_Handler);   // 删除开始任务 
    
    taskEXIT_CRITICAL();            // 退出临界区 -----------
}


/**
 * @brief       TASK1 任务函数
 * @param       无
 * @retval      无
 * @note        每500ms翻转一次LED0
 */
void task1(void *pvParameters) 
{ 
    while(1) 
    { 
        LED0=!LED0;
        vTaskDelay(500); 
    } 
}


/**
 * @brief       TASK2 任务函数
 * @param       无
 * @retval      无
 * @note        实现任务状态查询API函数使用
 */
void task2(void *pvParameters) 
{
    uint8_t key = 0;
    while(1) 
    {
        key = KEY_Scan(0);
        if(key == KEY0_PRES)
        {
            vTaskGetRunTimeStats(task_buff);
            printf("%s\r\n",task_buff);
        }
        vTaskDelay(10);
    }
}

FreeRTOSCode.h 文件代码如下:

c 复制代码
#ifndef __FREERTOS_CODE_H
#define __FREERTOS_CODE_H

void freertos_code(void);

#endif

main.c 文件代码如下:

c 复制代码
#include "sys.h" 
#include "delay.h" 
#include "usart.h" 
#include "led.h" 
#include "key.h"
#include "timer.h"
#include "FreeRTOS.h" 
#include "task.h"
#include "FreeRTOS_Code.h"


int main(void) 
{ 
    HAL_Init();
    sys_stm32_clock_init(RCC_PLL_MUL9);        // 设置时钟,72M
    delay_init(72);       // 延时函数初始化    
    usart_init(115200);   // 初始化串口 
    LED_Init();           // 初始化LED 
    KEY_Init();           // 按键初始化
 
    freertos_code();      //FreeRTOS代码
} 

⑤ 实验结果:

第二次检测结果中,task2 的运行时间 = 2643 × 10us = 26430us 。


相关推荐
时光の尘3 小时前
【STM32】两万字详解SD卡移植最新版本FatFs文件系统(ff16)
stm32·mcu·dma·sd·fatfs·sdio·ff16
bai5459364 小时前
STM32 CubeIDE 使用串口中断模式
stm32·单片机·嵌入式硬件
fanged6 小时前
STM32(4)--时钟树
stm32·单片机·嵌入式硬件
__万波__6 小时前
STM32L475蜂鸣器实验
stm32·单片机·嵌入式硬件
c++逐梦人8 小时前
进程的优先级与切换
linux·服务器·操作系统
List<String> error_P9 小时前
STM32 GPIO HAL库常用函数
stm32·单片机·hal库
小痞同学10 小时前
【铁头山羊STM32】HAL库 5.SPI部分
stm32·单片机·嵌入式硬件
蓬荜生灰10 小时前
STM32(5)-- 新建寄存器版工程
stm32·单片机·嵌入式硬件
大神与小汪10 小时前
STM32上进行Unix时间戳转换
stm32·嵌入式硬件·unix