赞
踩
在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。
多任务中系统的任务形式如下
void task_entry (void *parg)
{
/* 任务主体,无限循环且不能返回 */
for (;;) {
/* 任务主体代码 */
}
}
在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于 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];
任务是一个独立的函数,函数主体无限循环且不能返回。
其定义类似如下:
/* 软件延时 */ 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 ( ;; ){ } }
在裸机系统中,程序的主体是 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;
任务的栈,任务的函数实体,任务的控制块最终需要联系起来才能由系统进行统一调度。这个联系的工作就由任务创建函数 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 */
FreeRTOS 中,任务的创建有两种方法,一种是使用动态创建,一种是使用静态创建。
动态创建时,任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可以释放。静态创建时,任务控制块和栈的内存需要事先定义好,是静态的内存,任务删除时 ,内存不能释放 。
上面是静态创建例子:configSUPPORT_STATIC_ALLOCATION 在 FreeRTOSConfig.h 中定义,配置为 其为1。
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; } }
#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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。