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:调度器挂起标志。

 

 

 

 

 

 

留白

posted @ 2017-11-16 19:21  为民除害  阅读(2051)  评论(0编辑  收藏  举报