Free RTOS学习之任务创建源码分析


前言

本文的任务创建函数 基于FreeRTOS Kernel V10.0.1


一、任务创建API

FreeRTOS提供了两种基本的任务创建函数,分别为动态创建与静态创建。
函数原型为:

/* 动态创建任务 */
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 ) /* 任务句柄 */
/* 静态创建任务 */
TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,/* 任务的函数入口 */
								const char * const pcName, /* 任务名字 */
						const uint32_t ulStackDepth,/* 栈的深度 */
							void * const pvParameters,/* 传入参数 */
								UBaseType_t uxPriority,/* 任务优先级 */
					StackType_t * const puxStackBuffer,/* 任务的数据栈 */
					StaticTask_t * const pxTaskBuffer )/* 任务的信息栈、句柄 */

二、任务创建的内部实现

在FreeRTOS内部,每个任务都有自己专有的任务栈来存储相关信息,所以在创建一个任务时需要进行的操作包括:1)分配栈内存 2)
任务需要两个内存块,分别用来保存任务的数据、任务的控制块(TCB)。 使用xTaskCreate()创建任务时,这两个内存块在函数内部自动被申请了,不需要用户手动配置。
一般情况下,对于堆来说,生长方向是向上增长的,即堆的内存地址是增加的;对于栈来说,栈是向下生长的,即栈的内存地址是减小的。
在FreeRTOS源码中,对生长方向不同的栈都做了处理。
1)对于栈向下生长的情况,在创建任务时,先申请任务栈空间,再申请TCB空间。
2)对于栈向上生长的情况,在创建任务时,先申请TCB空间,再申请任务栈空间

本文中的栈是向下生长的。

1.任务内存申请

创建任务时,通过执行以下代码可以保证任务已经有内存空间了,并且通过TCB我就可以访问该任务的栈空间。TCB是一个任务的所有,任务的全部家当都受TCB控制。真不愧叫TCB(task control block)任务控制块啊。

StackType_t *pxStack;/* 任务栈内存申请 */
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL )
{
	pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );/* TCB内存申请 */
	if( pxNewTCB != NULL )
	pxNewTCB->pxStack = pxStack; /* 在TCB中存储任务栈的地址 */
}

2.任务初始化

在开始的任务创建函数中,传入的参数包括,任务入口,任务名字,任务栈深度,任务参数,任务优先级,任务句柄(TCB)
既然传入了这些参数,就需要把这些参数融合进任务中,如何实现任务的初始化呢?

2.1 初始化任务栈

/* 将任务的栈空间全部添入0xa5 : 1010 0101 填入0xa5的目的是增强抗干扰和方便调试 */
( void ) memset( pxNewTCB->pxStack, ( int ) 0xa5U, ( size_t ) ulStackDepth * sizeof( StackType_t ) );

StackType_t *pxTopOfStack;
/* 该指针指向任务栈的栈顶 注意:栈的地址向下生长 */
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( 0x0007) ) );/* 进行对齐操作 */
/* 在TCB中存储任务的名字 */
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
	pxNewTCB->pcTaskName[ x ] = pcName[ x ];
}
/* 在任务中存储任务优先级 */
pxNewTCB->uxPriority = uxPriority;

2.2 伪造现场

执行完以上代码后,任务置办的家当包括:一块土地、名字、社会地位,有了土地,但是还没有建筑。在这块土地上,我们要构建的建筑包括:任务入口(函数入口),传入的参数。
以下代码是伪造现场。为什么要伪造现场呢?是因为在RTOS中,任务是交替执行的,任务在发生交换时,会保存现场,任务执行完,会返回现场继续执行。但是在任务第一次创建的时候,需要我们自己创建现场,在以后的日子里,现场将由硬件完成保存。

/* 传入的参数包括了,指向任务栈顶的指针,任务入口地址,任务相关参数 */
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
/* 具体的伪造过程如下 */
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	pxTopOfStack--;/* 增加的偏移量用于说明MCU在中断进入/退出时使用堆栈的方式 */
	*pxTopOfStack = 0x01000000;	/* xPSR 程序状态寄存器的第24位置1,表示使用thumb指令集 */
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & 0xfffffffeUL;	/* PC CM3 中的指令至少是半字对齐的,所以 PC 的 LSB 总是读回 0*/
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR 保存函数的返回地址,返回函数使用汇编编写,关闭了所有中断,并进入死循环,*/

	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */
	return pxTopOfStack;
}

程序状态寄存器的第24位表示使用的thumb指令集
在这里插入图片描述
在这里插入图片描述

2.3 初始化任务句柄

将任务的TCB结构体赋值给任务句柄,这样通过操作任务句柄就能操作整个任务了。
通过任务句柄,我们可以改变任务优先级、删除任务、挂起任务、通知任务等操作。

if( ( void * ) pxCreatedTask != NULL )
	{
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}

2.4 将任务放入链表

前面我们为任务买了土地、盖了房子、取了名字、获取了社会地位,可谓是万事俱备,只欠东风了,接下来我们就应该将任务扔到残酷的社会的不同环境中进行工作。放入不同的环境在Free RTOS中即放入不同的链表。新创建的任务是处于就绪状态,时刻准备着工作。

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	uxCurrentNumberOfTasks++;/* 用于记录链表中存在的任务数 */
	if( pxCurrentTCB == NULL )/* 当前没有任务或者其余任务都挂起了 */
	{
		pxCurrentTCB = pxNewTCB;
		if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
		{
			prvInitialiseTaskLists();
		}
	}
	else /* 当前任务不止有一个 对比优先级 */
	{
		if( xSchedulerRunning == pdFALSE )
		{
			if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
			{
				pxCurrentTCB = pxNewTCB;
			}
		}
		
	}
}

总结

以上就是本次学习的主要内容,主要概述了Free RTOS创建任务的大致流程,而Free RTOS还提供了静态创建任务,但是其大概的过程是一样的,具体可以阅读Free RTOS源码。以上内容参考FreeRTOS Kernel V10.0.1 源码和《Cortex-M3内核权威指南》。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值