- 接着,CPU会调用正确的ISR。µC/OS-Ⅱ要求用户的ISR在开始时要保存剩下的处理器寄存器[F8.2(2)]。一旦寄存器保存好了,µC/OS-Ⅱ就要求用户或者调用OSIntEnter(),或者将变量OSIntNesting加1。在这个时候,被中断任务的堆栈中只包含了被中断任务的寄存器内容。现在,ISR可以执行中断服务了。并且如果ISR发消息给任务(通过调用OSMboxPost()或OSQPost()),恢复任务(通过调用OSTaskResume()),或者调用OSTimeTick()或OSTimeDlyResume()的话,有可能使更高优先级的任务处于就绪状态。
- 假设有一个更高优先级的任务处于就绪状态。µC/OS-Ⅱ要求用户的ISR在完成中断服务的时候调用OSIntExit()。OSIntExit()会告诉µC/OS-Ⅱ到了返回任务级代码的时间了。调用OSIntExit()会导致调用者的返回地址被保存到被中断的任务的堆栈中[F8.2(3)]。
- OSIntExit()刚开始时会禁止中断,因为它需要执行临界段的代码。根据OS_ENTER_CRITICAL()的不同执行过程(参看8.03.02),处理器的状态寄存器会被保存到被中断的任务的堆栈中[F8.2(4)]。OSIntExit()注意到由于有更高优先级的任务处于就绪状态,被中断的任务已经不再是要继续执行的任务了。在这种情况下,指针OSTCBHighRdy会被指向新任务的OS_TCB,并且OSIntExit()会调用OSIntCtxSw()来执行任务切换。调用OSIntCtxSw()也同样使返回地址被保存到被中断的任务的堆栈中[F8.2(5)]。
- 在用户切换任务的时候,用户只想将某些项([F8.2(1)]和[F8.2(2)])保留在堆栈中,并忽略其它项(F8.2(3),(4)和(5))。这是通过调整堆栈指针(加一个数在堆栈指针上)来完成的[F8.2(6)]。加在堆栈指针上的数必须是明确的,而这个数主要依赖于移植的目标处理器(地址空间可能是16,32或64位),所用的编译器,编译器选项,内存模式等等。另外,处理器状态字可能是8,16,32甚至64位宽,并且OSIntExit()可能会分配局部变量。有些处理器允许用户直接增加常量到堆栈指针中,而有些则不允许。在后一种情况下,可以通过简单的执行一定数量的pop(出栈)指令来实现相同的功能。一旦堆栈指针完成调整,新的堆栈指针会被保存到被切换出去的任务的OS_TCB中[F8.2(7)]。
- OSIntCtxSw()的原型如程序清单 L8.3所示。这些代码必须写在汇编语言中,因为用户不能直接从C语言中访问CPU寄存器。如果用户的编译器支持插入汇编语言代码的话,用户就可以将OSIntCtxSw()代码放到OS_CPU_C.C文件中,而不放到OS_CPU_A.ASM文件中。正如用户所看到的那样,除了第一行以外,OSIntCtxSw()的代码与OSCtxSw()是一样的。这样在移植实例中,用户可以通过“跳转”到OSCtxSw()中来减少OSIntCtxSw()代码量。
- 程序清单 L 8.3 OSIntCtxSw()的原型
- void OSIntCtxSw(void)
- {
- 调整堆栈指针来去掉在调用:
- OSIntExit(),
- OSIntCtxSw()过程中压入堆栈的多余内容;
- 将当前任务堆栈指针保存到当前任务的OS_TCB中:
- OSTCBCur->OSTCBStkPtr = 堆栈指针;
- 调用用户定义的OSTaskSwHook();
- OSTCBCur = OSTCBHighRdy;
- OSPrioCur = OSPrioHighRdy;
- 得到需要恢复的任务的堆栈指针:
- 堆栈指针 = OSTCBHighRdy->OSTCBStkPtr;
- 将所有处理器寄存器从新任务的堆栈中恢复出来;
- 执行中断返回指令;
- }
- 8.04.04 OSTickISR()
- µC/OS-Ⅱ要求用户提供一个时钟资源来实现时间的延时和期满功能。时钟节拍应该每秒钟发生10-100次。为了完成该任务,可以使用硬件时钟,也可以从交流电中获得50/60Hz的时钟频率。
- 用户必须在开始多任务调度后(即调用OSStart()后)允许时钟节拍中断。换句话说,就是用户应该在OSStart()运行后,µC/OS-Ⅱ启动运行的第一个任务中初始化节拍中断。通常所犯的错误是在调用OSInit()和OSStart()之间允许时钟节拍中断(如程序清单 L8.4所示)。
- 程序清单 L 8.4 在不正确的位置启动时钟节拍中断
- void main(void)
- {
- .
- .
- OSInit(); /* 初始化 オµC/OS-II */
- .
- .
- /* 应用程序初始化代码 ... */
- /* ... 调用OSTaskCreate()建立至少一个任务 */
- .
- .
- 允许时钟节拍中断; /* 千万不要在这里允许!!! */
- .
- .
- OSStart(); /* 开始多任务调度 */
- }
- 有可能在µC/OS-Ⅱ开始执行第一个任务前时钟节拍中断就发生了。在这种情况下,µC/OS-Ⅱ的运行状态不确定,用户的应用程序也可能会崩溃。
- 时钟节拍ISR的原型如程序清单 L8.5所示。这些代码必须写在汇编语言中,因为用户不能直接从C语言中访问CPU寄存器。如果用户的处理器可以通过单条指令来增加OSIntNesting,那么用户就没必要调用OSIntEnter()了。增加OSIntNesting要比通过函数调用和返回快得多。OSIntEnter()只增加OSIntNesting,并且作为临界段代码中受到保护。
- 程序清单 L 8.5 时钟节拍ISR的原型
- void OSTickISR(void)
- {
- 保存处理器寄存器;
- 调用OSIntEnter()或者直接将 OSIntNesting加1;
- 调用OSTimeTick();
- 调用OSIntExit();
- 恢复处理器寄存器;
- 执行中断返回指令;
- }
0430
最新推荐文章于 2023-09-26 17:42:14 发布