目录

前言

本节描述任务相关的控制。

主要讲解使用,源码分析后面对应章节会有。

学习本节前,建议同学们往前回忆下任务控制块的内容。

参考:

任务控制主要是对任务控制块的处理。

比如任务延时、重置任务优先级、任务挂起与恢复。

对于延时相关的代码细节,可以参考前面的【freertos】007-系统节拍和系统延时管理实现细节章节详细分析。

9.1 相对延时

9.1.1 函数原型

  1. void vTaskDelay( portTickTypexTicksToDelay );

9.1.2 函数说明

  • vTaskDelay()用于相对延时,是指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束。
  • xTicksToDelay参数用于设置延迟的时钟节拍个数。
  • 延时的最大值宏在portmacro.h中有定义:#define portMAX_DELAY (TickType_t )0xffffffffUL

9.1.3 参考例子

  1. static void lzmTestTask(void* parameter)
  2. {
  3. /* task init */
  4. printf("start lzmTestTask\r\n");
  5. for(;;)
  6. {
  7. /* 任务主体 */
  8. /* 延时1000个tick再跑 */
  9. vTaskDelay(1000);
  10. }
  11. }

9.2 绝对延时

该功能可用于周期性任务,保证执行频率不变。

9.2.1 函数原型

  1. BaseType_t vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );

9.2.2 函数说明

  • vTaskDelayUntil()用于绝对延时,也叫周期性延时。想象下精度不高的定时器。
  • pxPreviousWakeTime参数是存储任务上次处于非阻塞状态时刻的变量地址。
  • xTimeIncrement参数用于设置周期性延时的时钟节拍个数。
  • 返回:pdFALSE 说明延时失败。
  • 使用此函数需要在FreeRTOSConfig.h配置文件中开启:#defineINCLUDE_vTaskDelayUntil 1
  • 需要保证周期性延时比任务主体运行时间长。
  • 相对延时的意思是延时配置的N个节拍后恢复当前任务为就绪态。
  • 绝对延时的意思是延时配置的N个节拍后该任务跑回到当前绝对延时函数。

9.2.3 参考例子

  1. static void lzmTestTask(void* parameter)
  2. {
  3. portTickType last_wake_time = 0;
  4. /* task init */
  5. printf("start lzmTestTask\r\n");
  6. /* 重置下该变量 */
  7. last_wake_time = xTaskGetTickCount();
  8. for(;;)
  9. {
  10. /* 再确保任务主体占用CPU时长不会超过周期值(1000tick)的情况下,
  11. 不管任务主体跑多长时间,1000tick后依然内跑回这里。 */
  12. vTaskDelayUntil(&last_wake_time, 1000);
  13. /* 任务主体 */
  14. }
  15. }

9.3 获取任务优先级

9.3.1 函数原型

  1. UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );

9.3.2 函数说明

  • 用于获取任务的优先级。
  • xTask参数为任务句柄。传入NULL,表示获取当前调用该API的任务的优先级。
  • 使能方法:在FreeRTOSConfig.h中配置INCLUDE_vTaskPriorityGet为1。
  • 返回:任务优先级。

9.3.3 uxTaskPriorityGet()源码分析

  • 通过句柄获取任务控制块。
  • 通过任务控制块获取任务优先级并返回。
  1. UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask )
  2. {
  3. TCB_t const * pxTCB;
  4. UBaseType_t uxReturn;
  5. taskENTER_CRITICAL(); /* 加入临界 */
  6. {
  7. /* 获取任务控制块 */
  8. pxTCB = prvGetTCBFromHandle( xTask );
  9. /* 通过任务控制块获取任务优先级 */
  10. uxReturn = pxTCB->uxPriority;
  11. }
  12. taskEXIT_CRITICAL(); /* 退出临界 */
  13. return uxReturn;
  14. }

9.3.4 例子参考代码

  1. void vAFunction( void )
  2. {
  3. TaskHandle_t xHandle;
  4. /* 创建一个任务,存储该句柄 */
  5. xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
  6. // ...
  7. /* 使用句柄获取创建的任务的优先级 */
  8. if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
  9. {
  10. /* 任务可以改变自己的优先级 */
  11. }
  12. // ...
  13. /* 当前任务优先级比创建的任务优先级高? */
  14. if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
  15. {
  16. /* 当前优先级较高 */
  17. }
  18. }

9.4 设置任务优先级

任务优先级除了在创建时设置外,也可以在系统启动后重置,毕竟任务优先级的本质也只是任务控制块里面的一直成员值。

但是修改优先级时需要维护优先级继承机制。

9.4.1 函数原型

  1. void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );

9.4.2 函数说明

作用:

  • 该函数用于重置任务优先级。
  • 如果设置的优先级高于当前正在执行的任务,则会在函数返回之前进行上下文切换。

参数:

  • xTask:需要修改任务优先级的任务句柄。NULL时,表示修改当前任务的任务优先级。
  • uxNewPriority:新的任务优先级。在[0,configMAX_PRIORITIES - 1]范围内,否则会引起断言。

使能方法:使用该功能需要在FreeRTOSConfig.h中配置INCLUDE_vTaskPrioritySet为1。

9.4.3 vTaskPrioritySet()源码分析

更改任务优先级的实现,是更改任务控制块里面记录的任务优先级值,但是需要维护好优先级继承机制。

看到了源码,产生两个疑问:

  • 如果重置的优先级比优先级继承后的优先级还高,这种情况下为什么不更新该任务在用优先级?
  • 重置优先级后,好像没有重置该任务在事件链表中的顺序。所以重置任务优先级不会更改任务在现有事件阻塞链表的顺序。

重置优先级简要步骤:

  • 参数校验&参数纠正。
  • 获取任务优先级。任务优先级包括基优先级和在用优先级。
  • 任务调度需求检测。
  • 迁移就绪链表。
  • 触发任务调度。

9.4.3.1 参数校验

传入的优先级必须小于限制值,否则会触发断言。

  1. /* 断言式参数校验 */
  2. configASSERT( uxNewPriority < configMAX_PRIORITIES );
  3. /* 参数纠正 */
  4. if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
  5. {
  6. uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
  7. }

9.4.3.2 临界处理

重置任务优先级,涉及到就绪链表、事件链表的操作,而系统时钟节拍这些中断会设计到操作这些链表。

9.4.3.3 获取任务优先级

通过任务句柄获取任务控制块,通过任务控制块获取任务优先级。

如果使能了互斥量,及系统支持优先级继承机制时,需要区分基优先级uxBasePriority和在用优先级uxPriority

  1. /* 获取任务控制块 */
  2. pxTCB = prvGetTCBFromHandle( xTask );
  3. #if ( configUSE_MUTEXES == 1 )
  4. {
  5. /* 开启了互斥量就获取基优先级,处理优先级继承使用 */
  6. uxCurrentBasePriority = pxTCB->uxBasePriority;
  7. }
  8. #else
  9. {
  10. /* 没有开启互斥量功能就直接获取优先级 */
  11. uxCurrentBasePriority = pxTCB->uxPriority;
  12. }
  13. #endif

9.4.3.4 任务调度需求检查

在修改任务优先级前,先检查修改后是否需要进行任务调度,以下情况都需要任务调度:

  1. 新的任务优先级比当前在跑任务优先级高,需要标记触发任务调度。
  2. 把当前在跑任务优先级调低,需要标记触发任务调度。

实现代码如下:

  1. /* 检查是否需要标记任务调度 */
  2. if( uxNewPriority > uxCurrentBasePriority ) /* 新的优先级比基优先级更高了 */
  3. {
  4. if( pxTCB != pxCurrentTCB )
  5. {
  6. /* 如果需要修改的任务不是当前在跑任务,且新配置的优先级大于当前在跑的任务优先级,需要标记任务调度 */
  7. if( uxNewPriority >= pxCurrentTCB->uxPriority )
  8. {
  9. /* 标记任务调度 */
  10. xYieldRequired = pdTRUE;
  11. }
  12. }
  13. else
  14. {
  15. /* 如果被提高优先级的任务已经在跑了,就不需要任务切换 */
  16. }
  17. }
  18. else if( pxTCB == pxCurrentTCB ) /* 把当前任务优先级下调,也需要触发任务调度 */
  19. {
  20. /* 标记任务调度 */
  21. xYieldRequired = pdTRUE;
  22. }
  23. else
  24. {
  25. /* 下调其它任务优先级,不需要调度 */
  26. }

9.4.3.5 更新任务优先级

在更新任务优先级前,需要保存该任务在用优先级,等等用于迁移就绪链表。

  1. /* 获取该任务当前使用的优先级 */
  2. uxPriorityUsedOnEntry = pxTCB->uxPriority;

如果开启了互斥量功能,检查该任务是否处于优先级继承状态:

  • 如果是,则不更新该任务在用优先级值。

    • 源码是这样一个逻辑,但是本作者在这里保留个疑问:如果重置的优先级比优先级继承后的优先级还高,这种情况下为什么不更新该任务在用优先级?
  • 如果不是,则需要更新该任务在用优先级值。

  1. #if ( configUSE_MUTEXES == 1 )
  2. {
  3. /* 开启了互斥量功能,但是当前没有在优先级继承状态,可以更新当前任务在用优先级 */
  4. if( pxTCB->uxBasePriority == pxTCB->uxPriority )
  5. {
  6. pxTCB->uxPriority = uxNewPriority;
  7. }
  8. /* 更新基优先级 */
  9. pxTCB->uxBasePriority = uxNewPriority;
  10. }
  11. #else /* if ( configUSE_MUTEXES == 1 ) */
  12. {
  13. /* 没有开启互斥量功能就直接更新当前在用优先级 */
  14. pxTCB->uxPriority = uxNewPriority;
  15. }
  16. #endif /* if ( configUSE_MUTEXES == 1 ) */

9.4.3.6 更新事件链表

按照作者的想法,任务优先级会影响到该任务在事件链表中的位置,所以也需要对事件链表处理。

由于事件链表节点值按功能装载不同的值:

  • 一般情况下装载任务优先级,用于在事件链表中排序,如消息队列阻塞。
  • 如果事件节点挂入了事件组,则装载的是事件组数据。

所以修改该值前先判断当前是否装载任务优先级。

  1. /* 当前事件链表节点值是否被锁定。参考freertos事件组组件 */
  2. if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
  3. {
  4. /* 时间链表节点值没有被锁定,则默认用于保存任务优先级,用于事件链表排序。可更新事件链表节点值。 */
  5. listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) );
  6. }

当前freertos官方提供的修改任务优先级API内事件链表处理代码就这。

按照作者的想法,如果更新了任务优先级到事件节点值。

也应该检查下当前任务是否阻塞在有序事件链表中,如消息队列,这些都是按照优先级插入事件链表的,解除阻塞是取应该排序在前的任务的。

9.4.3.7 迁移就绪链表

如果被修改任务优先级的任务在就绪链表,需要迁移到新的优先级就绪链表中。

该任务如果处于就绪态,会存在在用优先级的就绪链表中,而不是基优先级的就绪链表。

迁移就绪链表时需要注意,如果迁出就绪链表后,该链表没有就绪任务了,需要对系统任务优先级位图值uxTopReadyPriority进行更新处理。

  • 开启优先级检索优化功能后,uxTopReadyPriority该值是一个位图值。
  • 关闭优先级检索优化功能后,uxTopReadyPriority该值就是系统就绪任务中最高优先级的值。

所以实现代码如下:

  1. /* 判断被调节优先级的任务是否处于就绪态,如果是,需要迁移到新的优先级的就绪链表。 */
  2. if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
  3. {
  4. /* 解除任务所有状态,即是迁出当前就绪链表。 */
  5. if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  6. {
  7. /* 如果当前就绪链表没有其它任务了,迁出就绪任务优先级位图值对应位。 */
  8. portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
  9. }
  10. /* 根据新的优先级重新插入就绪链表 */
  11. prvAddTaskToReadyList( pxTCB );
  12. }

所有功能都实现后,触发任务调度,退出临界后,便可进入调度异常的回调进行任务调度。

9.4.4 参考例子

  1. void vAFunction( void )
  2. {
  3. TaskHandle_t xHandle;
  4. /* 创建一个任务,存储该句柄 */
  5. xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
  6. // ...
  7. /* 使用句柄重置创建任务的优先级 */
  8. vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
  9. // ...
  10. /* 传入null,重置当前任务优先级 */
  11. vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
  12. }

9.5 挂起任务

9.5.1 函数原型

  1. void vTaskSuspend( TaskHandle_t xTaskToSuspend );

9.5.2 函数说明

参数:xTaskToSuspend:需要挂起的任务的任务句柄。为NULL时,挂起当前任务。

使能方法:在FreeRTOSConfig.h中配置INCLUDE_vTaskSuspend为1。

作用:挂起一个任务。任务挂起后,插入到就挂起链表中,该任务不会被调度,也无权占用CPU。

配对使用API:调用vTaskResume()恢复被挂起的任务到就绪链表。

9.5.3 vTaskSuspend()源码分析

9.5.3.1 进出临界

挂起任务的处理设计到任务状态链表和任务解除阻塞时间这些全局数据,而这些数据在滴答时钟或者其它中断回调中使用的后缀FromISR API中也可能用到。

所以为了维护这些数据的原子性,需要使用临界级别来实现。

进出临界使用的函数:

  1. /* 进入临界 */
  2. taskENTER_CRITICAL()
  3. /* 退出临界 */
  4. taskEXIT_CRITICAL()

9.5.3.2 获取任务控制块

  1. /* 获取需要挂起的任务句柄。传入NULL,即获取当前任务的句柄。 */
  2. pxTCB = prvGetTCBFromHandle( xTaskToSuspend );

9.5.3.3 任务转为挂起态

切换任务状态不是设置某个任务状态值,而是把任务按规则放到各种状态链表。

  1. 先解除任务所有状态,即是把任务从对应状态链表中迁出:
  • 移出后,如果返回0,说明当前链表没有挂载其它任务了,需要重新更新下系统就绪任务位图表。当然,虽然不知道该任务是不是挂起前是不是在就绪态,多做这步是没错的。另外,位图表需要开启优先级优化才生效。
  1. /* 解除任务所有状态。即是把任务从状态链表中迁出。 */
  2. if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  3. {
  4. /* 移出后如果当前优先级的就绪链表没有其它任务了,就需要重置下位图标。(开启了优先级优化功能才会生效) */
  5. taskRESET_READY_PRIORITY( pxTCB->uxPriority );
  6. }
  1. 解除任务状态后,也需要解除任务事件,从事件链表中移除当前任务:
  1. if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
  2. {
  3. /* 如果存在事件,需要从事件中移除。 */
  4. ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
  5. }
  1. 然后就可以把当前任务挂载到挂起任务链表:
  1. /* 把任务插入到挂起链表 */
  2. vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
  1. 还有任务通知需要处理。如果任务处于等待任务通知状态,如果收到任务通知,也可能从挂起链表中解除阻塞,所以必须解除任务通知状态到没有等待通知状态:
  1. #if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 任务通知功能 */
  2. {
  3. BaseType_t x;
  4. for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
  5. {
  6. if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
  7. {
  8. /* 如果任务正在等待任务通知,则当任务被挂起时,需要清除这些任务通知。 */
  9. pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
  10. }
  11. }
  12. }
  13. #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */

完成以上四小步才算是把任务从其它状态切入到挂起态(是挂起任务的挂起态)。

9.5.3.4 刷新系统解除阻塞任务时间

为了防止挂起的任务是下一个需要解除阻塞的任务而导致系统提前进入检索解除阻塞任务的多余操作,这里可以刷新下解除阻塞任务的时间。

  • 调度器启动了才会任务去跑,才会有任务进入限时阻塞。
  • 维护系统解除阻塞任务时间的值需要在临界区内。
  1. if( xSchedulerRunning != pdFALSE ) /* 调度器已经启动了 */
  2. {
  3. taskENTER_CRITICAL();
  4. {
  5. /* 如果调度器已经开启了,需要更新下一个需要解除任务阻塞的时间 */
  6. prvResetNextTaskUnblockTime();
  7. }
  8. taskEXIT_CRITICAL();
  9. }

9.5.3.5 任务调度器处理

如果挂起的任务是当前任务,那需要更新下pxCurrentTCB值。

  1. 如果调度器已经启动了,挂起当前任务后,需要强制触发任务调度。

  2. 如果调度器还没有启动,挂起了当前任务,就需要更新pxCurrentTCB值即可。等待调度器启动后先跑pxCurrentTCB

  • 如果全部任务都被挂起了,就设置pxCurrentTCB为空即可。下次创建任务或者恢复任务时会重置pxCurrentTCB。至少会在启动调度器时会创建空闲任务,所以在启动调度器前不必在乎pxCurrentTCB值是否为空。

  • 如果不是全部任务都被挂起,那就从就绪表中选出最合适的任务到pxCurrentTCB

    • 调用vTaskSwitchContext(),该任务的分析可以往前面的任务切换章节翻。
  1. if( pxTCB == pxCurrentTCB ) /* 挂起当前任务 */
  2. {
  3. if( xSchedulerRunning != pdFALSE )
  4. {
  5. /* 调度器正常运行,需要强制触发任务调度,把任务切走 */
  6. configASSERT( uxSchedulerSuspended == 0 );
  7. portYIELD_WITHIN_API();
  8. }
  9. else
  10. {
  11. if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
  12. {
  13. /* 如果所有任务都被挂起了,就把pxCurrentTCB值标记为空 */
  14. pxCurrentTCB = NULL;
  15. }
  16. else
  17. {
  18. /* 找出新的pxCurrentTCB值 */
  19. vTaskSwitchContext();
  20. }
  21. }
  22. }

9.6 恢复任务

9.6.1 函数原型

  1. void vTaskResume( TaskHandle_t xTaskToResume );

9.6.2 函数说明

xTaskToResume:需要解除挂起的任务句柄。

INCLUDE_vTaskSuspend必须定义为vTaskSuspend() 1,这个函数才生效。

该函数用于解除挂起的任务。

被一个或多个vTaskSuspend()调用挂起的任务将通过对vTaskResume()的单个调用重新可用。

9.6.3 实现分析

解除任务的挂起态的实现比较简单,主要思路:

  • 通过任务句柄找到任务控制块。
  • 判断该任务是否处于挂起态,就是判断当前任务的状态节点是否挂载在挂起链表。
  • 把当前任务从挂起链表迁到就绪链表。
  • 如果解除挂起态的任务优先级更高或相等,就触发一次任务调度。

9.6.4 完整代码实现

vTaskResume()

  1. #if ( INCLUDE_vTaskSuspend == 1 ) /* 使能 */
  2. void vTaskResume( TaskHandle_t xTaskToResume )
  3. {
  4. TCB_t * const pxTCB = xTaskToResume;
  5. /* 任务句柄不能为NULL */
  6. configASSERT( xTaskToResume );
  7. /* 正在跑的任务在运行态,不用处理。 */
  8. if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
  9. {
  10. taskENTER_CRITICAL(); /* 进入临界。因为下面操作涉及任务状态表等系统相关的全局变量。 */
  11. {
  12. if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) /* 如果该任务处于挂起态 */
  13. {
  14. /* 从挂起链表迁出 */
  15. ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
  16. /* 重新插入到就绪链表 */
  17. prvAddTaskToReadyList( pxTCB );
  18. /* 如果恢复的任务的优先级更高,就触发任务调度。 */
  19. if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
  20. {
  21. /* 触发任务调度 */ taskYIELD_IF_USING_PREEMPTION();
  22. }
  23. }
  24. }
  25. taskEXIT_CRITICAL(); /* 退出临界 */
  26. }
  27. else
  28. {
  29. mtCOVERAGE_TEST_MARKER();
  30. }
  31. }
  32. #endif /* INCLUDE_vTaskSuspend */

prvTaskIsTaskSuspended()

  1. #if ( INCLUDE_vTaskSuspend == 1 ) /* 使能 */
  2. static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask )
  3. {
  4. BaseType_t xReturn = pdFALSE;
  5. const TCB_t * const pxTCB = xTask;
  6. /* 访问xPendingReadyList,因此必须从临界区调用。所以需要在调用本函数前进入。 */
  7. /* 检查在跑任务是否挂起是没有意义的 */
  8. configASSERT( xTask );
  9. /* 检查该任务的状态 */
  10. if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) /* 该任务挂载在挂起链表 */
  11. {
  12. if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE )/* 该任务不是因为调度器挂起而暂时放到挂起链表的 */
  13. {
  14. /* 再判断该任务是否是因为等待事件而永久阻塞的,如果是,也不属于挂起态。 */
  15. if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE )
  16. {
  17. xReturn = pdTRUE;
  18. }
  19. }
  20. }
  21. return xReturn;
  22. }
  23. #endif /* INCLUDE_vTaskSuspend */

9.6.5 参考例子

  1. void vAFunction( void )
  2. {
  3. TaskHandle_t xHandle;
  4. /* 创建一个任务,存储该句柄 */
  5. xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
  6. // ...
  7. /* 挂起这个刚创建的任务 */
  8. vTaskSuspend( xHandle );
  9. // ...
  10. /* 挂起当前在跑任务 */
  11. vTaskSuspend( NULL );
  12. /* 在被其它任务恢复当前任务前,是不会跑到这里的 */
  13. }

附件

重置任务优先级:vTaskPrioritySet()

  1. void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
  2. {
  3. TCB_t * pxTCB;
  4. UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry;
  5. BaseType_t xYieldRequired = pdFALSE;
  6. /* 断言式参数校验 */
  7. configASSERT( uxNewPriority < configMAX_PRIORITIES );
  8. /* 参数纠正 */
  9. if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
  10. {
  11. uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
  12. }
  13. else
  14. {
  15. mtCOVERAGE_TEST_MARKER();
  16. }
  17. /* 进入临界处理 */
  18. taskENTER_CRITICAL();
  19. {
  20. /* 获取任务控制块 */
  21. pxTCB = prvGetTCBFromHandle( xTask );
  22. traceTASK_PRIORITY_SET( pxTCB, uxNewPriority );
  23. #if ( configUSE_MUTEXES == 1 )
  24. {
  25. /* 开启了互斥量就获取基优先级,处理优先级继承使用 */
  26. uxCurrentBasePriority = pxTCB->uxBasePriority;
  27. }
  28. #else
  29. {
  30. /* 没有开启互斥量功能就直接获取优先级 */
  31. uxCurrentBasePriority = pxTCB->uxPriority;
  32. }
  33. #endif
  34. /* 新配置的优先级和原有的优先级不一样才会处理 */
  35. if( uxCurrentBasePriority != uxNewPriority )
  36. {
  37. /* 检查是否需要标记任务调度 */
  38. if( uxNewPriority > uxCurrentBasePriority ) /* 新的优先级比基优先级更高了 */
  39. {
  40. if( pxTCB != pxCurrentTCB )
  41. {
  42. /* 如果需要修改的任务不是当前在跑任务,且新配置的优先级大于当前在跑的任务优先级,需要标记任务调度 */
  43. if( uxNewPriority >= pxCurrentTCB->uxPriority )
  44. {
  45. /* 标记任务调度 */
  46. xYieldRequired = pdTRUE;
  47. }
  48. else
  49. {
  50. mtCOVERAGE_TEST_MARKER();
  51. }
  52. }
  53. else
  54. {
  55. /* 如果被提高优先级的任务已经在跑了,就不需要任务切换 */
  56. }
  57. }
  58. else if( pxTCB == pxCurrentTCB ) /* 把当前任务优先级下调,也需要触发任务调度 */
  59. {
  60. /* 标记任务调度 */
  61. xYieldRequired = pdTRUE;
  62. }
  63. else
  64. {
  65. /* 下调其它任务优先级,不需要调度 */
  66. }
  67. /* 记录该任务当前使用的优先级 */
  68. uxPriorityUsedOnEntry = pxTCB->uxPriority;
  69. #if ( configUSE_MUTEXES == 1 )
  70. {
  71. /* 开启了互斥量功能,但是当前没有在优先级继承状态,可以更新当前任务在用优先级 */
  72. if( pxTCB->uxBasePriority == pxTCB->uxPriority )
  73. {
  74. pxTCB->uxPriority = uxNewPriority;
  75. }
  76. else
  77. {
  78. mtCOVERAGE_TEST_MARKER();
  79. }
  80. /* 更新基优先级 */
  81. pxTCB->uxBasePriority = uxNewPriority;
  82. }
  83. #else /* if ( configUSE_MUTEXES == 1 ) */
  84. {
  85. /* 没有开启互斥量功能就直接更新当前在用优先级 */
  86. pxTCB->uxPriority = uxNewPriority;
  87. }
  88. #endif /* if ( configUSE_MUTEXES == 1 ) */
  89. /* 当前事件链表节点值是否被锁定。参考freertos事件组组件 */
  90. if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
  91. {
  92. /* 时间链表节点值没有被锁定,则默认用于保存任务优先级,用于事件链表排序。可更新事件链表节点值。 */
  93. listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
  94. }
  95. else
  96. {
  97. mtCOVERAGE_TEST_MARKER();
  98. }
  99. /* 判断被调节优先级的任务是否处于就绪态,如果是,需要迁移到新的优先级的就绪链表。 */
  100. if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
  101. {
  102. /* 解除任务所有状态,即是迁出当前就绪链表。 */
  103. if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  104. {
  105. /* 如果当前就绪链表没有其它任务了,迁出就绪任务优先级位图值对应位。 */
  106. portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
  107. }
  108. else
  109. {
  110. mtCOVERAGE_TEST_MARKER();
  111. }
  112. /* 根据新的优先级重新插入就绪链表 */
  113. prvAddTaskToReadyList( pxTCB );
  114. }
  115. else
  116. {
  117. mtCOVERAGE_TEST_MARKER();
  118. }
  119. if( xYieldRequired != pdFALSE )
  120. {
  121. /* 触发任务调度 */
  122. taskYIELD_IF_USING_PREEMPTION();
  123. }
  124. else
  125. {
  126. mtCOVERAGE_TEST_MARKER();
  127. }
  128. /* 编译警告处理 */
  129. ( void ) uxPriorityUsedOnEntry;
  130. }
  131. }
  132. /* 退出临界 */
  133. taskEXIT_CRITICAL();
  134. }

挂起任务:vTaskSuspend()

  1. #if ( INCLUDE_vTaskSuspend == 1 )
  2. void vTaskSuspend( TaskHandle_t xTaskToSuspend )
  3. {
  4. TCB_t * pxTCB;
  5. taskENTER_CRITICAL();
  6. {
  7. /* 获取需要挂起的任务句柄。传入NULL,即获取当前任务的句柄。 */
  8. pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
  9. traceTASK_SUSPEND( pxTCB );
  10. /* 解除任务所有状态。即是把任务从状态链表中迁出。 */
  11. if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
  12. {
  13. /* 移出后如果当前优先级的就绪链表没有其它任务了,就需要重置下位图标。(开启了优先级优化功能才会生效) */
  14. taskRESET_READY_PRIORITY( pxTCB->uxPriority );
  15. }
  16. else
  17. {
  18. mtCOVERAGE_TEST_MARKER();
  19. }
  20. if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
  21. {
  22. /* 如果存在事件,需要从事件中移除。 */
  23. ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
  24. }
  25. else
  26. {
  27. mtCOVERAGE_TEST_MARKER();
  28. }
  29. /* 把任务插入到挂起链表 */
  30. vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
  31. #if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 任务通知功能 */
  32. {
  33. BaseType_t x;
  34. for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
  35. {
  36. if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
  37. {
  38. /* 如果任务正在等待任务通知,则当任务被挂起时,需要清除这些任务通知。 */
  39. pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
  40. }
  41. }
  42. }
  43. #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
  44. }
  45. taskEXIT_CRITICAL();
  46. if( xSchedulerRunning != pdFALSE )
  47. {
  48. taskENTER_CRITICAL();
  49. {
  50. /* 如果调度器已经开启了,需要更新下一个需要解除任务阻塞的时间 */
  51. prvResetNextTaskUnblockTime();
  52. }
  53. taskEXIT_CRITICAL();
  54. }
  55. else
  56. {
  57. mtCOVERAGE_TEST_MARKER();
  58. }
  59. if( pxTCB == pxCurrentTCB ) /* 挂起当前任务 */
  60. {
  61. if( xSchedulerRunning != pdFALSE )
  62. {
  63. /* 调度器正常运行,需要强制触发任务调度,把任务切走 */
  64. configASSERT( uxSchedulerSuspended == 0 );
  65. portYIELD_WITHIN_API();
  66. }
  67. else
  68. {
  69. if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
  70. {
  71. /* 如果所有任务都被挂起了,就把pxCurrentTCB值标记为空 */
  72. pxCurrentTCB = NULL;
  73. }
  74. else
  75. {
  76. /* 找出新的pxCurrentTCB值 */
  77. vTaskSwitchContext();
  78. }
  79. }
  80. }
  81. else
  82. {
  83. mtCOVERAGE_TEST_MARKER();
  84. }
  85. }
  86. #endif /* INCLUDE_vTaskSuspend */

【freertos】009-任务控制的更多相关文章

  1. 【freertos】005-启动调度器分析

    前言 本节主要讲解启动调度器. 这些都是与硬件相关,所以会分两条线走:posix和cortex m3. 原文:李柱明博客:https://www.cnblogs.com/lizhuming/p/160 ...

  2. FreeRTOS系列第13篇---FreeRTOS内核控制

    内核控制的一些功能须要移植层提供,为了方便移植.这些API函数用宏来实现,比方上下文切换.进入和退出临界区.禁止和使能可屏蔽中断.内核控制函数还包含启动和停止调度器.挂起和恢复调度器以及用于低功耗模式 ...

  3. ROS Learning-031 (提高篇-009 A Mobile Base-07) 控制移动平台 --- (操作)人机交互

    ROS 提高篇 之 A Mobile Base-07 - 控制移动平台 - (操作)人机交互 我使用的虚拟机软件:VMware Workstation 11 使用的Ubuntu系统:Ubuntu 14 ...

  4. FreeRTOS学习笔记3:内核控制及开启调度器

    内核控制函数API 应用层中不会用到taskYIELD() //任务切换.会自动切换当前就绪表里优先级最高的任务 临界区 //不能被打断的代码段任务中进入临界区任务中退出临界区中断服务进入临界区中断服 ...

  5. CSS控制超链接

    一.伪类 CSS控制元素的某种状态---偽类(用于向某些选择器添加特殊的效果)    偽类的语法:元素标签 偽类名称{属性:属性值;} 二.超链接        a:link:未访问的链接       ...

  6. FreeRTOS随记

    任务函数原型: void ATaskFunction(void * pvParameters); 任务不允许从实现函数中返回.如果一个任务不再需要,可以用vTaskDelete()删除; 一个任务函数 ...

  7. (一)Nodejs - 框架类库 - Nodejs异步流程控制Async

    简介 Async是一个流程控制工具包,提供了直接而强大的异步功能 应用场景 业务流程逻辑复杂,适应异步编程,减少回调的嵌套 安装 npm insatll async 函数介绍 Collections ...

  8. 转:在控制台中调试AngularJS应用

    在控制台中调试AngularJS应用 在创建AngularJS应用时,一个很棘手的问题是如何在Chrome,Firefox,以及IE的JavaScript控制台中访问深藏在应用中的数据和服务.本文将会 ...

  9. FreeRTOS 使用指南(转)

    源:FreeRTOS 使用指南 繁星电子开发团队制作 作为一个轻量级的操作系统,FreeRTOS 提供的功能包括:任务管理.时间管理.信号量.消息队列.内存管理.记录功能等,可基本满足较小系统的需要. ...

随机推荐

  1. 移动端的vw px rem之间换算

    一.vw px rem em是什么 1.vw:就是相对视口宽度(Viewport Width).1vw = 1% * 视口宽度.也就是说,一个视口就是100vw. 2.px:px应该是在css中使用最 ...

  2. vue2.0开发聊天程序(八) 初步完成

    项目地址 服务器源码地址:https://github.com/ermu592275254/chat-socket 网页源码地址:https://github.com/ermu592275254/ch ...

  3. 微信小程序获取当前时间戳、获取当前时间、时间戳加减

    //获取当前时间戳 var timestamp = Date.parse(new Date()); timestamp = timestamp / 1000; console.log("当前 ...

  4. FastAPI(七十一)实战开发《在线课程学习系统》接口开发-- 查看留言

    之前FastAPI(七十)实战开发<在线课程学习系统>接口开发--留言功能开发分享了留言开发,这次我们分享查看留言 梳理这里的逻辑,这个接口要依赖登录. 1.判断用户是否登录 2.判断对应 ...

  5. ajax - xhr level2新特性 json等众多内容

    1. 今天的内容其实挺多的,我们慢慢来说.首先第一个是xhr的基本使用,什么是xhr? XMLHTTPRequest是浏览器提供的js对象,可以请求服务器上的数据资源,包括我们前面一直用的jq里面的三 ...

  6. 数仓建设 | ODS、DWD、DWM等理论实战(好文收藏)

    本文目录: 一.数据流向 二.应用示例 三.何为数仓DW 四.为何要分层 五.数据分层 六.数据集市 七.问题总结 导读 数仓在建设过程中,对数据的组织管理上,不仅要根据业务进行纵向的主题域划分,还需 ...

  7. 基于深度学习的人脸性别识别系统(含UI界面,Python代码)

    摘要:人脸性别识别是人脸识别领域的一个热门方向,本文详细介绍基于深度学习的人脸性别识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择人脸图片.视频进行检 ...

  8. python 多进程共享全局变量之Manager()

    Manager支持的类型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和A ...

  9. zookeeper篇-zk的选举机制

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. 说说zk的选举机制 基础概念 zxid=事务id=一个时间戳,代表当前事 ...

  10. vue build 指定环境

    前言 其实很简单的东西,搜索时很是费劲,特此记录下来.网上有很多资料,但都是五花八门,特此记录 使用 项目根目录中创建环境变量使用文件 .env #所有环境都会加载 .env.development ...