FreeRTOS学习笔记——创建任务

主机环境:Windows

开发环境:MDK4.7.2

FreeRTOS版本:FreeRTOS8.1.2

目标环境:STM32F030C8T6

FreeRTOS中一个很重要的结构就是TCB任务控制块了,来实现对任务的管理,TCB的结构定义在tasks.c文件中

 

 
  1. typedef struct tskTaskControlBlock

  2. {

  3. volatile StackType_t *pxTopOfStack;

  4.  
  5. #if ( portUSING_MPU_WRAPPERS == 1 )

  6. xMPU_SETTINGS xMPUSettings;

  7. #endif

  8.  
  9. ListItem_t xGenericListItem;

  10. ListItem_t xEventListItem;

  11. UBaseType_t uxPriority;

  12. StackType_t *pxStack;

  13. char pcTaskName[ configMAX_TASK_NAME_LEN ];

  14.  
  15. #if ( portSTACK_GROWTH > 0 )

  16. StackType_t *pxEndOfStack;

  17. #endif

  18.  
  19. #if ( portCRITICAL_NESTING_IN_TCB == 1 )

  20. UBaseType_t uxCriticalNesting;

  21. #endif

  22.  
  23. #if ( configUSE_TRACE_FACILITY == 1 )

  24. UBaseType_t uxTCBNumber;

  25. UBaseType_t uxTaskNumber;

  26. #endif

  27.  
  28. #if ( configUSE_MUTEXES == 1 )

  29. UBaseType_t uxBasePriority;

  30. UBaseType_t uxMutexesHeld;

  31. #endif

  32.  
  33. #if ( configUSE_APPLICATION_TASK_TAG == 1 )

  34. TaskHookFunction_t pxTaskTag;

  35. #endif

  36.  
  37. #if ( configGENERATE_RUN_TIME_STATS == 1 )

  38. uint32_t ulRunTimeCounter;

  39. #endif

  40.  
  41. #if ( configUSE_NEWLIB_REENTRANT == 1 )

  42. /* Allocate a Newlib reent structure that is specific to this task.

  43. Note Newlib support has been included by popular demand, but is not

  44. used by the FreeRTOS maintainers themselves. FreeRTOS is not

  45. responsible for resulting newlib operation. User must be familiar with

  46. newlib and must provide system-wide implementations of the necessary

  47. stubs. Be warned that (at the time of writing) the current newlib design

  48. implements a system-wide malloc() that must be provided with locks. */

  49. struct _reent xNewLib_reent;

  50. #endif

  51.  
  52. } tskTCB;

由于FreeRTOS是用户可配置的,一些不需要的功能我们可以不添加以便节省资源,在STM32F0C8T6中是没有MPU(内存保护单元)的,因此这块代码完全不必去了解,另外一些应用程序调试、跟踪以及程序运行状态的一些参数我们也可以暂时不去管理,其中最基本的几个属性有栈顶指针pxTopOfStack、状态项xGenericListItem、事件项xEventListItem、任务优先级uxPriority、用户栈空间起始地址pxStack以及任务名称pcTaskName,其中任务长度是有限制的,configMAX_TASK_NAME_LEN有用户在FreeRTOSConfig.h文件中配置,根据MCU的栈增长方向可能需要栈底指针属性。

 

FreeRTOS的任务创建是以宏定义形式调用的形式如下

 

xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask )

实际中是调用的下面函数

 

 

xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ) )

在tasks.c中还有一些需要用的变量如下

 

 

 
  1. /* Other file private variables. --------------------------------*/

  2. PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U;

  3. PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) 0U;

  4. PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY;

  5. PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE;

  6. PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U;

  7. PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE;

  8. PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0;

  9. PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U;

  10. PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = portMAX_DELAY;

任务创建代码如下

 

 

 
  1. BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth,

  2. void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer,

  3. const MemoryRegion_t * const xRegions )

  4. {

  5. BaseType_t xReturn;

  6. TCB_t * pxNewTCB;

  7.  
  8. configASSERT( pxTaskCode );

  9. configASSERT( ( ( uxPriority & ( ~portPRIVILEGE_BIT ) ) < configMAX_PRIORITIES ) );

  10.  
  11. /* Allocate the memory required by the TCB and stack for the new task,

  12. checking that the allocation was successful. */

  13. pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer );

  14.  
  15. if( pxNewTCB != NULL )

  16. {

  17. StackType_t *pxTopOfStack;

  18.  
  19. #if( portUSING_MPU_WRAPPERS == 1 )

  20. /* Should the task be created in privileged mode? */

  21. BaseType_t xRunPrivileged;

  22. if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )

  23. {

  24. xRunPrivileged = pdTRUE;

  25. }

  26. else

  27. {

  28. xRunPrivileged = pdFALSE;

  29. }

  30. uxPriority &= ~portPRIVILEGE_BIT;

  31. #endif /* portUSING_MPU_WRAPPERS == 1 */

  32.  
  33. /* Calculate the top of stack address. This depends on whether the

  34. stack grows from high memory to low (as per the 80x86) or vice versa.

  35. portSTACK_GROWTH is used to make the result positive or negative as

  36. required by the port. */

  37. #if( portSTACK_GROWTH < 0 )

  38. {

  39. pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - ( uint16_t ) 1 );

  40. pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) );

  41.  
  42. /* Check the alignment of the calculated top of stack is correct. */

  43. configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

  44. }

  45. #else /* portSTACK_GROWTH */

  46. {

  47. pxTopOfStack = pxNewTCB->pxStack;

  48.  
  49. /* Check the alignment of the stack buffer is correct. */

  50. configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );

  51.  
  52. /* If we want to use stack checking on architectures that use

  53. a positive stack growth direction then we also need to store the

  54. other extreme of the stack space. */

  55. pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 );

  56. }

  57. #endif /* portSTACK_GROWTH */

  58.  
  59. /* Setup the newly allocated TCB with the initial state of the task. */

  60. prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth );

  61.  
  62. /* Initialize the TCB stack to look as if the task was already running,

  63. but had been interrupted by the scheduler. The return address is set

  64. to the start of the task function. Once the stack has been initialised

  65. the top of stack variable is updated. */

  66. #if( portUSING_MPU_WRAPPERS == 1 )

  67. {

  68. pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );

  69. }

  70. #else /* portUSING_MPU_WRAPPERS */

  71. {

  72. pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );

  73. }

  74. #endif /* portUSING_MPU_WRAPPERS */

  75.  
  76. if( ( void * ) pxCreatedTask != NULL )

  77. {

  78. /* Pass the TCB out - in an anonymous way. The calling function/

  79. task can use this as a handle to delete the task later if

  80. required.*/

  81. *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;

  82. }

  83. else

  84. {

  85. mtCOVERAGE_TEST_MARKER();

  86. }

  87.  
  88. /* Ensure interrupts don't access the task lists while they are being

  89. updated. */

  90. taskENTER_CRITICAL();

  91. {

  92. uxCurrentNumberOfTasks++;

  93. if( pxCurrentTCB == NULL )

  94. {

  95. /* There are no other tasks, or all the other tasks are in

  96. the suspended state - make this the current task. */

  97. pxCurrentTCB = pxNewTCB;

  98.  
  99. if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )

  100. {

  101. /* This is the first task to be created so do the preliminary

  102. initialisation required. We will not recover if this call

  103. fails, but we will report the failure. */

  104. prvInitialiseTaskLists();

  105. }

  106. else

  107. {

  108. mtCOVERAGE_TEST_MARKER();

  109. }

  110. }

  111. else

  112. {

  113. /* If the scheduler is not already running, make this task the

  114. current task if it is the highest priority task to be created

  115. so far. */

  116. if( xSchedulerRunning == pdFALSE )

  117. {

  118. if( pxCurrentTCB->uxPriority <= uxPriority )

  119. {

  120. pxCurrentTCB = pxNewTCB;

  121. }

  122. else

  123. {

  124. mtCOVERAGE_TEST_MARKER();

  125. }

  126. }

  127. else

  128. {

  129. mtCOVERAGE_TEST_MARKER();

  130. }

  131. }

  132.  
  133. uxTaskNumber++;

  134.  
  135. #if ( configUSE_TRACE_FACILITY == 1 )

  136. {

  137. /* Add a counter into the TCB for tracing only. */

  138. pxNewTCB->uxTCBNumber = uxTaskNumber;

  139. }

  140. #endif /* configUSE_TRACE_FACILITY */

  141. traceTASK_CREATE( pxNewTCB );

  142.  
  143. prvAddTaskToReadyList( pxNewTCB );

  144.  
  145. xReturn = pdPASS;

  146. portSETUP_TCB( pxNewTCB );

  147. }

  148. taskEXIT_CRITICAL();

  149. }

  150. else

  151. {

  152. xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;

  153. traceTASK_CREATE_FAILED();

  154. }

  155.  
  156. if( xReturn == pdPASS )

  157. {

  158. if( xSchedulerRunning != pdFALSE )

  159. {

  160. /* If the created task is of a higher priority than the current task

  161. then it should run now. */

  162. if( pxCurrentTCB->uxPriority < uxPriority )

  163. {

  164. taskYIELD_IF_USING_PREEMPTION();

  165. }

  166. else

  167. {

  168. mtCOVERAGE_TEST_MARKER();

  169. }

  170. }

  171. else

  172. {

  173. mtCOVERAGE_TEST_MARKER();

  174. }

  175. }

  176.  
  177. return xReturn;

  178. }

创建任务函数的第一步是创建该任务的TCB指针通过调用prvAllocateTCBAndStack()函数来实现

 

该函数实现如下

 

 
  1. static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer )

  2. {

  3. TCB_t *pxNewTCB;

  4.  
  5. /* Allocate space for the TCB. Where the memory comes from depends on

  6. the implementation of the port malloc function. */

  7. pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

  8.  
  9. if( pxNewTCB != NULL )

  10. {

  11. /* Allocate space for the stack used by the task being created.

  12. The base of the stack memory stored in the TCB so the task can

  13. be deleted later if required. */

  14. pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer );

  15.  
  16. if( pxNewTCB->pxStack == NULL )

  17. {

  18. /* Could not allocate the stack. Delete the allocated TCB. */

  19. vPortFree( pxNewTCB );

  20. pxNewTCB = NULL;

  21. }

  22. else

  23. {

  24. /* Avoid dependency on memset() if it is not required. */

  25. #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) )

  26. {

  27. /* Just to help debugging. */

  28. ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) );

  29. }

  30. #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */

  31. }

  32. }

  33.  
  34. return pxNewTCB;

  35. }

该函数很简单,申请一块TCB空间,如果申请成功之后则根据用户指定的栈深度来申请栈空间,如果栈空间申请成功则将该TCB指针返回上一级。栈空间申请的实现与FreeRTOS的内存管理有关,在portable/MemMang文件夹下有5个文件来实现内存分配管理,这里就先不详细了解了,后面再去深入了解吧

 


在TCB指针申请成功之后,第二步是初始化栈顶指针,一般而言栈的增长方向是向下的,即往地址小的方向增长,随着对栈的操作,栈顶指针不断地变化,pxStack是栈的起始地址,根据用户指定的栈深度来得到栈顶指针的地址,栈的结构如下

同时根据portBYTE_ALIGNMENT_MASK宏来指定栈顶指针的字节对齐方式,STM32是32位MCU,因此栈顶指针是4字节对齐的。

有关TCB的空间申请完毕之后就要填充TCB里面的一些变量属性了,由prvInitialiseTCBVariables函数来实现,该函数实现代码如下

 

 
  1. static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority,

  2. const MemoryRegion_t * const xRegions, const uint16_t usStackDepth )

  3. {

  4. UBaseType_t x;

  5.  
  6. /* Store the task name in the TCB. */

  7. for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )

  8. {

  9. pxTCB->pcTaskName[ x ] = pcName[ x ];

  10.  
  11. if( pcName[ x ] == 0x00 )

  12. {

  13. break;

  14. }

  15. else

  16. {

  17. mtCOVERAGE_TEST_MARKER();

  18. }

  19. }

  20.  
  21. /* Ensure the name string is terminated in the case that the string length

  22. was greater or equal to configMAX_TASK_NAME_LEN. */

  23. pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

  24.  
  25. /* This is used as an array index so must ensure it's not too large. First

  26. remove the privilege bit if one is present. */

  27. if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )

  28. {

  29. uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;

  30. }

  31. else

  32. {

  33. mtCOVERAGE_TEST_MARKER();

  34. }

  35.  
  36. pxTCB->uxPriority = uxPriority;

  37. #if ( configUSE_MUTEXES == 1 )

  38. {

  39. pxTCB->uxBasePriority = uxPriority;

  40. pxTCB->uxMutexesHeld = 0;

  41. }

  42. #endif /* configUSE_MUTEXES */

  43.  
  44. vListInitialiseItem( &( pxTCB->xGenericListItem ) );

  45. vListInitialiseItem( &( pxTCB->xEventListItem ) );

  46.  
  47. /* Set the pxTCB as a link back from the ListItem_t. This is so we can get

  48. back to the containing TCB from a generic item in a list. */

  49. listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB );

  50.  
  51. /* Event lists are always in priority order. */

  52. listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );

  53. listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB );

  54.  
  55. #if ( portCRITICAL_NESTING_IN_TCB == 1 )

  56. {

  57. pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U;

  58. }

  59. #endif /* portCRITICAL_NESTING_IN_TCB */

  60.  
  61. #if ( configUSE_APPLICATION_TASK_TAG == 1 )

  62. {

  63. pxTCB->pxTaskTag = NULL;

  64. }

  65. #endif /* configUSE_APPLICATION_TASK_TAG */

  66.  
  67. #if ( configGENERATE_RUN_TIME_STATS == 1 )

  68. {

  69. pxTCB->ulRunTimeCounter = 0UL;

  70. }

  71. #endif /* configGENERATE_RUN_TIME_STATS */

  72.  
  73. #if ( portUSING_MPU_WRAPPERS == 1 )

  74. {

  75. vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth );

  76. }

  77. #else /* portUSING_MPU_WRAPPERS */

  78. {

  79. ( void ) xRegions;

  80. ( void ) usStackDepth;

  81. }

  82. #endif /* portUSING_MPU_WRAPPERS */

  83.  
  84. #if ( configUSE_NEWLIB_REENTRANT == 1 )

  85. {

  86. /* Initialise this task's Newlib reent structure. */

  87. _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) );

  88. }

  89. #endif /* configUSE_NEWLIB_REENTRANT */

  90. }

是比较简单的一个函数,首先是采用字节拷贝方式来存储任务名称,记录任务的优先级且增加了优先级保护代码,初始化了状态项和事件项,同时设置了状态项和事件项的所有者为该任务的TCB指针,有关事件项赋值语句

 

 

listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );

还不了解为啥要用configMAX_PRIORITIES-uxPriority的值来存储,后面想到了再看吧。回到创建任务的函数中接着是初始化栈空间

 

 

 
  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. pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */

  6. *pxTopOfStack = portINITIAL_XPSR; /* xPSR */

  7. pxTopOfStack--;

  8. *pxTopOfStack = ( StackType_t ) pxCode; /* PC */

  9. pxTopOfStack--;

  10. *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */

  11. pxTopOfStack -= 5; /* R12, R3, R2 and R1. */

  12. *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */

  13. pxTopOfStack -= 8; /* R11..R4. */

  14.  
  15. return pxTopOfStack;

  16. }

即栈空间中高地址主要是存储通用寄存器的值,低地址空间用来存储用户数据,如下

如果用户需要用到任务句柄,在创建任务函数中将该任务的TCB指针赋给了任务句柄pxCreatedTask,之后进入临界区中,进入和退出临界区代码如下

 

 
  1. void vPortEnterCritical( void )

  2. {

  3. portDISABLE_INTERRUPTS();

  4. uxCriticalNesting++;//临界区嵌套标记加1

  5. __dsb( portSY_FULL_READ_WRITE );

  6. __isb( portSY_FULL_READ_WRITE );

  7. }

  8. /*-----------------------------------------------------------*/

  9.  
  10. void vPortExitCritical( void )

  11. {

  12. configASSERT( uxCriticalNesting );

  13. uxCriticalNesting--;//临界区嵌套标记减1

  14. if( uxCriticalNesting == 0 )

  15. {

  16. portENABLE_INTERRUPTS();

  17. }

  18. }


uxCriticalNesting变量是临界区嵌套标记,声明如下

 

static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;

在进入临界区时关闭中断,在退出临界区时判断是否有临界区嵌套,如果没有则打开中断,在临界区代码中更新当前任务数uxCurrentNumberOfTasks,如果当前TCB指针pxCurrentTCB为空,则将pxCurrentTCB指针指向新建任务的TCB,如果新建的任务是第一个任务则还需要初始化任务链表,进入prvInitialiseTaskLists()函数

 

 
  1. static void prvInitialiseTaskLists( void )

  2. {

  3. UBaseType_t uxPriority;

  4.  
  5. for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )

  6. {

  7. vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );

  8. }

  9.  
  10. vListInitialise( &xDelayedTaskList1 );

  11. vListInitialise( &xDelayedTaskList2 );

  12. vListInitialise( &xPendingReadyList );

  13.  
  14. #if ( INCLUDE_vTaskDelete == 1 )

  15. {

  16. vListInitialise( &xTasksWaitingTermination );

  17. }

  18. #endif /* INCLUDE_vTaskDelete */

  19.  
  20. #if ( INCLUDE_vTaskSuspend == 1 )

  21. {

  22. vListInitialise( &xSuspendedTaskList );

  23. }

  24. #endif /* INCLUDE_vTaskSuspend */

  25.  
  26. /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList

  27. using list2. */

  28. pxDelayedTaskList = &xDelayedTaskList1;

  29. pxOverflowDelayedTaskList = &xDelayedTaskList2;

  30. }

首先初始化的是就绪任务链表,初始化每一个优先级的就绪链表,此外还初始化两个延时链表以及挂起的就绪链表,并将两个延时链表指针指向了已初始化的两个延时链表。

如果当前TCB指针pxCurrentTCB不为空且调度器还没有运行则根据当前任务优先级判断是否需要更新pxCurrentTCB指针,pxCurrentTCB指针总是指向优先级最高的任务,更新任务数uxTaskNumber,完成之后将该任务插入相应的就绪链表中等待调度器调用,

 

 
  1. #define prvAddTaskToReadyList( pxTCB ) \

  2. traceMOVED_TASK_TO_READY_STATE( pxTCB ) \

  3. taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \

  4. vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xGenericListItem )


这里有个疑问好想xGenericListItem还没有包含有效值xItemValue

退出临界区代码后如果所有资源已准备就绪即xReturn=pdPass时,如果掉调度处于运行中则根据优先级判断是否需要执行一次任务切换,该代码最终调用vPortYield()函数,如下

 

 
  1. void vPortYield( void )

  2. {

  3. /* Set a PendSV to request a context switch. */

  4. *( portNVIC_INT_CTRL ) = portNVIC_PENDSVSET;

  5.  
  6. /* Barriers are normally not required but do ensure the code is completely

  7. within the specified behaviour for the architecture. */

  8. __dsb( portSY_FULL_READ_WRITE );

  9. __isb( portSY_FULL_READ_WRITE );

  10. }


进入PENDSV中断执行任务切换代码,

 

#define portNVIC_INT_CTRL			( ( volatile uint32_t *) 0xe000ed04 )

该寄存器的说明在ARMv6架构参考手册中有说明

 

#define portNVIC_PENDSVSET			0x10000000


将PENDSVSET置1进入PENDSV中断执行任务切换代码,但是我们创建任务的代码还没有执行完毕,没有返回,调度器就去执行新建任务的代码了,总觉得应该创建任务代码返回后再切换才对,写一个示例代码验证

 

 
  1. static const char *taskName2="Task2\r\n";

  2. void vTask1(void *pvParamters);

  3. void vTask2(void *pvParamters);

  4. void vTask1(void *pvParamters)

  5. {

  6. char *TskName;

  7. TskName = (char *)pvParamters;

  8. if(xTaskCreate(vTask2,"Task2\r\n",128,(void *)taskName2,2,NULL)== pdPASS)

  9. {

  10. uart_puts("create task2 success\r\n");

  11. }

  12. while(1)

  13. {

  14. uart_puts(TskName);

  15.  
  16. vTaskDelay(1000/portTICK_RATE_MS);

  17.  
  18. }

  19. }

  20. void vTask2(void *pvParamters)

  21. {

  22. char *TskName;

  23. TskName = (char *)pvParamters;

  24. while(1)

  25. {

  26. uart_puts(TskName);

  27.  
  28. vTaskDelay(1000/portTICK_RATE_MS);

  29.  
  30. }

  31. }

  32.  
  33. int main(void)

  34. {

  35. static const char *taskName1="Task1\r\n";

  36.  
  37.  
  38. uart_init (SET_UART (BAUD_115200,PAR_NONE)); //初始化串口

  39.  
  40. xTaskCreate(vTask1,"Task1",128,(void *)taskName1,1,NULL);

  41. /*启动调度器,任务开始执行*/

  42. vTaskStartScheduler();

  43. while(1);

  44. return 0;

  45. }


在主函数中创建TASK1,在TASK1的代码中创建TASK2,且TASK2的优先级要高于TASK1并接收创建TASK2的返回值,验证结果如下

运行结果果然与代码一致,创建完TASK2后,调度器发现TASK2的优先级要高于TASK1,执行了任务切换去执行TASK2的代码,当TASK2挂起时,TASK1获取了CPU继续执行,获取到了创建TASK2的返回值并输出了“create task2 success”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值