FreeRTOS任务
1、任务控制块TCB
任务控制块 一种基于ListItem链表结点的数据结构
typedef struct tskTaskControlBlock
{
/*栈顶指针*/
volatile StackType_t *pxTopOfStack;
/*配置 内存保护MPU*/
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif
/*任务管理的任务结点*/
ListItem_t xStateListItem;
/*事件管理的事件结点*/
ListItem_t xEventListItem;
/*任务优先级*/
UBaseType_t uxPriority;
/*任务堆栈指针 (栈起始地址)*/
StackType_t *pxStack;
/*任务名称*/
char pcTaskName[ configMAX_TASK_NAME_LEN ];
/*栈生长方向 对于CortexM3内核栈是向下生长 的满栈模型
即栈从高地址向低地址生长 portSTACK_GROWTH配置应<0
对于CortexM3没有*pxEndofStack尾指针的定义
*/
#if ( portSTACK_GROWTH > 0 )
StackType_t *pxEndOfStack;
#endif
/*临界区 TCB嵌套 */
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
#endif
/*启用系统性能跟踪 启用此宏用于调试*/
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */
UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */
#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
/*TCB的一个成员用于记录是否分配内存
堆栈和/或TCB是静态或动态分配
tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE仅当它可能用于
使用静态或动态分配RAM创建的任务。请注意
如果portUSING_MPU_WRAPPERS为1,则可以使用它创建受保护任务
静态分配的堆栈和动态分配的TCB*/
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
#endif
/*是否取消任务延时 一般不启用 为0*/
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
任务控制块实际是基于list数据结构的一块内存,基本成员+#define可配置成员组成
2、任务创建xTaskCreate(…)
动态创建函数
xTaskCreate
(
/*执行任务的函数 实际任务入口*/
TaskFunction_t pxTaskCode,
/*任务名称*/
const char * const pcName,
/*任务栈深度*/
const uint16_t usStackDepth,
/*任务形参*/
void * const pvParameters,
/*任务优先级*/
UBaseType_t uxPriority,
/*任务控制块指针*/
TaskHandle_t * const pxCreatedTask
)
动态创建任务函数流程
分配任务堆栈、控制块堆栈;动态内存分配用的heap4模型-小内存分配模型;
初始化任务并将任务添加至就绪列表
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,/*任务函数 真正的任务入口*/
const char * const pcName,/*任务名称*/
const uint16_t usStackDepth,/*任务堆栈深度*4Bytes*/
void * const pvParameters,/*任务形参*/
UBaseType_t uxPriority,/*任务优先级*/
TaskHandle_t * const pxCreatedTask )/*任务控制块指针*/
{
TCB_t *pxNewTCB;
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 ) ) );
if( pxNewTCB->pxStack == NULL )
{
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
}
#else /* CortexM3内核使用的是向下生长满栈模型 直接跳转到这里 */
{
StackType_t *pxStack;
/*动态内存分配任务堆栈 大小为usStackDepth*4Bytes
动态内存分配有heap2和heap4模型 其中 heap2模型会产生内存小碎片 heap4模型会合并一些动态内存碎片
*/
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL )
{ /*任务栈初始化完成后再初始化TCB控制块,与向上生长堆栈模型相反,向上生长会先分配TCB控制块,再分配任务堆栈*/
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /
/*TCB分配成功*/
if( pxNewTCB != NULL )
{
/*保存任务堆栈的位置到TCB的堆栈指针*/
pxNewTCB->pxStack = pxStack;
}
else
{
/*否则释放任务堆栈*/
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
#endif
if( pxNewTCB != NULL )
{
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/*初始化任务*/
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
/*添加任务到就绪列表*/
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
这里借鉴了别人的图片;即要保证TCB在task[0][0]位置,需要在堆栈初始化时,对于向下生长模型,先从task[0][TASK_STK_SIZE-1]压入任务栈,然后再压入TCB;对于向上生长堆栈模型,需要先压入TCB,再压入任务堆栈。
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
/*初始化任务*/
```c
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;
/*启用MPU保护*/
#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 */
/*配置栈溢出检测*/
#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) )
{
/* 填充已知的值到堆栈内再通过调试去读出 从而检查堆栈是否溢出 */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
#endif
/*确定pxTopofStack的位置=TCB的任务堆栈位置+堆栈深度即栈顶指针到了TCB的位置*/
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
/*栈指针8字节对其 cortexm3也是8字节对齐的*/
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 ) );
/*保存任务名称*/
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if( pcName[ x ] == 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
/*优先级限制不能超过最大优先级*/
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxNewTCB->uxPriority = uxPriority;
/*启用RTOS的互斥*/
#if ( configUSE_MUTEXES == 1 )
{
pxNewTCB->uxBasePriority = uxPriority;
pxNewTCB->uxMutexesHeld = 0;
}
#endif /* configUSE_MUTEXES */
/*初始化任务链表结点和事件列表结点 让其pvContainer指向NULL*/
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/*设置任务链表结点归属于此TCB*/
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
/*设置事件链表结点内的排序值*/
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
/*设置事件链表结点归属于此TCB*/
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 = 0UL;
}
#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 )
{
for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
{
pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
}
}
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
{
pxNewTCB->ulNotifiedValue = 0;
pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
/* Initialise this task's Newlib reent structure. */
_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
}
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
{
pxNewTCB->ucDelayAborted = pdFALSE;
}
#endif
#if( portUSING_MPU_WRAPPERS == 1 )
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
}
#else /* portUSING_MPU_WRAPPERS */
{
/*最重要的一步 初始化任务栈 与cortexm3内核相关 作用是将CPU寄存器压入任务堆栈*/
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif /* portUSING_MPU_WRAPPERS */
if( ( void * ) pxCreatedTask != NULL )
{
/*成功创建TCB后 返回NewTCB地址*/
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
``
在prvInitialiseNewTask最后需要调用pxPortInitialiseStack,将任务相关信息压入CPU的寄存器内,PC指针指向pxTaskCode也将被保存;当需要使用时会调回到pxTaskCode进入此任务。
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
pxTopOfStack--; /*已知pxTopofStack是已经指向TCB的指针*/
*pxTopOfStack = portINITIAL_XPSR; /* xPSR 的 bit24 必须置 1,即 0x01000000 */
pxTopOfStack--;
/*保存任务的入口地址到PC*/
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;
pxTopOfStack--;
/*LR链接了一个错误处理入口*/
*pxTopOfStack = ( StackType_t ) prvTaskExitError;
/*默认初始化R12 R3 R2 R1 这是CPU自动行为*/
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
/*手动压入剩余寄存器*/
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
/*最后的是R4也是pxTopofStack的位置*/
return pxTopOfStack;
}
理解为栈顶压入了CPU寄存器的相关内容,用于能恢复现场。从栈顶开始到栈底依次是CPU寄存器 TCB 任务堆栈 所以R4的下一个位置时TCB的首地址。
static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
taskENTER_CRITICAL();
{
uxCurrentNumberOfTasks++;/*一个全局变量 当任务创建成功后 会自加*/
/*当pxCurrentTCB是空表明这是第一个被创建的任务*/
if( pxCurrentTCB == NULL )/*pxCurrentTCB也是一个全局的TCB指针*/
{
pxCurrentTCB = pxNewTCB;/*全局变量指针指向这个TCB*/
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )/*在这之前pxCurrentTCB都为空 所以只有一个任务*/
{
prvInitialiseTaskLists();/*第一个创建时需要初始化就绪列表*/
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else/*当至少有一个任务存在时*/
{
if( xSchedulerRunning == pdFALSE )
{
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 /* configUSE_TRACE_FACILITY */
traceTASK_CREATE( pxNewTCB );
/*加入就绪列表*/
prvAddTaskToReadyList( pxNewTCB );
portSETUP_TCB( pxNewTCB );
}
taskEXIT_CRITICAL();
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();
}
}
将任务的TCB添加到就绪列表去管理,就绪列表是List_t的根节点数据结构,允许有多少个优先级则有多少个这样的根节点数据结构,将ListItem的TCB链表数据结构挂载到就绪列表中实现任务调度。