uCOSii中断处理过程详解

合集下载

第3章 μCOS-II的中断和时钟

第3章 μCOS-II的中断和时钟

cpu_sr = get_processor_psw();
disable_interrupts(); /* 处理临界代码*/ set_processor_psw(cpu_sr);
}
3.2 uC/OS-II的时钟
• 任何操作系统都要提供一个周期性的信号源,以供系统处理诸如 延时、超时等与时间有关的事件,这个周期性的信号源叫做时钟。 • 与大多数计算机系统一样,用硬件定时器产生一个周期为毫秒级 的周期性中断来实现系统时钟。最小的时钟单位就是两次中断之 间间隔的时间,这个最小时钟单位叫做时钟节拍。 • 硬件定时器以时钟节拍为周期定时的产生中断,该中断的中断服 务程序叫做OSTickISR(),中断服务程序通过调用函数 OSTimeTick()来完成系统在每个时钟节拍时需要做的工作。
为记录中断嵌套的层数 , μ C/OS-II内核定义 了一个全局变量 OSIntNesting。
NO
}
OSIntNesting++; } }
NO
任务是被中断的任务?
返回中断服务程序
if(OSIntNesting > 0){
NO 获得任务TCB的指针
OSIntNesting--;
执行中断级任务切换
这个函数在中断嵌套层数 计数器为0、调度器未被锁 定且从任务就绪表中查找 到的最高级就绪任务又不 是被中断的任务的条件下 将要进行任务切换,否则 就返回被中断的任务程序
C/OS节拍率最好选在10→100次/秒。 必须在多任务系统启动OSStart()以后,再开启时钟节拍器。

13
3.2.1 时钟节拍中断服务子程序
程序清单 : 时钟节拍中断服务子程序的示意代码
void OSTickISR(void) { 保存CPU寄存器; 调用OSIntEnter(); if(OSIntNesting == 1) { OSTCBCur -> OSTCBStkPtr = SP; } 调用OSTimeTick(); 清除中断; 开中断; 调用OSIntExit(); 恢复CPU寄存器; 执行中断返回指令; } // 记录中断嵌套层数

ucos-ii用户中断服务程序的编写

ucos-ii用户中断服务程序的编写

用户中断服务程序的编写1.调用OSIntEnter()或者直接OSIntNesting++;目的是告诉任务进入中断服务程序或者发生了用户中断,禁止任务切换。

2.中断处理进行相应的中断处理,可对信号量、消息邮箱、消息队列进行操作,用于任务间的通信3.调用OSIntExit()退出中断服务程序,进行任务级的任务切换串口中断服务程序实例void USART1_IRQHandler(void){unsigned int i;unsigned char msg[50];OS_CPU_SR cpu_sr;OS_ENTER_CRITICAL(); //保存全局中断标志,关总中断// T ell uC/OS-II that we are starting an ISROSIntNesting++;OS_EXIT_CRITICAL(); //恢复全局中断标志//OSTimeTick(); // Call uC/OS-II's OSTimeTick(),在os_core.c文件里定义,主要判断延时的任务是否计时到if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断读寄存器是否非空{// Read one byte from the receive data registermsg[RxCounter1++]= USART_ReceiveData(USART1); //将读寄存器的数据缓存到接收缓冲区里if(msg[RxCounter1-1]=='L'){msg[0]='L'; RxCounter1=1;} //判断起始标志if(msg[RxCounter1-1]=='F') //判断结束标志是否是"F"{for(i=0; i< RxCounter1; i++){TxBuffer1[i] =msg[i]; //将接收缓冲器的数据转到发送缓冲区,准备转发}TxBuffer1[RxCounter1]=0; //接收缓冲区终止符RxCounter1=0;//OSSemPost(Com1_SEM);OSMboxPost(Com1_MBOX,(void *)&msg);}}if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //{USART_ITConfig(USART1, USART_IT_TXE, DISABLE);}OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务就绪了,则执行一次任务切换}。

东软UCOS-II教程-03-中断与时钟

东软UCOS-II教程-03-中断与时钟
图3-3 中断服务子程序的流程图
3 Sept. 2008 Confidential
3.1.4 中断级任务切换
与任务级切换函数OSCtxSW() 的原因一样,中断级任务切换函数OSIntCtxSw ()通常 是用汇编语言来编写的:
OSIntCtxSw() { OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; SP = OSPrioHighRdy ->OSTCBStkPtr; 用出栈指令把 R1、 R2…… 弹入 CPU 的通用寄存器; RETI ; } // 中断返回,使 PC指向待运行任务 // 使 SP指向待运行任务堆栈 // 任务控制块的切换
学时:3.0学时 教学方法:讲授ppt+上机练 习+点评+案例分析
3 Sept. 2008 Confidential
3.1 uC/OS-II 的中断
� 中断:任务在运行过程中,应内部或外部异步事件的请求中止当前 任务,而去处理异步事件所要求的任务的过程叫做中断。 � 中断服务程序:应中断请求而运行的程序叫中断服务子程序 (ISR)。 � 中断向量:中断服务子程序的入口地址叫中断向量。 � CPU响应中断的条件: •至少有一个中断源向 CPU发出中断信号 •系统允许中断,且对此中断信号未予屏蔽

• OS_CRITICAL_METHOD==2 实现OS_ENTER_CRITICAL() 时,先在堆栈中保存中断的开/关状态,然后再 关中断;实现OS_EXIT_CRITICAL() 时,从堆栈中弹出原来中断的开/关状态; 方法2的示意性代码 #define OS_ENTER_CRITICAL() asm(“PUSH asm(“DI”) #define OS_EXIT_CRITICAL() asm(“POP PSW”) PSW”)

uCOS中断处理过程详解

uCOS中断处理过程详解

再看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++;//任务切换计数器加1OSIntCtxSw();//调用中断级任务切换函数}此段代码体现出了可剥夺型实时操作系统内核的特点.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+2MOV DX, WORD PTR DS:_OSTCBHighRdy ;DH=_OSTCBHighRdy+1;DL=_OSTCBHighRdyMOV WORD PTR DS:_OSTCBCur+2, AX ;_OSTCBCur+3=AH;_OSTCBCur+2=ALMOV WORD PTR DS:_OSTCBCur, DX ;_OSTCBCur+1=DH;_OSTCBCur=DL;OSTCBCur=OSTCBHighRdyMOV 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->OSTCBStkPtrPOP 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 == 3OS_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);//检查当前事件是不是信号量,不是则出错}#endifOS_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。

uCOS-II的任务切换机理及中断调度优化

uCOS-II的任务切换机理及中断调度优化

摘要:μC/OS-II是一种适用于嵌入式系统的抢占式实时多任务操作系统,开放源代码,便于学习和使用。

介绍μC/OS-II在任务级和中断级的任务切换原理,以及这一操作系统基于嵌入式系统的对于中断的处理;相对于内存资源较少的单片机,着重讨论一种优化的实用堆栈格式和切换形式,以提高资源的利用率;结合MSP430单片机,做具体的分析。

关键词:实时多任务操作系统μC/OS MSP430 中断堆栈引言在嵌入式操作系统领域,由Jean J. Labrosse开发的μC/OS,由于开放源代码和强大而稳定的功能,曾经一度在嵌入式系统领域引起强烈反响。

而其本人也早已成为了嵌入式系统会议(美国)的顾问委员会的成员。

不管是对于初学者,还是有经验的工程师,μC/OS开放源代码的方式使其不但知其然,还知其所以然。

通过对于系统内部结构的深入了解,能更加方便地进行开发和调试;并且在这种条件下,完全可以按照设计要求进行合理的裁减、扩充、配置和移植。

通常,购买RTOS往往需要一大笔资金,使得一般的学习者望而却步;而μC/OS对于学校研究完全免费,只有在应用于盈利项目时才需要支付少量的版权费,特别适合一般使用者的学习、研究和开发。

自1992 第1版问世以来,已有成千上万的开发者把它成功地应用于各种系统,安全性和稳定性已经得到认证,现已经通过美国FAA认证。

1 μC/OS-II的几大组成部分μC/OS-II可以大致分成核心、任务处理、时间处理、任务同步与通信,CPU的移植等5个部分。

核心部分(OSCore.c) 是操作系统的处理核心,包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分。

能够维持系统基本工作的部分都在这里。

任务处理部分(OSTask.c)任务处理部分中的内容都是与任务的操作密切相关的。

包括任务的建立、删除、挂起、恢复等等。

因为μC/OS-II是以任务为基本单位调度的,所以这部分内容也相当重要。

嵌入式实时操作系统uC OS-2教程(吴永忠)章 (4)

嵌入式实时操作系统uC OS-2教程(吴永忠)章 (4)
第4章 中断处理与时间管理
第4章 中断处理与时间管理
4.1 中断处理的基本概念 4.2 μC/OS-Ⅱ的中断处理 4.3 μC/OS-Ⅱ的时钟节拍 4.4 μC/OS-Ⅱ的时间管理 习题
第4章 中断处理与时间管理
4.1 中断处理的基本概念
4.1.1 中断 中断定义为CPU对系统内外发生的异步事件的响应。异步
中断响应考虑的是系统在最坏情况下的响应中断时间,而 不是平均时间。如某系统100次中有99次在100 μs之内响应中 断,只有一次响应中断的时间是250 μs,只能认为中断响应 时间是250 μs。
第4章 中断处理与时间管理
4.1.4 中断恢复时间 中断恢复时间(Interrupt Recovery)定义为CPU返回到被
第4章 中断处理与时间管理 在前后台系统中:
中断延迟
=
MAX
最长指令 时间
,关 最中 长断 时的 间

中断向量 距转时间
在不可剥夺型和不可剥夺内核中:
中断延迟 = MAX
最长指令 时间
,中用断户时关间
,中内断核时关间 +
中断向量 跳转时间
第4章 中断处理与时间管理
4.1.3 中断响应 中断响应定义为从中断发生起到开始执行中断用户处理程
特点是中断优先级高、延迟时间短、响应快、不能被嵌套、不 能忍受内核的延迟,一般常应用于紧急事件处理,如掉电保护 等。非屏蔽中断的规则如下:
(1) 在非屏蔽中断处理程序中,不能处理临界区代码、不 能使用内核提供的服务。
(2) 在非屏蔽中断处理程序中,参数的传递必须用全程变 量,且全程变量的字节长度必须能够一.6 μC/OS-Ⅱ的中断处理过程示意图
第4章 中断处理与时间管理

uCOS II在ARM处理器上移植过程中的中断处理

uCOS II在ARM处理器上移植过程中的中断处理

uCOS II在ARM处理器上移植过程中的中断处理uCOS II是一个源码藏匿、可移植、可固化、可剪裁和抢占式的实时多任务操作系统,其大部分源码是用ANSI C编写,与处理器硬件相关的部分用法汇编语言编写。

总量约200行的汇编语言部分被压缩到最低限度,以便于移植到任何一种其它的CPU上。

uCOS II最多可支持56个任务,其内核为占先式,总是执行就绪态的优先级最高的任务,并支持Semaphore (信号量)、Mailbox (邮箱)、MessageQueue(消息队列)等多种常用的进程间通信机制。

与大多商用RTOS不同的是,uCOS II藏匿全部的源代码.并可以免费获得,只对商业应用收取少量License费用。

uCOS II移植跟OS_CUP_C.C、OS_CPU_A.S、OS_CPU.H 3个文件有关,中断处理的移植占领了很大一部分内容。

作为移植的一个重点,本文以标准中断(IRQ)为例研究了移植中的中断处理。

1 uCOS II系统结构uCOS II的软硬件体系结构1。

应用程序处于囫囵系统的顶层.每个任务都可以认为自己独占了CPU,因而可以设计成为一个无限循环。

大部分代码是用法ANSI 书写的,因此uCOS II的可移植性较好。

尽管如此,仍然需要用法C和汇编语言写一些处理器相关的代码。

uCOS II 的移植需要满足以下要求:1)处理器的C编译器可以产生可重入代码:可以用法C调用进入和退出Critical Code(临界区代码);2)处理器必需支持硬件中断,并且需要一个定时中断源;3)处理器需能容纳一定数据的硬件堆栈;4)处理器需有能在CPU寄存器与内存和堆栈交换数据的命令。

移植uCOS II的主要工作就是处理器和编译器相关代码以及BSP(Board Support Package)的编写。

uCOS II处理器无关的代码提供uCOS II的系统服务,应用程序可以用法这些API函数举行内存管理、任务间通信以及创建、删除任务等。

uCOS中断处理过程详解

uCOS中断处理过程详解

再看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++;//任务切换计数器加1OSIntCtxSw();//调用中断级任务切换函数}此段代码体现出了可剥夺型实时操作系统内核的特点.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+2MOV DX, WORD PTR DS:_OSTCBHighRdy ;DH=_OSTCBHighRdy+1;DL=_OSTCBHighRdyMOV WORD PTR DS:_OSTCBCur+2, AX ;_OSTCBCur+3=AH;_OSTCBCur+2=ALMOV WORD PTR DS:_OSTCBCur, DX ;_OSTCBCur+1=DH;_OSTCBCur=DL;OSTCBCur=OSTCBHighRdyMOV 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->OSTCBStkPtrPOP 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 == 3OS_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);//检查当前事件是不是信号量,不是则出错}#endifOS_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。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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

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

二.UCOSII的中断过程的示意图三.具体中断过程1.中断到来,如果被CPU识别,CPU将查中断向量表,根据中断向量表,获得中断服务子程序的入口地址。

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

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

即:调用OSIntEnter()或OSIntNesting直接加1。

4.If(OSIntNesting==1){OSTCBCur->OSTCBStrPtr=SP;} //如果是第一层中断,则将堆栈指针保存到被中断任务的任务控制块中5.清中断源,否则在开中断后,这类中断将反复的打入,导致系统崩贵6.执行用户ISR7.中断服务完成后,调用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#endifOS_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”的原因==避免调整堆栈指针.出现这个问题的根源是当低优先级的任务被中断,当中断完成后由于有高优先级的任务就绪,则必须调度高优先级的任务,原来的低优先级任务继续被中断着,但是此时的低优先级任务的堆栈已经被破坏,已不能被调度程序直接调度了,要想被调度而必须调整堆栈指针。

如下图所示的场景:问题分析:要想理解加上上面两句的原因,不妨假设有下面场景出现: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 == 3OS_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]; //1OSPrioHighRdy = (INT8U)((OSIntExitY << 3) +OSUnMapTbl[OSRdyTbl[OSIntExitY]]); //2if (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次。

相关文档
最新文档