- µC/OS-Ⅱ的移植实例要求用户编写六个简单的C函数:
- OSTaskStkInit()
- OSTaskCreateHook()
- OSTaskDelHook()
- OSTaskSwHook()
- OSTaskStatHook()
- OSTimeTickHook()
- 唯一必要的函数是OSTaskStkInit(),其它五个函数必须得声明但没必要包含代码。
回顾一下,在µC/OS-Ⅱ中,无限循环的任务看起来就像其它的C函数一样。当任务开始被µC/OS-Ⅱ执行时,任务就会收到一个参数,好像它被其它的任务调用一样。
void MyTask (void *pdata) |
{ |
/* 对'pdata'做某些操作 */ |
for (;;) { |
/* 任务代码 */ |
} |
} |
如果我想从其它的函数中调用MyTask(),C编译器就会先将调用MyTask()的函数的返回地址保存到堆栈中,再将参数保存到堆栈中。实际上有些编译器会将pdata参数传至一个或多个寄存器中。在后面我会讨论这类情况。假定pdata会被编译器保存到堆栈中,OSTaskStkInit()就会简单的模仿编译器的这种动作,将pdata保存到堆栈中[F8.3(1)]。但是结果表明,与C函数调用不一样,调用者的返回地址是未知的。用户所拥有的是任务的开始地址,而不是调用该函数(任务)的函数的返回地址!事实上用户不必太在意这点,因为任务并不希望返回到其它函数中。
这时,用户需要将寄存器保存到堆栈中,当处理器发现并开始执行中断的时候,它会自动地完成该过程的。一些处理器会将所有的寄存器存入堆栈,而其它一些处理器只将部分寄存器存入堆栈。一般而言,处理器至少得将程序计数器的值(中断返回地址)和处理器的状态字存入堆栈[F8.3(2)]。很明显,处理器是按一定的顺序将寄存器存入堆栈的,而用户在将寄存器存入堆栈的时候也就必须依照这一顺序。
接着,用户需要将剩下的处理器寄存器保存到堆栈中[F8.3(3)]。保存的命令依赖于用户的处理器是否允许用户保存它们。有些处理器用一个或多个指令就可以马上将许多寄存器都保存起来。用户必须用特定的指令来完成这一过程。例如,Intel 80x86使用PUSHA 指令将8个寄存器保存到堆栈中。对Motorola 68HC11处理器而言,在中断响应期间,所有的寄存器都会按一定顺序自动的保存到堆栈中,所以在用户将寄存器存入堆栈的时候,也必须依照这一顺序。
现在是时候讨论这个问题了:如果用户的C编译器将pdata参数传递到寄存器中而不是堆栈中该作些什么?用户需要从编译器的文档中找到pdata储存在哪个寄存器中。pdata的内容就会随着这个寄存器的储存被放置在堆栈中。
一旦用户初始化了堆栈,OSTaskStkInit()就需要返回堆栈指针所指的地址[F8.3(4)]。OSTaskCreate()和OSTaskCreateExt()会获得该地址并将它保存到任务控制块(OS_TCB)中。处理器文档会告诉用户堆栈指针会指向下一个堆栈空闲位置,还是会指向最后存入数据的堆栈单元位置。例如,对Intel 80x86处理器而言,堆栈指针会指向最后存入数据的堆栈单元位置,而对Motorola 68HC11处理器而言,堆栈指针会指向下一个空闲的位置。