freeRtos源码解析(一)–RTOS概念以及任务创建
一、简介
1.1 代码来源及版本
代码来源于STM32CubeIDE工具一键生成功能,对应freeRtos版本为 FreeRTOS Kernel V10.2.1。对应MCU型号为STM32MP157D cortex-M4。
生成的中间层代码以及源码目录如下:
1.2 学习参考
[野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》、
《Cortex-M3权威指南(中文版)》或《Cortex-M3与Cortex-M4权威指南(中文版)》——寄存器组、MPU,中断与异常、双堆栈机制以及汇编语言基础部分。
RTOS的设计和MCU内核的设计是紧密相关的,因此学习RTOS需要对Cortex-M3/M4内核的特性有相应的认识。在国内学习freeRtos的资料非常多,大家可以很容易找到进行学习,彼此交流。
二、相关概念
2.1 任务的概念
在RTOS里面,任务指一个个具有独立栈空间,无限循环且不能返回的函数(LR指针默认压0或者异常,不能返回)。一般也称为线程(与linux内核中的线程实现不完全一致)。
freeRtos的核心,就是多任务 (包括任务创建,任务调度,任务通信)。
2.2 freeRtos与linux操作系统的区别
freeRtos仅涉及多任务系统以及任务通信机制,和简单的堆内存管理。如果需要TCP/IP和文件系统,还需要另外移植。
嵌入式linux系统时,属于全特性OS。其概念包括线程进程调度,文件系统,网络,内存管理等,属于集大成者。(Cortex-M系列处理器无法运行linux或windows等全特性OS,因为M系列处理器没有MMU。只有A系列才能支持)
2.3 何时使用嵌入式RTOS
根据《Cortex-M3与Cortex-M4权威指南(中文版)》中表述,RTOS的关键在于实时性,而linux由于MMU的管理,无法保证实时响应。
当前基于Cortex-M3/M4的应用越来越复杂,RTOS可以方便多任务的处理。同时在某些必要中间件,如lwip协议栈中,就抽象出任务来管理内核,需要搭配OS使用。
三、任务创建
下面介绍freeRtos内核是如何定义和创建任务的。 创建任务的函数定义:
#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;
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. */
#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 * ) pvPortMalloc( ( ( ( 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. */
pxStack = pvPortMalloc( ( ( ( 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. */
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. */
pxNewTCB->pxStack = pxStack;
}
else
{
/* The stack cannot be used as the TCB was not created. Free
it again. */
vPortFree( 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 */
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 /* configSUPPORT_DYNAMIC_ALLOCATION */
创建任务的流程依次做了如下步骤:
1)申请堆空间。
当portSTACK_GROWTH大于0时,先申请TCB控制块再申请栈空间ÿ