UCOS-II的在建立任务函数中要对新建任务的堆栈进行初始化。堆栈初始化函数原型是:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt);
void (*task)(void *pd): 定义一个函数指针变量task,这个函数指针指向的函数有一个void *类型的参数,没有返回值。
p_arg:是任务开始执行时,传递给任务的参数的指针。
Ptos:是分配给任务的堆栈栈顶的指针。
Opt:在OSTaskCreate()函数中调用 OSTaskStkInit()函数时,Opt为0。因为OSTaskStkInit()函数不支持在任务的建立过程中设置选项。OSTaskCreateExt()函数支持这个选项。
OSTaskStkInit()是一个指针函数,也就是返回值是一个指针,返回初始化后的堆栈的栈顶。
任务建立前要先建立任务堆栈。
我现在用的是LPC2214,ARM7内核,定义
typedef unsigned int OS_STK;
OS_STK为32位数据类型
#define TASK_STK_SIZE 512
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 ARM_SYS_MODE (0x0000001FL)
OSTaskStkInit()移植代码如下:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt; /* 'opt' is not used, prevent warning*/
stk = ptos; /* Load stack pointer */
*(stk) = (OS_STK)task; /*PC EntryPoint */
*(--stk ) = (INT32U)0x14141414L; /*R14(LR) */
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7
*(--stk) = (INT32U)0x06060606L; /* R6
*(--stk) = (INT32U)0x05050505L; /* R5
*(--stk) = (INT32U)0x04040404L; /* R4
*(--stk) = (INT32U)0x03030303L; /* R3
*(--stk) = (INT32U)0x02020202L; /* R2
*(--stk) = (INT32U)0x01010101L; /* R1
*(--stk) = (INT32U)p_arg; /* R0 : argument
*(--stk) = (INT32U)ARM_SYS_MODE; /* CPSR (Enable both IRQ and FIQ interrupts) */
return (stk);
}
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。
文章评论(0条评论)
登录后参与讨论