【10】FreeRTOS的任务相关API函数

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

  由于函数太多,这里主要讲述API函数如何使用。可结合正点原子《FreeRTOS开发手册》第11章内容“FreeRTOS其他任务API函数”进行学习。

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

  如果不清楚API函数如何使用还可在 FreeRTOS官网进行查询,里面有各个函数的应用示例。

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

1、实验目的:学习 FreeRTOS 任务状态与信息的查询API函数
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
start_task:用来创建task1和task2任务
task1:LED0每500ms闪烁一次,提示程序正在运行
task2:用于展示任务状态信息查询相关API函数的使用

本次实验基于本系列文章中【07】FreeRTOS的列表和列表项工程文件实现。
  将工程中的freertos_deom.c中删除与本次实验无关的程序。删除完的代码如下所示(此时编译程序无错误无警告):

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "delay.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/



/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
		taskENTER_CRITICAL();                              /*进入临界区*/
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
		xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);
		taskEXIT_CRITICAL();                              /*退出临界区*/
}

/* 任务1,实现LED0每500ms闪烁一次,用来提示系统正在运行 */
void task1( void * pvParameters )
{
    while(1)
    {
			LED0=~LED0;
			vTaskDelay(500);
    }
}
/* 任务2,实现任务状态查询API函数的使用 */
void task2( void * pvParameters )
{
	
    while(1)
    {
			vTaskDelay(500);
    }
}

2.1获取任务优先级函数uxTaskPriorityGet() -使用

UBaseType_t  uxTaskPriorityGet(  const TaskHandle_t xTask  )

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

形参:
xTask:要查找的任务句柄,NULL代表任务自身;
返回值:
UBaseType_t:整数变量(长整型变量),任务优先级数值。

1.首先将FreeRTOSConfig.h中的宏INCLUDE_uxTaskPriorityGet 置 1,该宏默认为1,即默认支持获取任务优先级。

#define INCLUDE_uxTaskPriorityGet                       1                       /* 获取任务优先级 */

2.在freertos_demo.c的task2函数中实现调用。在调用获取优先级函数uxTaskPriorityGet()的过程中,传入某个任务的任务句柄,将获取该任务句柄对应任务的任务优先级,而如果是传入NULL则表示获取当前正在执行任务的任务优先级。
task2代码如下所示:

/* 任务2,实现任务状态查询API函数的使用 */
void task2( void * pvParameters )
{
	
		UBaseType_t priority_num=0;
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
    while(1)
    {
			vTaskDelay(500);
    }
}

此时运行程序在串口中可打印task1的任务优先级为2,如果将输入改为NUll将打印task2的任务优先级3。

2.2设置任务优先级函数vTaskPrioritySet()-使用

void vTaskPrioritySet( TaskHandle_t xTask , UBaseType_t uxNewPriority )

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

形参:
xTask:任务句柄,NULL代表任务自身;
uxNewPriority:需要设置的任务优先级。

1.首先将宏INCLUDE_vTaskPrioritySet 置1,该宏默认为1,即默认支持设置任务优先级。

#define INCLUDE_vTaskPrioritySet                        1                       /* 设置任务优先级 */

2.在task2中将task1的任务优先级设置为30,传入task1的任务句柄,如果要设置task2的任务优先级,可以直接输入NULL

/* 任务2,实现任务状态查询API函数的使用 */
void task2( void * pvParameters )
{
	
		UBaseType_t priority_num=0;
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
    while(1)
    {
			vTaskDelay(500);
    }
}

此时下载程序到开发板可以看到串口打印的task1的任务优先级为30。

2.3获取任务数量函数uxTaskGetNumberOfTasks()-使用

UBaseType_t uxTaskGetNumberOfTasks( void )

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

返回值:
UBaseType_t:长整型变量,表示系统中任务的数量。

此函数不用设置宏,在task2中直接调用即可。

/* 任务2,实现任务状态查询API函数的使用 */
void task2( void * pvParameters )
{
	
		UBaseType_t priority_num=0;
		UBaseType_t task_num=0;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
    while(1)
    {
			vTaskDelay(500);
    }
}

将程序下载到开发板,串口中将打印任务数量为5,由于有start_task、task1、task2以及启动任务调度器时创建的空闲任务、软件定时器任务。

2.4获取所有任务的状态信息函数uxTaskGetSystemState()

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:接收信息的数组大小,(保存任务信息所需要的内存数量,即数组大小,类似设置a[n]中的n);
pulTotalRunTime:系统总运行时间,传入NULL 则省略总运行时间值。
返回值:
UBaseType_t:长整型变量,获取信息的任务数量(返回已经获取任务相关信息的任务数量)。

2.4.1结构体TaskStatus_t-介绍

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;

上面TaskStatus_t结构体中存在一个“任务原始优先级”变量uxBasePriority,是因为在程序运行时可以改变任务优先级,此变量用于保存任务创建时任务的原始优先级。

2.4.2函数uxTaskGetSystemState()-使用

1.将FreeRTOSConfig.h中的宏 configUSE_TRACE_FACILITY 置 1。

#define configUSE_TRACE_FACILITY                        1                       /* 1: 使能可视化跟踪调试, 默认: 0 */

2.在task2中调用函数uxTaskGetSystemState(),传入参数TaskStatus_t 类型的结构体数组变量首地址,需要在内部内存池中申请内存,由于每个结构体的大小统为sizeof(TaskStatus_t)所以,需要该大小乘任务总数量,即可得到任务信息结构体内存大小。使用函数mymalloc()申请地址需要在freertos_demo.c中包含头文件malloc.h。

#include "malloc.h"	
/* 任务2,实现任务状态查询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;
		uint8_t i=0;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
		status_array=mymalloc(SRAMIN,(sizeof(TaskStatus_t)*task_num));
		task_num2=uxTaskGetSystemState( status_array,task_num,NULL);
		printf("任务名称\t\t任务优先级\t\t任务编号\r\n");
		for(i=0; i<task_num2 ; i++)
		{
			printf("%s\t\t%ld\t\t%ld\r\n",
							status_array[i].pcTaskName,
							status_array[i].uxCurrentPriority,
							status_array[i].xTaskNumber
			);
		}
    while(1)
    {
			vTaskDelay(500);
    }
}

编译程序下载到开发板,发现会打印5个任务的相关信息,分别是task2、IDLE、task1、start_task、Tmr Svc,任务优先级分别是3、0、30、1、31(task1的任务优先级为30,是前面程序设置的),任务编号分别是5、2、4、1、3(任务编号是根据任务创建次序程序自动给予的编号,表示第几个被创建的任务)。

2.5获取单个任务的状态信息函数vTaskGetInfo()-使用

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

此函数用于获取指定的单个任务的状态信息,使用该函数需将宏 configUSE_TRACE_FACILITY 置 1(该宏和调用获取所有任务状态信息时需要置1的宏是同一个)

形参:
xTask:指定获取信息的任务的句柄(获取哪个任务的状态信息就将该任务的任务句柄传入进来)
pxTaskStatus :接收任务信息的变量(存储任务信息的内存)
xGetFreeStackSpace:任务栈历史剩余最小值,当为“pdFALSE” 则跳过这个步骤,当为“pdTRUE”则检查历史剩余最小堆栈;
eState:任务状态,可直接赋值,如想让程序获取则传入“eInvalid”(由于获取任务状态比较费时,所以一般可以直接赋值,如果要获取的任务正在运行则赋值为“运行态”)

eTaskState类型结构体可选变量如下所示(如果是运行态则赋值位0,结构体向下依次加1,就绪态则赋值为1):

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

1.将宏置1。
2.在task2函数中调用函数vTaskGetInfo(),其中的参数pxTaskStatus为内存首地址,需要使用函数mymalloc()在内部内存池中创建空间,大小为一个结构体类型TaskStatus_t的大小。

/* 任务2,实现任务状态查询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;
		uint8_t i=0;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
		status_array=mymalloc(SRAMIN,(sizeof(TaskStatus_t)*task_num));
		task_num2=uxTaskGetSystemState( status_array,task_num,NULL);
		printf("任务名称\t\t任务优先级\t\t任务编号\r\n");
		for(i=0; i<task_num2 ; i++)
		{
			printf("%s\t\t%ld\t\t%ld\r\n",
							status_array[i].pcTaskName,
							status_array[i].uxCurrentPriority,
							status_array[i].xTaskNumber
			);
		}
		
		status_array2=mymalloc(SRAMIN,sizeof(TaskStatus_t));
		vTaskGetInfo( task1_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);
    while(1)
    {
			vTaskDelay(500);
    }
}

2.6获取当前任务的任务句柄函数xTaskGetCurrentTaskHandle()-使用

TaskHandle_t    xTaskGetCurrentTaskHandle( void )

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

返回值:
TaskHandle_t :当前任务的任务句柄。

此函数直接调用即可,此处不作展示,和下一个函数一起使用

2.7根据函数名获取任务句柄函数xTaskGetHandle()-使用

TaskHandle_t xTaskGetHandle(const char * pcNameToQuery);

  此函数用于通过任务名获取任务句柄 , 使用该函数需将FreeRTOSConfig.h中的宏INCLUDE_xTaskGetHandle置1。

形参:
pcNameToQuery:任务名
返回值:
TaskHandle:任务句柄

1.将宏置1,默认为1。

#define INCLUDE_xTaskGetHandle                          1                       /* 通过任务名获取任务句柄 */

2.调用函数xTaskGetHandle()获取task任务句柄和创建task2时的任务句柄以及获取当前任务句柄函数xTaskGetCurrentTaskHandle()获取到的任务句柄进行对比。

/* 任务2,实现任务状态查询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;
		TaskHandle_t current_task_handle=0;
		uint8_t i=0;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
		status_array=mymalloc(SRAMIN,(sizeof(TaskStatus_t)*task_num));
		task_num2=uxTaskGetSystemState( status_array,task_num,NULL);
		printf("任务名称\t\t任务优先级\t\t任务编号\r\n");
		for(i=0; i<task_num2 ; i++)
		{
			printf("%s\t\t%ld\t\t%ld\r\n",
							status_array[i].pcTaskName,
							status_array[i].uxCurrentPriority,
							status_array[i].xTaskNumber
			);
		}
		
		status_array2=mymalloc(SRAMIN,sizeof(TaskStatus_t));
		vTaskGetInfo( task1_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( "task2" );
		current_task_handle=xTaskGetCurrentTaskHandle();
		printf("task2任务句柄:%#x\r\n",(int)task2_handler);
		printf("获取指定task2任务句柄:%#x\r\n",(int)task_handle);
		printf("获取当前运行任务的任务句柄:%#x\r\n",(int)current_task_handle);
    while(1)
    {
			vTaskDelay(500);
    }
}

编译程序下载到开发板,在串口中可以看到,两个函数获取到的task2任务句柄和创建任务时的task2的任务句柄一致。

2.7获取任务堆栈历史最小值函数uxTaskGetStackHighWaterMark()-使用

UBaseType_t    uxTaskGetStackHighWaterMark( TaskHandle_t  xTask )

此函数用于获取指定任务的任务栈历史最小剩余堆栈,使用该函数需将FreeRTOSConfig.h中的宏INCLUDE_uxTaskGetStackHighWaterMark 置1。

形参:
xTask:任务句柄;
返回值:
UBaseType_t:长整型变量,任务栈的历史剩余最小值。

1.将宏置1。

#define INCLUDE_uxTaskGetStackHighWaterMark             1                       /* 获取任务堆栈历史剩余最小值 */

2.将task2的任务句柄传入函数uxTaskGetStackHighWaterMark()即可得到task2任务栈历史最小剩余量,可根据此值修改创建task2时的任务栈大小(创建任务栈大小一般为栈使用量的2倍)

/* 任务2,实现任务状态查询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;
		TaskHandle_t current_task_handle=0;
		UBaseType_t task_min_stack=0;
		uint8_t i=0;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
		status_array=mymalloc(SRAMIN,(sizeof(TaskStatus_t)*task_num));
		task_num2=uxTaskGetSystemState( status_array,task_num,NULL);
		printf("任务名称\t\t任务优先级\t\t任务编号\r\n");
		for(i=0; i<task_num2 ; i++)
		{
			printf("%s\t\t%ld\t\t%ld\r\n",
							status_array[i].pcTaskName,
							status_array[i].uxCurrentPriority,
							status_array[i].xTaskNumber
			);
		}
		
		status_array2=mymalloc(SRAMIN,sizeof(TaskStatus_t));
		vTaskGetInfo( task1_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( "task2" );
		current_task_handle=xTaskGetCurrentTaskHandle();
		printf("task2任务句柄:%#x\r\n",(int)task2_handler);
		printf("获取指定task2任务句柄:%#x\r\n",(int)task_handle);
		printf("获取当前运行任务的任务句柄:%#x\r\n",(int)current_task_handle);
		
		
    while(1)
    {
			task_min_stack=uxTaskGetStackHighWaterMark( task2_handler );
			printf("task2历史剩余miniest栈位数为:%ld\r\n",task_min_stack);
			vTaskDelay(1000);
    }
}

编译下载到开发板,从串口中可以看到task2任务栈历史剩余最小量为76,而创建task2时给task2的任务栈大小为128,则使用了任务栈大小为52位。此处在串口中打印“小”字时会出现乱码,其他字没有问题。如果你有解决办法,欢迎留言,感激不尽。

2.8查询某个任务状态函数eTaskGetState()-使用

eTaskState    eTaskGetState(TaskHandle_t xTask)

  此函数用于查询某个任务的运行状态,使用此函数需将宏 INCLUDE_eTaskGetState 置1。

形参:
xTask:待获取状态任务的任务句柄;
返回值:
eTaskState:任务状态,eTaskState在2.5小节中已经介绍了

1.将宏置1,该宏默认为1。

#define INCLUDE_eTaskGetState                           1                       /* 获取任务状态 */

2.实现task2,注释打印task2任务栈历史最小位数。

/* 任务2,实现任务状态查询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;
		TaskHandle_t current_task_handle=0;
		UBaseType_t task_min_stack=0;
		uint8_t i=0;
		eTaskState state;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
		status_array=mymalloc(SRAMIN,(sizeof(TaskStatus_t)*task_num));
		task_num2=uxTaskGetSystemState( status_array,task_num,NULL);
		printf("任务名称\t\t任务优先级\t\t任务编号\r\n");
		for(i=0; i<task_num2 ; i++)
		{
			printf("%s\t\t%ld\t\t%ld\r\n",
							status_array[i].pcTaskName,
							status_array[i].uxCurrentPriority,
							status_array[i].xTaskNumber
			);
		}
		
		status_array2=mymalloc(SRAMIN,sizeof(TaskStatus_t));
		vTaskGetInfo( task1_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( "task2" );
		current_task_handle=xTaskGetCurrentTaskHandle();
		printf("task2任务句柄:%#x\r\n",(int)task2_handler);
		printf("获取指定task2任务句柄:%#x\r\n",(int)task_handle);
		printf("获取当前运行任务的任务句柄:%#x\r\n",(int)current_task_handle);
		
		state = eTaskGetState( task2_handler );
		printf("当前task2的运行状态为:%d\r\n",state);
    while(1)
    {
//			task_min_stack=uxTaskGetStackHighWaterMark( task2_handler );
//			printf("task2历史剩余miniest栈位数为:%ld\r\n",task_min_stack);
			vTaskDelay(1000);
    }
}

编译程序下载到开发板,可以看到task2的运行状态为0,即为运行态,而如果获取的是task1的运行状态,打印状态值为1,就绪态。

2.9以表格形式获取任务信息函数vTaskList()-使用

void   vTaskList(char * pcWriteBuffer)

  此函数用于以“表格”的形式获取系统中任务的信息 ;使用此函数需将宏 configUSE_TRACE_FACILITY configUSE_STATS_FORMATTING_FUNCTIONS 置1 。

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

函数vTaskList()打印的表格形式如下所示:
在这里插入图片描述

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

#define configUSE_TRACE_FACILITY                        1                       /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS            1                       /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */

2.创建全局变量数组task_buff用于保存表格数据信息。在task2直接调用函数vTaskList()即可。

char task_buff[500];
/* 任务2,实现任务状态查询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;
		TaskHandle_t current_task_handle=0;
		UBaseType_t task_min_stack=0;
		uint8_t i=0;
		eTaskState state;
	
		vTaskPrioritySet(task1_handler,30);
		priority_num=uxTaskPriorityGet( task1_handler );
		//priority_num=uxTaskPriorityGet( NULL );
		printf("task1任务优先级为:%ld\n\r",priority_num);
		
		task_num= uxTaskGetNumberOfTasks();
		printf("任务数量:%ld\r\n",task_num);
	
		status_array=mymalloc(SRAMIN,(sizeof(TaskStatus_t)*task_num));
		task_num2=uxTaskGetSystemState( status_array,task_num,NULL);
		printf("任务名称\t\t任务优先级\t\t任务编号\r\n");
		for(i=0; i<task_num2 ; i++)
		{
			printf("%s\t\t%ld\t\t%ld\r\n",
							status_array[i].pcTaskName,
							status_array[i].uxCurrentPriority,
							status_array[i].xTaskNumber
			);
		}
		
		status_array2=mymalloc(SRAMIN,sizeof(TaskStatus_t));
		vTaskGetInfo( task1_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( "task2" );
		current_task_handle=xTaskGetCurrentTaskHandle();
		printf("task2任务句柄:%#x\r\n",(int)task2_handler);
		printf("获取指定task2任务句柄:%#x\r\n",(int)task_handle);
		printf("获取当前运行任务的任务句柄:%#x\r\n",(int)current_task_handle);
		
		state = eTaskGetState( task2_handler );
		printf("当前task2的运行状态为:%d\r\n",state);
		
		vTaskList(  task_buff );
		printf("%s\r\n",task_buff);
    while(1)
    {
//			task_min_stack=uxTaskGetStackHighWaterMark( task2_handler );
//			printf("task2历史剩余miniest栈位数为:%ld\r\n",task_min_stack);
			vTaskDelay(1000);
    }
}

编译程序下载到开发板,可以看到有如下表格:
在这里插入图片描述

3.任务时间统计API函数vTaskGetRunTimeStats-实验

1、实验目的:学习 FreeRTOS 任务运行时间统计相关 API 函数的使用
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
start_task:用来创建task1和task2任务;
task1:LED0每500ms闪烁一次,提示程序正在运行;
task2:用于展示任务运行时间统计相关API函数的使用。

3.1时间统计API函数-介绍

Void    vTaskGetRunTimeStats( char * pcWriteBuffer )

  此函数用于统计任务的运行时间信息,(和函数vTaskList()类似,只传入存储数据的Buffer数组)使用此函数需将宏 configGENERATE_RUN_TIME_STAT configUSE_STATS_FORMATTING_FUNCTIONS 置1 。

形参:
pcWriteBuffe :接收任务运行时间信息的缓存指针

函数vTaskGetRunTimeStats()打印的表格形式如下所示:
在这里插入图片描述

Task:任务名称
Abs Time:任务实际运行的总时间(绝对时间,这里单位不一定是秒,需要根据定时器的中断频率再乘以运行的时间数值
% Time:占总处理时间的百分比
该函数主要用于调试阶段,查看空闲任务所占运行百分比及个别任务运行时间所占百分比,然后看是否个别任务运行时间占比较大,是否需要拆分。

3.2时间统计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() :用于初始化用于配置任务运行时间统计的时基定时器(时基定时器用于统计任务的运行时间);
注意:这个时基定时器的计时精度需高于系统时钟节拍精度的10至100倍(即0.1ms~0.01ms,也就是10us~100us)
② portGET_RUN_TIME_COUNTER_VALUE():用于获取该功能时基硬件定时器计数的计数值 。

3.3任务时间统计API函数-使用

本次实验将基于本篇文章中2.任务状态查询API函数-实验工程实现工程文件实现。
  首先将第2章内容相关的代码删除,只保留task_buff[]数组,在freertos_demo.c中保留以下代码:

#include "freertos_demo.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "delay.h"
#include "malloc.h"	  
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/



/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
		taskENTER_CRITICAL();                              /*进入临界区*/
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
		xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
    vTaskDelete(NULL);
		taskEXIT_CRITICAL();                              /*退出临界区*/
}

/* 任务1,实现LED0每500ms闪烁一次,用来提示系统正在运行 */
void task1( void * pvParameters )
{
    while(1)
    {
			LED0=~LED0;
			vTaskDelay(500);
    }
}
char task_buff[500];
/* 任务2,实现任务运行时间统计API函数的使用 */
void task2( void * pvParameters )
{
    while(1)
    {

			vTaskDelay(1000);
    }
}

  在Task2中实现任务时间统计函数vTaskGetRunTimeStats的调用,和调用函数vTaskList()类似,将保存数据的数组传入到此函数,然后直接打印字符数组即可。这里使用检测按键0是否按下,来输出各任务的运行所占百分比。(需要将task的任务延时改成10ms,不然会打印不及时)
最终将task2函数改成如下所示:

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

3.3.1将宏configGENERATE_RUN_TIME_STATSconfigUSE_STATS_FORMATTING_FUNCTIONS置1

在FreeRTOS官网中并没说要将宏configUSE_STATS_FORMATTING_FUNCTIONS置1,但是如果不置1,程序编译时将会报错。

#define configGENERATE_RUN_TIME_STATS                   1                       /* 1: 使能任务运行时间统计功能, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS            1                       /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 */

3.3.2完成宏portCONFIGURE_TIMER_FOR_RUNTIME_STATE()portGET_RUN_TIME_COUNTER_VALUE()

完成时基定时器的初始化:
在FreeRTOS中将宏portCONFIGURE_TIMER_FOR_RUNTIME_STATE()portGET_RUN_TIME_COUNTER_VALUE()宏定义为其他函数名,所以其他的函数,即可实现这两个函数的功能。

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;/*在其他文件定义,在该文件进行使用*/
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks

1.在timer.c中实现函数ConfigureTimeForRunTimeStats()的功能,这里用到的是定时器3,由于timer.c中已经实现了定时器3的程序,定时器3为通用定时器(2022正点原子FreeRTOS教程中选用的是TIM6基本定时器,此处有所不同
在tmer.c中完成时基初始化函数ConfigureTimeForRunTimeStats()并需要在Timer.h中声明此函数。这里使用通用定时器3,和本系列文章【05】FreeRTOS的中断管理中一致。由于TIM3的时钟来源是90MHz,预分频设置90-1,自动重装载值设置为10-1,定时器溢出时间为(10-1+1)*(90-1+1)/90,000,000=1/100,000ms=10us,为系统时钟节拍的一百倍。

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

2.修改中断回调函数,使得每次中断将FreeRTOSRunTimeTicks的值自加1,用以记录任务运行的时间。

//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM3_Handler))
    {
			FreeRTOSRunTimeTicks++;
    }
	else if (htim==(&TIM4_Handler))
	{
			printf("TIM4优先级为4正在运行!!!\r\n");
	}
}

实验结果
将程序编译下载到开发板,按下Key0,在串口助手中可以看到各任务的运行时间值,将此值和10us相乘即可得到真正的任务运行时间。
在这里插入图片描述

4.总结

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花落指尖❀

您的认可是小浪宝宝最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值