嵌入式操作系统原理第三章:uC/OS-II的中断和时钟目标:本章旨在向学员介绍uc/osII实时操作系统的中断概念及时间管理,通过本章的学习,学员应该掌握如下知识:1)uC/OSII的中断管理和中断服务程序的结构2)uC/OSII的系统时钟及实现方法3)时间管理服务:延时、取消延时时间:2.0学时教学方法:讲授PPT+练习3.1uC/OS-II的中断中断:任务在运行过程中,应内部或外部异步事件的请求中止当前任务,而去处理异步事件所要求的任务的过程叫做中断。中断服务程序:应中断请求而运行的程序叫中断服务子程序(ISR)。中断向量:中断服务子程序的入口地址叫中断向量。CPU响应中断的条件:•至少有一个中断源向CPU发出中断信号•系统允许中断,且对此中断信号未予屏蔽3.1.1uC/OS-II的中断过程uC/OS-II中断的响应过程:中断请求关闭中断转到中断向量保存CPU寄存器通知内核进入ISR通知内核退出ISR恢复CPU寄存器中断返回无新高级任务则返回原任务有新高级任务则运行高级任务ISR给任务发信号通知内核退出ISR恢复CPU寄存器中断返回图3-1中断的响应过程中断响应中断恢复任务响应时间中断恢复任务响应时间注意:对于可剥夺型内核,中断服务子程序结束后,系统进行一次任务调度去运行优先级最高的就绪任务,而不是一定要接续运行被中断的任务。voidOSIntEnter(void){if(OSRunning==True){if(OSIntNesting255){OSIntNesting++;}}}3.1.2中断的开始、离开voidOSIntExit(Void){OS_ENTER_CRITICAL();if(OSRunning==TRUE){if(OSIntNesting0){OSIntNesting--;}if((OSLockNesting==0)&&(OSIntNesting==0)){OSIntExity=OSUnMapTbl[OSRdyGrp];OSPrioHighRdy=(INT8U)((y3)+OSUnMapTbl[OSRdyTbl[y]]);if(OSPrioHighRdy!=OSPrioCur){OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];OSCtxSwCtr++;OSIntCtxSw();//中断级任务切换}}}OS_EXIT_CRITICAL();}进入中断嵌套层数=0?获得任务TCB的指针YESNONO返回中断服务程序未锁定调度器?NO获得最高级别就绪任务的prio任务是被中断的任务?YESYES执行中断级任务切换图3-2函数OSIntExit()的流程图为记录中断嵌套的层数,定义了一个全局变量OSIntNesting。函数作用就是把全局变量OSIntNesting加1,从而用它来记录中断嵌套的层数这个函数在中断嵌套层数计数器为0、调度器未被锁定且从任务就绪表中查找到的最高级就绪任务又不是被中断的任务的条件下将要进行任务切换,否则就返回被中断的服务程序voidOSIntEnter(void){if(OSRunning==True){if(OSIntNesting255){OSIntNesting++;}}}3.1.2中断的开始、离开voidOSIntExit(Void){OS_ENTER_CRITICAL();if(OSRunning==TRUE){if(OSIntNesting0){OSIntNesting--;}if((OSLockNesting==0)&&(OSIntNesting==0)){OSIntExity=OSUnMapTbl[OSRdyGrp];OSPrioHighRdy=(INT8U)((y3)+OSUnMapTbl[OSRdyTbl[y]]);if(OSPrioHighRdy!=OSPrioCur){OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];OSCtxSwCtr++;OSIntCtxSw();//中断级任务切换}}}OS_EXIT_CRITICAL();}进入中断嵌套层数=0?获得任务TCB的指针YESNONO返回中断服务程序未锁定调度器?NO获得最高级别就绪任务的prio任务是被中断的任务?YESYES执行中断级任务切换图3-2函数OSIntExit()的流程图为记录中断嵌套的层数,定义了一个全局变量OSIntNesting。函数作用就是把全局变量OSIntNesting加1,从而用它来记录中断嵌套的层数这个函数在中断嵌套层数计数器为0、调度器未被锁定且从任务就绪表中查找到的最高级就绪任务又不是被中断的任务的条件下将要进行任务切换,否则就返回被中断的服务程序3.1.3中断服务子程序的流程中断服务子程序入口OSTCBCur-OSTCBStkPtr=SPNO中断嵌套层OSIntNEsting=1?YES清中断源的中断申请标志图3-3中断服务子程序的流程图CPU寄存器内容进栈调用OSIntEnter()通知系统:进入中断服务程序重新开放中断运行中断服务代码调用OSIntExit()通知系统:推出中断服务程序恢复CPU寄存器内容执行中断返回指令在uC/OS-II中,通常用一个任务来完成异步事件的处理工作,而在中断服务程序中只是向任务发送消息的方法去激活这个任务。并非为每个任务都定义一个充分大的栈空间,中断嵌套时单独定义一个中断嵌套栈,在发生第1次中断时,中断服务程序将栈空间切换到中断嵌套栈,这样,以后发生的嵌套中断就一直使用这个栈空间。3.1.4中断级任务切换OSIntCtxSw(){OSTCBCur=OSTCBHighRdy;//任务控制块的切换OSPrioCur=OSPrioHighRdy;SP=OSPrioHighRdy-OSTCBStkPtr;//使SP指向待运行任务堆栈用出栈指令把R1、R2……弹入CPU的通用寄存器;RETI;//中断返回,使PC指向待运行任务}与任务级切换函数OSCtxSW()的原因一样,中断级任务切换函数OSIntCtxSw()通常是用汇编语言来编写的:3.1.5临界段在应用程序中经常有一些代码段必须不受任何干扰地连续运行,这样的代码叫做临阶段。怎样保证临阶段的安全?系统当有异步事件发生时会引发中断请求,CPU何时响应这个请求?需要的条件和策略?宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。临界段的概念:•当处理临界段代码时,需要关中断,处理完毕后,再开中断;•关中断时间是实时内核重要的指标之一;•在实际应用中,关中断的时间很大程度中取决于微处理器的结构和编译器生成的代码质量;•C/OS-II定义两个宏开关中断:OS_ENTER_CRITICAL();OS_EXIT_CRITICAL();因为这2个宏的定义取决于所使用的微处理器,因此在OS_CPU.H中可以找到相应的宏定义。OS_CPU.H是微处理器相关的。3.1.6开关中断的宏•OS_CRITICAL_METHOD==1用处理器指令关中断•执行OS_ENTER_CRITICAL(),关中断•执行OS_EXIT_CRITICAL(),开中断;方法1的示意性代码#defineOS_ENTER_CRITICAL()asm(“DI”)#defineOS_EXIT_CRITICAL()asm(“EI”)3.1.6.1开关中断的实现方法1以上代码所列假定编译器允许直接在C代码行之间插入行汇编语句。根据微处理器和C编译器的不同,通过在移植文件OS_CPU.H中配置OS_CRITICAL_METHOD来选择开/关中断的方法:方法2的示意性代码#defineOS_ENTER_CRITICAL()asm(“PUSHPSW”)asm(“DI”)#defineOS_EXIT_CRITICAL()asm(“POPPSW”)3.1.6.2开关中断的实现方法2一些编译器对插入的行汇编代码优化得并不好,上述办法未必可行,尤其是堆栈指针相对寻址模式时。••OS_CRITICAL_METHOD==2实现OS_ENTER_CRITICAL()时,先在堆栈中保存中断的开/关状态,然后再关中断;实现OS_EXIT_CRITICAL()时,从堆栈中弹出原来中断的开/关状态;第2种方法可使CPU中断允许标志的状态在临界段前和临阶段后不发生改变。voidSome_uCOS_II_Service(arguments){OS_CPU_SRcpu_sr……cpu_sr=get_processor_psw();disable_interrupts();/*处理临界代码*/set_processor_psw(cpu_sr);}方法3的示意性代码#defineOS_ENTER_CRITICAL()cpu_sr=get_processer_psw();disable_interrrupts();#defineOS_EXIT_CRITICAL()set-processer_psw(cpu_sr);3.1.6.3开关中断的实现方法3••OS_CRITICAL_METHOD==3把当前处理器的状态字保存在局部变量中(如OS_CPU_SR),关中断时保存,开中断时恢复。这样需要在选择用这种方法进入临界代码的应用程序中定义一个局部变量cpu_sr。第3种方法的前提条件:用户使用C编译器具有扩展功能,用户可获得程序状态字的值,这样就可以把该值保存在C语言函数的局部变量中,而不必压到堆栈里。•任何操作系统都要提供一个周期性的信号源,以供系统处理诸如延时、超时等与时间有关的事件,这个周期性的信号源叫做时钟。•硬件定时器产生一个周期为毫秒级的周期性中断来实现系统时钟。最小的时钟单位就是两次中断之间间隔的时间,这个最小时钟单位叫做时钟节拍。•硬件定时器以时钟节拍为周期定时的产生中断,该中断的中断服务程序叫做OSTickISR(),中断服务程序通过调用函数OSTimeTick()来完成系统在每个时钟节拍时需要做的工作。3.2uC/OS-II的时钟C/OS节拍率应选在10→100次/秒。必须在多任务系统启动OSStart()以后,再开启时钟节拍器。voidOSTickISR(void){保存CPU寄存器;调用OSIntEnter();//记录中断嵌套层数if(OSIntNesting==1){OSTCBCur-OSTCBStkPtr=SP;//在任务TCB中保存堆栈指针}调用OSTimeTick();//节拍处理清除中断;开中断;调用OSIntExit();恢复CPU寄存器;执行中断返回指令;}3.2.1时钟节拍中断服务子程序程序清单:时钟节拍中断服务子程序的示意代码因为使用C语言不便于对CPU的寄存器进行处理,所以这段代码使用汇编语言编写。3.2.2时钟节拍服务函数voidOSTimeTick(void){OS_TCB*ptcb;OSTimeTickHook();#ifOS_TIME_GET_SET_EN0OS_ENTER_CRITICAL();OSTime++;//记录节拍数OS_EXIT_CRITICAL();#endifif(OSRunning==TRUE){ptcb=OSTCBList;while(ptcb-OSTCBPrio!=OS_IDLE_PRIO){OS_ENTER_CRITICAL();if(ptcb-OSTCBDly!=0){if(--ptcb-OSTCBDly==0){//任务的延时时间减1if((ptcb-OSTCBStat&OS_STAT_SUSPEND)==OS_STAT_RDY){OSRdyGrp|=ptcb-OSTCBBitY;OSRdyTbl[ptcb-OSTCBY]|=ptcb-OSTCBBitX;}else{ptcb-OSTCBDly=1;}}}ptcb=ptcb-OSTCBNext;OS_EXIT_CRITICAL();}}}OSTimeTick()做了两件事情:1.给计