当前位置:   article > 正文

创建任务7.24_pxportinitialisestack

pxportinitialisestack

创建任务

1、什么是任务

在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。

多任务中系统的任务形式如下

void task_entry (void *parg)
{
   /* 任务主体,无限循环且不能返回 */
   for (;;) {
    /* 任务主体代码 */
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、创建任务

(1)定义任务栈

在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好全局数组,也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。

在多任务系统中,有多少个任务就需要定义多少个任务栈。

其定义类似如下:

#define TASK1_STACK_SIZE 128 (2)
StackType_t Task1Stack[TASK1_STACK_SIZE]; (1)

#define TASK2_STACK_SIZE 128
StackType_t Task2Stack[TASK2_STACK_SIZE];
  • 1
  • 2
  • 3
  • 4
  • 5

(2)定义任务函数

任务是一个独立的函数,函数主体无限循环且不能返回

其定义类似如下:

/* 软件延时 */
void delay (uint32_t count)
{
	for (; count!=0; count--);
}
/* 任务 1 */
void Task1_Entry( void *p_arg )
{
	for ( ;; ){
	}
}	
/* 任务 2 */
void Task2_Entry( void *p_arg ) 
{
	for ( ;; ){
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(3)定义任务控制块

在裸机系统中,程序的主体是 CPU 按照顺序执行的。而在多任务系统中,任务的执行是由系统调度的。

系统为了顺利的调度任务,为每个任务都额外定义了一个任务控制块,这个任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针,任务名称,任务的形参等。有了这个任务控制块之后,以后系统对任务的全部操作都可以通过这个任务控制块来实现。

任务控制块类型声明如下所示:

typedef struct tskTaskControlBlock
{
	volatile StackType_t *pxTopOfStack; /* 栈顶 */ 
	ListItem_t xStateListItem; /* 任务节点,这是一个内置在 TCB 控制块中的链表节点,通过这个节点,可以将任务控制块挂接到各种链表中。*/  
	StackType_t *pxStack; /* 任务栈起始地址 */ 
	/* 任务名称,字符串形式 */
	char pcTaskName[ configMAX_TASK_NAME_LEN ];
} tskTCB;
typedef tskTCB TCB_t;

/* 定义任务控制块 */
TCB_t Task1TCB;
TCB_t Task2TCB;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3、实现任务创建函数

任务的栈,任务的函数实体,任务的控制块最终需要联系起来才能由系统进行统一调度。这个联系的工作就由任务创建函数 xTaskCreateStatic()来实现。

(1)xTaskCreateStatic()函数

#if( configSUPPORT_STATIC_ALLOCATION == 1 ) 

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,//任务入口,即任务的函数名称。
							const char * const pcName, //任务名称,字符串形式
							const uint32_t ulStackDepth, //:任务栈大小,单位为字
							void * const pvParameters,//任务形参。 														StackType_t * const puxStackBuffer, //任务栈起始地址
							TCB_t * const pxTaskBuffer//任务控制块指针 ) 
{
	TCB_t *pxNewTCB;
	TaskHandle_t xReturn; //定义一个任务句柄 xReturn,任务句柄用于指向任务的 TCB

	if ( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ){
		pxNewTCB = ( TCB_t * ) pxTaskBuffer;
		pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
 
		/* 创建新的任务,调用 prvInitialiseNewTask()函数,创建新任务 */ 
		prvInitialiseNewTask( pxTaskCode, /* 任务入口 */
		pcName, /* 任务名称,字符串形式 */
		ulStackDepth, /* 任务栈大小,单位为字 */
		pvParameters, /* 任务形参 */
		&xReturn, /* 任务句柄 */
		pxNewTCB); /* 任务栈起始地址 */
	}
	else{
		xReturn = NULL;
	}
 
	/* 返回任务句柄,如果任务创建成功,此时 xReturn 应该指向任务控制块 */
	return xReturn;
}
	 
#endif /* configSUPPORT_STATIC_ALLOCATION */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

FreeRTOS 中,任务的创建有两种方法,一种是使用动态创建,一种是使用静态创建。

动态创建时,任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可以释放。静态创建时,任务控制块和栈的内存需要事先定义好,是静态的内存,任务删除时 ,内存不能释放 。

上面是静态创建例子:configSUPPORT_STATIC_ALLOCATION 在 FreeRTOSConfig.h 中定义,配置为 其为1。

(2)prvInitialiseNewTask()函数

static void prvInitialiseNewTask(TaskFunction_t pxTaskCode, 
								const char * const pcName, 
								const uint32_t ulStackDepth, 
								void * const pvParameters, 
								TaskHandle_t * const pxCreatedTask,
								TCB_t *pxNewTCB ) 
{
	StackType_t *pxTopOfStack;
	UBaseType_t x;
 
	/* 获取栈顶地址 */ 
	pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - (uint32_t ) );
	/* 向下做 8 字节对齐 */
	pxTopOfStack = ( StackType_t * ) \( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t )0x0007 ) ) );
 
	/* 将任务的名字存储在 TCB 中 */ 
	for ( x = (UBaseType_t)0;x<(UBaseType_t)configMAX_TASK_NAME_LEN;x++ ){
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];
		
		if ( pcName[ x ] == 0x00 ){
			break;
		}
	}
	/* 任务名字的长度不能超过 configMAX_TASK_NAME_LEN */ 
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

	/* 初始化 TCB 中的 xStateListItem 节点 */ 
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	/* 设置 xStateListItem 节点的拥有者 */ 
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); 

	/* 初始化任务栈 */ 
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack,
													pxTaskCode,
													pvParameters );
	
	 /* 让任务句柄指向任务控制块 */
	if ( ( void * ) pxCreatedTask != NULL ){
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

(3)pxPortInitialiseStack()函数

#define portINITIAL_XPSR ( 0x01000000 )
#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )

static void prvTaskExitError( void )
{
	/* 函数停止在这里 */
	for (;;);
}

StackType_t *pxPortInitialiseStack( StackType_t 														*pxTopOfStack,
								TaskFunction_t pxCode,
								void *pvParameters )
{
	/* 异常发生时,自动加载到 CPU 寄存器的内容 */ 
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR; 
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; 
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;
	pxTopOfStack -= 5; /* R12, R3, R2 and R1 默认初始化为 0 */
	*pxTopOfStack = ( StackType_t ) pvParameters; 

	/* 异常发生时,手动加载到 CPU 寄存器的内容 */ 
	pxTopOfStack -= 8;
 
	/* 返回栈顶指针,此时 pxTopOfStack 指向空闲栈 */
	return pxTopOfStack; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/925296
推荐阅读
相关标签
  

闽ICP备14008679号