FreeRTOS学习笔记五【任务管理-下】
空闲任务与空闲任务hook
空闲任务
在FreeRTOS学习笔记四【任务管理-中】描述了当所有用户任务处于非运行态时,调度程序将调用空闲任务。在内核中必须有一个但也只能有一个任务进入运行状态,为了确保这种情况,在调用vTaskStartScheduler()时,调度程序会自动创建一个空闲任务,它只是一个循环,因此始终能够运行。为了保证空闲任务不会组织跟高优先级的任务进入运行状态,它具有最低的优先级。如果需要在空闲任务的优先级上创建任务(共享空闲任务的优先级),需要设置FreeRTOSConfig.h中的configIDLE_SHOULD_YIELD,防止空闲任务占用太多的应用程序任务的处理时间,以提高效率。如果使用vTaskDelete()删除任务,Idle任务负责在删除任务后清理内核资源。
空闲任务hook
它是空闲任务每次执行时自动调用的函数。可以使用空闲hook(或空闲回调)函数将应用程序指定功能直接添加到空闲任务中。
空闲任务hook的常见用法:
- 执行低优先级,后台或连续处理的功能。
- 测量备用处理能力的数量。(只有当所有优先级较高的应用程序任务都无法执行时,空闲任务才会运行;因此,测量分配给空闲任务的处理时间量可清楚地看到剩余多少处理时间。)
- 将处理器置于低功耗模式,无需执行应用程序处理即可提供简单、自动的省电方法(但是此方法实现的省电效果低于后面描述的“低功耗支持”中的无空闲模式。
空闲任务hook函数必须遵守以下规则:
- 空闲任务hook函数绝不能阻塞或挂起空闲任务。
注意:以任何方式阻塞空闲任务都可能导致没有任何一个任务进入运行状态的情况。 - 如果应用程序使用vTaskDelete() API函数,则空闲任务hook必须在合理的时间内返回其调用方。 因为Idle任务负责在删除任务后清理内核资源。 如果空闲任务永久保留在空闲hook函数中,则不会发生资源清理。
空闲任务hook函数的原型:
void vApplicationIdleHook( void );
要调用空闲hook函数必须在FreeRTOSConfig.h中,将configUSE_IDLE_HOOK设置为1。
示例如下:
hook函数:
/* 定义一个变量记录进入空闲任务的次数 */
volatile uint32_t ulIdleCycleCount = 0UL;
/* 空闲hook函数必须为vApplicationIdleHook(),不带参数,并返回void。 */
void vApplicationIdleHook( void )
{
ulIdleCycleCount++;
}
任务函数:
void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
const TickType_t xDelay250ms = pdMS_TO_TICKS( 250 );
pcTaskName = ( char * ) pvParameters;
for( ;; )
{
/* 输出一个字符串和一个数字 */
vPrintStringAndNumber( pcTaskName, ulIdleCycleCount );
/* 延时 */
vTaskDelay( xDelay250ms );
}
}
主函数:
/* 定义传入任务的字符串 */
static const char *pcTextForTask1 = "Task 1 is running\r\n";
static const char *pcTextForTask2 = "Task 2 is running\r\n";
int main( void )
{
/* 创建任务1 */
xTaskCreate( vTaskFunction, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL );
/* 创建任务2 */
xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );
/* 启动任务调度器 */
vTaskStartScheduler();
for( ;; );
}
输出结果:
改变任务优先级
在FreeRTOS学习笔记四【任务管理-中】描述了任务调度程序会根据任务的优先级高低而先后调用任务,优先级高的先调用,优先级低的后调用,前面描述了如何设置任务的初始优先级,而有时需要在运行过程中调整任务的优先级。要调用任务优先级获取与设置的API函数,必须将FreeRTOSConfig.h文件中的INCLUDE_uxTaskPriorityGet和INCLUDE_vTaskPrioritySet设置为1,下面介绍这两个API函数。
/* 设置任务优先级 */
void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority );
参数:
- pxTask
需要设置优先级的任务的句柄,通过前面介绍的pxCreatedTask()获取。如果设置的是调用任务的优先级可以直接传入NULL。 - uxNewPriority
设置优先级。 最大为(configMAX_PRIORITIES-1)。
/* 获取任务的优先级 */
UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );
参数:
- pxTask
需要获取优先级的任务的句柄,通过前面介绍的pxCreatedTask()获取。如果获取的是调用任务的优先级可以直接传入NULL。
返回值:任务的优先级。
示例:
/* 任务1的任务函数 */
void vTask1( void *pvParameters )
{
UBaseType_t uxPriority;
/* 获取当前任务的优先级 */
uxPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* 打印字符串 */
vPrintString( "Task 1 is running\r\n" );
/* 修改任务2的优先级,让任务2的优先级高于任务1的优先级 */
vPrintString( "About to raise the Task 2 priority\r\n" );
vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
}
}
/* 任务2的任务函数 */
void vTask2( void *pvParameters )
{
UBaseType_t uxPriority;
/* 获取当前任务的优先级 */
uxPriority = uxTaskPriorityGet( NULL );
for( ;; )
{
/* 打印字符串 */
vPrintString( "Task 2 is running\r\n" );
/* 修改自身的优先级,让自身的优先级低于于任务1的优先级 */
vPrintString( "About to lower the Task 2 priority\r\n" );
vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
}
}
/* 保存任务2的句柄 */
TaskHandle_t xTask2Handle = NULL;
int main( void )
{
/* 创建任务1,任务1的初始优先级高于任务2,所以在启动调度程序时,任务1先执行*/
xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
/* 创建任务2,并保存任务2的句柄 */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
/* 启动调度程序 */
vTaskStartScheduler();
for( ;; );
}
输出结果:
删除任务
任务中可以使用vTaskDelete()函数删除自身或者其他任务,但需要将FreeRTOSConfig.h中的额INCLUDE_vTaskDelete设置为1。已经删除的任务无法再次进入运行状态。调用vTaskDelete()函数后空闲任务将释放分配给已删除任务的内存,因此,调用vTaskDelete()函数的不会饿死其他的任务。
注意:删除一个任务时,内核只能释放内核本省分配给任务的内存,而其他由应用程序分配的内存或资源必须由应用程序自己释放。
API原型如下:
void vTaskDelete( TaskHandle_t pxTaskToDelete );
参数:
- pxTaskToDelete
需要删除任务的句柄,由xTaskCreate()的参数获取。如果删除的是自身任务,可以直接传入NULL。
示例:
TaskHandle_t xTask2Handle = NULL;
void vTask1( void *pvParameters )
{
const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );
for( ;; )
{
vPrintString( "Task 1 is running\r\n" );
/* 创建任务2 */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
/* 延时*/
vTaskDelay( xDelay100ms );
}
}
void vTask2( void *pvParameters )
{
/* 任务2没有其他任务,就是删除自己 */
vPrintString( "Task 2 is running and about to delete itself\r\n" );
vTaskDelete( xTask2Handle );
}
int main( void )
{
/* 创建任务1 */
xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
vTaskStartScheduler();
for( ;; );
}
运行结果: