文章目录
- 创建任务 xTaskCreate函数原型
- 栈深度 usStackDepth 大小如何确定
- 任务堆空间分配
 
- 任务控制块 TCB
 
创建任务 xTaskCreate函数原型
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 )
根据前面的文章可知,要创建一个任务需要 4个要素:运行函数,函数参数,栈,任务优先级。
 在RTOS系统中创建任务函数 xTaskCreate,传入参数解析:
- TaskFunction_t pxTaskCode :运行函数
- const char * const pcName :函数名字
- const configSTACK_DEPTH_TYPE usStackDepth :栈大小
- void * const pvParameters :运行函数的参数
- UBaseType_t uxPriority : 函数优先级
- TaskHandle_t * const pxCreatedTask :任务控制块TCB 输出参数
栈深度 usStackDepth 大小如何确定
通过简单的示例来看下,普通的函数会产生多少栈大小,如何计算?
 
 (1)PUSH {r4-r5,lr}: 将寄存器r4, r5和链接寄存器(lr)压入栈中。这是为了保存这些寄存器的值,以便函数调用结束后可以恢复它们。
 (2)SUB sp,sp,#0x194: 从栈指针sp减去0x194(404),为局部变量分配栈空间。
 (3)MOV r5, r1: 将r1寄存器的值移动到r5寄存器。这通常是将输入参数的一个值保存到另一个寄存器中。
 (4)MOV r4, r0: 将r0寄存器的值移动到r4寄存器。同样,这可能是保存另一个输入参数。
 (5)MOV r1, #0x18C: 将立即数0x18C(404)移动到r1寄存器。
 (6)ADD r0, sp, #0x04: 将栈指针sp加上4,然后将结果移动到r0寄存器。
 (7)BL.W 0x0800020C __aeabi_memclr4: BL.W是ARM的分支链接指令,用于远距离跳转,并在链接寄存器中保存返回地址。
 (8)LDR r0, [pc, #24]: 从程序计数器pc地址加上24的位置加载数据到r0寄存器。
 (9)LDR r0, [r0, #0x00]: 从r0寄存器指向的地址加载数据到r0。
 (10)STR r0, [sp, #0x00]: 将r0寄存器的值存储到栈指针sp地址偏移0的位置。
 (11)LDR r0, [r4, #0x00]: 从r4寄存器指向的地址加载数据到r0。
 (12)STR r0, [sp, #0x190]: 将r0寄存器的值存储到栈指针sp地址偏移0x190的位置。
 (13)LDR r0, [r5, #0x00]: 从r5寄存器指向的地址加载数据到r0。
 (14)LDR r1, [sp, #0x190]: 从栈指针sp地址偏移0x190的位置加载数据到r1。
 (15)ADD r0, r0, r1: 将r0和r1寄存器的值相加,结果存储在r0。
 (16)STR r0, [sp, #0x190]: 将r0寄存器的值再次存储到栈指针sp地址偏移0x190的位置。
 (17)STR r0, [r4, #0x00]: 将r0寄存器的值存储到r4寄存器指向的地址。
 (18)ADD sp, sp, #0x194: 将栈指针sp加上0x194,释放之前分配的栈空间。
 (19)POP {r4-r5,pc}: 从栈中弹出之前保存的r4, r5和pc值,恢复它们,并返回到调用这个函数的代码处。
 
 栈大小可以根据任务中使用的临时变量估算出需要多大的栈空间,当然函数传入的栈大小要比估算的大些,以免超出越界访问。(一般为估算大小的两倍)
任务堆空间分配

 #define configTOTAL_HEAP_SIZE ((size_t)3072)
 RTOS系统中的栈是ucHeap数据,大小为3072字节
 每创建一个任务都会在ucHeap堆空间申请出自己的堆空间。
任务控制块 TCB
作用:在栈中为每个任务分配一个任务控制块(TCB)。存储任务状态信息,包括指向任务上下文的指针(任务的运行时环境,包括寄存器值)。完成创建任务后,会将任务的TCB赋给 pxCreatedTask 参数
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
结构体TCB
typedef struct tskTaskControlBlock
{volatile StackType_t    *pxTopOfStack;    /* 指向任务堆栈中最后一项的位置。这必须是TCB结构体的第一个成员。*/ListItem_t            xStateListItem;    /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ListItem_t            xEventListItem;        /*< Used to reference a task from an event list. */UBaseType_t            uxPriority;            /* 任务优先级。  0 是优先级最低的。 */StackType_t            *pxStack;            /* 栈的最开始指针 */char            pcTaskName[ configMAX_TASK_NAME_LEN ];/* 任务名称 */ } tskTCB;
由于任务会在栈中会以TCB结构体的形式描述,因此 xTaskCreate函数传入的参数都应该在这个结构中体现。
| const char * const pcName :函数名字 | char pcTaskName[ configMAX_TASK_NAME_LEN ]; | 
|---|---|
| UBaseType_t uxPriority : 函数优先级 | UBaseType_t uxPriority; | 
| const configSTACK_DEPTH_TYPE usStackDepthvolatile :栈深度 | StackType_t *pxTopOfStack StackType_t *pxStack; | 
发现在TCB结构体中没有最重要的运行函数以及参数,而在TCB结构体中存在两个链表。
 任务在创建的时候,会将PC指向函数名字,R0寄存器存的参数。在任务切换的时候,要将全部环境保存下来,当下次获得CPU使用权的时候,恢复现场运行任务。因此任务栈中保存一套任务环境。
 
StackType_t *pxStack;pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxNewTCB != NULL )
{/* Store the stack location in the TCB. */pxNewTCB->pxStack = pxStack;
}