μC/OS中的中断处理

μC/OS中,中断服务子程序要用汇编语言来写。然而,如果用户使用的C语言编译器支持在线汇编语言的话,用户可以直接将中断服务子程序代码放在C语言的程序文件中。中断服务子程序的示意码如程序清单L3.15所示。

程序清单 L3.15 μC/OS-II中的中断服务子程序.
用户中断服务子程序:                                                            
    保存全部CPU寄存器;                                      (1)                      
调用OSIntEnter()或OSIntNesting直接加1;               (2) 
    执行用户代码做中断服务;                                 (3)                  
    调用OSIntExit();                                     (4)                     
    恢复所有CPU寄存器;                                     (5)                      
    执行中断返回指令;                                      (6)         
用户代码应该将全部CPU寄存器推入当前任务栈[L3.15(1)]。注意,有些微处理器,例如Motorola68020(及68020以上的微处理器),做中断服务时使用另外的堆栈。
μC/OS-Ⅱ可以用在这类微处理器中,当任务切换时,寄存器是保存在被中断了的那个任务的栈中的。
	μC/OS-Ⅱ需要知道用户在做中断服务,故用户应该调用OSIntEnter(),或者将全程变量OSIntNesting[L3.15(2)]直接加1,如果用户使用的微处理器有存储器直接加1的单条指令的话。如果用户使用的微处理器没有这样的指令,必须先将OSIntNesting读入寄存器,再将寄存器加1,然后再写回到变量OSIatNesting中去,就不如调用OSIatEnter()。OSIntNesting是共享资源。OSIntEnter()把上述三条指令用开中断、关中断保护起来,以保证处理OSIntNesting时的排它性。直接给OSIntNesting加1比调用OSIntEnter()快得多,可能时,直接加1更好。要当心的是,在有些情况下,从OSIntEnter()返回时,会把中断开了。遇到这种情况,在调用OSIntEnter()之前要先清中断源,否则,中断将连续反复打入,用户应用程序就会崩溃!
	上述两步完成以后,用户可以开始服务于叫中断的设备了[L3.15(3)]。这一段完全取决于应用。μC/OS-Ⅱ允许中断嵌套,因为μC/OS-Ⅱ跟踪嵌套层数OSIntNesting。然而,为允许中断嵌套,在多数情况下,用户应在开中断之前先清中断源。
	调用脱离中断函数OSIntExit()[L3.15(4)]标志着中断服务子程序的终结,OSIntExit()将中断嵌套层数计数器减1。当嵌套计数器减到零时,所有中断,包括嵌套的中断就都完成了,此时μC/OS-Ⅱ要判定有没有优先级较高的任务被中断服务子程序(或任一嵌套的中断)唤醒了。如果有优先级高的任务进入了就绪态,μC/OS-Ⅱ就返回到那个高优先级的任务,OSIntExit()返回到调用点[L3.15(5)]。保存的寄存器的值是在这时恢复的,然后是执行中断返回指令[L3.16(6)]。注意,如果调度被禁止了(OSIntNesting>0),μC/OS-Ⅱ将被返回到被中断了的任务。
	以上描述的详细解释如图F3.5所示。中断来到了[F3.5(1)]但还不能被被CPU识别,也许是因为中断被μC/OS-Ⅱ或用户应用程序关了,或者是因为CPU还没执行完当前指令。一旦CPU响应了这个中断[F3.5(2)],CPU的中断向量(至少大多数微处理器是如此)跳转到中断服务子程序[F3.5(3)]。如上所述,中断服务子程序保存CPU寄存器(也叫做 CPU context)[F3.5(4)],一旦做完,用户中断服务子程序通知μC/OS-Ⅱ进入中断服务子程序了,办法是调用OSIntEnter()或者给OSIntNesting直接加1[F3.5(5)]。然后用户中断服务代码开始执行[F3.5(6)]。用户中断服务中做的事要尽可能地少,要把大部分工作留给任务去做。中断服务子程序通知某任务去做事的手段是调用以下函数之一:OSMboxPost(),OSQPost(),OSQPostFront(),OSSemPost()。中断发生并由上述函数发出消息时,接收消息的任务可能是,也可能不是挂起在邮箱、队列或信号量上的任务。用户中断服务完成以后,要调用OSIntExit()[F3.5(7)]。从时序图上可以看出,对被中断了的任务说来,如果没有高优先级的任务被中断服务子程序激活而进入就绪态,OSIntExit()只占用很短的运行时间。进而,在这种情况下,CPU寄存器只是简单地恢复[F3.5(8)]并执行中断返回指令[F3.5(9)]。如果中断服务子程序使一个高优先级的任务进入了就绪态,则OSIntExit()将占用较长的运行时间,因为这时要做任务切换[F3.5(10)]。新任务的寄存器内容要恢复并执行中断返回指令[F3.5(12)]。
图3.5 中断服务

	进入中断函数OSIntEnter()的代码如程序清单L3.16所示,从中断服务中退出函数OSIntExit()的代码如程序清单L3.17所示。如前所述,OSIntEnter()所做的事是非常少的。

程序清单 L3.16 通知μC/OS-Ⅱ,中断服务子程序开始了.
void OSIntEnter (void)
{
    OS_ENTER_CRITICAL();
    OSIntNesting++;
    OS_EXIT_CRITICAL();
}


程序清单 L3.17 通知μC/OS-Ⅱ,脱离了中断服务
void OSIntExit (void)
{
    OS_ENTER_CRITICAL();	                                            (1)
    if ((--OSIntNesting | OSLockNesting) == 0) {	                    (2)
        OSIntExitY    = OSUnMapTbl[OSRdyGrp];	                       (3)
        OSPrioHighRdy = (INT8U)((OSIntExitY << 3) +
                        OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
        if (OSPrioHighRdy != OSPrioCur) {
            OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];
            OSCtxSwCtr++;
            OSIntCtxSw();	                                           (4)
        }
    }
    OS_EXIT_CRITICAL();
}


	OSIntExit()看起来非常像OSSched()。但有三点不同。第一点,OSIntExit()使中断嵌套层数减1[L3.17(2)]而调度函数OSSched()的调度条件是:中断嵌套层数计数器和锁定嵌套计数器(OSLockNesting)二者都必须是零。第二个不同点是,OSRdyTbl[]所需的检索值Y是保存在全程变量OSIntExitY中的[L3.17(3)]。这是为了避免在任务栈中安排局部变量。这个变量在哪儿和中断任务切换函数OSIntCtxSw()有关,(见9.04.03节,中断任务切换函数)。最后一点,如果需要做任务切换,OSIntExit()将调用OSIntCtxSw()[L3.17(4)]而不是调用OS_TASK_SW(),正像在OSSched()函数中那样。
	调用中断切换函数OSIntCtxSw()而不调用任务切换函数OS_TASK_SW(),有两个原因,首先是,如程序清单中L3.5(1)和图F3.6(1)所示,一半的工作,即CPU寄存器入栈的工作已经做完了。第二个原因是,在中断服务子程序中调用OSIntExit()时,将返回地址推入了堆栈[L3.15(4)和F3.6(2)]。OSIntExit()中的进入临界段函数OS_ENTER_CRITICAL()或许将CPU的状态字也推入了堆栈L3.7(1)和F3.6(3)。这取决于中断是怎么被关掉的(见第8章移植μC/OS-Ⅱ)。最后,调用OSIntCtxSw()时的返回地址又被推入了堆栈[L3.17(4)和F3.1(4)],除了栈中不相关的部分,当任务挂起时,栈结构应该与μC/OS-Ⅱ所规定的完全一致。OSIntCtxSw()只需要对栈指针做简单的调整,如图F3.6(5)所示。换句话说,调整栈结构要保证所有挂起任务的栈结构看起来是一样的。

 

图3.6中断中的任务切换函数OSIntCtxSw()调整栈结构
 
有的微处理器,像Motorola 68HC11中断发生时CPU寄存器是自动入栈的,且要想允许中断嵌套的话,在中断服务子程序中要重新开中断,这可以视作一个优点。确实,如果用户中断服务子程序执行得非常快,用户不需要通知任务自身进入了中断服务,只要不在中断服务期间开中断,也不需要调用OSIntEnter()或OSIntNesting加1。程序清单L3。18中的示意代码表示这种情况。一个任务和这个中断服务子程序通讯的唯一方法是通过全程变量。
 
程序清单 L3.18 Motorola 68HC11中的中断服务子程序
M68HC11_ISR:                  /* 快中断服务程序,必须禁止中断*/
    所有寄存器被CPU自动保存;
    执行用户代码以响应中断;
    执行中断返回指令;






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值