freeRTOS源码解析4--tasks.c 4

4.2.9 周期任务用的延迟--xTaskDelayUntil

接口:BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

形参1:pxPreviousWakeTime,上一次唤醒时间,第一次需要用接口xTaskGetTickCount()获取;
形参2:xTimeIncrement,想要延迟的时间。

返回值:用于判读任务是否确实需要delay。

 1 BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
 2                                 const TickType_t xTimeIncrement )
 3 {
 4     TickType_t xTimeToWake;
 5     BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
 6 
 7     configASSERT( pxPreviousWakeTime );
 8     configASSERT( ( xTimeIncrement > 0U ) );
 9 
10     vTaskSuspendAll();
11     {
12         /* Minor optimisation. The tick count cannot change in this
13          * block. */
14         const TickType_t xConstTickCount = xTickCount;
15 
16         configASSERT( uxSchedulerSuspended == 1U );
17 
18         /* Generate the tick time at which the task wants to wake. */
19         /* 计算任务下次唤醒的时间 */
20         xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
21 
22         if( xConstTickCount < *pxPreviousWakeTime )
23         {
24             /* The tick count has overflowed since this function was
25              * lasted called.  In this case the only time we should ever
26              * actually delay is if the wake time has also  overflowed,
27              * and the wake time is greater than the tick time.  When this
28              * is the case it is as if neither time had overflowed. */
29             /* tick值已经溢出了, 只有xTimeToWake也溢出并大于xConstTickCount
30              * 时, 才需要延迟 */
31             /* 假设tick是8位的, 当前值为0x5(从FF加到5), pxPreviousWakeTime为0xFD,
32              * 当xTimeIncrement为1时, xTimeToWake未溢出, 当xTimeIncrement为0x15时,
33              * xTimeToWake溢出。 显然当xTimeIncrement为1时, xTimeToWake比
34              * pxPreviousWakeTime和xConstTickCount都大, 但不需要延迟, 因为tick已过,
35              * 当xTimeIncrement为0x15时, xTimeToWake比pxPreviousWakeTime小但比xConstTickCount
36              * 大, tick要等到0x12才到延迟时间, 所以需要进行延迟. */
37             if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
38             {
39                 xShouldDelay = pdTRUE;
40             }
41             else
42             {
43                 mtCOVERAGE_TEST_MARKER();
44             }
45         }
46         else
47         {
48             /* The tick time has not overflowed.  In this case we will
49              * delay if either the wake time has overflowed, and/or the
50              * tick time is less than the wake time. */
51             /* tick没有溢出, 那么xTimeToWake溢出或比xConstTickCount小,
52              * 必然是需要延迟的. */
53             if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
54             {
55                 xShouldDelay = pdTRUE;
56             }
57             else
58             {
59                 mtCOVERAGE_TEST_MARKER();
60             }
61         }
62 
63         /* Update the wake time ready for the next call. */
64         *pxPreviousWakeTime = xTimeToWake;
65 
66         if( xShouldDelay != pdFALSE )
67         {
68             /* prvAddCurrentTaskToDelayedList() needs the block time, not
69              * the time to wake, so subtract the current tick count. */
70             /* 延迟列表记录的是阻塞时间而不是唤醒时间, 所以需要减去tick. */
71             prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
72         }
73         else
74         {
75             mtCOVERAGE_TEST_MARKER();
76         }
77     }
78     xAlreadyYielded = xTaskResumeAll();
79 
80     /* Force a reschedule if xTaskResumeAll has not already done so, we may
81      * have put ourselves to sleep. */
82     if( xAlreadyYielded == pdFALSE )
83     {
84         taskYIELD_WITHIN_API();
85     }
86     else
87     {
88         mtCOVERAGE_TEST_MARKER();
89     }
90 
91     return xShouldDelay;
92 }
xTaskDelayUntil

 4.2.10 成对的信息获取接口,一般的和带有FromISR后缀的

这类接口比较简单,主要用于获取TCB中的某些参数,这里挑选任务优先级获取接口为例,重点在于带有FromISR的接口。一般的接口很简单,这里就直接放出来即可。

 1 UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask )
 2 {
 3     TCB_t const * pxTCB;
 4     UBaseType_t uxReturn;
 5 
 6     taskENTER_CRITICAL();
 7     {
 8         /* If null is passed in here then it is the priority of the task
 9          * that called uxTaskPriorityGet() that is being queried. */
10         pxTCB = prvGetTCBFromHandle( xTask );
11         uxReturn = pxTCB->uxPriority;
12     }
13     taskEXIT_CRITICAL();
14 
15     return uxReturn;
16 }
 1 UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask )
 2 {
 3     TCB_t const * pxTCB;
 4     UBaseType_t uxReturn;
 5     UBaseType_t uxSavedInterruptStatus;
 6 
 7     /* RTOS ports that support interrupt nesting have the concept of a
 8      * maximum  system call (or maximum API call) interrupt priority.
 9      * Interrupts that are  above the maximum system call priority are keep
10      * permanently enabled, even when the RTOS kernel is in a critical section,
11      * but cannot make any calls to FreeRTOS API functions.  If configASSERT()
12      * is defined in FreeRTOSConfig.h then
13      * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
14      * failure if a FreeRTOS API function is called from an interrupt that has
15      * been assigned a priority above the configured maximum system call
16      * priority.  Only FreeRTOS functions that end in FromISR can be called
17      * from interrupts  that have been assigned a priority at or (logically)
18      * below the maximum system call interrupt priority.  FreeRTOS maintains a
19      * separate interrupt safe API to ensure interrupt entry is as fast and as
20      * simple as possible.  More information (albeit Cortex-M specific) is
21      * provided on the following link:
22      * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
23     /* 检查调用带有FromISR后缀的中断优先级是否高于
24      * configMAX_SYSCALL_INTERRUPT_PRIORITY, 是的话会进入assert */
25     portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
26 
27     /* MISRA Ref 4.7.1 [Return value shall be checked] */
28     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */
29     /* coverity[misra_c_2012_directive_4_7_violation] */
30     /* 获取当前中断屏蔽优先级, 并设置中断屏蔽优先级为configMAX_SYSCALL_INTERRUPT_PRIORITY
31      * 相当于进入了临界区并返回了当前中断屏蔽优先级 */
32     uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR();
33     {
34         /* If null is passed in here then it is the priority of the calling
35          * task that is being queried. */
36         pxTCB = prvGetTCBFromHandle( xTask );
37         uxReturn = pxTCB->uxPriority;
38     }
39     /* 将保存的中断屏蔽优先级设置回去, 相当于退出临界区 */
40     taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
41 
42     return uxReturn;
43 }

4.2.11 设置任务优先级--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     /* Ensure the new priority is valid. */
 10     if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
 11     {
 12         uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
 13     }
 14     else
 15     {
 16         mtCOVERAGE_TEST_MARKER();
 17     }
 18 
 19     taskENTER_CRITICAL();
 20     {
 21         /* If null is passed in here then it is the priority of the calling
 22          * task that is being changed. */
 23         pxTCB = prvGetTCBFromHandle( xTask );
 24 
 25         #if ( configUSE_MUTEXES == 1 )
 26         {
 27             uxCurrentBasePriority = pxTCB->uxBasePriority;
 28         }
 29         #else
 30         {
 31             uxCurrentBasePriority = pxTCB->uxPriority;
 32         }
 33         #endif
 34 
 35         if( uxCurrentBasePriority != uxNewPriority )
 36         {
 37             /* The priority change may have readied a task of higher
 38              * priority than a running task. */
 39             if( uxNewPriority > uxCurrentBasePriority )
 40             {
 41                 /* 提升优先级 */
 42                 #if ( configNUMBER_OF_CORES == 1 )
 43                 {
 44                     if( pxTCB != pxCurrentTCB )
 45                     {
 46                         /* The priority of a task other than the currently
 47                          * running task is being raised.  Is the priority being
 48                          * raised above that of the running task? */
 49                         /* 设置的优先级比当前运行的任务高, 需要yield */
 50                         if( uxNewPriority > pxCurrentTCB->uxPriority )
 51                         {
 52                             xYieldRequired = pdTRUE;
 53                         }
 54                         else
 55                         {
 56                             mtCOVERAGE_TEST_MARKER();
 57                         }
 58                     }
 59                     else
 60                     {
 61                         /* The priority of the running task is being raised,
 62                          * but the running task must already be the highest
 63                          * priority task able to run so no yield is required. */
 64                     }
 65                 }
 66                 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
 67             }
 68             else if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
 69             {
 70                 /* Setting the priority of a running task down means
 71                  * there may now be another task of higher priority that
 72                  * is ready to execute. */
 73                 /* 降低当前运行任务的优先级, 则更高优先级的就绪任务需要抢占 */
 74                 #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
 75                     if( pxTCB->xPreemptionDisable == pdFALSE )
 76                 #endif
 77                 {
 78                     xYieldRequired = pdTRUE;
 79                 }
 80             }
 81             else
 82             {
 83                 /* Setting the priority of any other task down does not
 84                  * require a yield as the running task must be above the
 85                  * new priority of the task being modified. */
 86             }
 87 
 88             /* Remember the ready list the task might be referenced from
 89              * before its uxPriority member is changed so the
 90              * taskRESET_READY_PRIORITY() macro can function correctly. */
 91             /* 获取任务当前的优先级, 有可能是继承来的优先级 */
 92             uxPriorityUsedOnEntry = pxTCB->uxPriority;
 93 
 94             #if ( configUSE_MUTEXES == 1 )
 95             {
 96                 /* Only change the priority being used if the task is not
 97                  * currently using an inherited priority or the new priority
 98                  * is bigger than the inherited priority. */
 99                 /* 只有非继承来的优先级, 或提升优先级才可以直接修改pxTCB->uxPriority */
100                 if( ( pxTCB->uxBasePriority == pxTCB->uxPriority ) || ( uxNewPriority > pxTCB->uxPriority ) )
101                 {
102                     pxTCB->uxPriority = uxNewPriority;
103                 }
104                 else
105                 {
106                     mtCOVERAGE_TEST_MARKER();
107                 }
108 
109                 /* The base priority gets set whatever. */
110                 pxTCB->uxBasePriority = uxNewPriority;
111             }
112             #else /* if ( configUSE_MUTEXES == 1 ) */
113             {
114                 pxTCB->uxPriority = uxNewPriority;
115             }
116             #endif /* if ( configUSE_MUTEXES == 1 ) */
117 
118             /* Only reset the event list item value if the value is not
119              * being used for anything else. */
120             /* xEventListItem的值未被使用, 才允许更新这个值 */
121             if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == ( ( TickType_t ) 0U ) )
122             {
123                 listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) );
124             }
125             else
126             {
127                 mtCOVERAGE_TEST_MARKER();
128             }
129 
130             /* If the task is in the blocked or suspended list we need do
131              * nothing more than change its priority variable. However, if
132              * the task is in a ready list it needs to be removed and placed
133              * in the list appropriate to its new priority. */
134             /* 如果任务处于就绪列表, 则需要将其放到修改后的优先级就绪列表 */
135             if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
136             {
137                 /* The task is currently in its ready list - remove before
138                  * adding it to its new ready list.  As we are in a critical
139                  * section we can do this even if the scheduler is suspended. */
140                 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
141                 {
142                     /* It is known that the task is in its ready list so
143                      * there is no need to check again and the port level
144                      * reset macro can be called directly. */
145                     portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
146                 }
147                 else
148                 {
149                     mtCOVERAGE_TEST_MARKER();
150                 }
151 
152                 prvAddTaskToReadyList( pxTCB );
153             }
154             else
155             {
156                 #if ( configNUMBER_OF_CORES == 1 )
157                 {
158                     mtCOVERAGE_TEST_MARKER();
159                 }
160                 #endif
161             }
162 
163             if( xYieldRequired != pdFALSE )
164             {
165                 /* The running task priority is set down. Request the task to yield. */
166                 /* 开始yield, 但是在退出临界区后才会进入sv中断? */
167                 taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB );
168             }
169             else
170             {
171                 mtCOVERAGE_TEST_MARKER();
172             }
173 
174             /* Remove compiler warning about unused variables when the port
175              * optimised task selection is not being used. */
176             ( void ) uxPriorityUsedOnEntry;
177         }
178     }
179     taskEXIT_CRITICAL();
180 }
vTaskPrioritySet

  补个流程图

 

4.2.12 挂起任务--vTaskSuspend

还是直接看代码注释,解释得比较清楚了,接口功能可以顾名思义,逻辑也还好不复杂。

  1 void vTaskSuspend( TaskHandle_t xTaskToSuspend )
  2 {
  3     TCB_t * pxTCB;
  4 
  5     taskENTER_CRITICAL();
  6     {
  7         /* If null is passed in here then it is the running task that is
  8          * being suspended. */
  9         pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
 10 
 11         /* Remove task from the ready/delayed list and place in the
 12          * suspended list. */
 13         if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
 14         {
 15             taskRESET_READY_PRIORITY( pxTCB->uxPriority );
 16         }
 17         else
 18         {
 19             mtCOVERAGE_TEST_MARKER();
 20         }
 21 
 22         /* Is the task waiting on an event also? */
 23         /* 任务挂起, 任务就不管是否在等待事件 */
 24         if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
 25         {
 26             ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
 27         }
 28         else
 29         {
 30             mtCOVERAGE_TEST_MARKER();
 31         }
 32 
 33         vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
 34 
 35         /* 如果在等待通知, 也直接退出等待 */
 36         #if ( configUSE_TASK_NOTIFICATIONS == 1 )
 37         {
 38             BaseType_t x;
 39 
 40             for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )
 41             {
 42                 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
 43                 {
 44                     /* The task was blocked to wait for a notification, but is
 45                      * now suspended, so no notification was received. */
 46                     pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
 47                 }
 48             }
 49         }
 50         #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
 51     }
 52     taskEXIT_CRITICAL();
 53 
 54     #if ( configNUMBER_OF_CORES == 1 )
 55     {
 56         UBaseType_t uxCurrentListLength;
 57 
 58         if( xSchedulerRunning != pdFALSE )
 59         {
 60             /* Reset the next expected unblock time in case it referred to the
 61              * task that is now in the Suspended state. */
 62             /* 如果下个解除阻塞的时间对应的任务正好是挂起的任务,
 63              * 无论如何重置一下下个解除阻塞的时间 */
 64             taskENTER_CRITICAL();
 65             {
 66                 prvResetNextTaskUnblockTime();
 67             }
 68             taskEXIT_CRITICAL();
 69         }
 70         else
 71         {
 72             mtCOVERAGE_TEST_MARKER();
 73         }
 74 
 75         if( pxTCB == pxCurrentTCB )
 76         {
 77             if( xSchedulerRunning != pdFALSE )
 78             {
 79                 /* The current task has just been suspended. */
 80                 /* 当前运行的任务挂起, 需要yield */
 81                 configASSERT( uxSchedulerSuspended == 0 );
 82                 portYIELD_WITHIN_API();
 83             }
 84             else
 85             {
 86                 /* The scheduler is not running, but the task that was pointed
 87                  * to by pxCurrentTCB has just been suspended and pxCurrentTCB
 88                  * must be adjusted to point to a different task. */
 89 
 90                 /* Use a temp variable as a distinct sequence point for reading
 91                  * volatile variables prior to a comparison to ensure compliance
 92                  * with MISRA C 2012 Rule 13.2. */
 93                 uxCurrentListLength = listCURRENT_LIST_LENGTH( &xSuspendedTaskList );
 94 
 95                 if( uxCurrentListLength == uxCurrentNumberOfTasks )
 96                 {
 97                     /* No other tasks are ready, so set pxCurrentTCB back to
 98                      * NULL so when the next task is created pxCurrentTCB will
 99                      * be set to point to it no matter what its relative priority
100                      * is. */
101                     /* 所有创建的任务处于挂起状态, 只能等新任务创建了 */
102                     pxCurrentTCB = NULL;
103                 }
104                 else
105                 {
106                     /* 切换下上下文, 因为挂起的是当前任务, 也有可能就绪列表为空,
107                      * 会导致pxCurrentTCB = idle, 否则会选择就绪列表中优先级最高
108                      * 任务, 这个接口在这里只是设置了pxCurrentTCB参数, 并pend了
109                      * yield, 因为当前调度器处于暂停状态 */
110                     vTaskSwitchContext();
111                 }
112             }
113         }
114         else
115         {
116             mtCOVERAGE_TEST_MARKER();
117         }
118     }
119     #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
120 }
vTaskSuspend

 

  这篇就到这里了,内容也比较多,下篇开始vTaskResume和vTaskStartScheduler。

下篇再见!

posted @ 2024-09-10 22:51  freeManX1807  阅读(53)  评论(0编辑  收藏  举报