freeRTOS原始碼解析4--tasks.c 4

freeManX1807發表於2024-09-10

4.2.9 週期任務用的延遲--xTaskDelayUntil

介面:BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

形參1:pxPreviousWakeTime,上一次喚醒時間,第一次需要用介面xTaskGetTickCount()獲取;
形參2:xTimeIncrement,想要延遲的時間。

返回值:用於判讀任務是否確實需要delay。

freeRTOS原始碼解析4--tasks.c 4
 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

介面雖然較長,但註釋較多,且邏輯較為簡單,直接看程式碼裡的註釋即可。

freeRTOS原始碼解析4--tasks.c 4
  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

還是直接看程式碼註釋,解釋得比較清楚了,介面功能可以顧名思義,邏輯也還好不復雜。

freeRTOS原始碼解析4--tasks.c 4
  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。

下篇再見!

相關文章