uCOSii中断处理过程详解(一)

uCOSii中断处理过程详解(一)
uCOSii中断处理过程详解(一)

一. UCOSII的中断过程简介

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

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

二.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.执行中断返回指令.

四.相关代码

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

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”的原因==避免调整堆栈指针.

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

问题分析:

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

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组.

按键中断

#include /* common defines and macros */ #include "derivative.h" /* derivative-specific definitions */ #define LED PORTB #define LED_dir DDRB #define KEY1 PTIH_PTIH3 #define KEY2 PTIH_PTIH2 #define KEY3 PTIH_PTIH1 #define KEY4 PTIH_PTIH0 #define KEY1_dir DDRH_DDRH3 #define KEY2_dir DDRH_DDRH2 #define KEY3_dir DDRH_DDRH1 #define KEY4_dir DDRH_DDRH0 unsigned char data=0x01; unsigned char direction=1; //设置灯亮的方向,0向左,1向右。unsigned char time=5; //设置灯闪的速度。 /*************************************************************/ /* 延时函数*/ /*************************************************************/ void delay(unsigned int n) { unsigned int i,j; for(j=0;j

操作系统实验一中断处理

实习一中断处理 一、实习内容 模拟中断事件的处理。 二、实习目的 现代计算机系统的硬件部分都设有中断机构,它是实现多道程序设计的基础。中断机 构能发现中断事件,且当发现中断事件后迫使正在处理器上执行的进程暂时停止执行,而让操作系统的中断处理程序占有处理器去处理出现的中断事件。对不同的中断事件,由于它们的性质不同,所以操作系统应采用不同的处理。通过实习了解中断及中断处理程序的作用。本实习模拟“时钟中断事件”的处理,对其它中断事件的模拟处理,可根据各中断事件的性质确定处理原则,制定算法,然后依照本实习,自行设计。 三、实习题目 模拟时钟中断的产生及设计一个对时钟中断事件进行处理的模拟程序。 [提示]: (1) 计算机系统工作过程中,若出现中断事件,硬件就把它记录在中断寄存器中。中 断寄存器的每一位可与一个中断事件对应,当出现某中断事件后,对应的中断寄存器的某一位就被置成―1‖。 处理器每执行一条指令后,必须查中断寄存器,当中断寄存器内容不为―0‖时,说明有中断事件发生。硬件把中断寄存器内容以及现行程序的断点存在主存的固定单元,且让操作系统的中断处理程序占用处理器来处理出现的中断事件。操作系统分析保存在主存固定单元中的中断寄存器内容就可知道出现的中断事件的性质,从而作出相应的处理。 本实习中,用从键盘读入信息来模拟中断寄存器的作用,用计数器加1 来模拟处理器 执行了一条指令。每模拟一条指令执行后,从键盘读入信息且分析,当读入信息=0 时,表示无中断事件发生,继续执行指令;当读入信息=1 时,表示发生了时钟中断事件,转时钟中断处理程序。 (2)假定计算机系统有一时钟,它按电源频率(50Hz)产生中断请求信号,即每隔20 毫秒产生一次中断请求信号,称时钟中断信号,时钟中断的间隔时间(20 毫秒)称时钟单

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++;//任务切换计数器加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

stm32 之 中断按键初始化(注意事项)

stm32 之 中断按键初始化(注意事项) 之前做终端按键的时候都是只做了一个,没有做多个,昨天在把所有按键都设置成中断模式的时候遇到问题,于是乎还跟一个网上的哥们进行了热议,后来还是我发现了问题!最终把问题给解决了! 我的按键的GPIO连接有点奇葩,他不是连续的,这可能就是竞赛板故意设置的难度吧! 首先管脚初始化: [cpp]view plaincopyprint? 1. GPIO_InitTypeDef key; 2. 3. RCC->APB2ENR |= ((1<<0)|(1<<2)|(1<<3)); 4. 5. key.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8; 6. key.GPIO_Mode = GPIO_Mode_IPD; 7. GPIO_Init(GPIOA, &key); 8. 9. key.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;

10. key.GPIO_Mode = GPIO_Mode_IPD; 11. GPIO_Init(GPIOB, &key); 全部设置成输入模式,AFIO再时钟使能的时候不要忘记了!这里我就 不多说了! 然后就是中断组设置: NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC初始化: [cpp]view plaincopyprint? 1. key_nvic.NVIC_IRQChannel = EXTI0_IRQn; 2. key_nvic.NVIC_IRQChannelCmd = ENABLE; 3. key_nvic.NVIC_IRQChannelPreemptionPriority = 0; 4. key_nvic.NVIC_IRQChannelSubPriority = 1; 5. NVIC_Init(&key_nvic); 重点都不在这,值得注意的是下面: 我第一次在配置EXTI Line的时候这样配置! GPIO_EXTILineConfig(GPIO_PortSourceGPIOA|GPIO_PortSourceGPIOB,\ GPIO_PinSource0|GPIO_PinSource1|GPIO_PinSource2|GPIO_PinSource8);大致一看,貌似很正常啊!但是问题就出在这! 我们跳转到GPIO_PinSourcex和GPIO_PortSourceGPIOx哪里看看:[cpp]view plaincopyprint? 1. #define GPIO_PortSourceGPIOA ((uint8_t)0x00) 2. #define GPIO_PortSourceGPIOB ((uint8_t)0x01) 3. #define GPIO_PortSourceGPIOC ((uint8_t)0x02) 4. #define GPIO_PortSourceGPIOD ((uint8_t)0x03) 5. #define GPIO_PortSourceGPIOE ((uint8_t)0x04) 6. #define GPIO_PortSourceGPIOF ((uint8_t)0x05) 7. #define GPIO_PortSourceGPIOG ((uint8_t)0x06) [cpp]view plaincopyprint?

ARM中异常中断处理概述

异常中断处理概述 1.ARM中异常中断处理概述 1)在正常程序执行过程中,每执行一条ARM指令,程序计数器寄存器PC的值加4个字 节;每执行一条Thumb指令,程序计数器寄存器PC的值加两个字节.整个过程是顺序执行. 2)通过跳转指令,程序可以跳转到特定的地址标号处执行,或者跳转到特定的子程序处 执行; B指令用于执行跳转操作; BL指令在执行跳转操作的同时,保存子程序的返回地址; BX指令在执行跳转操作的同时,根据目标地址的最低位可以将程序状态切换到Thumb状态; BLX指令执行3个操作:跳转到目标地址处执行,保存子程序的返回地址(R15保存在R14中),根据目标地址的最低位可以将程序状态切换到Thumb状态. 3)当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执 行.在当异常中断处理程序执行完成后,程序返回到发生中断的指令的下一条指令处执行. 4)在进入异常中断处理程序时,要保存被中断的程序的执行现场,在从异常中断处理程 序退出时,要恢复被中断的程序的执行现场.本章讨论ARM体系中的异常中断机制. 2.ARM体系中异常中断种类. ARM体系中的异常中断如下表所示:

3. 中断向量表中指定了各异常中断及其处理程序的对应关系.它通常存放在存储地址的低端.在ARM体系中,异常中断向量表的大小为32字节.其中,每个异常中断占据4个字节大小,保留了4个字节空间. 每个异常中断对应的中断向量表的4 .通过这两种指令,程序将跳转到相应的异常中断处理程序处执行. 当几个异常中断同时发生时,就必须按照一定的次序来处理这些异常中断.在ARM 中通过给各异常中断富裕一定的优先级来实现这种处理次序.当然有些异常中断是不坑能同时发生的,如指令预取中止异常中断和软件中断(SWI)异常中断是有同一条指令的执行触发的,他们是不可能同时发生的.处理器执行某个特定的异常中断的过程中,称为处理器处于特定的中断模式.各异常中断的中断向量地址以及中断的处理优先级如表2所示. 4.异常中断使用的寄存器 各异常中断对应着一定的处理器模式.应用程序通常运行在用户模式下.ARM中的处理器模式如表3所示. 各种不同的处理器模式可能有对应于该处理器模式的物理寄存器组,如表4所示,其中,R13_svc表示特权模式下的R13寄存器,R13_abt表示中止模式下的R13寄存器,其余的各寄存器名称含义类推. 表4 各处理器模式的物理寄存器组

基于STM32的单个按键中断的例子

#include"stm32f10x.h" //K1点亮LED , K2熄灭LED void RCC_Config(void); void GPIO_Config(void); void NVIC_Config(void); void EXTI_Config(void); int main(void) { RCC_Config(); GPIO_Config(); NVIC_Config(); EXTI_Config(); while(1); } void RCC_Config(void) { SystemInit(); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB 2Periph_AFIO,ENABLE); } void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_5; //PC5-K1 PC2-K2 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOC,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5; //led1 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure);

uCOSii中断处理过程详解(一)

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

四.相关代码 与编译器相关的数据类型: typedef unsigned char BOOLEAN; typedef unsigned char INT8U; typedef unsigned int OS_STK; //堆栈入口宽度为16 位(一) void OSIntEnter (void)的理解 uCOS_II.H中定义:

中断处理程序设计

课程实验报告 课程名称:汇编语言程序设计 实验名称:实验四 实验时间: 2015-6-16,14:30-17:30 实验地点:南一楼804室 指导教师:李专 专业班级:学号: 姓名: 同组学生: 报告日期: 成绩: 计算机科学与技术学院

一、原创性声明 本人郑重声明:本报告的内容由本人独立完成,有关观点、方法、数据和文献等的引用已经在文中指出。除文中已经注明引用的内容外,本报告不包含任何其他个人或集体已经公开发表的作品或成果,不存在剽窃、抄袭行为。 特此声明! 学生签字: 日期: 二、评语与成绩评定 1.指导老师评语 2.实验成绩评定 实验完成质量得分(70分)(实验步骤清晰详细深入,实验记录真实完整等)报告撰写质量得分(30分) (报告规范、完整、通顺、 详实等) 总成绩(100分) 指导教师签字: 日期:

目录 1.实验目的 (1) 2.实验内容 (1) 2.1任务一 (1) 2.2任务二 (1) 2.3任务三 (2) 2.4任务四 (2) 3实验过程 (2) 3.1任务一 (2) 3.1.1实验要求 (2) 3.1.2实验结果 (2) 3.2任务二 (4) 3.2.1设计思想及存储分配 (4) 3.2.2程序框图 (5) 3.2.3源程序代码 (6) 3.2.4实验结果 (7) 3.3任务三 (7) 3.3.1源程序代码 (7) 3.3.2实验结果 (11) 3.4任务四 (12) 3.4.1源程序代码 (12) 3.4.2实验结果 (16) 4.实验体会 (16)

1.实验目的 (1) 掌握中断矢量表的概念 (2)掌握中断处理程序设计的技巧 (3)掌握简化段定义、函数调用伪指令 (4)了解Win32程序的编程方法及编译、链接方法 2.实验内容 2.1任务一 用三种方式获取中断类型码10H对应的中断处理程序的入口地址。 要求:(1) 直接运行调试工具(TD.EXE),观察中断矢量表中的信息; (2) 编写程序,用 DOS功能调用方式获取,观察相应的出口参数与(1) 中看到的结果是否相同(使用TD观看即可) (3) 编写程序,直接读取相应内存单元,观察读到的数据与(1)看到的结 果是否相同(使用TD观看即可)。 2.2任务二 编写一个中断服务程序并驻留内存,要求在程序返回DOS操作系统后,键盘的按键A变成了按键B、按键B变成了按键A。 提示:(1) 对于任何DOS程序,不管其采用什么方法获取按键,最后都是通过执行16H号软中断的0号和10H号功能调用来实现的。所以,你只需接 管16H号软中断的0号和10号功能调用并进行相应的处理; (2) 获得一个按键扫描码的方法:在TD中执行16H中断的0号和10H号 功能调用,按相应的键,观察AH中的内容。 资料:16H中断的0号和10H号功能 功能描述:从键盘读入字符 入口参数:AH = 00H——读键盘 = 10H——读扩展键盘 出口参数:AH =键盘的扫描码 AL =字符的ASCII码

怎么在中断中实现一个按键第一次按键之后,立刻切换到一个循环状态。

问题的提出:怎么在中断中实现一个按键第一次按键之后,立刻切换到一个循环状态,然后同一个按键第二次按下时结束循环,回到中断入口? 疑难点: 1.中断是实时扫描的,但是中断中改变的状态量在主函数中却不能实时读取,这样不能达到立刻切换的目的。 2.如果把循环子函数放在中断中,那么仅仅用一个状态变量的话,是不可能时刻改变它的值的,因为在C中状态变量相当于一个存储器。 例子说明解答方法: 例子:想中断按键按下时,循环执行Led子函数,再次按下时,停止循环,回到主函数继续执行。 很多人是这样写的: void int0(void)interrupt 0 { if (k1==0) { delay_ms(10); if(k1==0) { m=!m; while(!k1); while(m) Led(); } } } 这样写为什么不行呢?因为m状态量改变一次之后,若m==1,则进入while()循环,此后即使中断改变了m的值,但是while(m) Led();一直在进行while(1)循环,所以m即使再怎么改变,按键都不能复位到初始化状态。下面提出这样一串代码: void int0(void)interrupt 0 { if (k1==0) { delay_ms(10); if(k1==0) { m=!m; while(!k1); while(k1&&m) Led(); } } } 这样因为K1按键弹出后立刻变为1,而k1它属于口线,并不是变量,所以实时改变,一旦按键再次按下时,K1变0,即可从while()中挑出,则程序会再次去读出m,此时将m值激活,则可达到再次按键初始化问题,并且反应比起在主函数中扫描要灵敏N倍。

IRQ0中断处理全过程

IRQ0中断处理全过程 1:系统注册IRQ0(时钟中断)的下半部分的处理过程。 在\kernel\sched.c的sched_init函数中 init_bh(TIMER_BH, timer_bh);/*TIMER_BH==0*/ init_bh(TQUEUE_BH, tqueue_bh);/*TQUEUE_BH==2*/ init_bh(IMMEDIATE_BH, immediate_bh);/*IMMEDIATE_BH==11*/ init_bh(TIMER_BH, timer_bh)把timer_bh函数注册为定时器的下半部分。 来看看init_bh是怎么处理的。 去掉一些加琐解琐的东西,就变成以下了。 void init_bh(int nr, void (*routine)(void)) { bh_base[nr] = routine; atomic_set(&bh_mask_count[nr], 0); bh_mask |= 1 << nr; } 就是简单的设置bh_base和bh_mask. 看看这些的定义: atomic_t bh_mask_count[32]; unsigned long bh_active = 0; unsigned long bh_mask = 0; void (*bh_base[32])(void); bh_base[] 31 bh_active 0 Bottom half handler(timer_bh) 31 bh_mask 0 不好意思,画的这么难看:P 如果bh_mask的第N位被置为1,则表明bh_base[]中的第N个指针指向了一个Bottom half 例程。 如果bh_active的第N位被置为1,则表明一旦调度进程许可,立即调用第N个Bottom hal f 例程。 bh_mask_count[]跟踪为每个下半部分提出的enable/disable请求嵌套对的数组。2.系统初始化时钟中断(IRQ0) ①先看看start_kernel(\init\main.c)吧 有几个跟时钟中断(IRQ0)相关的函数。 init_IRQ(); sched_init(); time_init(); sched_init()就是注册时钟中断的下半部分,其实IRQ0两个下半部分,一个前面已经看 到,还有一个下半部分init_bh(TQUEUE_BH, tqueue_bh);下面会看到。 ②init_IRQ函数在\arch\i386\kernel\irq.c 比较重要的调用就是init_ISA_irqs();

外部按键中断延时控制LED设计

成绩 实训报告 题目:外部按键中断延时控制LED设计课程名称: ARM嵌入式系统实训 学生姓名:徐欣郑亮亮杨康宁 任课教师:权循忠 系别: 电子工程学院 专业:通信工程 年级: 13级 实训时间: 2015年11月13日 电子工程学院

外部按键中断延时控制LED设计 学生:徐欣郑亮亮杨康宁 指导老师:权循忠 电子工程学院通信工程专业 实训目的 1.掌握STM32项目开发流程; 2.学会画出算法流程图; 3.掌握LED的控制编程。 二、实训内容 (1)LED0亮2秒,LED0灭2秒;循环(1)的操作5次; (2)LED1亮3秒,LED1灭3秒;循环(2)的操作5次; (3)然后到(1)循环。 三、实训过程 1.人员分配: 徐欣负责程序部分,利用c语言设计算法,建立工程项目,生成目标文件,并将目标文件编程下载到开发板,验证算法;郑亮亮负责绘制电路图,进行电路设计及分析;杨康宁负责程序流程框图以及完成实验报告。 2.电路设计: (1)要求使用LED0和LED1两个LED,连接图如下: 图1、LED原理图

(2)所用到的硬件只有LED(DS0和DS1)。LED与MCU连接,实现定时控制LED闪烁,其原理图如下: 图2、LED与STM32连接原理图 3.电路分析: 因为用Proteus绘图软件来绘制原理图,Proteus中没有MCU芯片,通过手绘将这些芯片和引脚绘出来,不能进行仿真。 LED0(DS0)与PB5相连;LED1(DS1)与PE5相连。 4.算法设计

算法解释: 通过if 选择语句实现按键选择功能, 按下KEY0时,实现函数 LED0=0; delay_ms(1500); delay_ms(1500); 按下KEY1时,实现函数 LED0=0; delay_ms(1500); delay_ms(1500); 按下WK_UP 时,实现函数 LED0=0; LED1=0; delay_ms(1500); delay_ms(1500); delay_ms(1500); delay_ms(500); 延时3S LED0,1灭 LED0灭 LED0灭 延时5S 延时3S KEY0按下? KEY2按下? KEY1按下? LED0,1亮 LED0亮 LED0亮 开始 初始化I/O 为输入,开启I/O 复用时钟,设置I/O 与中断线的映射关系,初始化线上中断,设置触发条件等,配置中断分组(NVIC ),并使能中断编 写中断服务函数 检测按键

Linux中断处理流程

Linux中断处理流程 先从函数注册引出问题吧。 一、中断注册方法 在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义: int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) irq是要申请的硬件中断号。 handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。 irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程 序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的 SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的) dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。devname设置中断名称,在cat /proc/interrupts中可以看到此名称。 request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。 关于中断注册的例子,大家可在内核中搜索下request_irq。 在编写驱动的过程中,比较容易产生疑惑的地方是: 1、中断向量表在什么位置?是如何建立的? 2、从中断开始,系统是怎样执行到我自己注册的函数的? 3、中断号是如何确定的?对于硬件上有子中断的中断号如何确定? 4、中断共享是怎么回事,dev_id的作用是? 本文以2.6.26内核和S3C2410处理器为例,为大家讲解这几个问题。 二、异常向量表的建立 在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。V和中断向量表的对应关系如下: V=0 ~ 0x00000000~0x0000001C V=1 ~ 0xffff0000~0xffff001C arch/arm/mm/proc-arm920.S中 .section ".text.init", #alloc, #execinstr __arm920_setup: ...... orr r0, r0, #0x2100 @ ..1. ...1 ..11 (1) //bit13=1 中断向量表基址为0xFFFF0000。R0的值将被付给CP15的C1.

实验五 键盘中断实验

实验五键盘中断实验 一、实验目的 1.熟练运用CodeWarrior嵌入式开发系统环境、C语言、调试方式。 2.复习串行通信接口(SCI)的内容。 3.加强键盘中断基本原理及编程原理的理解。 4.理解“行扫描”法的原理并能进行键值识别和键值编码。 5.理解键盘接线原理图(如图5-1)。 二、知识要点 本实验采用的是4×4矩阵式键盘(以下简称键盘)。PTG4、PTD2、PTD3、PTD7分别接四根列线,定义为输入且上拉,PTG0~PTG3分别接四根行线,且定义为输出。行扫描法是使键盘的某一行输出为低电平,其余行为高电平,然后读取列值,如果列值中有某位为低电平,则表明该行和列交点处的键被按下;若为全高则再扫描下一行,直至扫描完全部的行线为止。这样就可以确定是哪一行哪一列交点的键被按下。 MCU与键盘接线原理图: 图5-1 4×4键盘与MCU接法示例 键盘的c语言编程: 1)初始化,先按IO口方式初始化,即定义列线为输入且上拉,行线为输出,然后依输入口的键盘功能初始化相应的寄存器。 2)定义键值表 3)扫描一次,读取键值 4)获得键盘定义值 行扫描法是使键盘的某一行输出为低电平,其余行为高电平,然后读取列值,如果列值中有某位为低电平,则表明该行和列交点处的键被按下;若为全高则再扫描下一行,直至扫描完全部的行线为止。这样就可以确定是哪一行哪一列交点的键被按下。

设置键盘中断允许寄存器,当键盘有键被按下时,立即产生中断,中断程序处理按键事件,比如确定哪个键被按下,然后转换为该键的定义值。 键盘的键面标示码(即定义值)与MCU识别的键值对应关系通过列表对应起来,即键盘定义表对应表示。当通过“行扫描”法获得某个键的键值时,通过查表法就可以得到它的定义值。 该键盘中断方式程序的主程序主体是一个死循环,且是一个空循环体,所有处理的过程代码放在中断程序中。 三、演示性实验 在光盘资料中提供读者键盘实例程序文件夹。 编程采用规范要求编写,将键盘独立成一个构件,如C语言中,形成key.h头文件和key.c源文件。头文件对键盘的所用端口寄存器或引脚进行宏定义以及初始化函数和驱动函数声明。源文件对初始化函数和驱动函数进行定义。具体实现代码见光盘。 四、设计性实验 要求按下的一个键的键值和键面定义值(键的ASCII码值)通过串口在PC方软件界面显示。 当键值被按下时,高端虚拟键盘被按下,或者在高端PC机中显示对应按键值。 1、资源使用 键盘的数据线分别接在MCU 口的号引脚、口的号引脚。 2、硬件设计(标识引脚名) 图5-2 4×4键盘按键的信息显示在PC机界面的连线图 3、软件设计 1)MCU端程序流程图 2)编程 (1)下面填写主程序main。 (填写主程序main.c) 键盘中断程序 (填写C语言编写的键盘中断程序)

C51中断处理过程

C51中断处理过程 3 C51中断处理过程 C51编译器支持在C源程序中直接开发中断过程,因此减轻了使用汇编语言的繁琐工作,提高了开发效率。中断服务函数的完整语法如下: void函数名(void)[模式] [再入]interrupt n [using r] 其中n(0~31)代表中断号。C51编译器允许32个中断,具体使用哪个中断由80C51系列的芯片决定。r(0~3)代表第r组寄存器。在调用中断函数时,要求中断过程调用的函数所使用的寄存器组必须与其相同。"再入"用于说明中断处理函数有无"再入"能力。C51编译器及其对C语言的扩充允许编程者对中断所有方面的控制和寄存器组的使用。这种支持能使编程者创建高效的中断服务程序,用户只须在C语言下关心中断和必要的寄存器组切换操作。例3 设单片机的fosc=12MHz,要求用T0的方式1编程,在P1.0脚输出周期为2ms的方波。例3 设单片机的fosc=12MHz,要求用T0的方式1编程,在P1.0脚输出周期为2ms的方波。用C语言编写的中断服务程序如下: #include sbit P1_0=P1^0; void timer0(void)interrupt 1 using 1 { /*T0中断服务程序入口*/ P1_0=!P1_0; TH0=-(1000/256); /*计数初值重装*/ TL0=-(1000%256); } void main(void) { TMOD=0x01; /*T0工作在定时器方式1*/ P1_0=0; TH0=-(1000/256); /*预置计数初值*/ TL0=-(1000%256); EA=1; /*CPU开中断*/ ET0=1; /*T0开中断*/ TR0=1; /*启动T0*/ do{}while(1); } 在编写中断服务程序时必须注意不能进行参数传递,不能有返回值。 8051 系列 MCU 的基本结构包括:32 个 I/O 口(4 组8 bit 端口);两个16 位定时计数器;全双工串行通信;6 个中断源(2 个外部中断、2 个定时/计数器中断、1 个串口输入/输出中断),两级中断优先级;128 字节内置RAM;独立的 64K 字节可寻址数据和代码区。中断发生后,MCU 转到 5 个中断入口处之一,然后执行相应的中断服务 处理程序。中断程序的入口地址被编译器放在中断向量中,中断向量位于程序代码段的最低地址处,注意这里的串口输入/输出中断共用一个中断向量。8051的中断向量表如下: 中断源中断向量 --------------------------- 上电复位 0000H 外部中断0 0003H 定时器0 溢出 000BH 外部中断1 0013H 定时器1 溢出 001BH

按键中断程序

* 1个LED指示灯,对应的GPIO为 : PC0输出为1点亮LED 输出为0关闭LED 另外PC1为外部中断触发:按键按下时为低电平,即可设置为低电平触发 /* GPIO配置函数 */ void GPIO_Configuration(void) { /*定义2个结构体变量 */ GPIO_InitTypeDef GPIO_InitStructure; /*开启GPIOB,GPIOC ,复用口时钟的时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /*给GPIOC_Pin_1一个初始值*/ GPIO_ResetBits(GPIOC, GPIO_Pin_0); 配置IO口,初始化IO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ; // 将连接LED的GPIO设置为推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为2MHZ的速度,响应时间,没要求越小越好GPIO_Mode_IPU为输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //初始化GPIOC GPIO_Init(GPIOC, &GPIO_InitStructure); /*给KEY_InitStructure.GPIO_Pin KEY_InitStructure.GPIO_Mode KEY_InitStructure.GPIO_Speed付初始值*/

arm中断处理流程

ARM编程特别是系统初始化代码的编写中通常需要实现中断的响应、解析跳转和返回等操作,以便支持上层应用程序的开发,而这往往是困扰初学者的一个难题。中断处理的编程实现需要深入了解ARM内核和处理器本身的中断特征,从而设计一种快速简便的中断处理机制。需要说明的是,具体的上层高级语言编写的中断服务函数不在本文的讨论范围之内。 ARM处理器异常中断处理概述 当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。当异常中断处理程序执行完成后,程序返回到发生中断的指令的下一条指令处执行。在进入异常中断处理程序时,要保存被中断的程序的执行现场。从异常中断处理程序退出时,要恢复被中断的程序的执行现场。ARM体系中通常在存储地址的低端固化了一个32字节的硬件中断向量表,用来指定各异常中断及其处理程序的对应关系。当一个异常出现以后,ARM微处理器会执行以下几步操作: 1)保存处理器当前状态、中断屏蔽位以及各条件标志位; 2)设置当前程序状态寄存器CPSR中相应的位; 3)将寄存器lr_mode设置成返回地址; 4)将程序计数器(PC)值设置成该异常中断的中断向量地址,从而跳转到相应的异常中断处理程序处执行。 在接收到中断请求以后, ARM处理器内核会自动执行以上四步,程序计数器PC总是跳转到相应的固定地址。从异常中断处理程序中返回包括下面两个基本操作: 1)恢复被屏蔽的程序的处理器状态; 2)返回到发生异常中断的指令的下一条指令处继续执行。 当异常中断发生时,程序计数器PC所指的位置对于各种不同的异常中断是不同的,同样,返回地址对于各种不同的异常中断也是不同的。例外的是,复位异常中断处理程序不需要返回,因为整个应用系统是从复位异常中断处理程序开始执行的。 支持中断跳转的解析程序 解析程序的概念和作用 如前所述,ARM处理器响应中断的时候,总是从固定的地址开始的,而在高级语言环境下开发中断服务程序时,无法控制固定地址开始的跳转流程。为了使得上层应用程序与硬件中断跳转联系起来,需要编写一段中间的服务程序来进行连接。这样的服务程序常被称作中断解析程序。 每个异常中断对应一个4字节的空间,正好放置一条跳转指令或者向PC寄存器赋值的数据访问指令。理论上可以通过这两种指令直接使得程序跳转到对应的中断处理程序中去。但实际上由于函数地址值为未知和其它一些问题,并不这么做。这里给出一种常用的中断跳转流程:

Linux中断处理过程浅析

linux中断响应和处理过程: 首先中断属于异常的一种。异常,就是可以打断CPU正常运行流程的一些事情,比如说外部中断,未定义的指定,试图修改只读数据,执行SWI指定(software interrupt instructin,软件中断指令,比如说上层调用sys_read,sys_write就会产生swi)等。 内核启动时在start_kernel函数(init/main.c)中调用trap_init , init_IRQ两个函数来设置异常的处理函数。 trap_init函数(arch/arm/kernel/traps.c) void_init trap_init(void) { ...... memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); ....... } 上面两条定义的是异常向量的存放地方,即:__stubs_start~~~~~ __stubs_end之间就是异常向量. 接下来我们看异常向量之间的定义:(arch/arm/kernel/entry-armv.s) .equ stubs_offset, __vectors_start + 0x200 - __stubs_start .globl __vectors_start __vectors_start: ARM( swi SYS_ERROR0 ) //复位时.CPU交执行这条指令 THUMB( svc #0 ) THUMB( nop ) W(b) vector_und + stubs_offset //未定义异常时,CPU将执行这条跳转指令 W(ldr) pc, .LCvswi + stubs_offset //swi异常 W(b) vector_pabt + stubs_offset //指令预取止 W(b) vector_dabt + stubs_offset //数据访问中止 W(b) vector_addrexcptn + stubs_offset //没有用到 W(b) vector_irq + stubs_offset //irq中断 W(b) vector_fiq + stubs_offset //fig中断(快速中断) .globl __vectors_end __vectors_end: 各种异常的处理函数可以分为五类,分别分布在下面不同的文件中: 1、arch/arm/kernel/traps.c中 处理未定义指令异常,总入口函数为do_undefinstr

实验二 按键中断实验

实验二按键中断实验 一、实验目的 了解中断的含义 二、实验内容 板子加电后,按动板子上K1-K3按键,可控制对应的LED1-LED3的亮灭,该实验学习了外部中断(EXTI)程序的编制及控制流程。 三、实验仪器、设备 计算机、开发板、keil软件 四、硬件设计 在开发板上V6、V7、V8分别与MCU的PB5、PD6、PD3相连,如下图所示 键盘部分如下图所示: 例程所用到的列扫描线:PC5,PC2,PC3。 例程所用到的行扫描线(EXTI中断线):PE2。

五、实验要求和步骤 开发板上有3个蓝色状态指示灯V6(LED1),V7(LED2),V8(LED3),通过对应的按键K1-K3,控制LED的亮灭,将PE2引脚配置为外部中断,当其上出现下降沿时产生一个中断,根据扫描PC5,PC2,PC3来判别是哪个按键按下。 首先我们了解一下什么是外部中断/事件控制器(EXTI)。 外部中断/事件控制器由19个产生事件/中断要求的边沿检测器组成。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以被独立的屏蔽。挂起寄存器保持着状态线的中断要求。 EXTI控制器的主要特性如下: 每个中断/事件都有独立的触发和屏蔽 每个中断线都有专用的状态位 支持多达19 个中断/事件请求 检测脉冲宽度低于APB2 时种宽度的外部信号 如要产生中断,中断线必须事先配置好并被激活。这是根据需要的边沿检测通过设置2个触发寄存器,和在中断屏蔽寄存器的相应位写“1”到来允许中断请求。当需要的边沿在外部中断线上发生时,将产生一个中断请求,对应的挂起位也随之被置1。通过写“1”到挂起寄存器,可以清除该中断请求。为产生事件触发,事件连接线必须事先配置好并被激活。这是根据需要的边沿检测通过设置2个触发寄存器,和在事件屏蔽寄存器的相应位写“1”到来允许事件请求。当需要的边沿在事件连线上发生时,将产生一个事件请求脉冲,对应的挂起位不被置1。通过在软件中断/事件寄存器写“1”,一个中断/事件请求也可以通过软件来产生。 本次实验需要组件的工程文件文档如下: USER--stm32f10x_it.c 为中断服务程序主程序,我们对主程序进行一次详细的注释。 //______________________主程序____________________________________________________________________ int main(void) { unsigned char a=0,b=0,c=0; /*完成对系统时钟的设置,例程中通过系统时钟设置函数,外接晶振采用8Mhz,经过片内频率合成,9倍频,设置为72MHz的时钟。*/ RCC_Configuration(); /*嵌套向量中断控制器

相关文档
最新文档