中断的介绍
什么是中断?官方定义:正常程序运行时发生了事先设定的事件,需要暂停原来运行的程序而转到处理目前,需要马上处理的事件。 对于大部分没有接触过计算机原理的人来说都是很陌生的。下面我们来举一个很简单的例子。
有一天,小强刷着手机走在路上,这时候小强的鞋带开了,这时候小强把手里的手机交给小强旁边的瑶瑶,瑶瑶替小强拿着,小强去系鞋带,系完鞋带,小强再从瑶瑶手里拿回手机。在这个过程中,小强停下刷手机,系鞋带,拿回手机这一个过程就叫做中断过程。
中断过程分为几个:首先有触发事件(在单片机中这个事件需要你提前定义,否则它不能判断是不是属于中断事件),也就是例子中鞋带开了。其次它需要一个可返回性,也就是系鞋带后再拿回手机这一个过程。这时候就需要我们的一个老朋友就是堆栈(记住是堆栈,而不是堆也不是单纯的栈),之前说过它是一个类似弹夹的东西,也就是先入后出,后入先出的结构。他就可以帮助我们暂时的存储我们中断的事情,也就是例子中的瑶瑶。发生中断后,PC(程序计数器)就会指向0004H也就是中断向量。执行中断事件发生后,你所需要执行的任务。执行完后,再从堆栈中取回刚才被中断的事情的地址值给PC,继续执行程序。具体的过程,如下图:
这里的GIE相当于中断事情发生的标识符,当你调试程序的时候你可以通过查询GIE观察中断是否发生。
GIE介绍:总的中断允许位(当GIE=0的时候,所有中断事件(称为中断源)都没法引起中断程序的发生,只有当GIE=1的时候,中断事件(称为中断源)才有可能引起中断程序。类比卵细胞膜反应和透明带反应,阻止其他中断事件继续进入中断程序)
具体的物理结构可以看下面的图
注意:可以看到图中字母结尾有E和F的,E就是使能端口(类似数电中三态门中的使能端口EN),F是标志位(Flag)当标志位为1时候,说明中断源发出中断的信号,这个1需要你手动复位为0,相当于每次中断程序执行的时候,GIE会自动复位,而这个标志位不会,需要你每次手动操作。
下面是几个重要的寄存器的使用图:
1.中断控制寄存器(interrupt control register缩写:INTCON)
2.第一外围寄存器
使能寄存器
标志位控制
3.第二外围寄存器
使能
标志位
注意:如果你要使用RB0即外部中断(RB0/INT)的中断边沿选择,需要用到一个之前学过的寄存器与中断直接相关的位为INTEDG。0为下降沿
复习一下,第一位那个表示B口弱上拉允许位,1为禁止。注意这里如果要设置弱上拉,请用OPTION_REG去设置,而不能直接用RBPU去设置。
注意:中断不允许嵌套执行,也就是递归使用!!!
作业:在RB0上接开关,PORTD端口接8盏灯,利用RB0/INT中断的方式实现开关打开时,PORTD奇数端口接的灯亮;开关闭合时,PORTD偶数端口接的灯亮。利用中断完成这个作业!!!请自己先尝试一下,再看看,这道题目前存在两种主要的解法。加油!!!
电路图如下:
/* * File: demo04.c * Author: Xueziqiang * * Created on 2022年3月19日, 下午11:01 */ // PIC16F887 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1 #pragma config FOSC = XT // Oscillator Selection bits (XT oscillator: Crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config MCLRE = OFF // RE3/MCLR pin function select bit (RE3/MCLR pin function is digital input, MCLR internally tied to VDD) #pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled) #pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled) #pragma config BOREN = OFF // Brown Out Reset Selection bits (BOR disabled) #pragma config IESO = OFF // Internal External Switchover bit (Internal/External Switchover mode is disabled) #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled) #pragma config LVP = OFF // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)
// CONFIG2 #pragma config BOR4V = BOR40V // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V) #pragma config WRT = OFF // Flash Program Memory Self Write Enable bits (Write protection off)
// #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. //在RB0上接开关,PORTD端口接8盏灯,利用RB0/INT中断的方式实现开关打开时,PORTD奇数端口接的灯亮;开关闭合时,PORTD偶数端口接的灯亮。 #include <xc.h> void DELAY(unsigned int n); void __interrupt()ISR(void); void main(void) { //端口初始化,在使用端口前需要对端口进行初始化设定 TRISD=0B00000000; // 端口D全部定义为输出口 0为输出 PORTD=0; // 初始状态下,灯全部熄灭 TRISB=0b00000001; // 端口B定义 RB0为输入口,其他随意,没有使用到 ANSEL=0; //定义低八位对应为数字量输入 详细可查询IO口的** ANSELH=0; //定义高八位对应为数字量输入 详细可查询IO口的** OPTION_REG=0b10000000; //禁止弱上拉,并设置为下降沿中断 //这里我使用到的是RB0/INT外部事件触发的中断,这里需要将这个口(第五位)的使能端打开 如果你使用其他的使能口,那就打开其他的使能口。//第二种方法用到是RB口电平变化,这时候就打开第四位的使能 INTCON=0b10010000; //注意第八位为全局中断使能,需要打开(为1) /*/下面是对灯进行初始化,有人问为什么?原因在于,刚开始,你不知道开关的状态是什么,所以后面灯亮就很可能是随机的,不符合题目的要求。 * 这时候就要对灯也进行按照开关的状态进行初始化。 * 最前面对灯的初始化(PORTD=0;)是一个普遍的初始化,即没有建立灯与输入口的逻辑关系 */ if(RB0==1){ PORTD=0b01010101;//开关打开,此时奇数灯亮 记住端口RD0对应是最右边的那一位。不要搞反了!!! INTEDG=0;//将端口触发状态设置为下降沿,因为此时开关是断开的,RB0处在高电平,当中断事件(开关闭合,RB0等于低电平)这时候是下降沿过程,所以触发的方式就应该设置为下降沿 } else{ PORTD=0b10101010;//偶数 INTEDG=1;//将端口触发状态设置为上升沿,因为此时开关是闭合的,RB0处在低电平,当中断事件(开关闭合,RB0等于高电平)这时候是上升沿过程,所以触发的方式就应该设置为上升沿 } while(1); //让程序等待,等待外部中断事件的发生 return; } void DELAY(unsigned int n) { unsigned int j; char k; for (j=0;j<n;j++){ for (k=1;k>0;k--) NOP();//延时语句,NOP()意思是啥也不做 但是也需要占据一个指令周期 } } void __interrupt()ISR(void){ if(INTF==1){ //外部事件中断标志位为1,意味着这时候发生了外部中断 DELAY(100); //延迟,对于按钮开关起到防止抖动的作用。这里其实可以省略 INTF=0; //!!!一定要注意,一定要把标志位复位 if(RB0==0){ PORTD=0b10101010; INTEDG=1;//将端口触发状态设置为上升沿,因为此时开关是闭合的,RB0处在低电平,当中断事件(开关闭合,RB0等于高电平)这时候是上升沿过程,所以触发的方式就应该设置为上升沿 } else{ PORTD=0b01010101; INTEDG=0;//将端口触发状态设置为下降沿,因为此时开关是断开的,RB0处在高电平,当中断事件(开关闭合,RB0等于低电平)这时候是下降沿过程,所以触发的方式就应该设置为下降沿 } } }
|