原创 UCOS-II任务堆栈初始化函数移植

2010-9-1 17:38 3176 5 5 分类: MCU/ 嵌入式

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个数据的地方。


593d2a21-62f5-4d1c-a296-4cf189e33cf1.JPG


栈顶和栈底刚开始我总是弄混,想当然认为栈顶应该就是在堆栈中所有数据的最上面。理解成最上面有歧义,因为还有个方向问题。可以把最先送入堆栈的数看做是最上面,也可以把最后进入堆栈的数看做是最上面。我觉得准确的理解就是栈顶是最先送入堆栈的数放的地方。


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。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
5
关闭 站长推荐上一条 /3 下一条