04.FreeRTOS任务创建与删除

04. FreeRTOS任务创建与任务删除

1. FreeRTOS创建和删除任务相关API函数

函数描述
xTaskCreate()动态方式创建任务
xTaskCreateStatic()静态方式创建任务
xTaskCreateRestricted()动态方式创建使用 MPU 限制的任务
xTaskCreateRestrictedStatic()静态方式创建使用 MPU 限制的任务
vTaskDelete()删除任务

在这里插入图片描述

  1. 函数xTaskCreate()

    动态方式创建函数,任务的任务控制块及栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配,若使用此函数,需要在FreeRTOSConfig.h文件中将宏configSUPPORT_DYNAMIC_ALLOCATION配置为1。此函数创建的任务会立刻进入就绪态,由任务调度器运行。函数原型如下所示:

    在这里插入图片描述
    在这里插入图片描述

  2. 函数xTaskCreateStatic()

    静态方式创建任务,任务的任务控制块及任务的栈空间所需的内存,需要由用户分配提供,若使用此函数,需要在FreeRTOSConfig.h文件中将宏 configSUPPORT_STATIC_ALLOCATION配置为1。此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。函数原型如下所示:

    在这里插入图片描述
    在这里插入图片描述

  3. 函数vTaskDelete()

    此函数用于删除已被创建的任务,被删除的任务将被从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除,要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露。若使用此函数,需要在FreeRTOSConfig.h文件中将宏INCLUDE_vTaskDelete配置为1。函数原型如下所示:

    在这里插入图片描述

2. 动态创建任务及具体实现

  • 步骤:

    1. 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1
    2. 定义函数入口参数
    3. 编写任务函数
  • 动态创建任务函数内部实现:

    1. 申请堆栈内存和任务控制块内存
    2. TCB结构体成员赋值
    3. 添加新任务到就绪列表中
  • 任务控制块:

    在这里插入图片描述

  • 具体代码实现:

    本实验共创建6个任务,1个开始任务和5个不同功能的任务函数

    任务优先级、任务堆栈大小、任务句柄:

    #define START_TASK_PRIO       1
    #define START_TASK_STACK_SIZE 128
    TaskHandle_t start_task_handler;
    
    #define TASK1_TASK_PRIO       2
    #define TASK1_TASK_STACK_SIZE 128
    TaskHandle_t task1_task_handler;
    
    #define TASK2_TASK_PRIO       3
    #define TASK2_TASK_STACK_SIZE 128
    TaskHandle_t task2_task_handler;
    
    #define TASK3_TASK_PRIO       4
    #define TASK3_TASK_STACK_SIZE 128
    TaskHandle_t task3_task_handler;
    
    #define TASK4_TASK_PRIO       5
    #define TASK4_TASK_STACK_SIZE 128
    TaskHandle_t task4_task_handler;
    
    #define TASK5_TASK_PRIO       6
    #define TASK5_TASK_STACK_SIZE 128
    TaskHandle_t task5_task_handler;
    
    //LCD刷屏时使用的颜色
    uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED, MAGENTA, GREEN, CYAN, YELLOW,BROWN, BRRED, GRAY};
    

    主函数入口:

    void freertos_Dynamic_Create(void)
    {
    	lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
        lcd_show_string(10, 47, 220, 24, 24, "Task Create&Delete", LIGHTGREEN);
        
        lcd_draw_rectangle(5, 110, 115, 314, BLACK);
        lcd_draw_rectangle(125, 110, 234, 314, BLACK);
        lcd_draw_line(5, 130, 115, 130, BLACK);
        lcd_draw_line(125, 130, 234, 130, BLACK);
    	lcd_show_string(15, 80, 110, 16, 16, "Task1: 000", GREEN);
    	lcd_show_string(135, 80, 110, 16, 16, "Task2: 000", GREEN);
        lcd_show_string(15, 111, 110, 16, 16, "Task4: 000", BLUE);
        lcd_show_string(135, 111, 110, 16, 16, "Task5: 000", BLUE);
    	
    	xTaskCreate((TaskFunction_t        )   start_task,           //指向任务函数的指针
    				(char *                )   "start_task",         //任务名称
    				(configSTACK_DEPTH_TYPE)   START_TASK_STACK_SIZE,//任务堆栈大小,字节为单位
    				(void *                )   NULL,                 //传递给任务函数的参数
    				(UBaseType_t           )   START_TASK_PRIO,      //任务优先级
    				(TaskHandle_t *        )   &start_task_handler    //任务句柄:任务控制块
    	);
    				
        vTaskStartScheduler();  //开启任务调度
    }
    

    开始任务:

    void start_task(void* pvParamter)
    {
    	taskENTER_CRITICAL();   // 进入临界区 
    	
    	xTaskCreate((TaskFunction_t        )   task1,                 //指向任务函数的指针
    				(char *                )   "task1",               //任务名称
    				(configSTACK_DEPTH_TYPE)   TASK1_TASK_STACK_SIZE, //任务堆栈大小,字节为单位
    				(void *                )   NULL,                  //传递给任务函数的参数
    				(UBaseType_t           )   TASK1_TASK_PRIO,       //任务优先级
    				(TaskHandle_t *        )   &task1_task_handler    //任务句柄:任务控制块
    	);
    				
    	xTaskCreate((TaskFunction_t        )   task2,                 //指向任务函数的指针
    				(char *                )   "task2",               //任务名称
    				(configSTACK_DEPTH_TYPE)   TASK2_TASK_STACK_SIZE, //任务堆栈大小,字节为单位
    				(void *                )   NULL,                  //传递给任务函数的参数
    				(UBaseType_t           )   TASK2_TASK_PRIO,       //任务优先级
    				(TaskHandle_t *        )   &task2_task_handler    //任务句柄:任务控制块
    	);	
    
    	xTaskCreate((TaskFunction_t        )   task3,                  //指向任务函数的指针
    				(char *                )   "task3",                //任务名称
    				(configSTACK_DEPTH_TYPE)   TASK3_TASK_STACK_SIZE,  //任务堆栈大小,字节为单位
    				(void *                )   NULL,                   //传递给任务函数的参数
    				(UBaseType_t           )   TASK3_TASK_PRIO,        //任务优先级
    				(TaskHandle_t *        )   &task3_task_handler     //任务句柄:任务控制块
    	);
    				
    	xTaskCreate((TaskFunction_t        )   task4,                  //指向任务函数的指针
    				(char *                )   "task4",                //任务名称
    				(configSTACK_DEPTH_TYPE)   TASK4_TASK_STACK_SIZE,  //任务堆栈大小,字节为单位
    				(void *                )   NULL,                   //传递给任务函数的参数
    				(UBaseType_t           )   TASK4_TASK_PRIO,        //任务优先级
    				(TaskHandle_t *        )   &task4_task_handler     //任务句柄:任务控制块
    	);
    
    	xTaskCreate((TaskFunction_t        )   task5,                  //指向任务函数的指针
    				(char *                )   "task5",                //任务名称
    				(configSTACK_DEPTH_TYPE)   TASK5_TASK_STACK_SIZE,  //任务堆栈大小,字节为单位
    				(void *                )   NULL,                   //传递给任务函数的参数
    				(UBaseType_t           )   TASK5_TASK_PRIO,        //任务优先级
    				(TaskHandle_t *        )   &task5_task_handler     //任务句柄:任务控制块				
    	);	
    
    	vTaskDelete(NULL);
    				
    	taskEXIT_CRITICAL();    // 退出临界区 
    }
    

    任务一:实现LED0每500ms翻转一次

    void task1(void* pvParamter)
    {
    	uint32_t task1_num = 0;
    	
    	while(1)
    	{
    		printf("task1正在运行!!!\r\n");
    		lcd_show_xnum(71, 80, ++task1_num, 3, 16, 0x80, GREEN);
    		LED0_TOGGLE();
    		vTaskDelay(500);
    	}
    }
    

    任务二:实现LED1每500ms翻转一次

    void task2(void* pvParamter)
    {
    	uint32_t task2_num = 0;
    	
    	while(1)
    	{
    		printf("task2正在运行!!!\r\n");
    		lcd_show_xnum(191, 80, ++task2_num, 3, 16, 0x80, GREEN);
    		LED1_TOGGLE();
    		vTaskDelay(500);
    	}
    }
    

    任务三:删除任务一

    void task3(void* pvParamter)
    {
    	uint8_t key = 0;
    	while(1)
    	{
    		printf("task3正在运行!!!\r\n");
    		key = key_scan(0);
    		
    		if(key == KEY0)
    		{
    			if(task1_task_handler != NULL)
    			{
    				printf("删除task1任务!!!\r\n");
    				vTaskDelete(task1_task_handler);
    				task1_task_handler = NULL;
    			}			
    		}
    			
    		vTaskDelay(10);
    	}
    }
    

    任务四:不同颜色填充块一

    void task4(void *pvParameter)
    {
        uint32_t task4_num = 0;
        
        while(1)
        {
            lcd_fill(6, 131, 114, 313, lcd_discolor[++task4_num % 11]);
            lcd_show_xnum(71, 111, task4_num, 3, 16, 0x80, BLUE);
            
            vTaskDelay(500);
        }
    }
    

    任务五:不同颜色填充块二

    void task5(void *pvParameter)
    {
        uint32_t task5_num = 0;
        
        while(1)
        {
            lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task5_num % 11)]);
            lcd_show_xnum(191, 111, task5_num, 3, 16, 0x80, BLUE);
            
            vTaskDelay(500);
        }
    }
    
  • 函数xTaskCreate()的内部实现(源码):

    在这里插入图片描述

    步骤:

    1. 申请堆栈内存(返回首地址)

      pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
      
    2. 申请任务控制块内存(返回首地址)

      pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 
      
    3. 把前面申请的堆栈地址 赋值给控制块的堆栈成员

      pxNewTCB->pxStack = pxStack;
      
    4. 调用prvInitialiseNewTask初始化任务控制块中的成员

      prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
      
    5. 调用prvAddNewTaskToReadyList添加新创建的任务到就绪列表

      prvAddNewTaskToReadyList( pxNewTCB );
      

      具体源码:

          BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                                  const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                  const configSTACK_DEPTH_TYPE usStackDepth,
                                  void * const pvParameters,
                                  UBaseType_t uxPriority,
                                  TaskHandle_t * const pxCreatedTask )
          {
              TCB_t * pxNewTCB;
              BaseType_t xReturn;
      
              /* If the stack grows down then allocate the stack then the TCB so the stack
               * does not grow into the TCB.  Likewise if the stack grows up then allocate
               * the TCB then the stack.
      			如果堆栈向下扩展,则先分配堆栈然后分配TCB,这样堆栈*不会扩展到TCB。
      		    同样,如果堆栈向上扩展,则再分配TCB然后堆栈*/
              #if ( portSTACK_GROWTH > 0 )
                  {
                      /* Allocate space for the TCB.  Where the memory comes from depends on
                       * the implementation of the port malloc function and whether or not static
                       * allocation is being used. */
                      pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
      
                      if( pxNewTCB != NULL )
                      {
                          /* Allocate space for the stack used by the task being created.
                           * The base of the stack memory stored in the TCB so the task can
                           * be deleted later if required. */
                          pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
      
                          if( pxNewTCB->pxStack == NULL )
                          {
                              /* Could not allocate the stack.  Delete the allocated TCB. */
                              vPortFree( pxNewTCB );
                              pxNewTCB = NULL;
                          }
                      }
                  }
              #else /* portSTACK_GROWTH */
                  {
                      StackType_t * pxStack;
      
                      /* Allocate space for the stack used by the task being created.
      				   为正在创建的任务所使用的堆栈分配空间*/
      				//1.申请堆栈内存(返回首地址)
                      pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */
      
                      if( pxStack != NULL )
                      {
                          /* Allocate space for the TCB. */
      					//2.申请任务控制块内存(返回首地址)
                          pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */
      
                          if( pxNewTCB != NULL )
                          {
                              /* Store the stack location in the TCB. */
      						//3.把前面申请的堆栈地址 赋值给控制块的堆栈成员
                              pxNewTCB->pxStack = pxStack;
                          }
                          else
                          {
                              /* The stack cannot be used as the TCB was not created.  Free
                               * it again. */
                              vPortFreeStack( pxStack );
                          }
                      }
                      else
                      {
                          pxNewTCB = NULL;
                      }
                  }
              #endif /* portSTACK_GROWTH */
      
              if( pxNewTCB != NULL )
              {
                  #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */
                      {
                          /* Tasks can be created statically or dynamically, so note this
                           * task was created dynamically in case it is later deleted. */
                          pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
                      }
                  #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */
      			
      			//4.调用prvInitialiseNewTask初始化任务控制块中的成员
                  prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
                  //5.调用prvAddNewTaskToReadyList添加新创建的任务到就绪列表
      			prvAddNewTaskToReadyList( pxNewTCB );
                  xReturn = pdPASS;
              }
              else
              {
                  xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
              }
      
              return xReturn;
          }
      

      上述第4步调用prvInitialiseNewTask初始化任务控制块中的成员的步骤:

      1. 用已知值填充堆栈以协助调试,初始化堆栈0xa5

        ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
        
      2. 记录栈顶,保存在pxTopOfStack,并8字节向下对齐

        pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
        pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
        
      3. 保存任务名字到pxNewTCB->pcTaskName[x]

        for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
        {
        	pxNewTCB->pcTaskName[ x ] = pcName[ x ];
        
        	if( pcName[ x ] == ( char ) 0x00 )
        	{
        		break;
        	}
        	else
        	{
        		mtCOVERAGE_TEST_MARKER();
        	}
        }
        
      4. 保存任务优先级到pxNewTCB->uxPriority中

        pxNewTCB->uxPriority = uxPriority;
        
      5. 设置状态列表项的所属控制块,设置事件列表项的值

        vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
        vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
        
      6. 列表项的插入是从小到大插入,所以这里将越高优先级的任务他的事件列表项值设置越小,这样就可以拍到前面

        listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
        
      7. 调用pxPortInitialiseStack:初始化任务堆栈,用于保存当前任务上下文寄存器信息已备后续任务切换使用

        pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
        
      8. 将任务句柄等于任务控制块

        *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
        

      具体源码:

      static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
                                        const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                        const uint32_t ulStackDepth,
                                        void * const pvParameters,
                                        UBaseType_t uxPriority,
                                        TaskHandle_t * const pxCreatedTask,
                                        TCB_t * pxNewTCB,
                                        const MemoryRegion_t * const xRegions )
      {
          StackType_t * pxTopOfStack;
          UBaseType_t x;
      
          #if ( portUSING_MPU_WRAPPERS == 1 )
              /* Should the task be created in privileged mode? */
              BaseType_t xRunPrivileged;
      
              if( ( uxPriority & portPRIVILEGE_BIT ) != 0U )
              {
                  xRunPrivileged = pdTRUE;
              }
              else
              {
                  xRunPrivileged = pdFALSE;
              }
              uxPriority &= ~portPRIVILEGE_BIT;
          #endif /* portUSING_MPU_WRAPPERS == 1 */
      
          /* Avoid dependency on memset() if it is not required. */
          #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
              {
                  /* Fill the stack with a known value to assist debugging. */
      			//1.用已知值填充堆栈以协助调试,初始化堆栈0xa5
                  ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
              }
          #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */
      
          /* Calculate the top of stack address.  This depends on whether the stack
           * grows from high memory to low (as per the 80x86) or vice versa.
           * portSTACK_GROWTH is used to make the result positive or negative as required
           * by the port. */
          #if ( portSTACK_GROWTH < 0 )
              {
      			//2.记录栈顶,保存在pxTopOfStack
                  pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] );
      			//8字节向下对齐
                  pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type.  Checked by assert(). */
      
                  /* Check the alignment of the calculated top of stack is correct. */
                  configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
      
                  #if ( configRECORD_STACK_HIGH_ADDRESS == 1 )
                      {
                          /* Also record the stack's high address, which may assist
                           * debugging. */
                          pxNewTCB->pxEndOfStack = pxTopOfStack;
                      }
                  #endif /* configRECORD_STACK_HIGH_ADDRESS */
              }
          #else /* portSTACK_GROWTH */
              {
                  pxTopOfStack = pxNewTCB->pxStack;
      
                  /* Check the alignment of the stack buffer is correct. */
                  configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
      
                  /* The other extreme of the stack space is required if stack checking is
                   * performed. */
                  pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
              }
          #endif /* portSTACK_GROWTH */
      
          /* Store the task name in the TCB. */
          if( pcName != NULL )
          {
      		//3.保存任务名字到pxNewTCB->pcTaskName[x]
              for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
              {
                  pxNewTCB->pcTaskName[ x ] = pcName[ x ];
      
                  /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than
                   * configMAX_TASK_NAME_LEN characters just in case the memory after the
                   * string is not accessible (extremely unlikely). */
                  if( pcName[ x ] == ( char ) 0x00 )
                  {
                      break;
                  }
                  else
                  {
                      mtCOVERAGE_TEST_MARKER();
                  }
              }
      
              /* Ensure the name string is terminated in the case that the string length
               * was greater or equal to configMAX_TASK_NAME_LEN. */
      		//末尾加结束符
              pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
          }
          else
          {
              /* The task has not been given a name, so just ensure there is a NULL
               * terminator when it is read out. */
              pxNewTCB->pcTaskName[ 0 ] = 0x00;
          }
      
          /* This is used as an array index so must ensure it's not too large. */
          configASSERT( uxPriority < configMAX_PRIORITIES );
      
          if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
          {
              uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
          }
          else
          {
              mtCOVERAGE_TEST_MARKER();
          }
      	//4.保存任务优先级到pxNewTCB->uxPriority中
          pxNewTCB->uxPriority = uxPriority;
          #if ( configUSE_MUTEXES == 1 )
              {
                  pxNewTCB->uxBasePriority = uxPriority;
                  pxNewTCB->uxMutexesHeld = 0;
              }
          #endif /* configUSE_MUTEXES */
      	
      	//5.设置状态列表项的所属控制块,设置事件列表项的值
          vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
          vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
      
          /* Set the pxNewTCB as a link back from the ListItem_t.  This is so we can get
           * back to  the containing TCB from a generic item in a list. */
      	//6.列表项的插入是从小到大插入,所以这里将越高优先级的任务他的事件列表项值设置越小,这样就可以拍到前面
          listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
      
          /* Event lists are always in priority order. */
          listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
          listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
      
          #if ( portCRITICAL_NESTING_IN_TCB == 1 )
              {
                  pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
              }
          #endif /* portCRITICAL_NESTING_IN_TCB */
      
          #if ( configUSE_APPLICATION_TASK_TAG == 1 )
              {
                  pxNewTCB->pxTaskTag = NULL;
              }
          #endif /* configUSE_APPLICATION_TASK_TAG */
      
          #if ( configGENERATE_RUN_TIME_STATS == 1 )
              {
                  pxNewTCB->ulRunTimeCounter = ( configRUN_TIME_COUNTER_TYPE ) 0;
              }
          #endif /* configGENERATE_RUN_TIME_STATS */
      
          #if ( portUSING_MPU_WRAPPERS == 1 )
              {
                  vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
              }
          #else
              {
                  /* Avoid compiler warning about unreferenced parameter. */
                  ( void ) xRegions;
              }
          #endif
      
          #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
              {
                  memset( ( void * ) &( pxNewTCB->pvThreadLocalStoragePointers[ 0 ] ), 0x00, sizeof( pxNewTCB->pvThreadLocalStoragePointers ) );
              }
          #endif
      
          #if ( configUSE_TASK_NOTIFICATIONS == 1 )
              {
                  memset( ( void * ) &( pxNewTCB->ulNotifiedValue[ 0 ] ), 0x00, sizeof( pxNewTCB->ulNotifiedValue ) );
                  memset( ( void * ) &( pxNewTCB->ucNotifyState[ 0 ] ), 0x00, sizeof( pxNewTCB->ucNotifyState ) );
              }
          #endif
      
          #if ( configUSE_NEWLIB_REENTRANT == 1 )
              {
                  /* Initialise this task's Newlib reent structure.
                   * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
                   * for additional information. */
                  _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
              }
          #endif
      
          #if ( INCLUDE_xTaskAbortDelay == 1 )
              {
                  pxNewTCB->ucDelayAborted = pdFALSE;
              }
          #endif
      
          /* Initialize the TCB stack to look as if the task was already running,
           * but had been interrupted by the scheduler.  The return address is set
           * to the start of the task function. Once the stack has been initialised
           * the top of stack variable is updated. */
          #if ( portUSING_MPU_WRAPPERS == 1 )
              {
                  /* If the port has capability to detect stack overflow,
                   * pass the stack end address to the stack initialization
                   * function as well. */
                  #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
                      {
                          #if ( portSTACK_GROWTH < 0 )
                              {
                                  pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged );
                              }
                          #else /* portSTACK_GROWTH */
                              {
                                  pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged );
                              }
                          #endif /* portSTACK_GROWTH */
                      }
                  #else /* portHAS_STACK_OVERFLOW_CHECKING */
                      {
                          pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
                      }
                  #endif /* portHAS_STACK_OVERFLOW_CHECKING */
              }
          #else /* portUSING_MPU_WRAPPERS */
              {
                  /* If the port has capability to detect stack overflow,
                   * pass the stack end address to the stack initialization
                   * function as well. */
                  #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
                      {
                          #if ( portSTACK_GROWTH < 0 )
                              {
                                  pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters );
                              }
                          #else /* portSTACK_GROWTH */
                              {
                                  pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters );
                              }
                          #endif /* portSTACK_GROWTH */
                      }
                  #else /* portHAS_STACK_OVERFLOW_CHECKING */
                      {
      					//7.调用pxPortInitialiseStack:初始化任务堆栈,用于保存当前任务上下文寄存器信息已备后续任务切换使用
                          pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
                      }
                  #endif /* portHAS_STACK_OVERFLOW_CHECKING */
              }
          #endif /* portUSING_MPU_WRAPPERS */
      
          if( pxCreatedTask != NULL )
          {
              /* Pass the handle out in an anonymous way.  The handle can be used to
               * change the created task's priority, delete the created task, etc.*/
      		//8.将任务句柄等于任务控制块
              *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
          }
          else
          {
              mtCOVERAGE_TEST_MARKER();
          }
      }
      

      上述第5步调用prvAddNewTaskToReadyList添加新创建的任务到就绪列表的步骤:

      1. 记录任务数量uxCurrentNumberOfTasks++

        uxCurrentNumberOfTasks++;
        
      2. 判断新创建的任务是否为第一个任务

        //如果创建的是第一个任务,初始化任务列表prvInitialiseTaskLists
        if( pxCurrentTCB == NULL )
        {
             pxCurrentTCB = pxNewTCB;
        
             if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
             { 
                  prvInitialiseTaskLists();
             }
             else
             {
                  mtCOVERAGE_TEST_MARKER();
             }
        }
        //如果创建的不是第一个任务,并且调度器还未开始启动,比较新任务与正在执行的任务
        //优先级大小,新任务优先级大的话,将当前控制块重新指向新的控制块
        else
        {
              if( xSchedulerRunning == pdFALSE )
              {
                   if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                   {
                       pxCurrentTCB = pxNewTCB;
                   }
                   else
                   {
                       mtCOVERAGE_TEST_MARKER();
                   }
               }
               else
               {
                    mtCOVERAGE_TEST_MARKER();
               }
        }
        
      3. 将新的任务控制块添加到就绪列表中,使用函数prvAddTaskToReadyList。将uxTopReadyPriority相应bit置一,表示相应优先级有就绪任务,比如任务优先级为5,就将该变量的位5置一,方便后续任务切换判断,对应的就绪列表是否有任务存在。

        prvAddTaskToReadyList( pxNewTCB );
        
      4. 如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换

        if( xSchedulerRunning != pdFALSE )
        {
            if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
            {
                taskYIELD_IF_USING_PREEMPTION();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
        

      具体源码:

      static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
      {
          /* Ensure interrupts don't access the task lists while the lists are being
           * updated. */
          taskENTER_CRITICAL();
          {
      		//1.记录任务数量uxCurrentNumberOfTasks++
              uxCurrentNumberOfTasks++;
      		
      		//2.判断新创建的任务是否为第一个任务
      		//如果创建的是第一个任务,初始化任务列表prvInitialiseTaskLists
              if( pxCurrentTCB == NULL )
              {
                  /* There are no other tasks, or all the other tasks are in
                   * the suspended state - make this the current task. */
                  pxCurrentTCB = pxNewTCB;
      
                  if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
                  {
                      /* This is the first task to be created so do the preliminary
                       * initialisation required.  We will not recover if this call
                       * fails, but we will report the failure. */
                      prvInitialiseTaskLists();
                  }
                  else
                  {
                      mtCOVERAGE_TEST_MARKER();
                  }
              }
      		//如果创建的不是第一个任务,并且调度器还未开始启动,比较新任务与正在执行的任务
      		//优先级大小,新任务优先级大的话,将当前控制块重新指向新的控制块
              else
              {
                  /* If the scheduler is not already running, make this task the
                   * current task if it is the highest priority task to be created
                   * so far. */
                  if( xSchedulerRunning == pdFALSE )
                  {
                      if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
                      {
                          pxCurrentTCB = pxNewTCB;
                      }
                      else
                      {
                          mtCOVERAGE_TEST_MARKER();
                      }
                  }
                  else
                  {
                      mtCOVERAGE_TEST_MARKER();
                  }
              }
      
              uxTaskNumber++;
      
              #if ( configUSE_TRACE_FACILITY == 1 )
                  {
                      /* Add a counter into the TCB for tracing only. */
                      pxNewTCB->uxTCBNumber = uxTaskNumber;
                  }
              #endif /* configUSE_TRACE_FACILITY */
              traceTASK_CREATE( pxNewTCB );
      		
      		//3.将新的任务控制块添加到就绪列表中,使用函数prvAddTaskToReadyList
      		//将uxTopReadyPriority相应bit置一,表示相应优先级有就绪任务,比如任务优先级为5,就将该
      		//变量的位5置一,方便后续任务切换判断,对应的就绪列表是否有任务存在
              prvAddTaskToReadyList( pxNewTCB );
      
              portSETUP_TCB( pxNewTCB );
          }
          taskEXIT_CRITICAL();
      	
      	//4.如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换
          if( xSchedulerRunning != pdFALSE )
          {
              /* If the created task is of a higher priority than the current task
               * then it should run now. */
              if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
              {
                  taskYIELD_IF_USING_PREEMPTION();
              }
              else
              {
                  mtCOVERAGE_TEST_MARKER();
              }
          }
          else
          {
              mtCOVERAGE_TEST_MARKER();
          }
      }
      
  • 实验结果:
    一共有五个任务,任务点亮LED0;任务二点亮LED;任务三删除任务1;任务四填充块一;任务五填充块二。从实验结果可以看出,LED0和LED1同时闪烁,间隔500ms;两个填充块已填充不同的颜色;当按键KEY0按下时,任务1被删除,LED0停止闪烁。

3. 静态创建任务及具体实现

  • 步骤:

    1. 需将宏configSUPPORT_STATIC_ALLOCATION 配置为 1

      // 1: 支持静态申请内存, 默认: 0 
      #define configSUPPORT_STATIC_ALLOCATION                 1                       
      
    2. 定义空闲任务和定时器任务的任务堆栈及TCB

      //空闲任务配置
      StaticTask_t idle_task_tcb;
      StackType_t idle_task_stack[configMINIMAL_STACK_SIZE];
      
      //软件定时器任务配置
      StaticTask_t timer_task_tcb;
      StackType_t timer_task_stack[configTIMER_TASK_STACK_DPTH];
      
    3. 实现两个接口函数

      /*空闲任务内存分配*/
      void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
      									StackType_t ** ppxIdleTaskStackBuffer,
      									uint32_t * pulIdleTaskStackSize )
      {
      	* ppxIdleTaskTCBBuffer = &idle_task_tcb;
      	* ppxIdleTaskStackBuffer = idle_task_stack;
      	* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
      }
      
      /*软件定时器内存分配*/
      void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
      									 StackType_t ** ppxTimerTaskStackBuffer,
      									 uint32_t * pulTimerTaskStackSize )
      {
      	* ppxTimerTaskTCBBuffer = &timer_task_tcb;
      	* ppxTimerTaskStackBuffer = timer_task_stack;
      	* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
      }
      
    4. 定义函数入口参数

      freertos_Static_Create();
      
    5. 编写任务函数

      #define START_TASK_PRIO       1
      #define START_TASK_STACK_SIZE 128
      TaskHandle_t start_task_handler;
      StackType_t start_task_stack[START_TASK_STACK_SIZE];
      StaticTask_t start_task_tcb;
      
      #define TASK1_TASK_PRIO       2
      #define TASK1_TASK_STACK_SIZE 128
      TaskHandle_t task1_task_handler;
      StackType_t  task1_task_stack[TASK1_TASK_STACK_SIZE];
      StaticTask_t task1_task_tcb;
      
      #define TASK2_TASK_PRIO       3
      #define TASK2_TASK_STACK_SIZE 128
      TaskHandle_t task2_task_handler;
      StackType_t  task2_task_stack[TASK2_TASK_STACK_SIZE];
      StaticTask_t task2_task_tcb;
      
      #define TASK3_TASK_PRIO       4
      #define TASK3_TASK_STACK_SIZE 128
      TaskHandle_t task3_task_handler;
      StackType_t  task3_task_stack[TASK3_TASK_STACK_SIZE];
      StaticTask_t task3_task_tcb;
      
      
      void start_task(void * pvParamter)
      {
      	taskENTER_CRITICAL();
      	
          task1_task_handler = xTaskCreateStatic(
                          (TaskFunction_t)  task1,       
                          (char *        )  "task1",         
                          (uint32_t      )   TASK1_TASK_STACK_SIZE,      
                          ( void *       )   NULL,  
                          (UBaseType_t   )   TASK1_TASK_PRIO,
                          (StackType_t * )   task1_task_stack,          
                          (StaticTask_t *)   &task1_task_tcb); 
      					
      	task2_task_handler = xTaskCreateStatic(
                          (TaskFunction_t)  task2,       
                          (char *        )  "task2",         
                          (uint32_t      )   TASK2_TASK_STACK_SIZE,      
                          ( void *       )   NULL,  
                          (UBaseType_t   )   TASK2_TASK_PRIO,
                          (StackType_t * )   task2_task_stack,          
                          (StaticTask_t *)   &task2_task_tcb);  	
      
      	task3_task_handler = xTaskCreateStatic(
                          (TaskFunction_t)  task3,       
                          (char *        )  "task3",         
                          (uint32_t      )   TASK3_TASK_STACK_SIZE,      
                          ( void *       )   NULL,  
                          (UBaseType_t   )   TASK3_TASK_PRIO,
                          (StackType_t * )   task3_task_stack,          
                          (StaticTask_t *)   &task3_task_tcb);  
      
      	vTaskDelete(start_task_handler);
      	taskEXIT_CRITICAL();
      }
      
      /*任务一:实现LED0每500ms翻转一次*/
      void task1(void* pvParamter)
      {
      	uint32_t task1_num = 0;
      	
      	while(1)
      	{
      		printf("task1正在运行!!!\r\n");
      		lcd_show_xnum(71, 80, ++task1_num, 3, 16, 0x80, GREEN);
      		LED0_TOGGLE();
      		vTaskDelay(500);
      	}
      }
      
      /*任务二:实现LED1每500ms翻转一次*/
      void task2(void* pvParamter)
      {
      	uint32_t task2_num = 0;
      	
      	while(1)
      	{
      		printf("task2正在运行!!!\r\n");
      		lcd_show_xnum(191, 80, ++task2_num, 3, 16, 0x80, GREEN);
      		LED1_TOGGLE();
      		vTaskDelay(500);
      	}
      }
      
      /*任务三:删除任务一*/
      void task3(void* pvParamter)
      {
      	uint8_t key = 0;
      	while(1)
      	{
      		printf("task3正在运行!!!\r\n");
      		key = key_scan(0);
      		
      		if(key == KEY0)
      		{
      			if(task1_task_handler != NULL)
      			{
      				printf("删除task1任务!!!\r\n");
      				vTaskDelete(task1_task_handler);
      				task1_task_handler = NULL;
      			}			
      		}
      		vTaskDelay(10);
      	}
      }
      

4. 删除任务及具体实现

在这里插入图片描述

内部实现:

  1. 获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身

    pxTCB = prvGetTCBFromHandle( xTaskToDelete );
    
  2. 将被删除任务,移除所在列表:将该任务从列表中移除,包括:就绪、阻塞、挂起、事件等列表

    //2.将被删除任务,移除所在列表:将该任务从列表中移除,包括:就绪、阻塞、挂起、事件等列表
    if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
    {
         taskRESET_READY_PRIORITY( pxTCB->uxPriority );
    }
    else
    {
         mtCOVERAGE_TEST_MARKER();
    }
    
  3. 判断所需要删除的任务

    删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行
    if( pxTCB == pxCurrentTCB )
    {
        vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
        ++uxDeletedTasksWaitingCleanUp;
        traceTASK_DELETE( pxTCB );
        portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
    }
    //删除其他任务,当前任务数量--
    else
    {
        --uxCurrentNumberOfTasks;
        traceTASK_DELETE( pxTCB );
        prvResetNextTaskUnblockTime();
    }
    
    
  4. 更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务

    prvResetNextTaskUnblockTime();
    

具体实现:

    void vTaskDelete( TaskHandle_t xTaskToDelete )
    {
        TCB_t * pxTCB;

        taskENTER_CRITICAL();
        {
            /* If null is passed in here then it is the calling task that is
             * being deleted. */
			//1.获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
            pxTCB = prvGetTCBFromHandle( xTaskToDelete );

            /* Remove task from the ready/delayed list. */
			//2.将被删除任务,移除所在列表:将该任务从列表中移除,包括:就绪、阻塞、挂起、事件等列表
            if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
            {
                taskRESET_READY_PRIORITY( pxTCB->uxPriority );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* Is the task waiting on an event also? */
            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* Increment the uxTaskNumber also so kernel aware debuggers can
             * detect that the task lists need re-generating.  This is done before
             * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
             * not return. */
            uxTaskNumber++;
			
			//判断所需要删除的任务
			//删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行
            if( pxTCB == pxCurrentTCB )
            {
                /* A task is deleting itself.  This cannot complete within the
                 * task itself, as a context switch to another task is required.
                 * Place the task in the termination list.  The idle task will
                 * check the termination list and free up any memory allocated by
                 * the scheduler for the TCB and stack of the deleted task. */
                vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

                /* Increment the ucTasksDeleted variable so the idle task knows
                 * there is a task that has been deleted and that it should therefore
                 * check the xTasksWaitingTermination list. */
                ++uxDeletedTasksWaitingCleanUp;

                /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
                 * portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */
                traceTASK_DELETE( pxTCB );

                /* The pre-delete hook is primarily for the Windows simulator,
                 * in which Windows specific clean up operations are performed,
                 * after which it is not possible to yield away from this task -
                 * hence xYieldPending is used to latch that a context switch is
                 * required. */
                portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
            }
			//删除其他任务,当前任务数量--
			//更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务
            else
            {
                --uxCurrentNumberOfTasks;
                traceTASK_DELETE( pxTCB );

                /* Reset the next expected unblock time in case it referred to
                 * the task that has just been deleted. */
                prvResetNextTaskUnblockTime();
            }
        }
        taskEXIT_CRITICAL();

        /* If the task is not deleting itself, call prvDeleteTCB from outside of
         * critical section. If a task deletes itself, prvDeleteTCB is called
         * from prvCheckTasksWaitingTermination which is called from Idle task. */
        if( pxTCB != pxCurrentTCB )
        {
            prvDeleteTCB( pxTCB );
        }

        /* Force a reschedule if it is the currently running task that has just
         * been deleted. */
        if( xSchedulerRunning != pdFALSE )
        {
            if( pxTCB == pxCurrentTCB )
            {
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值