| 上一篇 | 下一篇 |
|---|---|
| 任务状态与信息查询 API 函数 |
目 录
-
- [2)任务时间统计相关 API 函数](#2)任务时间统计相关 API 函数)
2)任务时间统计相关 API 函数
函数有很多,但是常用的就一个。
2.1)vTaskGetRunTimeStats()
这个函数主要用于调试,实际产品中一般不用,因为占用时间还是比较大的
声明:Void vTaskGetRunTimeStats( char * pcWriteBuffer )
功能:此函数用于 统计任务的运行时间信息 ,使用此函数需将宏 configGENERATE_RUN_TIME_STAT(默认为0) 、configUSE_STATS_FORMATTING_FUNCTIONS 置 1(默认为1)
形参:
-
pcWriteBuffer:指向保存任务运行时间信息的存储区的指针(数组名调用时为指针)保存的内容为:
cTask : 任务名称 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 。