FreeRTOS任务相关API函数

1. FreeRTOS任务相关API函数介绍

函数描述
uxTaskPriorityGet()获取任务优先级
vTaskPrioritySet()设置任务优先级
uxTaskGetNumberOfTasks()获取系统中任务的数量
uxTaskGetSystemState()获取所有任务状态信息
vTaskGetInfo()获取指定单个的任务信息
xTaskGetCurrentTaskHandle()获取当前任务的任务句柄
xTaskGetHandle()根据任务名获取该任务的任务句柄
uxTaskGetStackHighWaterMark()获取任务的任务栈历史剩余最小值
eTaskGetState()获取任务状态
vTaskList()以“表格”形式获取所有任务的信息
vTaskGetRunTimeStats()获取任务的运行时间

学习资料可参考手册《FreeRTOS开发指南》第11章 —— “FreeRTOS其他任务API函数”

1.1 函数介绍

UBaseType_t  uxTaskPriorityGet(  const TaskHandle_t xTask  )

此函数用于获取指定任务的任务优先级,使用该函数需将宏 INCLUDE_uxTaskPriorityGet 置 1

形参描述
xTask要查找的任务句柄,NULL代表任务自身
返回值描述
整数任务优先级数值
void vTaskPrioritySet( TaskHandle_t xTask , UBaseType_t uxNewPriority )

此函数用于改变某个任务的任务优先级,使用该函数需将宏 INCLUDE_vTaskPrioritySet 为 1

形参描述
xTask任务句柄,NULL代表任务自身
uxNewPriority需要设置的任务优先级
UBaseType_t uxTaskGetNumberOfTasks( void )

此函数用于获取系统中任务的任务数量

返回值描述
整型系统中任务的数量
UBaseType_t  uxTaskGetSystemState(   TaskStatus_t * const pxTaskStatusArray,
                                      				const UBaseType_t uxArraySize,
                                      				configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime  )

此函数用于获取系统中所有任务的任务状态信息,使用该函数需将宏 configUSE_TRACE_FACILITY 置 1

形参描述
xTaskStatusArray指向 TaskStatus_t 结构体数组首地址
uxArraySize接收信息的数组大小
pulTotalRunTime系统总运行时间,为 NULL 则省略总运行时间值
返回值描述
整型获取信息的任务数量

TaskStatus_t 任务状态信息结构体介绍:

UBaseType_t  uxTaskGetSystemState(   TaskStatus_t * const pxTaskStatusArray,
                                      				const UBaseType_t uxArraySize,
                                      				configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime  )

typedef struct xTASK_STATUS
{
    TaskHandle_t 					xHandle;                       		/* 任务句柄 */ 
    const char *		 			pcTaskName;                     	/* 任务名 */ 
    UBaseType_t						xTaskNumber;                     	/* 任务编号(任务创建次序) */ 
    eTaskState e					CurrentState;                    	/* 任务状态 */ 
    UBaseType_t 					uxCurrentPriority;               	/* 任务优先级 */ 
    UBaseType_t 					uxBasePriority;                 	/* 任务原始优先级 */ 
    configRUN_TIME_COUNTER_TYPE 	ulRunTimeCounter; 					/* 任务运行时间*/
    StackType_t * 					pxStackBase;                    	/* 任务栈基地址 */ 
    configSTACK_DEPTH_TYPE 			usStackHighWaterMark;  				/* 任务栈历史剩余最小值 */ 
} TaskStatus_t;

为什么有原始优先级?因为互斥信号量这些是有一个优先级继承的问题,优先级是会改变的,所以这里有个变量就是保存原始的一个优先级,就这么一个作用。

void vTaskGetInfo( 	TaskHandle_t 	xTask,
					TaskStatus_t * 	pxTaskStatus,
					BaseType_t 		xGetFreeStackSpace,
					eTaskState 		eState  )

此函数用于获取指定的单个任务的状态信息,使用该函数需将宏 configUSE_TRACE_FACILITY 置 1

形参描述
xTask指定获取信息的任务的句柄
pxTaskStatus接收任务信息的变量
xGetFreeStackSpace任务栈历史剩余最小值,当为“pdFALSE” 则跳过这个步骤,当为“pdTRUE”则检查历史剩余最小堆栈
eState任务状态,可直接赋值,如想获取代入“eInvalid”

eTaskState 结构体:

typedef enum
{   
	eRunning = 0,	/* 运行态 */ 
	eReady			/* 就绪态 */ 
	eBlocked, 		/* 阻塞态 */ 
	eSuspended, 	/* 挂起态 */ 
	eDeleted, 		/* 任务被删除 */ 
	eInvalid		/* 无效 */ 
} eTaskState;

因为通过这个函数来获取任务状态的时候,它是要费时间的,所以我们一般可以直接赋值。比如说 task2 正在运行,那就给它一个运行态,eRunning 写进来;如果说无所谓,你浪费点时间就浪费点时间,那么就带入这个 eInvalid 无效态,把这个参数给些进来,那么此时呢它就会帮我们获取它此时的这个任务的任务状态。

TaskHandle_t    xTaskGetCurrentTaskHandle( void ) 

此函数用于获取当前任务的任务句柄, 使用该函数需将宏 INCLUDE_xTaskGetCurrentTaskHandle 置 1

返回值描述
TaskHandle_t当前任务的任务句柄
TaskHandle_t xTaskGetHandle(const char * pcNameToQuery); 

此函数用于通过任务名获取任务句柄 , 使用该函数需将宏 INCLUDE_xTaskGetHandle 置 1

形参描述
pcNameToQuery任务名
返回值描述
TaskHandle任务句柄
UBaseType_t    uxTaskGetStackHighWaterMark( TaskHandle_t  xTask )

此函数用于获取指定任务的任务栈历史最小剩余堆栈;那这个跟设置堆栈大小还是非常有密切联系的。如果这个值非常小,就代表堆栈可能有溢出风险,所以这个历史最小值尽量大一点,可以通过这个历史最小值来设置任务堆栈的大小。(正常大概大一倍左右)

比如说剩余的历史最小堆栈值很小,就代表要溢出了,此时可以加大一点任务堆栈;如果剩余很大,代表很多都没用到,所以可以适当减小一点。

使用该函数需将宏 INCLUDE_uxTaskGetStackHighWaterMark 置 1

形参描述
xTask任务句柄
返回值描述
UBaseType_t任务栈的历史剩余最小值
eTaskState    eTaskGetState(TaskHandle_t xTask)

此函数用于查询某个任务的运行状态(就绪态、阻塞态、挂起态等等),使用此函数需将宏 INCLUDE_eTaskGetState 置1

形参描述
xTask待获取状态任务的任务句柄
返回值描述
eTaskState任务状态
typedef enum
{   
	eRunning = 0,	/* 运行态 */ 
	eReady			/* 就绪态 */ 
	eBlocked, 		/* 阻塞态 */ 
	eSuspended, 	/* 挂起态 */ 
	eDeleted, 		/* 任务被删除 */ 
	eInvalid		/* 无效 */ 
} eTaskState;
void   vTaskList(char * pcWriteBuffer)

此函数用于以“表格”的形式获取系统中任务的信息 ;

使用此函数需将宏 configUSE_TRACE_FACILITY 和 configUSE_STATS_FORMATTING_FUNCTIONS 置 1

形参描述
pcWriteBuffer接收任务信息的缓存指针

Name : 创建任务的时候给任务分配的名字。
State : 任务的壮态信息, B 是阻塞态, R 是就绪态, S 是挂起态, D 是删除态,X 代表运行态。
Priority : 任务优先级。
Stack : 任务堆栈的“高水位线”,就是堆栈历史最小剩余大小。
Num : 任务编号,这个编号是唯一的,当多个任务使用同一个任务名的时候可以通过此编号来做区分。

  • 表格如下所示:
    在这里插入图片描述

2. 任务状态查询API函数实验

实验目的:学习 FreeRTOS 任务状态与信息的查询API函数
实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:
start_task:用来创建task1和task2任务
task1:LED0每500ms闪烁一次,提示程序正在运行
task2:用于展示任务状态信息查询相关 API 函数的使用

2.1 任务函数实现

task2

char task_buff[500];
/* 任务二,实现任务状态查询API函数使用 */
void task2( void * pvParameters )
{
    UBaseType_t priority_num = 0;
    UBaseType_t task_num = 0;
    UBaseType_t task_num2 = 0;
    TaskStatus_t * status_array = 0;
    TaskStatus_t * status_array2 = 0;
    TaskHandle_t task_handle = 0;
    UBaseType_t task_stack_min = 0;
    eTaskState state = 0;
    
    uint8_t i = 0;
    
    vTaskPrioritySet( task2_handler,4 );								/* 设置任务优先级 */
    priority_num = uxTaskPriorityGet( NULL );							/* 获取任务优先级 */
    printf("task2任务优先级为%ld\r\n",priority_num);
    
    task_num = uxTaskGetNumberOfTasks();								/* 获取系统中任务的数量 */
    printf("任务数量:%ld\r\n",task_num);
    
    status_array = mymalloc(SRAMIN,(sizeof(TaskStatus_t) * task_num));	/* 申请位置:SRAM */
    task_num2 = uxTaskGetSystemState( status_array,task_num,NULL);		/* 获取所有任务状态信息 */
    printf("任务名\t\t任务优先级\t任务编号\r\n");
    for(i = 0; i < task_num2; i++)
    {
        printf("%s\t\t%ld\t%ld\r\n",
                status_array[i].pcTaskName,
                status_array[i].uxCurrentPriority,
                status_array[i].xTaskNumber);
    }
    
    status_array2 = mymalloc(SRAMIN,sizeof(TaskStatus_t));
    vTaskGetInfo( task2_handler,status_array2,pdTRUE,eInvalid);			/* 获取指定单个的任务信息 */
    printf("任务名:%s\r\n",status_array2->pcTaskName);
    printf("任务优先级:%ld\r\n",status_array2->uxCurrentPriority);
    printf("任务编号:%ld\r\n",status_array2->xTaskNumber);
    printf("任务状态:%d\r\n",status_array2->eCurrentState);
    
    task_handle = xTaskGetHandle( "task1" );							/* 根据任务名获取该任务的任务句柄 */
    printf("任务句柄:%#x\r\n",(int)task_handle);
    printf("task1的任务句柄:%#x\r\n",(int)task1_handler);
    
    state = eTaskGetState( task2_handler );								/* 获取任务状态 */
    printf("当前task2的任务状态为:%d\r\n",state);
    
    vTaskList( task_buff );												/* 以“表格”形式获取所有任务的信息 */
    printf("%s\r\n",task_buff);
    while(1)
    {
//        task_stack_min = uxTaskGetStackHighWaterMark( task2_handler );
//        printf("task2历史剩余最小堆栈为%ld\r\n",task_stack_min);
        vTaskDelay(1000);
    }
}

Tmr_Svc(软件定时器)是阻塞态,start_task(开始任务)是删除态,task2 是运行态,task1 和 IDLE(空闲任务)是就绪态。

3. 时间统计API函数介绍

void    vTaskGetRunTimeStats( char * pcWriteBuffer ) 

此函数用于统计任务的运行时间信息,使用此函数需将宏 configGENERATE_RUN_TIME_STAT、configUSE_STATS_FORMATTING_FUNCTIONS 置1

形参描述
pcWriteBuffer接收任务运行时间信息的缓存指针

Task:任务名称 Abs
Time:任务实际运行的总时间(绝对时间)。单位不是秒,而是看定时器的中断频率,然后再乘以这个数值才是真正的时间。
%Time:占总处理时间的百分比

  • 表格如下所示:
    在这里插入图片描述
    你的空闲时间越长,代表你的应用任务压力就越小,因为我们说过空闲任务它是 cpu 空闲的时候才会去执行的,如果 cpu 有很多空闲时间,就代表 cpu 压力不大;如果 cpu 这个空闲任务时间占比很小,就代表有些任务可能占用太大的时间。像这里,GenQ 也占了 24%,那我们看到这个占这么大,那我们是不是可以把它拆分一下,拆分小一点,这个就是在调试的时候用的比较多,在正式的产品,比如我们调试好了,我们就把这些功能给删掉就行了(需要)。

所以这个功能主要是在用于调试阶段,我们调试产品的时候,用了 FreeRTOS 的时候,我们这时候可以用这个函数来统计一下每个任务它们各自的一个时间占比。它就这么一个作用。

3.1 时间统计API函数使用流程

  1. 将宏 configGENERATE_RUN_TIME_STATS 置1
  2. 将宏 configUSE_STATS_FORMATTING_FUNCTIONS 置1
  3. 当将此宏 configGENERATE_RUN_TIME_STAT 置1之后,还需要实现2个宏定义:
    • portCONFIGURE_TIMER_FOR_RUNTIME_STATE() :用于初始化用于配置任务运行时间统计的时基定时器;(开启任务调度器函数中调用,自己实现)
    • portGET_RUN_TIME_COUNTER_VALUE():用于获取该功能时基硬件定时器计数的计数值,用来返回当前“时间”。

滴答定时器是来给系统提供时钟节拍的,那这个时基定时器是用来统计任务运行时间的,这两个是有区别的。

  • 注意:这个时基定时器的计时精度(分辨率)需高于系统时钟节拍精度的10至100倍!时基越快,统计的数据就越准确。那系统时钟节拍是 1ms 中断一次,那么高它 10~100 倍,也就是说是 0.1ms 到 0.01ms,也就是 10μs 到 100μs 这么一个范围,这个时基定时器的频率就在这么一个范围。那这个呢是在官网上都是可以找到它这些描述的。
  • 所以我们一会用 100 倍来统计,也就我们现在的系统时钟节拍是 1ms,那 100 倍就 0.01 ms,也就是 10μs,我们一会把时基定时器配置成 10μs 中断一次。

4. 任务时间统计API函数实验

实验目的:学习 FreeRTOS 任务运行时间统计相关 API 函数的使用
实验设计:将设计三个任务:start_task、task1、task2

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

4.1 宏置 1

  1. 将宏 configGENERATE_RUN_TIME_STATS 置1
  2. 将宏 configUSE_STATS_FORMATTING_FUNCTIONS 置1

4.2 宏定义

btim.h

void ConfigureTimeForRunTimeStats(void);		/* 时基定时器初始化函数声明 */

btim.c

时基定时器初始化:

TIM_HandleTypeDef g_timx_handle;         						/* 定时器参数句柄 */

/**
 * @brief       基本定时器TIMX定时中断初始化函数
 * @note
 *              基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              基本定时器的时钟为APB1时钟的2倍, 而APB1为45M, 所以定时器时钟 = 90Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr : 自动重装值。
 * @param       psc : 时钟预分频数
 * @retval      无
 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
    g_timx_handle.Instance = BTIM_TIMX_INT;                     /* 定时器x */
    g_timx_handle.Init.Prescaler = psc;                         /* 分频 */
    g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 递增计数模式 */
    g_timx_handle.Init.Period = arr;                            /* 自动装载值 */
    HAL_TIM_Base_Init(&g_timx_handle);
    
    HAL_TIM_Base_Start_IT(&g_timx_handle);                      /* 使能定时器x和定时器更新中断 */
}


uint32_t FreeRTOSRunTimeTicks;									/* 定义计数值 */
/* 时基定时器的初始化 */
void ConfigureTimeForRunTimeStats(void)
{
    btim_timx_int_init(10-1, 90-1);  							/* 100倍的系统时钟节拍 */
    FreeRTOSRunTimeTicks = 0;									/* 初始化计数值,把它给清零 */
}

我们现在要设置 10μs,大家可以算一下,我们的基本定时器用了 TIM6 ,它的时钟是 90M(我们这里是以 429 为例,429 主频是 180M,那它是 TIM6,是它的一半,也就是 90M),所以我们这个分频值可以设置 90,90M 后面有 6 个 0,除以 90,等于 1M,1M 是不是 1μs,那我们现在要 10μs,就把这个重装载值设置成 10 就可以了,那这样我们就设置好了。

然后就在中断里面 FreeRTOSRunTimeTicks 要进行自加了,我们每进来一次中断服务函数,我去进行一次加一。

/**
 * @brief       定时器底层驱动,开启时钟,设置中断优先级。此函数会被HAL_TIM_Base_Init()函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        BTIM_TIMX_INT_CLK_ENABLE();                     		/* 使能TIMx时钟 */
        HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 6, 0); 		/* 抢占6,子优先级0 */
        HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);         		/* 开启ITMx中断 */
    }
}
/**
 * @brief      	基本定时器TIMX中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_handle);  						/* 定时器回调函数 */
}

/**
 * @brief       回调函数,定时器中断服务函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        FreeRTOSRunTimeTicks++;									/* 计数值自加 */
    }
}

我们这里初始化它是 10μs 中断一次,也就是说 10μs 会进来一次中断服务函数,把 FreeRTOSRunTimeTicks 进行一次加一。那这个就是计数值。

4.3 任务函数实现

task2

char task_buff[500];
/* 任务二,实现任务运行时间统计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);
    }
}

那这样其实就可以了。

task1 它运行时间是非常非常短的,而 task2 因为调用了 vTaskGetRunTimeStats 这个函数,所以它所用的时间就比较长了,相对于 task1 来说,所以我们在调试阶段才会去用到这个函数去检查一下我们的任务时间的一个情况。在一般的一个正式产品的话,我们就会把它给删掉了,就不会用到它了,因为它占据的时间是比较长的。

5. 总结

在这里插入图片描述

  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值