首页 uCOSii中断处理过程详解

uCOSii中断处理过程详解

举报
开通vip

uCOSii中断处理过程详解 一.   UCOSII的中断过程简介 系统接收到中断请求后,如果CPU处于开中断状态,系统就会中止正在运行的当前任务,而按中断向量的指向去运行中断服务子程序,当中断服务子程序运行完成后,系统会根据具体情况返回到被中止的任务继续运行,或转向另一个中断优先级别更高的就绪任务。 由于UCOS II是可剥夺型的内核,所以中断服务程序结束后,系统会根据实际情况进行一次任务调度,如果有优先级更高的任务,就去执行优先级更高的任务,而不一定要返回被中断了的任务。 二.UCOSII的中断过程的示意图 三.具体中断...

uCOSii中断处理过程详解
一.   UCOSII的中断过程简介 系统接收到中断请求后,如果CPU处于开中断状态,系统就会中止正在运行的当前任务,而按中断向量的指向去运行中断服务子程序,当中断服务子程序运行完成后,系统会根据具体情况返回到被中止的任务继续运行,或转向另一个中断优先级别更高的就绪任务。 由于UCOS II是可剥夺型的内核,所以中断服务程序结束后,系统会根据实际情况进行一次任务调度,如果有优先级更高的任务,就去执行优先级更高的任务,而不一定要返回被中断了的任务。 二.UCOSII的中断过程的示意图 三.具体中断过程 1.中断到来,如果被CPU识别,CPU将查中断向量 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf ,根据中断向量表,获得中断服务子程序的入口地址。 2.将CPU寄存器的内容压入当前任务的任务堆栈中(依处理器的而定,也可能压入被压入被中断了的任务堆栈中。 3.通知操作系统将进入中断服务子程序。即:调用OSIntEnter()或OSIntNesting直接 加1。 4.If(OSIntNesting==1) {OSTCBCur->OSTCBStrPtr=SP;} //如果是第一层中断,则将堆栈指针保存到被中断任务的任务控制块中 5.清中断源,否则在开中断后,这类中断将反复的打入,导致系统崩贵 6.执行用户ISR 7.中断服务完成后,调用OSIntExit().如果没有高优先级的任务被中断服务子程序激活而进入就绪态,那么就执行被中断了的任务,且只占用很短的时间. 8.恢复所有CPU寄存器的值. 9.执行中断返回指令.   四.相关代码         与编译器相关的数据类型:         typedef unsigned char BOOLEAN;         typedef unsigned char INT8U;         typedef unsigned int OS_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中定义: typedef struct os_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”的原因 ==避免调整堆栈指针.   出现这个问题的根源是当低优先级的任务被中断,当中断完成后由于有高优先级的任务就绪,则必须调度高优先级的任务,原来的低优先级任务继续被中断着,但是此时的低优先级任务的堆栈已经被破坏,已不能被调度程序直接调度了,要想被调度而必须调整堆栈指针。如下图所示的场景:   问题 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 :    要想理解加上上面两句的原因,不妨假设有下面场景出现:       void MyTask(void)       {         ...       }       该任务在执行过程中被中断打断,下面是它的服务子程序 void MyISR(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之前先把堆栈指针保存下来,以后调度该用户任务时,直接从此恢复堆栈指针,而不再管实际的堆栈内容了(因为下面的内容相对于调度程序来说已经没有用处了)      (三) void OSIntExit (void)的理解    OS_CPU.H中的宏定义:    typedef unsigned short OS_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) { #if OS_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_EXT INT8U  OSRdyTbl[OS_RDY_TBL_SIZE];是怎么回事        #define OS_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,       /* 0x00 to 0x0F */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x10 to 0x1F */     5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x20 to 0x2F */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x30 to 0x3F */     6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x40 to 0x4F */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x50 to 0x5F */     5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x60 to 0x6F */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x70 to 0x7F */     7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x80 to 0x8F */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x90 to 0x9F */     5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xA0 to 0xAF */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xB0 to 0xBF */     6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xC0 to 0xCF */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xD0 to 0xDF */     5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xE0 to 0xEF */     4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0        /* 0xF0 to 0xFF */ };  理解: 我把问题转化为: “一个无符号的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,最低为1位相对第0位偏移量为2,则查表时,以4为索引,马上就得到2这个数字。(即:OSUnMapTb1[4]==2) b.构建OSRdyGrp和OSRdyTb1[]算法:   代码原型在OS_CORE.C中,实际代码大致如下:(prio为任务优先级)     INT8U  OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt) {          …………         ptcb->OSTCBY         = prio >> 3;                     ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];         ptcb->OSTCBX         = prio & 0x07;         ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];         …………..         OSRdyGrp               |= ptcb->OSTCBBitY;                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;         ………….. }//此函数在创建任务时被调用,即OSTaskCreate(..)中,用于初始化任务控制块     以上代码可以等效于:      OSRdyGrp  |=  OSMapTbl[prio>>3];      OSRdyTb1[prio>>3] |= OSMapTbl[prio&0x07]; 此处 prio >> 3 是右移3位,就相当于连续除以3个2,因此相当于:prio / 8 ; prio & 0x07 是求低3位的值,而由高5位构成的值正好是8的整数倍,因此是取余运算, 即:prio % 8 因此又可将算法等效为:     OSRdyGrp  |=  OSMapTbl[prio / 8]; OSRdyTb1[prio / 8] |= OSMapTbl[prio % 8]; 算法的作用相当于如下流程:(假定prio=28)          就这样把任务的优先级放入了就绪表.在此我产生一个疑问,”prio>>3与prio&0x07并不直观,为什么不用prio/8与prio%8呢?”我做如下解释: 处理器一般具有如下结构   累加器是具有移位功能的,prio>>3可以在累加器中完成而不必进入ALU,而prio/8则不同,要进入ALU,ALU处理速度不如累加器,如果采用prio/8将降低操作系统的实时性,同样prio&0x07只是一个间单的位与操作,而prio%8则还要经过ALU,如采用prio%8也将降低实时性. 现在回到OSIntExit()处,看1,2,3处的代码: OSIntExitY  = OSUnMapTbl[OSRdyGrp];                //1                           OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);//2                           if (OSPrioHighRdy != OSPrioCur)                    //3 {                                 OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];                                 OSCtxSwCtr++;                                 OSIntCtxSw();                                                  } 在uCOS_II.H中有如下定义: OS_EXT  INT8U   OSIntExitY;//8位无符号全局整型变量,用于存放就绪表中就绪的任务组 OS_EXT  INT8U   OSPrioHighRdy;// 8位无符号全局整型变量,用于存放具有最高优先级任务的优先级 OSUnMapTbl[]:用于计算偏移量,偏移量即为优先级最高的就绪任务组在OSRdyGrp中的位置 以及优先级最高的任务在最高优先级任务组OSRdyTbl[N](N表示最高优先级任务组,0<=N<=7)中的位置. OSIntExitY  = OSUnMapTbl[OSRdyGrp];//表示获得具有最高优先级的组 例如OSRdyGrp值为01101000(0x68),则第3,5,6组中有任务就绪,查表OSUnMapTbl[0x68]==3,即优先级最高任务组为第3组. OSUnMapTbl[OSRdyTbl[OSIntExitY]] //表示获得最高优先级任务组中的优先级最高的任务 例如OSRdyTbl[3]的值为01110000(0x70),则第4,5,6位中有任务绪,OSUnMapTbl[0x70]==4,即优先级最高的任务在组中位于第4位. OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); //就是3*8+4==28,再经强制类型转换成INT8U型,赋给OSPrioHighRdy 这样OSPrioHighRdy就获得了就绪表中优先级最高的任务 再看3处代码: 在uCOS_II.H中有如下定义:     OS_EXT  OS_TCB  *OSTCBPrioTbl[OS_LOWEST_PRIO + 1];//定义指向任务控制块的指针数//组,且每个优先级在同一时刻只对应一个任务     OS_EXT  INT8U   OSPrioCur;//用于保存目前任务的优先级     OS_EXT  INT32U  OSCtxSwCtr;//32位无符号全局整型变量,作为任务切换计数器     OS_EXT  OS_TCB   *OSTCBHighRdy;//指向最高优先级任务任务控制块的指针          if (OSPrioHighRdy != OSPrioCur)                     //就绪态任务中的最高优先级已不是目前任务的优先级,则进行中断级的任务//切换 {                   OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];                   //将最高优先级任务控制块指针指向当前优先级最高的任务的任务控制块 OSCtxSwCtr++;//任务切换计数器加1                   OSIntCtxSw();//调用中断级任务切换函数                               }    此段代码体现出了可剥夺型实时操作系统内核的特点.     OSIntCtxSw()在80x86上的移植代码,此代码在OS_CPU_A.ASM中,代码如下: _OSIntCtxSw PROC   FAR ;             CALL   FAR PTR _OSTaskSwHook ; 调用OSTaskSwHook()函数,此函数在 ;OS_CPU_C.C中只是个空函数,留给用户 ;在代码移植时自定义 ;             MOV    AX, SEG _OSTCBCur   ;由于发生了段转移,恢复刚才(当前任务)数 MOV    DS, AX;       据段  ;             MOV    AX, WORD PTR DS:_OSTCBHighRdy+2 ;AH=_OSTCBHighRdy+3                                                    ;AL=_OSTCBHighRdy+2             MOV    DX, WORD PTR DS:_OSTCBHighRdy   ;DH=_OSTCBHighRdy+1                                                    ;DL=_OSTCBHighRdy             MOV    WORD PTR DS:_OSTCBCur+2, AX     ;_OSTCBCur+3=AH                                                    ;_OSTCBCur+2=AL MOV    WORD PTR DS:_OSTCBCur, DX       ;_OSTCBCur+1=DH                                        ;_OSTCBCur=DL ;OSTCBCur=OSTCBHighRdy             MOV    AL, BYTE PTR DS:_OSPrioHighRdy  ;             MOV    BYTE PTR DS:_OSPrioCur, AL;OSPrioCur= OSPrioHighRdy ;             LES    BX, DWORD PTR DS:_OSTCBHighRdy ;取址指令             MOV    SS, ES:[BX+2]                   ;             MOV    SP, ES:[BX]                     ;                    ;SS:SP=OSTCBHighRdy->OSTCBStkPtr  POP    DS ;DS出栈             POP    ES ;ES出栈             POPA  ;CPU其余寄存器出栈 ;             IRET ; 中断返回 ; _OSIntCtxSw ENDP 以上汇编代码在移植时根据处理器不同要作修改 四.在ISR中通知任务做事的理解(以OSSemPost()为例)   在理解OSSemPost(),先要理解事件,如下是事件的数据结构: typedef struct {     INT8U   OSEventType;//事件类型,这里是OS_EVENT_TYPE_SEM即信号量     INT8U   OSEventGrp; //等待任务所在的组     INT16U  OSEventCnt; //当事件是信号量时,使用此计数器     void   *OSEventPtr; //信号量时不使用     INT8U   OSEventTbl[OS_EVENT_TBL_SIZE];//等待任务列表 } OS_EVENT;    其中OSEventGrp与OSEventTbl[]构成等待事件的任务列表,前面所讲的OSRdyGrp与OSRdyTbl[]具有同样的功能,划分也一模一样.   在ISR中调用函数OSSemPost(),给任务发信息,此函数在OS_SEM.C中: INT8U  OSSemPost (OS_EVENT *pevent) { #if OS_CRITICAL_METHOD == 3     OS_CPU_SR  cpu_sr; #endif    //定义开关中断类型 #if OS_ARG_CHK_EN > 0//如果启用了函数参数检查功能则进行参数检查     if (pevent == (OS_EVENT *)0) {          return (OS_ERR_PEVENT_NULL);//检查是否有事件发生,如果没有则报错     }     if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {         return (OS_ERR_EVENT_TYPE);//检查当前事件是不是信号量,不是则出错     } #endif     OS_ENTER_CRITICAL();//关中断 if (pevent->OSEventGrp != 0x00) { //如果等待事件发生的任务列表不为空,                  //即有任务处于等待状态,则进入if         OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM);         //使对应事件的任务从等待变为就绪         OS_EXIT_CRITICAL();//开中断         OS_Sched(); //进行任务调度         return (OS_NO_ERR);     } if (pevent->OSEventCnt < 65535) { //如果等待事件发生的任务列表为空,且信号量计数                  //器的值小于65535,则信号量计数器加1,否则不执                                 //行if,而报信号量益出 pevent->OSEventCnt++;         OS_EXIT_CRITICAL();         return (OS_NO_ERR);     }     OS_EXIT_CRITICAL();     return (OS_SEM_OVF);} 附: uCOS_II 大致的启动过程: main() { ...... OSInit(); ...... OSTaskCreate();//此函数在OS_TASK.C中,用于创建任务,调用了三个重要的系统函数         //它们是OSTaskInit();OS_TCBInit();OS_Sched(); ...... OSStart(); } OSTaskCreate()//此函数只能在main()及任务中调用,中断服务子程序不能调用 { ...... OSTaskStkInit();//此函数在OS_CPU_C.C中,用于创建任务堆栈,在移植过程中可根据 //具体情况做修改 OS_TCBInit();//此函数在OS_CORE.C中,用于初始化任务控制块,及就绪表 ...... OS_Sched()();//此函数在OS_CORE.C中,是任务级调度函数,作用是获得最高优先级任务 ...... //并进行调度,此函数包含一个重要函数OS_TASK_SW() } OSStart() { ...... If(没有任务启动) { 获取最高优先级任务   OSStartHighRdy();//此函数在OS_CPU_A.ASM中,用于启动任务,在移植过程中随处理器           //不同要作修改 } } OS_TASK_SW()//在OS_CPU.H中它是一个宏定义,用于产生任务切换的中断,移植中要作修改 #define  uCOS                 0x80 #define  OS_TASK_SW()         asm  INT   uCOS 为什么在OSTaskCreate()中调用OS_Sched()后还要调用OSStart()来启用任务呢?     事实上在从main()中创建的任务是不执行OS_Sched()函数的,因为此时的任务并未启动,OSRunning的值为0。任务启动要通过OSStart()才行。相反,当在一个已启动的任务中调用OSTaskCreate()就通过OS_Sched()函数(OSRunning==1),而不用OSStart(),OSStart()只在操作系统启动时调用,任务中不调用 一.任务调度中的几个函数的区别: ----------------------------------------uCOSII启动时的任务调度-------------------------------------- OSStartHighRdy():该函数在OS_CPU_A.ASM中原形如下: _OSStartHighRdy  PROC FAR             MOV    AX, SEG _OSTCBHighRdy  ;             MOV    DS, AX ;获得要运行任务的任务控制块所在段的段址             CALL   FAR PTR _OSTaskSwHook ;调用用户自定义的任务切换接口函数             MOV    AL, 1 ;0             MOV    BYTE PTR DS:_OSRunning, AL ;置任务运行标志 ;             LES    BX, DWORD PTR DS:_OSTCBHighRdy ;             MOV    SS, ES:[BX+2] ;             MOV    SP, ES:[BX+0] ;从任务控制块首指的四个8位内存单元获得该任务的任务堆栈的地址 ;             POP    DS  ;DS出栈至任务堆栈             POP    ES  ;ES出栈至任务堆栈             POPA       ;将其余CPU寄存器出栈 ;             IRET  ;恢复代码段及指令寄存器内容,运行任务 _OSStartHighRdy  ENDP 该函数由OSStart()调用 void  OSStart (void)//在OS_CORE.C中 {     INT8U y;     INT8U x;     if (OSRunning == FALSE) {         y             = OSUnMapTbl[OSRdyGrp];         x             = OSUnMapTbl[OSRdyTbl[y]];         OSPrioHighRdy = (INT8U)((y << 3) + x);         OSPrioCur     = OSPrioHighRdy;         OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];         OSTCBCur      = OSTCBHighRdy;         OSStartHighRdy();       }//如果多任务环境还没有启动,则先获得就绪表中任务的最高优先级,再获得该优先级下任务的任务控制块的地址(通过OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy], OSTCBPrioTbl[]是一个指针数组,用于存放各优先级任务的任务控制块的地址,它在OS_TCBInit()中被赋值),然后调用OSStartHighRdy()启动任务。 } -------------------------------任务级的任务切换-------------------------------- OSCtxSw():函数在OS_CPU_A.ASM中的原形如下: _OSCtxSw    PROC   FAR ;             PUSHA  ;将所有CPU寄存器压栈             PUSH   ES ;经附加段寄存器压栈             PUSH   DS ;将数据段寄存器压栈             MOV    AX, SEG _OSTCBCur ;获取当前任务所在段的段址,放入DS             MOV    DS, AX ;             LES    BX, DWORD PTR DS:_OSTCBCur  ;获取当前任务任务控制块的段地址及偏移地址             MOV    ES:[BX+2], SS ;将当前任务堆栈的断址保存在当前任务的任务控制块中             MOV    ES:[BX+0], SP ;将当前任务堆栈的偏移地址保存在当前的任务控制块中             CALL   FAR PTR _OSTaskSwHook ;             MOV    AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy             MOV    DX, WORD PTR DS:_OSTCBHighRdy               MOV    WORD PTR DS:_OSTCBCur+2, AX                 MOV    WORD PTR DS:_OSTCBCur, DX                   MOV    AL, BYTE PTR DS:_OSPrioHighRdy  ; OSPrioCur = OSPrioHighRdy             MOV    BYTE PTR DS:_OSPrioCur, AL     ;             LES    BX, DWORD PTR DS:_OSTCBHighRdy  ;将最高优先级任务的堆栈             MOV    SS, ES:[BX+2]                   ;指针放回CPU的堆栈段寄存             MOV    SP, ES:[BX]                     ;器及堆栈指针寄存器中 ;SS:SP= OSTCBHighRdy->OSTCBStkPtr此时的任务堆栈已改变,变为最高优先级任务(已是当前任务)的任务堆栈             POP    DS ;             POP    ES ;             POPA ;             IRET ; _OSCtxSw    ENDP 该函数并非由OS_Sched()直接调用而是通过软中断指令INT 0x80(CPU为80x86), 产生中断,到中断向量表中找的OSCtxSw()的入口地址,然后跳转到该函数并执行的。 OS_Sched() 函数原形如下: void  OS_Sched (void) { #if OS_CRITICAL_METHOD == 3         OS_CPU_SR  cpu_sr; #endif          INT8U   y;         OS_ENTER_CRITICAL();                i
本文档为【uCOSii中断处理过程详解】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_445557
暂无简介~
格式:doc
大小:353KB
软件:Word
页数:36
分类:互联网
上传时间:2013-02-26
浏览量:26