赞
踩
声明:本文为博主的学习篇章,欢迎大家指错,共同学习
在FreeRTOS中最最最主要的部分就是任务,FreeRTOS内部所有的东西基本都是为了任务而存在的。
在FreeRTOS中,一共提供了两种创建任务的形式:动态创建和静态创建。
让我先来看看创建任务的声明
/* 动态创建 */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, /* 任务函数入口 */ const char * const pcName, /* 任务的名字 */ const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈的深度 */ void * const pvParameters, /* 任务的参数 */ UBaseType_t uxPriority, /* 任务优先级 */ TaskHandle_t * const pxCreatedTask /* 任务TCB句柄 */) PRIVILEGED_FUNCTION; #endif /* 静态创建 */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) BaseType_t xTaskCreateStatic( TaskFunction_t pxTaskCode, /* 任务函数入口 */ const char * const pcName, /* 任务的名字 */ const configSTACK_DEPTH_TYPE usStackDepth, /* 任务堆栈的深度 */ void * const pvParameters, /* 任务的参数 */ UBaseType_t uxPriority, /* 任务优先级 */ TaskHandle_t * const pxCreatedTask /* 任务TCB句柄 */) PRIVILEGED_FUNCTION; #endif
无论是动态创建还是静态创建都需要将某个置1才能够创建。configSUPPORT_DYNAMIC_ALLOCATION和configSUPPORT_STATIC_ALLOCATION都位于FreeRTOSConfig.h文件中。从声明中可以看出动态创建和静态创建除了函数名不同,其他的参数都是一样的,让我来看看都需要些什么参数!
在了解任务创建流程之前需要先了解什么是任务TCB,任务TCB里面都有些什么。
任务TCB也就是任务控制块,它用于存储任务的状态信息。每个任务都有自己的任务TCB。与任务相关的地方都会涉及到任务TCB,先来看看任务TCB都有些什么东西
typedef struct tskTaskControlBlock* TaskHandle_t; typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack; /* 指向当前任务堆栈的栈顶 */ #if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /* MPU设置 */ #endif ListItem_t xStateListItem; /* 任务的状态列表项 */ ListItem_t xEventListItem; /* 任务的事件列表项 */ UBaseType_t uxPriority; /* 任务的优先级 */ StackType_t *pxStack; /* 指向当前任务堆栈的起始位置 */ char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务的名字 */ #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) StackType_t *pxEndOfStack; /* 指向当前任务堆栈的栈底 */ #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxCriticalNesting; /* 保存临界区嵌套深度 */ #endif #if ( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTCBNumber; /* 保存一个数值,每个任务都有唯一的值 */ UBaseType_t uxTaskNumber; /* 保存一个特殊数值 */ #endif #if ( configUSE_MUTEXES == 1 ) UBaseType_t uxBasePriority; /* 保存任务的基础优先级 */ UBaseType_t uxMutexesHeld; #endif #if ( configUSE_APPLICATION_TASK_TAG == 1 ) TaskHookFunction_t pxTaskTag; #endif #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; #endif #if( configGENERATE_RUN_TIME_STATS == 1 ) uint32_t ulRunTimeCounter; /* 记录任务在运行状态下执行的总时间 */ #endif #if ( configUSE_NEWLIB_REENTRANT == 1 ) struct _reent xNewLib_reent; #endif #if( configUSE_TASK_NOTIFICATIONS == 1 ) volatile uint32_t ulNotifiedValue; /* 与任务通知相关 */ volatile uint8_t ucNotifyState; #endif #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) uint8_t ucStaticallyAllocated; /* 如果堆栈由静态数组分配,则设置为pdTRUE,如果堆栈是动态分配的,则设置为pdFALSE */ #endif #if( INCLUDE_xTaskAbortDelay == 1 ) uint8_t ucDelayAborted; #endif #if( configUSE_POSIX_ERRNO == 1 ) int iTaskErrno; #endif } tskTCB;
以上为TCB结构的定义,让我看看都有些什么东西。
typedef struct tskTaskControlBlock* TaskHandle_t; /* 在文件task.h中 */
TaskHandle_t TaskAHandle = NULL;
xTaskCreate(vTask_A,"Task A",120,NULL,1,&TaskAHandle);
创建任务之前首先我们要明白一些必要的操作:
1、任务函数的入口需要提前声明。
2、任务句柄需要提前定义,因为任务创建的函数会在内部对任务句柄进行数据填充,这里提前一个空的句柄即可。任务句柄被TaskHandle_t修饰,TaskHandle_t在文件task.h中是tskTaskControlBlock*的重定义,也就是说这个任务句柄就是这个任务的TCB。
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) 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; /* 1 */ BaseType_t xReturn; #if( portSTACK_GROWTH > 0 ) { pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); if( pxNewTCB != NULL ) { pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ if( pxNewTCB->pxStack == NULL ) { vPortFree( pxNewTCB ); pxNewTCB = NULL; } } } #else { StackType_t *pxStack; pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /* 2 */ if( pxStack != NULL ) { pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /* 3 */ if( pxNewTCB != NULL ) { pxNewTCB->pxStack = pxStack; /* 4 */ } else { vPortFree( pxStack ); } } else { pxNewTCB = NULL; } } #endif if( pxNewTCB != NULL ) /* 5 */ { #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /* 6 */ { pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; } #endif /* 7 */ prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); prvAddNewTaskToReadyList( pxNewTCB ); /* 8 */ xReturn = pdPASS; } else { xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; } return xReturn; } #endif
这个API相对来说并不是很复杂,创建任务可以分为八个步骤:
1、创建一些临时变量,防止内存分配出错时对传入参数的误操作。
2、申请任务堆栈的空间,临时保存在pxStack中。空间的大小主要与堆栈的深度和堆栈一个数据的大小有关。
3、如果申请到了任务堆栈的空间,就申请TCB的空间,临时保存在pxNewTCB中,否则就将pxNewTCB指向空。
4、如果申请到了TCB的空间就将申请到的堆栈空间的地址存入TCB的pxStack成员中,否则就释放刚刚申请到的堆栈空间。
5、如果申请到了TCB的空间就需要对TCB里面成员进行初始化,否则返回错误。
6、tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE是一个宏,在FreeRTOS.h中定义。
#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \
( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) )
portUSING_MPU_WRAPPERS、configSUPPORT_DYNAMIC_ALLOCATION、configSUPPORT_STATIC_ALLOCATION是定义在FreeRTOSConfig.h中的宏,是给用户配置的。这些宏主要的作用是定义任务堆栈分配的方式。tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE为1时表示任务堆栈是动态分配的,否则就是静态分配的。
7、初始化任务TCB其他的成员,并将任务TCB赋给参数pxCreatedTask,此后的pxCreatedTask就是任务A的任务TCB了。参考下面部分。
8、将任务A加入到就绪任务列表中,参考下面部分。
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, const char * const pcName, 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 ) /* 使用MPU内存保护单元 */ BaseType_t xRunPrivileged; if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) { xRunPrivileged = pdTRUE; } else { xRunPrivileged = pdFALSE; } uxPriority &= ~portPRIVILEGE_BIT; #endif #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) { ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); } #endif #if( portSTACK_GROWTH < 0 ) /* 堆栈是向下生长的 */ { pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); #if( configRECORD_STACK_HIGH_ADDRESS == 1 ) { pxNewTCB->pxEndOfStack = pxTopOfStack; } #endif } #else /* 堆栈是向上生长的 */ { pxTopOfStack = pxNewTCB->pxStack; configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); } #endif if( pcName != NULL ) /* 将任务的名字存入TCB中 */ { 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->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; } else { pxNewTCB->pcTaskName[ 0 ] = 0x00; } if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) /* 将任务的优先级存入任务TCB中 */ { uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; } else { mtCOVERAGE_TEST_MARKER(); } pxNewTCB->uxPriority = uxPriority; #if ( configUSE_MUTEXES == 1 ) /* 如果需要使用互斥量,保存创建任务时的优先级,以便优先级反转后的恢复 */ { pxNewTCB->uxBasePriority = uxPriority; pxNewTCB->uxMutexesHeld = 0; } #endif vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); /* 初始化任务状态列表项 */ vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); /* 初始化任务事件列表项 */ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); /* 将任务状态列表项的pvOwner指向任务A */ listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /* 初始化事件列表项的xItemValue */ listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); /* 将任务事件列表项的pvOwner指向任务A */ #if ( portCRITICAL_NESTING_IN_TCB == 1 ) { pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; } #endif #if ( configUSE_APPLICATION_TASK_TAG == 1 ) { pxNewTCB->pxTaskTag = NULL; } #endif #if ( configGENERATE_RUN_TIME_STATS == 1 ) { pxNewTCB->ulRunTimeCounter = 0UL; } #endif #if ( portUSING_MPU_WRAPPERS == 1 ) { vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); } #else { ( void ) xRegions; } #endif #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) { for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) { pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; } } #endif #if ( configUSE_TASK_NOTIFICATIONS == 1 ) /* 初始化任务TCB中有关任务通知的成员 */ { pxNewTCB->ulNotifiedValue = 0; pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; } #endif #if ( configUSE_NEWLIB_REENTRANT == 1 ) { _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); } #endif #if( INCLUDE_xTaskAbortDelay == 1 ) { pxNewTCB->ucDelayAborted = pdFALSE; } #endif #if( portUSING_MPU_WRAPPERS == 1 ) { #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) { #if( portSTACK_GROWTH < 0 ) { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged ); } #else { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged ); } #endif } #else { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); } #endif } #else { #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) { #if( portSTACK_GROWTH < 0 ) { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); } #else { pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); } #endif } #else { /* 初始化任务TCB的任务堆栈 */ pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); } #endif } #endif if( pxCreatedTask != NULL ) { /* 将任务的任务TCB赋给pxCreatedTask */ *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; } else { mtCOVERAGE_TEST_MARKER(); } }
初始化TCB看着复杂,其实就只有几步比较重要。
首先是MPU部分,FreeRTOS是支持MPU的,第一步是使用MPU时需要用到的配置;第二步是对任务堆栈栈顶的初始化,首先需要知道任务堆栈的生长方向,然后将栈顶地址临时保存到pxTopOfStack中;第三步是将任务的名字存入任务TCB中,如果名字为空就存入0x00;第四步是将任务的优先级存入任务TCB中;第五步是为使用互斥量准备的,保存创建任务时任务的优先级。互斥量的使用会涉及到优先级反转,使用完后需要将优先级恢复为一开始的优先级,此时这个成员变量就能派上用场了;第六步是初始化任务状态列表项和初始化任务事件列表项;第七步是初始化任务TCB中有关任务通知的成员;第八步是初始化任务堆栈并将栈顶地址赋给pxTopOfStack;第九步是将任务的任务TCB赋给pxCreatedTask。
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) { pxTopOfStack--; *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ pxTopOfStack--; *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */ pxTopOfStack--; *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ pxTopOfStack--; *pxTopOfStack = portINITIAL_EXC_RETURN; pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ return pxTopOfStack; }
这里主要是一些寄存器的入栈操作。
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { taskENTER_CRITICAL(); /* 进入临界区 */ { uxCurrentNumberOfTasks++; /* 任务统计数量加一 */ if( pxCurrentTCB == NULL ) { pxCurrentTCB = pxNewTCB; /* 如果当前没有任务在运行,就将此任务赋给pxCurrentTCB */ if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) { prvInitialiseTaskLists(); /* 如果当前任务的数量为一,即第一次创建任务就初始化一些列表 */ } else { mtCOVERAGE_TEST_MARKER(); } } else { if( xSchedulerRunning == pdFALSE ) /* 如果没有打开调度器 */ { /* 如果新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务) */ if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) { pxCurrentTCB = pxNewTCB; } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } } uxTaskNumber++; /* 任务控制块的编号加一 */ #if ( configUSE_TRACE_FACILITY == 1 ) /* 可视化追踪功能 */ { pxNewTCB->uxTCBNumber = uxTaskNumber; } #endif traceTASK_CREATE( pxNewTCB ); prvAddTaskToReadyList( pxNewTCB ); /* 添加任务到就绪列表中 */ portSETUP_TCB( pxNewTCB ); /* 将pxNewTCB设置为空 */ } taskEXIT_CRITICAL(); /* 退出临界区 */ if( xSchedulerRunning != pdFALSE ) { /* 如果创建的任务的优先级高于当前任务,那么现在应该运行它 */ if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); /* 进行任务切换 */ } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } }
这里面有太多东西需要了解了,并且这里面涉及到了调度器的知识,所以我打算把这个放到下一篇去。
到这里任务的创建就完成了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。