提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
这前面有很多函数是介绍过的,这里重要讨论任务运行时间、任务状态查询这两种函数。
可以看到,例程中基本对前面各种查询任务信息的函数进行了使用。
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));
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);
}
}
首先,在任务开始时,通过vTaskPrioritySet函数将task2的优先级设置为4,然后使用uxTaskPriorityGet函数获取任务优先级,并打印出来。
接下来,使用uxTaskGetNumberOfTasks函数获取当前系统中的任务数量,并打印出来。
然后,通过uxTaskGetSystemState函数获取当前所有任务的状态信息,并使用for循环遍历打印出任务名、任务优先级和任务编号。
接着,使用vTaskGetInfo函数获取指定单个任务(task2)的信息,并打印出任务名、任务优先级、任务编号和任务状态。
使用xTaskGetHandle函数根据任务名获取任务句柄,并打印出任务句柄。
使用eTaskGetState函数获取task2的任务状态,并打印出来。
使用vTaskList函数以表格形式展示所有任务的信息,并将信息保存在task_buff数组中,并打印出task_buff。
最后,通过一个无限循环来保持任务的运行,并使用vTaskDelay函数延迟1秒。
注释部分的代码是获取任务的历史剩余最小堆栈的示例,可以根据需要进行取消注释使用。
vTaskGetRunTimeStats函数是用于获取任务的运行时间统计信息的API函数。它接受一个用于存储运行时间统计信息的字符数组作为参数,并将任务的运行时间统计信息填充到该字符数组中。
在这个函数被调用时,它会计算每个任务的运行时间、运行次数和运行时间占总运行时间的比例。然后,它将这些信息格式化为字符串,并存储在pcWriteBuffer指向的字符数组中。
这个函数通常用于调试和性能分析,可以帮助开发人员了解每个任务的实际运行时间和调度情况,从而进行性能优化和任务调度的改进。
在使用vTaskGetRunTimeStats函数时,需要确保传递给函数的字符数组长度足够大,以容纳运行时间统计信息的字符串。否则,可能会导致数据溢出或截断。
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);
}
}
在这段代码中,task2任务使用一个无限循环来持续运行。
在循环中,通过key_scan函数检测是否按下了按键0。如果按下了按键0,就调用vTaskGetRunTimeStats函数获取任务的运行时间统计信息,并将信息打印出来。
然后,使用vTaskDelay函数延迟10个时钟节拍,以控制任务的调度间隔。
这段代码的作用是在任务运行过程中,通过按下按键0来触发获取任务的运行时间统计信息,并将信息打印出来。这可以用于实时监测任务的运行时间,帮助开发人员进行性能分析和调优。
vTaskGetRunTimeStats函数获取任务的运行时间统计信息,这个函数一般用在调试阶段,用来可任务运行时间,但是本身这个函数执行花费的时间也大,所以正式使用时一般不用。
vTaskDelay()函数是相对延时函数。它会使当前任务进入阻塞状态,暂时释放处理器资源,并在指定的时间后重新被调度执行。该延时时间是相对于当前时间的相对时间。例如,如果调用vTaskDelay(1000)函数,任务将在当前时间的基础上延迟1000个时钟节拍。
xTaskDelayUntil()函数是绝对延时函数。它会使当前任务进入阻塞状态,并在指定的绝对时间点后重新被调度执行。该延时时间是相对于系统运行的绝对时间的。例如,如果调用xTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000))函数,任务将在xLastWakeTime变量记录的时间点的基础上延迟1000毫秒。
这两个函数都会阻塞当前任务的执行,但是vTaskDelay()函数的延时时间是相对于当前时间的相对时间,而xTaskDelayUntil()函数的延时时间是相对于系统运行的绝对时间的。选择哪个函数取决于具体的应用需求。
vTaskDelay()函数和传统的delay延时函数在实现方式和使用方式上有一些区别。
阻塞方式:vTaskDelay()函数是通过阻塞当前任务的方式来实现延时。它会使当前任务暂时释放处理器资源,并在延时时间结束后重新被调度执行。而传统的delay延时函数是通过循环等待的方式来实现延时,它会占用处理器资源,并在延时时间结束后继续执行后面的代码。
精确性:vTaskDelay()函数是基于FreeRTOS的任务调度器来实现的,可以提供相对较高的延时精确性。它可以在任务级别进行精确的延时,不受其他任务的影响。而传统的delay延时函数通常是基于处理器的时钟周期进行延时,受到处理器速度和其他任务的影响,精确性较差。
多任务处理:vTaskDelay()函数是FreeRTOS多任务操作系统的一部分,可以与其他任务共享处理器资源,并能够在多个任务之间进行切换。而传统的delay延时函数通常是在单任务环境下使用,不适用于多任务操作系统。
综上所述,vTaskDelay()函数适用于在FreeRTOS系统中进行任务级别的延时操作,能够提供较高的延时精确性和多任务处理能力。传统的delay延时函数适用于单任务环境下的简单延时操作,精确性较差。选择哪种延时方式取决于具体的应用需求和使用场景。
vTaskDelay()函数的实现依赖于FreeRTOS任务调度器和时钟节拍。
在FreeRTOS中,每个任务都有一个任务控制块(Task Control Block,TCB),其中保存了任务的状态、优先级、堆栈指针等信息。任务调度器根据任务的优先级和调度策略,决定哪个任务应该获得处理器资源运行。
当任务调用vTaskDelay()函数时,它会将自己的状态设置为阻塞状态,并将延时时间保存到任务控制块中。然后,任务调度器会从就绪任务列表中选择一个最高优先级的就绪任务来运行。
任务调度器会根据系统的时钟节拍(Tick)来跟踪时间的流逝。每个时钟节拍的长度由FreeRTOS配置文件中的configTICK_RATE_HZ宏定义确定。任务调度器会在每个时钟节拍发生时进行调度决策。
.当延时时间结束时,任务调度器会将任务的状态从阻塞状态改为就绪状态,并将任务加入就绪任务列表中。然后,任务调度器会在下一个时钟节拍发生时,选择该任务来运行。
总结来说,vTaskDelay()函数的实现利用了FreeRTOS任务调度器和时钟节拍的机制,将任务的状态设置为阻塞状态,并在延时时间结束时将任务的状态改为就绪状态,从而实现延时的效果。
代码为:
/* 任务一,演示相对延时函数 */
void task1( void * pvParameters )
{
while(1)
{
LED0_TOGGLE(); /* PB1 */
delay_ms(20);
vTaskDelay(500);
}
}
/* 任务二,演示绝对延时函数 */
void task2( void * pvParameters )
{
TickType_t xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
while(1)
{
LED1_TOGGLE(); /* PB0 */
delay_ms(20);
vTaskDelayUntil(&xLastWakeTime,500);
}
}
任务一中的vTaskDelay(500)函数会使任务一进入阻塞状态,暂时释放处理器资源,并在当前时间的基础上延迟500个时钟节拍后重新被调度执行。在任务一的循环中,LED0会每20毫秒切换一次状态,然后通过vTaskDelay(500)函数实现500毫秒的延时。
任务二中的vTaskDelayUntil(&xLastWakeTime, 500)函数会使任务二进入阻塞状态,并在xLastWakeTime变量记录的时间点的基础上延迟500个时钟节拍后重新被调度执行。在任务二的循环中,LED1会每20毫秒切换一次状态,然后通过vTaskDelayUntil(&xLastWakeTime, 500)函数实现500毫秒的延时。
这两个任务通过调用不同的延时函数来实现不同的延时方式。任务一使用相对延时函数vTaskDelay(),延时时间是相对于当前时间的相对时间;任务二使用绝对延时函数vTaskDelayUntil(),延时时间是相对于系统运行的绝对时间的。