freeRTOS 新建任務


xTaskCreate創建任務
|
+----prvInitialiseNewTask 初始化任務TCB
|    |
|    +----pxPortInitialiseStack初始化任務堆棧
|
+----prvAddNewTaskToReadyList 把新任務加入到任務就緒列表

最后引用了一篇文章,里面介紹了freeRTOS里的全局變量的含義

 

 

 

任務控制塊TCB

 1 typedef struct tskTaskControlBlock
 2 {
 3     volatile StackType_t *pxTopOfStack; //任務堆棧棧頂
 4     #if ( portUSING_MPU_WRAPPERS == 1 )
 5     xMPU_SETTINGSxMPUSettings; //MPU 相關設置
 6     #endif
 7     ListItem_t xStateListItem; //狀態列表項
 8     ListItem_t xEventListItem; //事件列表項
 9     UBaseType_t uxPriority; //任務優先級
10     StackType_t *pxStack;   //任務堆棧起始地址
11     char pcTaskName[ configMAX_TASK_NAME_LEN ];//任務名字
12 #if ( portSTACK_GROWTH > 0 ) 13 StackType_t *pxEndOfStack; //任務堆棧棧底 14 #endif
15 #if ( portCRITICAL_NESTING_IN_TCB == 1 ) 16 UBaseType_t uxCriticalNesting; //臨界區嵌套深度 17 #endif 18
#if ( configUSE_TRACE_FACILITY == 1 ) //trace 或 debug 的時候用到 19 UBaseType_t uxTCBNumber; 20 UBaseType_t uxTaskNumber; configUSE_TRACE_FACILITY定義時,表示freertos開啟了trace功能,支持trace task states,系統運行信息,每個任務的運行時間。uxTCBNumber協助進行信息打印。 21 #endif 22
#if ( configUSE_MUTEXES == 1 ) 23 UBaseType_t uxBasePriority; //任務基礎優先級,優先級反轉之前的優先級 24 UBaseType_t uxMutexesHeld; //任務獲取到的互斥信號量個數 25 #endif
26 #if ( configUSE_APPLICATION_TASK_TAG == 1 ) //任務標簽 27 TaskHookFunction_t pxTaskTag; 28 #endif 29 #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) //與本地存儲有關 30 void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; 32 #endif
33 #if( configGENERATE_RUN_TIME_STATS == 1 ) 34 uint32_t ulRunTimeCounter; //用來記錄任務運行總時間,可以得到CPU占用率 35 #endif
36 #if ( configUSE_NEWLIB_REENTRANT == 1 ) 37 struct _reent xNewLib_reent; //定義一個 newlib 結構體變量,可重入性方面的東東。。 38 #endif 39 #if( configUSE_TASK_NOTIFICATIONS == 1 )//任務通知相關變量 40 volatile uint32_t ulNotifiedValue; //任務通知值 41 volatile uint8_t ucNotifyState; //任務通知狀態 42 #endif 43 #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 44 //用來標記任務是動態創建的還是靜態創建的,如果是靜態創建的此變量就為 pdTURE, 45 //如果是動態創建的就為 pdFALSE 46 uint8_t ucStaticallyAllocated; 47 #endif 48 #if( INCLUDE_xTaskAbortDelay == 1 ) 49 uint8_t ucDelayAborted; 50 #endif 51 } tskTCB; 52 //新版本的 FreeRTOS 任務控制塊重命名為 TCB_t,但是本質上還是 tskTCB,主要是為了兼容 53 //舊版本的應用。 54 typedef tskTCB TCB_t;

 

任務創建函數

 1     BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
 2                             const char * const pcName,
 3                             const uint16_t usStackDepth,
 4                             void * const pvParameters,
 5                             UBaseType_t uxPriority,
 6                             TaskHandle_t * const pxCreatedTask ) 
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */ 7 { 8 TCB_t *pxNewTCB; 9 BaseType_t xReturn; 10 11 /* If the stack grows down then allocate the stack then the TCB so the stack 12 does not grow into the TCB. Likewise if the stack grows up then allocate 13 the TCB then the stack. 向上增長堆棧*/ 14 #if( portSTACK_GROWTH > 0 ) 15 { 16 /* Allocate space for the TCB. Where the memory comes from depends on 17 the implementation of the port malloc function and whether or not static 18 allocation is being used. */ 19 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 20 21 if( pxNewTCB != NULL ) 22 { 23 /* Allocate space for the stack used by the task being created. 24 The base of the stack memory stored in the TCB so the task can 25 be deleted later if required. */ 26 pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
/*lint !e961 MISRA exception as the casts are only redundant for some ports. */ 27 28 if( pxNewTCB->pxStack == NULL ) 29 { 30 /* Could not allocate the stack. Delete the allocated TCB. */ 31 vPortFree( pxNewTCB ); 32 pxNewTCB = NULL; 33 } 34 } 35 } 36 #else /* portSTACK_GROWTH 只關注下邊部分*/ 37 { 38 StackType_t *pxStack; //uint32_t 39 40 /* Allocate space for the stack used by the task being created. 申請堆棧空間*/ 41 pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
/*lint !e961 MISRA exception as the casts are only redundant for some ports. */ 42 43 if( pxStack != NULL ) 44 { 45 /* Allocate space for the TCB. 申請任務控制塊空間*/ 46 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
/*lint !e961 MISRA exception as the casts are only redundant for some paths. */ 47 48 if( pxNewTCB != NULL ) 49 { 50 /* Store the stack location in the TCB. */ 51 pxNewTCB->pxStack = pxStack; 52 } 53 else 54 { 55 /* The stack cannot be used as the TCB was not created. Free 56 it again. */ 57 vPortFree( pxStack ); 58 } 59 } 60 else 61 { 62 pxNewTCB = NULL; 63 } 64 } 65 #endif /* portSTACK_GROWTH */ 66 67 if( pxNewTCB != NULL ) 68 { 69 #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 70 { 71 /* Tasks can be created statically or dynamically, so note this 72 task was created dynamically in case it is later deleted. 標記任務是動態創建的*/ 73 pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; 74 } 75 #endif /* configSUPPORT_STATIC_ALLOCATION */ 76 77 prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); 78 prvAddNewTaskToReadyList( pxNewTCB ); 79 xReturn = pdPASS; 80 } 81 else 82 { 83 xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; 84 } 85 86 return xReturn; 87 }

 

 

一、初始化新任務的TCB:

  1 static void prvInitialiseNewTask(   TaskFunction_t pxTaskCode,
  2                                     const char * const pcName,
  3                                     const uint32_t ulStackDepth,
  4                                     void * const pvParameters,
  5                                     UBaseType_t uxPriority,
  6                                     TaskHandle_t * const pxCreatedTask,
  7                                     TCB_t *pxNewTCB,
  8                                     const MemoryRegion_t * const xRegions ) 
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */ 9 { 10 StackType_t *pxTopOfStack; 11 UBaseType_t x; 12 13 #if( portUSING_MPU_WRAPPERS == 1 ) 【略】 14 /* Should the task be created in privileged mode? */ 15 BaseType_t xRunPrivileged; 16 if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) 17 { 18 xRunPrivileged = pdTRUE; 19 } 20 else 21 { 22 xRunPrivileged = pdFALSE; 23 } 24 uxPriority &= ~portPRIVILEGE_BIT; 25 #endif /* portUSING_MPU_WRAPPERS == 1 */ 26 27 /* Avoid dependency on memset() if it is not required. 堆棧溢出檢測、追蹤功能打開時,使用已知值填充stack*/ 28 #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) 29 { 30 /* Fill the stack with a known value to assist debugging. */ 31 ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); 32 } 33 #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ 34 35 /* Calculate the top of stack address. This depends on whether the stack 36 grows from high memory to low (as per the 80x86) or vice versa. 37 portSTACK_GROWTH is used to make the result positive or negative as required 38 by the port. */ 39 #if( portSTACK_GROWTH < 0 ) 【略】 40 { 41 pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); 42 pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 */ 43 44 /* Check the alignment of the calculated top of stack is correct. */ 45 configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); 46 } 47 #else /* portSTACK_GROWTH */ 48 { 49 pxTopOfStack = pxNewTCB->pxStack; 50 51 /* Check the alignment of the stack buffer is correct. */ 52 configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); 53 54 /* The other extreme of the stack space is required if stack checking is 55 performed. */ 56 pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); //計算棧頂地址 57 } 58 #endif /* portSTACK_GROWTH */ 59 60 /* Store the task name in the TCB. 拷貝任務名字*/ 61 for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) 62 { 63 pxNewTCB->pcTaskName[ x ] = pcName[ x ]; 64 65 /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than 66 configMAX_TASK_NAME_LEN characters just in case the memory after the 67 string is not accessible (extremely unlikely). */ 68 if( pcName[ x ] == 0x00 ) 69 { 70 break; 71 } 72 else 73 { 74 mtCOVERAGE_TEST_MARKER(); 75 } 76 } 77 78 /* Ensure the name string is terminated in the case that the string length 79 was greater or equal to configMAX_TASK_NAME_LEN. */ 80 pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; 81 82 /* This is used as an array index so must ensure it's not too large. First 83 remove the privilege bit if one is present. */ 任務優先級 84 if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) 85 { 86 uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; 87 } 88 else 89 { 90 mtCOVERAGE_TEST_MARKER(); 91 } 92 93 pxNewTCB->uxPriority = uxPriority; 94 #if ( configUSE_MUTEXES == 1 ) 【略】 95 { 96 pxNewTCB->uxBasePriority = uxPriority; 97 pxNewTCB->uxMutexesHeld = 0; 98 } 99 #endif /* configUSE_MUTEXES */ 100 101 vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); 初始化兩個列表項 102 vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); 103 104 /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get 105 back to the containing TCB from a generic item in a list. */ 106 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); 設置列表項的Owner 107 108 /* Event lists are always in priority order. */ 事件列表是按優先級排序的? 109 listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 */ 110 listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); 111 112 #if ( portCRITICAL_NESTING_IN_TCB == 1 ) 【臨界區嵌套深度】 113 { 114 pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; 115 } 116 #endif /* portCRITICAL_NESTING_IN_TCB */ 117 118 #if ( configUSE_APPLICATION_TASK_TAG == 1 ) 【任務標簽】 119 { 120 pxNewTCB->pxTaskTag = NULL; 121 } 122 #endif /* configUSE_APPLICATION_TASK_TAG */ 123 124 #if ( configGENERATE_RUN_TIME_STATS == 1 ) 【運行時間統計】 125 { 126 pxNewTCB->ulRunTimeCounter = 0UL; 127 } 128 #endif /* configGENERATE_RUN_TIME_STATS */ 129 130 #if ( portUSING_MPU_WRAPPERS == 1 ) 【MPU保護】 131 { 132 vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); 133 } 134 #else 135 { 136 /* Avoid compiler warning about unreferenced parameter. */ 137 ( void ) xRegions; 138 } 139 #endif 140 141 #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) 【線程本地存儲指針】 142 { 143 for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) 144 { 145 pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; 146 } 147 } 148 #endif 149 150 #if ( configUSE_TASK_NOTIFICATIONS == 1 ) 【任務通知】 151 { 152 pxNewTCB->ulNotifiedValue = 0; 153 pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; 154 } 155 #endif 156 157 #if ( configUSE_NEWLIB_REENTRANT == 1 ) 【略】 158 { 159 /* Initialise this task's Newlib reent structure. */ 160 _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); 161 } 162 #endif 163 164 #if( INCLUDE_xTaskAbortDelay == 1 ) 【略】 165 { 166 pxNewTCB->ucDelayAborted = pdFALSE; 167 } 168 #endif 169 170 /* Initialize the TCB stack to look as if the task was already running, 171 but had been interrupted by the scheduler. The return address is set 172 to the start of the task function. Once the stack has been initialised 173 the top of stack variable is updated. */ 174 #if( portUSING_MPU_WRAPPERS == 1 ) 175 { 176 pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); 177 } 178 #else /* portUSING_MPU_WRAPPERS */ 179 { 180 pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); 初始化任務堆棧 181 } 182 #endif /* portUSING_MPU_WRAPPERS */ 183 184 if( ( void * ) pxCreatedTask != NULL ) 185 { 186 /* Pass the handle out in an anonymous way. The handle can be used to 187 change the created task's priority, delete the created task, etc.*/ 188 *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; 轉換為任務句柄 189 } 190 else 191 { 192 mtCOVERAGE_TEST_MARKER(); 193 } 194 } 195 /*-----------------------------------------------------------*/

 

初始化新任務TCB  調用  初始化堆棧:

 1 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
 2 {
 3     /* Simulate the stack frame as it would be created by a context switch
 4     interrupt. */  
 5 
 6     /* Offset added to account for the way the MCU uses the stack on entry/exit
 7     of interrupts, and to ensure alignment. */
 8     pxTopOfStack--;
 9 
10     *pxTopOfStack = portINITIAL_XPSR;    /* xPSR */
11     pxTopOfStack--;
12     *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;    /* PC */
13     pxTopOfStack--;
14     *pxTopOfStack = ( StackType_t ) prvTaskExitError;    /* LR */
15 
16     /* Save code space by skipping register initialisation. */
17     pxTopOfStack -= 5;    /* 跳過 R12, R3, R2 and R1. */
18     *pxTopOfStack = ( StackType_t ) pvParameters;    /* R0 */
19 
20     /* A save method is being used that requires each task to maintain its
21     own exec return value. */
22     pxTopOfStack--;
23     *pxTopOfStack = portINITIAL_EXEC_RETURN;
24 
25     pxTopOfStack -= 8;    /* 跳過 R11, R10, R9, R8, R7, R6, R5 and R4. */
26 
27     return pxTopOfStack;
28 }

在port.c中

 

原子freeRTOS資料:

堆棧是用來在進行上下文切換的時候保存現場的,一般在新創建好一個堆棧以后會對其先
進行初始化處理,即Cortex-M 內核的某些寄存器賦初值。這些初值就保存在任務堆棧中,保
存的順序按照: xPSRR15(PC)R14(LR)R12R3~R0R11~R14
(1)、寄存器 xPSR 值為 portINITIAL_XPSR,其值為 0x01000000xPSR Cortex-M4 的一
個內核寄存器,叫做程序狀態寄存器,0x01000000 表示這個寄存器的 bit24 1,表示處於 Thumb
狀態,即使用的 Thumb 指令。
(2)、寄存器 PC 初始化為任務函數 pxCode
(3)、寄存器 LR 初始化為函數 prvTaskExitError
(4)、跳過 4 個寄存器, R12R3R2R1,這四個寄存器不初始化。
(5)、寄存器 R0 初始化為 pvParameters,一般情況下,函數調用會將 R0~R3 作為輸入參數,
R0 也可用作返回結果,如果返回值為 64 位,則 R1 也會用於返回結果,這里的 pvParameters
作為任務函數的參數,保存在寄存器 R0 中。
(6)、保存 EXC_RETURN 值,用於設置退出 SVC PendSV 中斷時,處理器應該處於什
么狀態。處理器進入異常或中斷服務程序(ISR)時,鏈接寄存器 R14(LR)的數值會被更新為
EXC_RETURN 數值,之后該數值會在異常處理結束時觸發異常返回。這里人為的設置為
0XFFFFFFD,表示退出異常以后 CPU 進入線程模式並且使用進程棧!關於 EXC_RETURN
更多信息請參考《權威指南》“第 8 章 深入了解異常處理”。
(7)、跳過 8 個寄存器, R11R10R8R7R6R5R4
經過上面的初始化之后,此時的堆棧結果如圖所示:

 

 

 去看看一下EXC_RETURN是什么東西。

       (1)有中斷請求時,中斷控制器NVIC獲取中斷源硬件設備的中斷向量號,並通過識別的中斷向量號,將對應硬件中斷源模塊的中斷狀態寄存器中的“中斷活動位”置位。
  (2)響應中斷的時候,首先要做的就是保存中斷現場:依次將PC、xPSR、R0~R3、R12、LR這個8個寄存器壓入任務堆棧(任務棧的棧頂指針使用PSP),這一步是由硬件壓入的,注意這里的順序。
  (3)堆棧指針切換為使用MSP進行堆棧操作,系統計算EXC_RETURN值①(EXC_RETURN值只是用於區分返回后換入Handler模式還是線程模式。②以及棧操作使用MSP還是PSP)並將它賦值給LR,此時的LR值已經被修改為EXC_RETURN供異常在返回時判斷使用,更新中斷號程序狀態寄存器IPSR中的低8位為新的中斷號,更換程序計數器PC值為中斷服務例程的入口地址。
  (4)執行中斷服務例程,當執行PUSH {LR}語句時將EXC_RETURN值壓入堆棧,中斷服務例程結束時,更新NVIC寄存器,將中斷狀態寄存器中對應的“中斷活動位”清除,接着執行POP {PC}就是將EXC_RETURN彈出到PC,此時會將MSP切換為PSP,CM4中使用把EXC_RETURN寫入PC來觸發中斷返回動作序列,中斷返回動作序列就是將之前壓入的8個寄存器彈出。把EXC_RETURN寫入PC有還有兩種形式,一種是當LR寄存器中存放了EXC_RETURN時,使用BX LR即可返回,另外一種就是將PC作為目的寄存器,使用LDR或者LDM指令也可以啟動中斷返回序列。此時會將之前壓入堆棧的PC、xPSR、R0~R3、R12、LR這8個寄存器已經全部恢復。

地址:http://jeremybai.github.io/blog/2014/03/03/mqx-interrupt-3

 

 

 

二、初始化完TCB,初始化完任務Stack,接下來是:把任務加入到任務列表中。

FreeRTOS 使用不同的列表,表示任務的不同狀態,在文件 tasks.c 中就定義了多個列表和指向列表的指針來完成不同的功能 

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
PRIVILEGED_DATA static List_t xDelayedTaskList1;  兩個延時任務列表,一個用於記錄已經延時溢出的任務列表
PRIVILEGED_DATA static List_t xDelayedTaskList2;  
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;
PRIVILEGED_DATA static List_t xPendingReadyList;
列表數組 pxReadyTasksLists[]就是任務就緒列表,數組大小為 configMAX_PRIORITIES,
也就是說一個優先級一個列表,這樣相同優先級的任務就使用一個列表。

把新建的任務加入到就緒任務列表中。

 1 static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
 2 {
 3     /* Ensure interrupts don't access the task lists while the lists are being updated. */
 5     taskENTER_CRITICAL();
 6     {
 7         uxCurrentNumberOfTasks++;
 8         if( pxCurrentTCB == NULL )  全局變量,當前沒有其他任務
 9         {
10             /* There are no other tasks, or all the other tasks are in
11             the suspended state - make this the current task. */
12             pxCurrentTCB = pxNewTCB;  更新這個全局變量
         更新之后的動作,應該是調度器進行的操作。
13 14 if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) 15 { 16 /* This is the first task to be created so do the preliminary 初步 17 initialisation required. We will not recover if this call 18 fails, but we will report the failure. */ 19 prvInitialiseTaskLists(); 初始化任務列表 20 } 21 else 22 { 23 mtCOVERAGE_TEST_MARKER(); 24 } 25 } 26 else 已經有其他任務 27 { 28 /* If the scheduler is not already running, make this task the 29 current task if it is the highest priority task to be created 30 so far. */ 31 if( xSchedulerRunning == pdFALSE ) 調度器沒運行 32 { 33 if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) 34 { 35 pxCurrentTCB = pxNewTCB; 新加入的任務優先級高的話,則更新全局變量為pxNewTCB 36 } 37 else 38 { 39 mtCOVERAGE_TEST_MARKER(); 40 } 41 } 42 else 43 { 44 mtCOVERAGE_TEST_MARKER(); 45 } 46 } 47 48 uxTaskNumber++; trace和debug相關的,TCB內有此變量。 49 50 #if ( configUSE_TRACE_FACILITY == 1 ) 51 { 52 /* Add a counter into the TCB for tracing only. */ 53 pxNewTCB->uxTCBNumber = uxTaskNumber; 54 } 55 #endif /* configUSE_TRACE_FACILITY */ 56 traceTASK_CREATE( pxNewTCB ); 57 58 prvAddTaskToReadyList( pxNewTCB ); 加入任務就緒列表 59 60 portSETUP_TCB( pxNewTCB ); 沒動作 61 } 62 taskEXIT_CRITICAL(); 63 64 if( xSchedulerRunning != pdFALSE ) 調度器正在運行 65 { 66 /* If the created task is of a higher priority than the current task 67 then it should run now. */ 68 if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) 69 { 70 taskYIELD_IF_USING_PREEMPTION(); 而且新加入的任務優先級高,則置位PendSV,完成任務切換 71 } 72 else 73 { 74 mtCOVERAGE_TEST_MARKER(); 75 } 76 } 77 else 78 { 79 mtCOVERAGE_TEST_MARKER(); 80 } 81 }

如果新建的任務要加入的任務就緒列表中,沒有其他任務:則把新建任務 更新到 pxCurrentTCB中。並初始化所有的幾個任務列表。

如果新建的任務要加入的任務就緒列表中,有其他任務&&

1. 新建任務優先級最高:則把新建任務 更新到pxCurrentTCB中。

2. 新建任務優先級不是最高:不更新pxCurrentTCB

 

新建任務加入任務就緒表。

最后,調度器正在運行的話,

1. 新建任務優先級最高,則觸發任務切換。

2. 新建任務優先級不是最高,不動作。

 

 

其中涉及到的:初始化所有的幾個任務列表。

static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;

    for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
    {
        vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );  初始化所有優先級對應的“任務就緒列表”
    }

    vListInitialise( &xDelayedTaskList1 );  初始化兩個延時任務列表
    vListInitialise( &xDelayedTaskList2 );
    vListInitialise( &xPendingReadyList );  初始化已經就緒,但還沒加入到就緒任務列表的,任務列表

    #if ( INCLUDE_vTaskDelete == 1 )
    {
        vListInitialise( &xTasksWaitingTermination );  等待銷毀的任務列表
    }
    #endif /* INCLUDE_vTaskDelete */

    #if ( INCLUDE_vTaskSuspend == 1 )
    {
        vListInitialise( &xSuspendedTaskList );  掛起任務列表
    }
    #endif /* INCLUDE_vTaskSuspend */

    /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList
    using list2. */
    pxDelayedTaskList = &xDelayedTaskList1;           初始化兩個指針
    pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
/*-----------------------------------------------------------*/

和,任務加入就緒任務列表

/*
 * Place the task represented by pxTCB into the appropriate ready list for
 * the task.  It is inserted at the end of the list.
 */
#define prvAddTaskToReadyList( pxTCB )                                                                \
    traceMOVED_TASK_TO_READY_STATE( pxTCB );                                                          \
    taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );      見后                          \
把任務狀態列表項,加入到“相應優先級的”任務就緒列表中。 vListInsertEnd(
&( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) /*-----------------------------------------------------------*/

taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );   

刪除任務”分析里面,也會調用這個API,目的是維護一個變量,

這個變量的功能:

1. 在使用通用方法找到最高優先級任務時,它用來記錄最高優先級任務的優先級。

2. 在使用硬件方法找到最高優先級任務時,它的每一位(共32bit)的狀態代表這個優先級上邊,有沒有就緒的任務。

找到最高優先級任務的方法,見“任務切換”一節。

 

 

 

========================================================

找到一篇文章對於freeRTOS內核的解析非常的到位,這里貼出地址:

http://www.tk4479.net/jorhai

以下內容來自上邊的地址:

首先介紹下任務存在下面幾種狀態 

運行:此時CPU正在運行任務。 

就緒:ready,已經具備執行條件,但是需要CPU進行調度,才能成為運行任務。 

阻塞:如果任務當前正在等待某個時間點、事件或信號量,這個任務處於阻塞狀態。

  比如一個任務調用vTaskDelay()后會阻塞到延時周期到為止。

  任務也可能阻塞在隊列或信號量事件上。

  進入阻塞狀態的任務通常有一個“超時”周期,當事件超時后解除阻塞。 

掛起:調用vTaskSuspend()可以使任務進入掛起狀態。這些任務無法被調度器調度到,除非調用xTaskResume()將任務從掛起狀態移除。

Freertos使用TaskHandle_t 標識任務句柄:

typedef void * TaskHandle_t;

TaskHandle_t 其實指向tskTCB,標識一個任務的詳細信息和運行時的堆棧等。 

 

 

task.c中的關鍵全局變量:

  • pxCurrentTCB:記錄現在運行的任務;
  • pxReadyTasksLists:記錄處於ready狀態,等待被調度運行的任務,這是一個鏈表數組。就緒任務列表的數組個數,就是優先級的個數,在調度時可以從優先級高的readylist中先進行調度。
  • xDelayedTaskList1:定義了一個delay的task鏈表。
  • xDelayedTaskList2:定義了一個delay的task鏈表,delay的task鏈表是指調用了taskdelay()或者因為阻塞動作被延時的任務,延時的單位為tick。Delaylist按照delay的tick時間進行排序,之所以定義了兩個,是為了防止xTickCount發生反轉時,一個list無法完全標識。
  • xTickCount:無符號數字,標識運行后到現在的系統產生的tick數,每個tick發生時,xTickCount++,當xTickCount發生翻轉時,pxDelayedTaskList和pxOverflowDelayedTaskList進行對調,Overflowlist變為正常的delay list。時間管理
  • pxDelayedTaskList和pxOverflowDelayedTaskList是鏈表指針,分包指向xDelayedTaskList1和xDelayedTaskList2。
  • xPendingReadyList:任務進入就緒狀態,但是沒有放入readylist鏈表。這種情況發生在調度器被停止時,有些任務進入到ready狀態,這時就講任務加入到xPendingReadyList,等待調度器開始時,從新進行一次調度。 
    任何操作系統都有臨界區或者在執行某段代碼時不想被打斷,防止破壞某些關鍵操作的完整性。Freertos可以采取多種方式避免,如果是不希望被中斷打斷,需要調用:
task.h
#define
taskENTER_CRITICAL() portENTER_CRITICAL() #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL() #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
portmacro.h
#define
portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() 設置BASEPRI的值為最大 #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) 設置BASEPRI的值為0 #define portENTER_CRITICAL() vPortEnterCritical() 調用↑邊第一個,並且CriticalNesting++(臨界段嵌套次數) #define portEXIT_CRITICAL() vPortExitCritical() 先CriticalNesting--,如果這個值為0,調用↑邊第二個
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() 設置BASEPRI的值為最大,返回原來BASEPRI的值 #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) 設置BASEPRI的值為x

歸根結底,這幾個臨界區的設置,都跟BASEPRI寄存器杠上了。(設置到寄存器里的值如果是0,則是屏蔽所有中斷。設為10,那么中斷優先級0~9的異常和中斷都可以執行。)

  • xTasksWaitingTermination:表示等待結束的任務,注意是為了釋放這些任務的資源(動態申請的TCB和棧)。
  • xSuspendedTaskList:表示被掛起的任務列表。
  • xIdleTaskHandle:表示空閑任務句柄,優先級為0,即最低。
  • uxCurrentNumberOfTasks:目前所有的任務數。
  • uxTopReadyPriority:記錄當前ready list中優先級最高的任務。(重要)
  • xSchedulerRunning:調度器是否運行。
  • uxPendedTicks:vTaskSuspendAll暫停了調度器,如果這期間tick的timer發送中斷,這時uxPendedTicks記錄了未被處理的ticks個數。見時間管理
  • xYieldPending:在某種臨界狀態下,任務狀態發生改變,需要等待從新調度。
  • xNumOfOverflows:記錄tick計數翻轉的次數。
  • uxTaskNumber:用來記錄全局任務數,為新建的任務分配一個task number。(不大懂哎~)
  • xNextTaskUnblockTime:記錄了延時任務列表中,第一個需要被喚醒的任務時間點。系統運行的每一個節拍,都會跟這個變量比對,如果滿足任務延時完成,則進行相應操作。見時間管理
  • uxSchedulerSuspended:調度器掛起標志。

 

 

 

 

 

 

留白


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM