文章目录
- 创建任务 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;
}