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 }
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 }
补个流程图
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 }
这篇就到这里了,内容也比较多,下篇开始vTaskResume和vTaskStartScheduler。
下篇再见!