UCOS-II的在建立任务函数中要对新建任务的堆栈进行初始化。堆栈初始化函数原型是:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg,OS_STK *ptos, INT16U opt);
void
p_arg:是任务开始执行时,传递给任务的参数的指针。
Ptos:是分配给任务的堆栈栈顶的指针。
Opt:在OSTaskCreate()函数中调用OSTaskStkInit()函数时,Opt为0。因为OSTaskStkInit()函数不支持在任务的建立过程中设置选项。OSTaskCreateExt()函数支持这个选项。
OSTaskStkInit()是一个指针函数,也就是返回值是一个指针,返回初始化后的堆栈的栈顶。
任务建立前要先建立任务堆栈。
我现在用的是LPC2214,ARM7内核,定义
typedef unsigned int
OS_STK为32位数据类型
#define
OS_STK TaskStartStk[TASK_STK_SIZE];
定义一个数组作为任务堆栈,堆栈长度512*4 = 2K字节,乘4是因为OS_STK是32位数据类型。
当调用函数OSTaskCreate()创建一个任务时,把数组的指针传递给函数OSTaskCreate()中的堆栈栈顶指针ptos,就可以把该数组和任务关联起来成为该任务的任务堆栈。
先了解下栈顶和栈底。
栈顶是堆栈中存储第一个数据的地方。栈底是堆栈中存储最后1个数据的地方。
栈顶和栈底刚开始我总是弄混,想当然认为栈顶应该就是在堆栈中所有数据的最上面。理解成最上面有歧义,因为还有个方向问题。可以把最先送入堆栈的数看做是最上面,也可以把最后进入堆栈的数看做是最上面。我觉得准确的理解就是栈顶是最先送入堆栈的数放的地方。
TaskStartStk[0]是数组中的最低地址,TaskStartStk[TASK_STK_SIZE -1]是数组中的最高地址。
如果是递增方式堆栈,那么栈顶就是最低地址&TaskStartStk[0],栈底是最高地址& TaskStartStk[TASK_STK_SIZE- 1]。
如果是递减方式堆栈,那么栈顶就是最高地址&TaskStartStk[TASK_STK_SIZE- 1],栈底是最低地址&TaskStartStk[0]。
Ucos在LPC2214上移植,堆栈为递减方式,
#define
OSTaskStkInit()移植代码如下:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg,OS_STK *ptos, INT16U opt)
{
}
OSTaskStkInit()返回值就是初始化完成后的堆栈指针,放在任务控制块的开始,OSTCBStkPtr中。堆栈初始化完成后堆栈指针是&TaskStartStk[TASK_STK_SIZE- 1-15];这个值应该放在R13(SP)中,这里为什么没处理R13呢?
在任务第一次开始执行时,操作系统首先得到任务的任务控制块,然后从任务控制块得到任务的堆栈指针,再把这个堆栈指针送到R13(SP)。然后再OSCtxSw()函数中把初始化的各个寄存器的值送到对应的CPU寄存器,所以在OSTaskStkInit()函数里不用处理R13。在任务执行过一次之后,任务被中断切换到其它任务之前,就会从R13(SP)得到堆栈指针当前的位置,在OSCtxSw()函数中把CPU的R0-R12,R14等值压到R13(SP)指定的堆栈中。
OSTaskStkInit()中数据进栈的顺序要和OSCtxSw()中数据出栈的顺序对应。
PC值是最后出栈,所以要最先进栈。把任务函数的地址压入堆栈。出栈后,任务函数地址送入PC后,就开始执行任务函数。
R14是返回地址,但是任务函数是一个无限循环,只有在任务调度时才会退出,而在任务调度时切换到另一个任务时,会把另一个任务堆栈中存储的R14的值送到R14,保证程序在另一个任务被中断时的断点继续运行。所以在初始化堆栈时,R14的值是没有用的,可以随意赋值。
R1-R12可以随意赋值。
R0用来存储任务传递的参数。选择R0,是因为任务的参数在编译时是通过R0来传递的。
最后设置一个任务运行时的状态寄存器CPSR值,压入堆栈。
返回堆栈初始化完成后的指针给任务控制块的OSTCBStkPtr。