uCOSii中断处理过程详解.docx

上传人:b****3 文档编号:3664569 上传时间:2022-11-24 格式:DOCX 页数:14 大小:208.07KB
下载 相关 举报
uCOSii中断处理过程详解.docx_第1页
第1页 / 共14页
uCOSii中断处理过程详解.docx_第2页
第2页 / 共14页
uCOSii中断处理过程详解.docx_第3页
第3页 / 共14页
uCOSii中断处理过程详解.docx_第4页
第4页 / 共14页
uCOSii中断处理过程详解.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

uCOSii中断处理过程详解.docx

《uCOSii中断处理过程详解.docx》由会员分享,可在线阅读,更多相关《uCOSii中断处理过程详解.docx(14页珍藏版)》请在冰豆网上搜索。

uCOSii中断处理过程详解.docx

uCOSii中断处理过程详解

一.  UCOSII的中断过程简介

系统接收到中断请求后,如果CPU处于开中断状态,系统就会中止正在运行的当前任务,而按中断向量的指向去运行中断服务子程序,当中断服务子程序运行完成后,系统会根据具体情况返回到被中止的任务继续运行,或转向另一个中断优先级别更高的就绪任务。

由于UCOSII是可剥夺型的内核,所以中断服务程序结束后,系统会根据实际情况进行一次任务调度,如果有优先级更高的任务,就去执行优先级更高的任务,而不一定要返回被中断了的任务。

二.UCOSII的中断过程的示意图

三.具体中断过程

1.中断到来,如果被CPU识别,CPU将查中断向量表,根据中断向量表,获得中断服务子程序的入口地址。

2.将CPU寄存器的内容压入当前任务的任务堆栈中(依处理器的而定,也可能压入被压入被中断了的任务堆栈中。

3.通知操作系统将进入中断服务子程序。

即:

调用OSIntEnter()或OSIntNesting直接

加1。

4.If(OSIntNesting==1){OSTCBCur->OSTCBStrPtr=SP;}//如果是第一层中断,则将堆栈指针保存到被中断任务的任务控制块中

5.清中断源,否则在开中断后,这类中断将反复的打入,导致系统崩贵

6.执行用户ISR

7.中断服务完成后,调用OSIntExit().如果没有高优先级的任务被中断服务子程序激活而进入就绪态,那么就执行被中断了的任务,且只占用很短的时间.

8.恢复所有CPU寄存器的值.

9.执行中断返回指令.

 

四.相关代码

       与编译器相关的数据类型:

       typedefunsignedcharBOOLEAN;

       typedefunsignedcharINT8U;

       typedefunsignedintOS_STK;//堆栈入口宽度为16位

(一)void OSIntEnter(void)的理解

uCOS_II.H中定义:

#ifdef  OS_GLOBALS

#define OS_EXT

#else

#define OS_EXT extern

#endif    //定义全局宏OS_EXT

#ifndef TRUE

#define TRUE  1

#endif

            

OS_EXT BOOLEAN  OSRunning;//定义外部BOOLEAN类型全局变量,用来指示

//核是否在运行

           OS_EXT INT8U  OSIntNesting;//定义外部8位无符号整型数全局变量,用来表

                                        //示中断嵌套层数

OS_CORE.C中的OSIntEnter()函数原型:

void OSIntEnter(void)

{

                    if(OSRunning==TRUE)//如果内核正在运行则进入if

{

                             if(OSIntNesting<255)//如果嵌套层数小于255,则可以继//续

{

                                     OSIntNesting++;//嵌套层数加1

                                  }

                          }

}

    

    

(二)在中断服务子程序中加if(OSIntNesting==1){…}的原因           

uCOS_II.H中定义:

typedefstructos_tcb{

             OS_STK   *OSTCBStkPtr;//声明指向任务堆栈栈顶的16位指针

  ………………

}OS_TCB;//定义名为OS_TCB的结构体数据类型,即任务控制块的数据结构

OS_EXT OS_TCB  *OSTCBCur;//声明一个指向任务控制块的全局指针变量

                          //用于指向当前任务的任务控制块

中断服务程序中添加的代码:

if(OSIntNesting==1)

{

                     OSTCBCur->OSTCBStkPtr=SP;//如果是第一层中断,则将被中断任务       

                                                //的堆栈指针保存在被中断任务的任务

                                                //任务控制块中                                                                                        

                   }

关于uCOS-II的中断服务程序(ISR)中必须加“OSIntNesting==1”的原因==避免调整堆栈指针.

  出现这个问题的根源是当低优先级的任务被中断,当中断完成后由于有高优先级的任务就绪,则必须调度高优先级的任务,原来的低优先级任务继续被中断着,但是此时的低优先级任务的堆栈已经被破坏,已不能被调度程序直接调度了,要想被调度而必须调整堆栈指针。

如下图所示的场景:

 

问题分析:

   要想理解加上上面两句的原因,不妨假设有下面场景出现:

     voidMyTask(void)

     {

       ...

     }

     该任务在执行过程中被中断打断,下面是它的服务子程序

voidMyISR(void)

     {

      保存现场(PUSHA)

      OSIntEnter();

      //此时的堆栈指针是正确的,再往下就不对了,应该在此处保存用户任务堆栈指针

            

      OSIntExit();

      恢复现场(POPA)

   中断返回

     }   

 OSIntExit(),大体如下:

     OSIntExit()

     {

       OS_ENTER_CRITICAL();

        if(OSIntNesting==0&&OSLockNesting==0){

          找到目前系统中就绪表中优先级最的任务  

     如果不是当前任务,则调度它执行

          OSIntCtxSw();

       }

       OS_EXIT_CRITICAL();

     }

综上所述,任务调用链如下:

  

   MyTask-->MyISR-->

                ①    OSIntExit-->

                           ②       OS_ENTER_CRITICAL();③

                                       OSIntCtxSw();       ④

然而在实际的移植过程中,需要调整的指针偏移量是与编译器相关的,如果想要避免调整,显然一个简单的方法就是在调用OSIntExit之前先把堆栈指针保存下来,以后调度该用户任务时,直接从此恢复堆栈指针,而不再管实际的堆栈内容了(因为下面的内容相对于调度程序来说已经没有用处了)

    (三)voidOSIntExit(void)的理解

  OS_CPU.H中的宏定义:

  typedefunsignedshortOS_CPU_SR;     //定义OS_CPU_SR为16位的CPU状态寄存器

 #if     OS_CRITICAL_METHOD==1

 #define OS_ENTER_CRITICAL() asm CLI//OS_ENTER_CRITICAL()即为将处理器标志

                                      //寄存器的中断标志为清0,不允许中断

#define OS_EXIT_CRITICAL()  asm STI//OS_ENTER_CRITICAL()即为将处理器标志

                                      //寄存器的中断标志为置1,允许中断

#endif                                //此一整段代码定义为开关中断的方式一

                                       

#if     OS_CRITICAL_METHOD==2

#define OS_ENTER_CRITICAL() asm{PUSHF;CLI}//将当前任务的CPU的标志寄存器入

//然后再将中断标志位清0

#define OS_EXIT_CRITICAL()  asm POPF //将先前压栈的标志寄存器的值出栈,恢复

                                                                 //到先前的状态,如果先前允许中断则现在

                                                                //仍允许,先前不允许现在仍不允许

#endif                                //此一整段代码定义为开关中断的方式二

                                       

#if     OS_CRITICAL_METHOD==3

#define OS_ENTER_CRITICAL() (cpu_sr=OSCPUSaveSR())//保存CPU的状态寄存器到

                                                      //变量cpu_sr中,cpu_sr

                                                      //为OS_CPU_SR型变量

#define OS_EXIT_CRITICAL()  (OSCPURestoreSR(cpu_sr))//从cpu_sr中恢复状态寄存

                                                                                        //器

#endif                                 //此一整段代码定义为开关中断的方式三,

                                       //此段代码只是示意代码,OSCPUSaveSR()及

//OSCPURestoreSR(cpu_sr)具体什么函数由

//用户编译器所提供的函数决定.

//以上宏定义非常重要,在使用不同处理器时要使用相应处理器的开关中断指令,在代码移//植时很有用

uCOS_II.H中定义:

OS_EXT INT8U  OSLockNesting;//8位无符号全局整数,表示锁定嵌套计数器

void OSIntExit(void)

{

#ifOS_CRITICAL_METHOD==3 

         OS_CPU_SR cpu_sr;

#endif  //采用开关中断方式三

         

           if(OSRunning==TRUE)//如果内核正在运行,则进入if

{

                  OS_ENTER_CRITICAL();//进入临界段,关中断

                  if(OSIntNesting>0)//判断最外层中断任务是否已完成

{

                         OSIntNesting--;//由于此层中断任务已完成,中断嵌套计数器减//一

                      }

                  if((OSIntNesting==0)&&(OSLockNesting==0))

             //OSIntNesting==0表示程序的最外层中断任务以完成,OSLockNesting==0

             //表示是否存在任务锁定,整句代码的意思是如果全部中断处理完了且没有其他

             //任务锁定任务调度则执行下列任务调度代码

{

                         OSIntExitY =OSUnMapTbl[OSRdyGrp];                 //1

                         OSPrioHighRdy=(INT8U)((OSIntExitY<<3)+OSUnMapTbl[OSRdyTbl[OSIntExitY]]); //2

                             if(OSPrioHighRdy!

=OSPrioCur)                 //3

{

                                     OSTCBHighRdy =OSTCBPrioTbl[OSPrioHighRdy];

                                     OSCtxSwCtr++;

                                     OSIntCtxSw();                   

                                  }

                      }

                  OS_EXIT_CRITICAL();//开中断

               }

}

要理解1,2,3处的代码含义.首先要理解任务是如何调度的,所以先讲一下任务调度的核心算法:

a.数据结构:

1.就绪表:

就绪表包含两个变量,他们分别是OSRdyGrp(在uCOS_II.H中为OS_EXT INT8U OSRdyGrp;即8位无符号整型的全局变量)和OSRdyTb1[](在uCOS_II.H中为OS_EXT INT8U  OSRdyTbl[OS_RDY_TBL_SIZE];)

      先分析OS_EXTINT8U OSRdyTbl[OS_RDY_TBL_SIZE];是怎么回事

      #defineOS_LOWEST_PRIO 12//在OS_CFG.H中

这个宏定义了任务所能具有的最低优先级,那么此处共有从0到12共13个优先级,用户在代码移植时可以修改它,自定义所需要的优先级个数,但max(OS_LOWEST_PRIO)==63

#define OS_RDY_TBL_SIZE  ((OS_LOWEST_PRIO)/8+1)//在uCOS_II.中

OS_RDY_TBL_SIZE用于确定数组OSRdyTbl[]的大小,如果OS_LOWEST_PRIO==63,则上述宏实际上为#define OS_RDY_TBL_SIZE 8,由于每个数组元素为8位,如果每一位表示一个优先级,则共有8*8=64个优先级 

现在回到就绪表,操作系统将优先级分为8组,优先级从0到7分为第一组,对应于OSRdyGrp的第0位,从8到15分为第二组,对应于OSRdyGrp的第1位,以此类推,64个优先级就有下面的对应关系(OSRdyTb1[]每组元素的每一位代表一个优先级):

OSRdyTb1[0]--------------优先级从0到7--------------OSRdyGrp第0位

OSRdyTb1[1]--------------优先级从8到15-------------OSRdyGrp第1位

OSRdyTb1[2]--------------优先级从16到23-------------OSRdyGrp第2位

OSRdyTb1[3]--------------优先级从24到31-------------OSRdyGrp第3位

OSRdyTb1[4]--------------优先级从32到39-------------OSRdyGrp第4位

OSRdyTb1[5]--------------优先级从40到47-------------OSRdyGrp第5位

OSRdyTb1[6]--------------优先级从48到55-------------OSRdyGrp第6位

OSRdyTb1[7]--------------优先级从55到63-------------OSRdyGrp第7位

现在再做如下对应:

当OSRdyTbl[0]中的任何一位是1时,OSRdyGrp的第0位置1,

当OSRdyTbl[1]中的任何一位是1时,OSRdyGrp的第1位置1,

当OSRdyTbl[2]中的任何一位是1时,OSRdyGrp的第2位置1,

当OSRdyTbl[3]中的任何一位是1时,OSRdyGrp的第3位置1,

当OSRdyTbl[4]中的任何一位是1时,OSRdyGrp的第4位置1,

当OSRdyTbl[5]中的任何一位是1时,OSRdyGrp的第5位置1,

当OSRdyTbl[6]中的任何一位是1时,OSRdyGrp的第6位置1,

当OSRdyTbl[7]中的任何一位是1时,OSRdyGrp的第7位置1,

如果置1表示有任务进入就绪态,那么上面的表可以理解为:

OSRdyGrp的第N位(0<=N<=7)为1,那么在OSRdyTb1[N]中至少有一位是1,也就是说在OSRdyTb1[N]对应的任务中至少有一个任务处于就绪态

 

该表在OS_CORE.C中定义如下:

INT8U const OSMapTbl[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

    //8位无符号整型常量数组

      3.表(数组)OSUnMapTb1[]:

用于求出一个8位整型数最低位为1的位置

该数组在OS_CORE.C中定义如下:

       INT8U const OSUnMapTbl[]={

   0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x00to0x0F*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x10to0x1F*/

   5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x20to0x2F*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x30to0x3F*/

   6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x40to0x4F*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x50to0x5F*/

   5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x60to0x6F*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x70to0x7F*/

   7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x80to0x8F*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0x90to0x9F*/

   5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0xA0to0xAF*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0xB0to0xBF*/

   6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0xC0to0xCF*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0xD0to0xDF*/

   5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,      /*0xE0to0xEF*/

   4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0       /*0xF0to0xFF*/

};

 理解:

我把问题转化为:

“一个无符号的8位整数,如何确定最低位为1的位的位置?

即对于任意一个8位整型数,比如4,考虑它的二进制位中所有为1的位,确定最低位为1的位置(相对第0位的偏移),一般来讲首先想到的方法是移位的方法.如:

pos=0;//pos用于统计相对于第0位的偏移

while(!

(num&0x01))//与00000001按位于,如果最低位为1,退出循环,即找到最低位//为1的位

{

num=num>>1;//将二进制数右移一位

pos++;//进行一次移位,则pos加一

}

最后得到的pos就是所有位中为1的最低位的偏移量,但这样计算需要时间,尽管最多右移7次。

为了节省时间,使用的方法是“空间换时间”的办法,即把8位无符号数,所有可能的情况的都列了出来,共有256个数字,把每个数字的最低为1位的位置都预先计算好。

比如4对应二进制数为100,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1