函数预览

函数 描述
taskYIELD() 任务切换
taskENTER_CRITICAL() 进入临界区,用于任务中。
taskEXIT_CRITICAL() 退出临界区,用于任务中。
taskENTER_CRITICAL_FROM_ISR() 进入临界区,用于中断服务函数中。
taskEXIT_CRITICAL_FROM_ISR() 退出临界区,用于中断服务函数中。
taskDISABLE_INTERRUPTS() 关闭中断。
taskENABLE_INTERRUPTS() 打开中断。
vTaskStartScheduler() 开启任务调度器。
vTaskEndScheduler() 关闭任务调度器。
vTaskSuspendAll() 挂起任务调度器。
xTaskResumeAll() 恢复任务调度器。
vTaskStepTick() 设置系统节拍值。

函数详解

这里只介绍部分函数,剩下的一些都是很好理解的,就不过多介绍。

关闭任务调度器

此函数在文件 tasks.c 中有如下定义:

  1. void vTaskEndScheduler( void )
  2. {
  3. portDISABLE_INTERRUPTS(); //关闭中断
  4. xSchedulerRunning = pdFALSE; //标记任务调度器停止运行
  5. vPortEndScheduler(); //调用硬件层关闭中断的处理函数
  6. }

函数 vPortEndScheduler()在 port.c 中有定义,这个函数在移植 FreeRTOS 的时候要根据实际使用的处理器来编写,此处没有实现这个函数,只是简单的加了一行断言,函数如下:

  1. void vPortEndScheduler( void )
  2. {
  3. configASSERT( uxCriticalNesting == 1000UL );
  4. }

挂起任务调度器

挂起任务调度器,调用此函数不需要关闭可屏蔽中断即可挂起任务调度器,此函数在文件tasks.c 中有如下定义:

  1. void vTaskSuspendAll( void )
  2. {
  3. ++uxSchedulerSuspended;
  4. }

此函数只是简单的将变量 uxSchedulerSuspended 加一,uxSchedulerSuspended 是挂起嵌套计数器,调度器挂起是支持嵌套的。使用函数 xTaskResumeAll()可以恢复任务调度器,调用了几次 vTaskSuspendAll()挂起调度器,同样的也得调用几次 xTaskResumeAll()才会最终恢复任务调度器。
假设现在有这样一种情况,任务 1 的优先级为 10,此时任务 1 由于等待队列(关于队列的知识后面会有专门的章节讲)TestQueue 而处于阻塞态。但是有段其他的代码调用函数vTaskSuspendAll()挂起了任务调度器,在还没有调用 xTaskResumeAll()恢复任务调度器之前,有个在外部中断发生了,在中断服务程序里面调用函数 xQueueSendFromISR()向任务 1 发送了队列 TestQueue。如果任务调度器没有阻塞的话函数 xQueueSendFromISR()会使任务 1 进入就绪态,也就是将任务 1 添加到优先级 10 对应的就绪列表 pxReadyTasksLists[10]中,这样当任务切换的时候任务 1 就会运行。但是现在任务调度器由于函数 vTaskSuspendAll()而挂起,这个时候任务 1 就不是添加到任务就绪列表 pxReadyTasksLists[10]中了,而是添加到另一个叫做xPendingReadyList 的列表中,xPendingReadyList 是个全局变量,在文件 tasks.c 中有定义。当调用函数 xTaskResumeAll()恢复调度器的时候就会将挂到列表 xPendingReadyList 中的任务重新移动到它们所对应的就绪列表 pxReadyTasksLists 中。

恢复任务调度器

  1. BaseType_t xTaskResumeAll( void )
  2. {
  3. TCB_t *pxTCB = NULL;
  4. BaseType_t xAlreadyYielded = pdFALSE;
  5. configASSERT( uxSchedulerSuspended );
  6. //进入临界区
  7. taskENTER_CRITICAL();
  8. {
  9. //调度器挂起嵌套计数器 uxSchedulerSuspended 减一
  10. --uxSchedulerSuspended;
  11. //如果 uxSchedulerSuspended 为 0 说明所有的挂起都已经解除,调度器可以开始运行了。
  12. if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
  13. {
  14. if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
  15. {
  16. /* while()循环处理列表 xPendingReadyList,只要列表 xPendingReadyList 不为空,说明还
  17. * 有任务挂到了列表 xPendingReadyList 上,这里需要将这些任务从列表 xPendingReadyList 上移
  18. * 除并添加到这些任务所对应的就绪列表中。
  19. * /
  20. while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
  21. {
  22. //遍历列表 xPendingReadyList,获取挂到列表 xPendingReadyList 上的任务对应的任务控制块。
  23. pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY\ ( ( &xPendingReadyList ) );
  24. //将任务从事件列表上删除。
  25. ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
  26. //将任务从壮态列表上移除。
  27. ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
  28. //调用函数 prvAddTaskToReadyList()将任务添加到就绪列表中。
  29. prvAddTaskToReadyList( pxTCB );
  30. //判断任务的优先级是否高于当前正在运行的任务,如果是的话需要将 xYieldPending 标记为 pdTRUE,表示需要进行任务切换。
  31. if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) {
  32. xYieldPending = pdTRUE;
  33. }
  34. else
  35. {
  36. mtCOVERAGE_TEST_MARKER();
  37. }
  38. }
  39. /************************************************************************/
  40. /****************************省略部分代码********************************/
  41. /************************************************************************/
  42. //进行任务切换。
  43. if( xYieldPending != pdFALSE )
  44. {
  45. #if( configUSE_PREEMPTION != 0 )
  46. {
  47. //标记在函数 xTaskResumeAll()中进行了任务切换,变量 xAlreadyYielded 用于标记在函数 xTaskResumeAll()中是否有进行任务切换。
  48. xAlreadyYielded = pdTRUE;
  49. }
  50. #endif
  51. //调用函数 taskYIELD_IF_USING_PREEMPTION()进行任务切换,此函数本质上是一个宏,其实最终调用是通过调用函数 portYIELD()来完成任务切换的。
  52. taskYIELD_IF_USING_PREEMPTION();
  53. }
  54. else
  55. {
  56. mtCOVERAGE_TEST_MARKER();
  57. }
  58. }
  59. }
  60. else
  61. {
  62. mtCOVERAGE_TEST_MARKER();
  63. }
  64. }
  65. //退出临界区
  66. taskEXIT_CRITICAL();
  67. //返回变量 xAlreadyYielded,如果为 pdTRUE 的话表示在函数 xTaskResumeAll()中进行了任务切换,如果为 pdFALSE 的话表示没有进行任务切换。
  68. return xAlreadyYielded;
  69. }

设置系统节拍数

此 函 数 在 使 用 FreeRTOS 的 低 功 耗 tickless 模 式 的 时 候 会 用 到 , 即 宏configUSE_TICKLESS_IDLE 为 1。当使能低功耗 tickless 模式以后在执行空闲任务的时候系统时钟节拍中断就会停止运行,系统时钟中断停止运行的这段时间必须得补上,这个工作就是由函数 vTaskStepTick()来完成的,此函数在文件 tasks.c 中有如下定义:

  1. void vTaskStepTick( const TickType_t xTicksToJump )
  2. {
  3. configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );
  4. //函数参数 xTicksToJump 是要加上的时间值,系统节拍计数器 xTickCount 加上这个时间值得到新的系统时间
  5. xTickCount += xTicksToJump;
  6. traceINCREASE_TICK_COUNT( xTicksToJump );
  7. }