04. FreeRTOS任务创建与任务删除
1. FreeRTOS创建和删除任务相关API函数
函数 | 描述 |
---|---|
xTaskCreate() | 动态方式创建任务 |
xTaskCreateStatic() | 静态方式创建任务 |
xTaskCreateRestricted() | 动态方式创建使用 MPU 限制的任务 |
xTaskCreateRestrictedStatic() | 静态方式创建使用 MPU 限制的任务 |
vTaskDelete() | 删除任务 |
-
函数xTaskCreate()
动态方式创建函数,任务的任务控制块及栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配,若使用此函数,需要在
FreeRTOSConfig.h
文件中将宏configSUPPORT_DYNAMIC_ALLOCATION
配置为1。此函数创建的任务会立刻进入就绪态,由任务调度器运行。函数原型如下所示:
-
函数xTaskCreateStatic()
静态方式创建任务,任务的任务控制块及任务的栈空间所需的内存,需要由用户分配提供,若使用此函数,需要在
FreeRTOSConfig.h
文件中将宏configSUPPORT_STATIC_ALLOCATION
配置为1。此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。函数原型如下所示:
-
函数vTaskDelete()
此函数用于删除已被创建的任务,被删除的任务将被从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除,要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露。若使用此函数,需要在
FreeRTOSConfig.h
文件中将宏INCLUDE_vTaskDelete
配置为1。函数原型如下所示:
2. 动态创建任务及具体实现
-
步骤:
- 将宏
configSUPPORT_DYNAMIC_ALLOCATION
配置为1
- 定义函数入口参数
- 编写任务函数
- 将宏
-
动态创建任务函数内部实现:
- 申请堆栈内存和任务控制块内存
- TCB结构体成员赋值
- 添加新任务到就绪列表中
-
任务控制块:
-
具体代码实现:
本实验共创建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()的内部实现(源码):
步骤:
-
申请堆栈内存(返回首地址)
pxStack = pvPortMallocStack( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
-
申请任务控制块内存(返回首地址)
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
-
把前面申请的堆栈地址 赋值给控制块的堆栈成员
pxNewTCB->pxStack = pxStack;
-
调用
prvInitialiseNewTask
初始化任务控制块中的成员prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
-
调用
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
初始化任务控制块中的成员的步骤:-
用已知值填充堆栈以协助调试,初始化堆栈0xa5
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
-
记录栈顶,保存在pxTopOfStack,并8字节向下对齐
pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
-
保存任务名字到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(); } }
-
保存任务优先级到pxNewTCB->uxPriority中
pxNewTCB->uxPriority = uxPriority;
-
设置状态列表项的所属控制块,设置事件列表项的值
vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
-
列表项的插入是从小到大插入,所以这里将越高优先级的任务他的事件列表项值设置越小,这样就可以拍到前面
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
-
调用pxPortInitialiseStack:初始化任务堆栈,用于保存当前任务上下文寄存器信息已备后续任务切换使用
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
-
将任务句柄等于任务控制块
*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
添加新创建的任务到就绪列表的步骤:-
记录任务数量uxCurrentNumberOfTasks++
uxCurrentNumberOfTasks++;
-
判断新创建的任务是否为第一个任务
//如果创建的是第一个任务,初始化任务列表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(); } }
-
将新的任务控制块添加到就绪列表中,使用函数prvAddTaskToReadyList。将uxTopReadyPriority相应bit置一,表示相应优先级有就绪任务,比如任务优先级为5,就将该变量的位5置一,方便后续任务切换判断,对应的就绪列表是否有任务存在。
prvAddTaskToReadyList( pxNewTCB );
-
如果调度器已经开始运行,并且新任务的优先级更大的话,进行一次任务切换
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. 静态创建任务及具体实现
-
步骤:
-
需将宏
configSUPPORT_STATIC_ALLOCATION
配置为 1// 1: 支持静态申请内存, 默认: 0 #define configSUPPORT_STATIC_ALLOCATION 1
-
定义空闲任务和定时器任务的任务堆栈及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];
-
实现两个接口函数
/*空闲任务内存分配*/ 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; }
-
定义函数入口参数
freertos_Static_Create();
-
编写任务函数
#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. 删除任务及具体实现
内部实现:
-
获取所要删除任务的控制块:通过传入的任务句柄,判断所需要删除哪个任务,NULL代表删除自身
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
-
将被删除任务,移除所在列表:将该任务从列表中移除,包括:就绪、阻塞、挂起、事件等列表
//2.将被删除任务,移除所在列表:将该任务从列表中移除,包括:就绪、阻塞、挂起、事件等列表 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); } else { mtCOVERAGE_TEST_MARKER(); }
-
判断所需要删除的任务
删除任务自身,需先添加到等待删除列表,内存释放将在空闲任务执行 if( pxTCB == pxCurrentTCB ) { vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); ++uxDeletedTasksWaitingCleanUp; traceTASK_DELETE( pxTCB ); portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); } //删除其他任务,当前任务数量-- else { --uxCurrentNumberOfTasks; traceTASK_DELETE( pxTCB ); prvResetNextTaskUnblockTime(); }
-
更新下一个任务的阻塞超时时间,以防被删除的任务就是下一个阻塞超时的任务
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();
}
}
}