从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(四)FreeRTOS 的启动流程
1、未到主函数之前:
我们知道,在系统上电的时候第一个执行的是启动文件里面由汇编编写的复位函数Reset_Handler,具体见下面的代码清单。复位函数的最后会调用 C 库函数__main。 __main 函数的主要工作是初始化系统的堆和栈,最后调用 C 中的 main 函数,从而去到 C 的世界。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
2、创建任务 xTaskCreate()函数:
在 main()函数中,我们直接可以对 FreeRTOS 进行创建任务操作,因为 FreeRTOS 会自动帮我们做初始化的事情,比如初始化堆内存。
这种简单的特点使得 FreeRTOS 在初学的时候变得很简单,我们自己在 main()函数中直接初始化我们的板级外设——BSP_Init(),然后进行任务的创建即可——xTaskCreate(),在任务创建中, FreeRTOS 会帮我们进行一系列的系统初始化,在创建任务的时候,会帮我们初始化堆内存,具体见下面的代码清单。
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
if ( pxStack != NULL ) {
/* 分配任务控制块内存 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if ( pxNewTCB != NULL ) {
/* 将堆栈位置存储在 TCB 中。 */
pxNewTCB->pxStack = pxStack;
}
}
/*
省略代码
......
*/
}
/* 分配内存函数 */
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
vTaskSuspendAll();
{
/*如果这是对 malloc 的第一次调用,那么堆将需要初始化来设置空闲块列表。 */
if ( pxEnd == NULL ) {
prvHeapInit();
} else {
mtCOVERAGE_TEST_MARKER();
}
/*
省略代码
......
*/
}
}
在未初始化内存的时候一旦调用了xTaskCreate()函数, FreeRTOS 就会帮我们自动进行内存的初始化,内存的初始化具体见下面的代码清单。注意,此函数是 FreeRTOS 内部调用的,目前我们暂时不用管这个函数的实现,在后面我们会仔细讲解 FreeRTOS 的内存管理相关知识,现在我们知道 FreeRTOS 会帮我们初始话系统要用的东西即可。
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
size_t uxAddress;
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
uxAddress = ( size_t ) ucHeap;
/* 确保堆在正确对齐的边界上启动。 */
if ( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) {
uxAddress += ( portBYTE_ALIGNMENT - 1 );
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
}
pucAlignedHeap = ( uint8_t * ) uxAddress;
/* xStart 用于保存指向空闲块列表中第一个项目的指针。
void 用于防止编译器警告*/
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
/* pxEnd 用于标记空闲块列表的末尾,并插入堆空间的末尾。 */
uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
uxAddress -= xHeapStructSize;
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
pxEnd = ( void * ) uxAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
/* 首先,有一个空闲块,其大小可以占用整个堆空间,减去 pxEnd 占用的空间。 */
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
/* 只存在一个块 - 它覆盖整个可用堆空间。 因为是刚初始化的堆内存*/
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t