这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » ucos ii下的硬中断服务问题

共13条 1/2 1 2 跳转至

ucos ii下的硬中断服务问题

工程师
2012-05-18 22:20:27     打赏

       前天在做实验时,想在ucos ii 系统下触发一下外部中断玩玩,可没想到,按常归的中断要求编写完中断服务例程后,程序竟然跑飞了,触发按键中断后,我调试了一下,程序在一个默认的异常服务死循环里出不来了,我就郁闷了,想了之后,以为当OS 控制系统后在不同优先级任务间切换,不能再响应来自外部事件触发的中断呢,所以程序崩溃了呢,后来才发现,这是一个多么可笑的想法啊,呵呵,现在终于弄明白了,写点心得记录下来,以便完全理解。

         看了ucos ii对中断的要求后才发现错哪了,任务调度之所以分为任务级任务调度和中断级任务调度,就是因为这个原因,学那么长时间了,竟然还不知道中断级任务调度是怎么调用了,悲剧啊,思而不学则殆,学而不思则惘,唉,把状态改了,以作警示,很明显,任务级任务调度是在任务间切换时调用的,具体什么时候调用呢?当一个任务有延时时,就会调用以便让一个更高优先级的任务进入就绪状态。而中断级任务调度呢,当然是在中断时调用以便让……这么简单的道理,怎么想不到呢?好像从来没想过这个问题……。

    查资料后发现了这两个函数:OSIntEnter()和OSIntExit(),这两个函数是用来干啥呢?看源码吧!

void  OSIntEnter (void)
{
    if (OSRunning == TRUE)

      {
        if (OSIntNesting < 255)

           {
            OSIntNesting++;                      /* Increment ISR nesting level                        */
           }

      }
}

       这个函数的源码真简单啊,不就是让一个变量加1吗?再来看一下注释:

Description: This function is used to notify uC/OS-II that you are about to service an interrupt
*              service routine (ISR).  This allows uC/OS-II to keep track of interrupt nesting and thus
*              only perform rescheduling at the last nested ISR.
* .
* Arguments  : none
*
* Returns    : none
*
* Notes      : 1) This function should be called with interrupts already disabled
*              2) Your ISR can directly increment OSIntNesting without calling this function because
*                 OSIntNesting has been declared 'global'. 
*              3) You MUST still call OSIntExit() even though you increment OSIntNesting directly.
*              4) You MUST invoke OSIntEnter() and OSIntExit() in pair.  In other words, for every call
*                 to OSIntEnter() at the beginning of the ISR you MUST have a call to OSIntExit() at the
*                 end of the ISR.
*              5) You are allowed to nest interrupts up to 255 levels deep.
*              6) I removed the OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() around the increment because
*                 OSIntEnter() is always called with interrupts disabled.

解释如下:  这个函数用来通知OS我们正准备进入中断服务例程,这允许OS保持中断嵌套的路径(次数),
  以便于仅在最后一次嵌套的中断退出时才执行任务调度。

  1.这个函数应当在关闭中断的情况下调用,为啥?涉及到全局变量的操作了吧,也就是在调用前后加下它哥俩:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。

 2.可以直接增加OSIntNesting,而不必调用这个函数,因为这个变量是全局的!

 3.一定要调用OSIntExit()这个函数,即使你直接增加OSIntNesting这个变量。也就是说,上面两个函数是哥俩,一定要成对使用,但是由于大哥本质上只是增加了中断嵌套次数,可以不用大哥,但一定得用小弟,为啥?下面再细说!

  后央三句自己看吧,挺简单的!

再看老二函数源码:

void  OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif   
    
    if (OSRunning == TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0) {                            /* Prevent OSIntNesting from wrapping       */
            OSIntNesting--;
        }
        if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Reschedule only if all ISRs complete ... */
            OSIntExitY    = OSUnMapTbl[OSRdyGrp];          /* ... and not locked.                      */
            OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);
            if (OSPrioHighRdy != OSPrioCur) {              /* No Ctx Sw if current task is highest rdy */
                OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];
                OSCtxSwCtr++;                              /* Keep track of the number of ctx switches */
                OSIntCtxSw();                              /* Perform interrupt level ctx switch       */
            }
        }
        OS_EXIT_CRITICAL();
    }
}

源码不多,也不少,不过关键的需要理解的就那一行:OSIntCtxSw(),这是啥?中断级任务调度函数啊,终于出来了,看它的注释吧:

*********************************************************************************************************
*                                               EXIT ISR
*
* Description: This function is used to notify uC/OS-II that you have completed serviving an ISR.  When
*              the last nested ISR has completed, uC/OS-II will call the scheduler to determine whether
*              a new, high-priority task, is ready to run.
*
* Arguments  : none
*
* Returns    : none
*
* Notes      : 1) You MUST invoke OSIntEnter() and OSIntExit() in pair.  In other words, for every call
*                 to OSIntEnter() at the beginning of the ISR you MUST have a call to OSIntExit() at the
*                 end of the ISR.
*              2) Rescheduling is prevented when the scheduler is locked (see OS_SchedLock())

翻译如下:这个函数用来通知OS我们已经完成了中断服务程充,当最后一次被嵌套的中断也完成时,OS将调用scheduler来决定是否有一个新的,更高优先级的任务,已经主绪。(当然没有更高的时就恢复被中断的任务)

 

      总结:我遇到情况的主要原因大概是,当按普通方法写中断服务例程时,没有通知操作系统,触发中断时当前执行任务被中断,然后退出中断服务例程时,系统找不到原先被中断的任务,不知道该干啥,所以就进入了默认异常处理程序,死在里面出不来了,呵呵,不知道这样理解对不对,呵呵!




关键词: 中断     服务     问题     任务     这个     函数    

助工
2013-01-04 10:40:23     打赏
2楼
我也遇到了这样的情况,我用的是按键触发中断,就是一直死在那里出不来,你是怎么解决的.......
能把你的解决后的源码发我一份吗?
我的邮箱zsjalive@126.com

高工
2013-01-04 21:08:52     打赏
3楼
这个其实是操作系统内核任务调度的精华所在:
裸奔的程序,中断产生后,CPU根据中断优先级和使能情况,及时响应中断,保护现场和恢复现场,PC指针和现场寄存器数据都保持在系统的堆栈中,由CPU自行处理进栈和出栈,然后根据PC指针继续之前被中断所打断的程序执行;
在uCOS-II中就不一样了,每一个任务都有自己的退栈,用于保持自己的现场数据和寄存器,中断服务程序也是如此,所以从中断返回时,必须调用操作系统内核的任务切换函数(或者宏),才能恢复之前被打断的优先级最高的任务的执行,或者心就绪的最高优先级任务(通过恢复任务堆栈的方式,注意这里每个任务的堆栈所占的内存RAM的空间不一样,处于不同的全局数组)

助工
2013-01-05 10:23:01     打赏
4楼

我的情况是这样的,将ucos移植到S3C2410上之后,我先只建立了一个任务TASK1(优先级10),就是打印123(我加几个任务都可以,已测试)。后来跟楼主的想法一样,想加个外部中断的任务TASK2(优先级5)玩玩,我加的是用按键控制LED。全速运行时,由于开始没有按键触发中断,只是在两个任务间简单的切换,当我触发按键后,就一直循环运行在按键中断服务程序中,为什么说一直循环运行在中断服务程序中呢,我在服务程序中加了一句打印消息之后再全速运行,触发中断就一直在DNW上打印消息......而不切换到TASK1,不知道这是为什么?
我的问题好像跟楼主的相似......


助工
2013-01-05 10:25:34     打赏
5楼

我的情况是这样的,将ucos移植到S3C2410上之后,我先只建立了一个任务TASK1(优先级10),就是打印123(我加几个任务都可以,已测试)。后来跟楼主的想法一样,想加个外部中断的任务TASK2(优先级5)玩玩,我加的是用按键控制LED。全速运行时,由于开始没有按键触发中断,只是在两个任务间简单的切换,当我触发按键后,就一直循环运行在按键中断服务程序中,为什么说一直循环运行在中断服务程序中呢,我在服务程序中加了一句打印消息之后再全速运行,触发中断就一直在DNW上打印消息......而不切换到TASK1,不知道这是为什么?


高工
2013-01-05 18:12:04     打赏
6楼

你清中断标志了吗?


助工
2013-01-05 18:23:40     打赏
7楼
你是说再中断服务程序中清中断标志还是?

高工
2013-01-06 13:04:39     打赏
8楼

当然是在中断服务程序中啊


助工
2013-01-07 09:00:36     打赏
9楼

我用的中断服务程序是原先裸板上的程序,我看了哈,中断清了。我在中断服务程序中加了两句OSIntNesting-=3;
    OSTimeDly(2);后,再全速运行,触发中断后就可以切换出来(切换到任务1),但是还是有在DNW上显示中断服务程序中的打印消息,只不过打印消息的次数有限(比如说100次),打印完消息后就切换到TASK1,TASK1运行完后又切换到中断服务程序中的打印消息,打印100后再切换到TASK1,反复。。。。。中途触发按键的话也会响应中断去运行中断服务程序.

我解释下为什么我要加OSIntNesting-=3;我设断点调试的时候发现运行到中断服务程序的时候,OSIntNesting为3,我就尝试的;后来也尝试OSIntNesting-=6或9,都可以....
为什么要加 OSTimeDly(2);我想的话,在中断服务程序中要切换出去(也就是要把CPU的控制权要交出去,需要OSTimeDly()或挂起任务,我就尝试了哈)

不知道是为什么,我觉得我的情况跟楼主的很相似........


高工
2013-01-07 15:52:10     打赏
10楼

那是因为中断嵌套计数器OSIntNesting>0,内核是不能进行任务调度的,详见内核任务调度函数源代码。


共13条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]