freeRtos源码解析(一)–任务创建与任务调度
一、简介 ??1.1 代码来源及版本 ??代码来源于STM32CubeIDE工具一键生成功能,对应freeRtos版本为 FreeRTOS Kernel V10.2.1。对应MCU型号为STM32MP157D cortex-M4。 ?? 生成的中间层代码以及源码目录如下:
??1.2 学习参考 ??[野火?]《FreeRTOS 内核实现与应用开发实战—基于STM32》、 ?《FreeRTOS高级篇》https://blog.csdn.net/zhzht19861011/article/details/51418383 ??freeRtos学习的人非常广泛,资料众多,每个版本间有细微的差别,可以相互参考。
二、任务与freeRtos ??freeRtos的核心,一句话就是:在裸机系统上实现多任务的实时调度以及提供任务间的通信同步机制。 归结为一个词,就是多任务 (任务创建,任务调度,任务通信)。 ??有些人把任务理解为linux中的线程,不过在freeRtos中没有进程线程的概念,统一的叫法是任务task而不是thread。进程、线程的概念要到了linux内核中才存在。
三、任务创建 ?? 3.1 xTaskCreate 任务创建接口 ??任务创建对应的过程非常重要,需要仔细阅读。接口代码如下:
#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_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
{
StackType_t *pxStack;
pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL )
{
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
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
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
#endif
??相关宏定义:
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define portSTACK_GROWTH ( -1 )
tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE
??因此从函数38行开始,申请栈大小*4字节的堆空间,作为任务栈的大小。
pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
??申请TCB任务控制块的空间,并且为其成员pxStack 赋值,作为任务栈的起始地址。
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
pxNewTCB->pxStack = pxStack;
?? 3.2 prvInitialiseNewTask 初始化TCB任务控制块 ??初始化TCB任务控制块的成员,代码如下:
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 )
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 )
{
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 )
{
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 );
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );
#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 )
{
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
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif
}
#endif
if( pxCreatedTask != NULL )
{
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
?? 3.3 pxPortInitialiseStack 初始化任务栈指针(根据MCU内核通用寄存器偏移) ?? 3.4 prvAddNewTaskToReadyList 将TCB控制块加入到ready列表中 ??至此,完成了任务的创建和初始化。
四、启动任务调度器 ??启动任务调度器之后,CPU正式开始按照任务的方式调度各任务。
|