目录
- 1.任务相关API函数-介绍
- 2.任务状态查询API函数-实验
- 2.1获取任务优先级函数`uxTaskPriorityGet() `-使用
- 2.2设置任务优先级函数`vTaskPrioritySet()`-使用
- 2.3获取任务数量函数`uxTaskGetNumberOfTasks()`-使用
- 2.4获取所有任务的状态信息函数`uxTaskGetSystemState()`
- 2.5获取单个任务的状态信息函数`vTaskGetInfo()`-使用
- 2.6获取当前任务的任务句柄函数`xTaskGetCurrentTaskHandle()`-使用
- 2.7根据函数名获取任务句柄函数`xTaskGetHandle()`-使用
- 2.7获取任务堆栈历史最小值函数`uxTaskGetStackHighWaterMark()`-使用
- 2.8查询某个任务状态函数`eTaskGetState()`-使用
- 2.9以表格形式获取任务信息函数`vTaskList()`-使用
- 3.任务时间统计API函数`vTaskGetRunTimeStats`-实验
- 4.总结
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_STATS和configUSE_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相乘即可得到真正的任务运行时间。