8.05.01OSTaskStkInt()
OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInt()来初始化任务的堆栈结构,因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。图8.3显示了OSTaskStkInt()放到正被建立的任务堆栈中的东西。注意,在这里我假定了堆栈是从上往下长的。下面的讨论同样适用于从下往上长的堆栈。
在用户建立任务的时候,用户会传递任务的地址,pdata指针,任务的堆栈栈顶和任务的优先级给OSTaskCreate()和OSTaskCreateExt()。虽然OSTaskCreateExt()还要求有其它的参数,但这些参数在讨论OSTaskStkInt()的时候是无关紧要的。为了正确初始化堆栈结构,OSTaskStkInt()只要求刚才提到的前三个参数和一个附加的选项,这个选项只能在OSTaskCreateExt()中得到。
图 8.3 堆栈初始化(pdata通过堆栈传递)
回顾一下,在μC/OS-Ⅱ中,无限循环的任务看起来就像其它的C函数一样。当任务开始被μC/OS-Ⅱ执行时,任务就会收到一个参数,好像它被其它的任务调用一样。
voidMyTask(void*pdata)
{
/* 对'pdata'做某些操作 */
for(;;){
/* 任务代码 */
}
}
如果我想从其它的函数中调用MyTask(),C编译器就会先将调用MyTask()的函数的返回地址保存到堆栈中,再将参数保存到堆栈中。实际上有些编译器会将pdata参数传至一个或多个寄存器中。在后面我会讨论这类情况。假定pdata会被编译器保存到堆栈中,OSTaskStkInit()就会简单的模仿编译器的这种动作,将pdata保存到堆栈中[F8.3(1)]。但是结果表明,与C函数调用不一样,调用者的返回地址是未知的。用户所拥有的是任务的开始地址,而不是调用该函数(任务)的函数的返回地址!事实上用户不必太在意这点,因为任务并不希望返回到其它函数中。
这时,用户需要将寄存器保存到堆栈中,当处理器发现并开始执行中断的时候,它会自动地完成该过程的。一些处理器会将所有的寄存器存入堆栈,而其它一些处理器只将部分寄存器存入堆栈。一般而言,处理器至少得将程序计数器的值(中断返回地址)和处理器的状态字存入堆栈[F8.3(2)]。很明显,处理器是按一定的顺序将寄存器存入堆栈的,而用户在将寄存器存入堆栈的时候也就必须依照这一顺序。
接着,用户需要将剩下的处理器寄存器保存到堆栈中[F8.3(3)]。保存的命令依赖于用户的处理器是否允许用户保存它们。 有些处理器用一个或多个指令就可以马上将许多寄存器都保存起来。用户必须用特定的指令来完成这一过程。例如,Intel80x86使用PUSHA指令将8个寄存器保存到堆栈中。对Motorola68HC11处理器而言,在中断响应期间,所有的寄存器都会按一定顺序自动的保存到堆栈中,所以在用户将寄存器存入堆栈的时候,也必须依照这一顺序。
现在是时候讨论这个问题了: 如果用户的C编译器将pdata参数传递到寄存器中而不是堆栈中该作些什么?用户需要从编译器的文档中找到pdata储存在哪个寄存器中。pdata的内容就会随着这个寄存器的储存被放置在堆栈中。
图 8.4 堆栈初始化(pdata通过寄存器传递)
一旦用户初始化了堆栈,OSTaskStkInit()就需要返回堆栈指针所指的地址[F8.3(4)]。
OSTaskCreate()和OSTaskCreateExt()会获得该地址并将它保存到任务控制块(OS_TCB)中。
处理器文档会告诉用户堆栈指针会指向下一个堆栈空闲位置, 还是会指向最后存入数据的堆栈单元位置。例如,对Intel80x86处理器而言,堆栈指针会指向最后存入数据的堆栈单元位置,而对Motorola68HC11处理器而言,堆栈指针会指向下一个空闲的位置。
8.05.02OSTaskCreateHook()
当用OSTaskCreate()或OSTaskCreateExt()建立任务的时候就会调用OSTaskCreateHook()。该函数允许用户或使用用户的移植实例的用户扩展μC/OS-Ⅱ的功能。
当μC/OS-Ⅱ设置完了自己的内部结构后,会在调用任务调度程序之前调用OSTaskCreateHook()。该函数被调用的时候中断是禁止的。因此用户应尽量减少该函数中的代码以缩短中断的响应时间。
当OSTaskCreateHook()被调用的时候,它会收到指向已建立任务的OS_TCB的指针,这样它就可以访问所有的结构成员了。当使用OSTaskCreate()建立任务时,OSTaskCreateHook()的功能是有限的。但当用户使用OSTaskCreateExt()建立任务时,用户会得到OS_TCB中的扩展指针OSTCBExtPtr),该指针可用来访问任务的附加数据,如浮点寄存器,MMU寄存器,任务计数器的内容,以及调试信息。
只用当OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生OSTaskCreateHook()的代码。这样,使用用户的移植实例的用户可以在其它的文件中重新定义hook函数。
8.05.03OSTaskDelHook()
当任务被删除的时候就会调用OSTaskDelHook()。 该函数在把任务从μC/OS-Ⅱ的内部任务链表中解开之前被调用。当OSTaskDelHook()被调用的时候,它会收到指向正被删除任务的OS_TCB的指针, 这样它就可以访问所有的结构成员了。 OSTaskDelHook()可以用来检验TCB扩展是否被建立了(一个非空指针)并进行一些清除操作。OSTaskDelHook()不返回任何值。
只用当OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生OSTaskDelHook()的代码。
8.05.04OSTaskSwHook()
当发生任务切换的时候调用OSTaskSwHook()。不管任务切换是通过OSCtxSw()还是OSIntCtxSw()来执行的都会调用该函数。OSTaskSwHook()可以直接访问OSTCBCur和OSTCBHighRdy,因为它们是全局变量。OSTCBCur指向被切换出去的任务的OS_TCB,而OSTCBHighRdy指向新任务的OS_TCB。 注意在调用OSTaskSwHook()期间中断一直是被禁止的。
因为代码的多少会影响到中断的响应时间,所以用户应尽量使代码简化。OSTaskSwHook()没有任何参数,也不返回任何值。
只用当OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生 OSTaskSwHook()的代码。
8.05.05OSTaskStatHook()
OSTaskStatHook()每秒钟都会被OSTaskStat()调用一次。用户可以用OSTaskStatHook()来扩展统计功能。例如,用户可以保持并显示每个任务的执行时间,每个任务所用的CPU份额,以及每个任务执行的频率等等。OSTaskStatHook()没有任何参数,也不返回任何值。
只用当OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生OSTaskStatHook()的代码。
8.05.06OSTimeTickHook()
OSTaskTimeHook()在每个时钟节拍都会被OSTaskTick()调用。实际上,OSTaskTimeHook()是在节拍被μC/OS-Ⅱ真正处理,并通知用户的移植实例或应用程序之前被调用的。OSTaskTimeHook()没有任何参数,也不返回任何值。
只用当OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生OSTaskTimeHook()的代码。
无论何时建立任务,在分配好和初始化TCB后就会调用该函数,当然任务的堆栈结构也已经初始化好了。OSTaskCreateHook()允许用户用自己的方式来扩展任务建立函数的功能。例如用户可以初始化和存储与任务相关的浮点寄存器,MMU寄存器以及其它寄存器的内容。通常,用户可以存储用户的应用程序所分配的附加的内存信息。用户还可以通过使用OSTaskCreateHook()来触发示波器或逻辑分析仪,以及设置断点。
参数
ptcb是指向所创建任务的任务控制块的指针。
返回值
无
注意事项
该函数在被调用的时候中断是禁止的。 因此用户应尽量减少该函数中的代码以缩短中断
的响应时间。
范例
该例子假定了用户是用OSTaskCreateExt()建立任务的,因为它希望在任务OS_TCB中
有.OSTCBExtPtr域,该域包含了指向浮点寄存器的指针。
VoidOSTaskCreateHook(OS_TCB*ptcb)
{
if(ptcb->OSTCBExtPtr!=(void*)0){
/* 储存浮点寄存器的内容到..*/
/*..TCB扩展域中 */
}
}
当用户通过调用OSTaskDel()来删除任务时都会调用该函数。这样用户就可以处理OSTaskCreateHook()所分配的内存。 OSTaskDelHook()就在TCB从TCB链中被移除前被调用。
用户还可以通过使用OSTaskDelHook()来触发示波器或逻辑分析仪,以及设置断点。
参数
ptcb是指向所创建任务的任务控制块的指针。
返回值
无
注意事项
该函数在被调用的时候中断是禁止的。 因此用户应尽量减少该函数中的代码以缩短中断
的响应时间。
范例
voidOSTaskDelHook(OS_TCB*ptcb)
{
/* 输出信号触发示波器 */
}
当执行任务切换时都会调用该函数。全局变量OSTCBHighRdy指向得到CPU的任务的TCB,而OSTCBCur指向被切换出去的任务的TCB。OSTaskSwHook()在保存好了任务的寄存器和保存好了指向当前任务TCB的堆栈指针后马上被调用。 用户可以用该函数来保存或恢复浮点寄存器或MMU寄存器的内容,来得到任务执行时间的轨迹以及任务被切换进来的次数等等。
参数
无
返回值
无
注意事项
该函数在被调用的时候中断是禁止的。 因此用户应尽量减少该函数中的代码以缩短中断的响应时间。
范例
voidOSTaskSwHook(void)
{
/* 将浮点寄存器的内容储存在当前任务的TCB扩展域中。 */
/* 用新任务的TCB扩展域中的值更新浮点寄存器的内容。 */
}
该函数每秒钟都会被μC/OS-Ⅱ的统计任务调用。OSTaskStatHook()允许用户加入自己的统计功能。
参数
无
返回值
无
注意事项
统计任务大概在调用OSStart()后再过5秒开始执行。注意,当OS_TASK_STAT_EN或者
OS_TASK_CREATE_EXT_EN被置为0时,该函数不会被调用。
范例
voidOSTaskStatHook(void)
{
/* 计算所有任务执行的总时间 */
/* 计算每个任务的执行时间在总时间内所占的百分比 */
}
只要发生时钟节拍,该函数就会被OSTimeTick()调用。一旦进入OSTimeTick()就会马上调用OSTimeTickHook()以允许执行用户的应用程序中的与时间密切相关的代码。用户还可以通过使用该函数触发示波器或逻辑分析仪来调试,或者为仿真器设置断点。
参数
无
返回值
无
注意事项
OSTimeTick()通常是被ISR调用的, 所以时钟节拍ISR的执行时间会因为用户在该函数
中提供的代码而增加。当OSTimeTick()被调用的时候,中断可以是禁止的也可以是允许的,
这主要取决于该处理器上的移植是怎样进行的。如果中断是禁止的,该函数将会影响到中断
响应时间。
范例
voidOSTimeTickHook(void)
{
/* 触发示波器 */
}