【单片机原理及其应用】第四章中断的介绍和习题分析

中断的介绍

什么是中断?官方定义:正常程序运行时发生了事先设定的事件,需要暂停原来运行的程序而转到处理目前,需要马上处理的事件。 对于大部分没有接触过计算机原理的人来说都是很陌生的。下面我们来举一个很简单的例子。
有一天,小强刷着手机走在路上,这时候小强的鞋带开了,这时候小强把手里的手机交给小强旁边的瑶瑶,瑶瑶替小强拿着,小强去系鞋带,系完鞋带,小强再从瑶瑶手里拿回手机。在这个过程中,小强停下刷手机,系鞋带,拿回手机这一个过程就叫做中断过程
中断过程分为几个:首先有触发事件(在单片机中这个事件需要你提前定义,否则它不能判断是不是属于中断事件),也就是例子中鞋带开了。其次它需要一个可返回性,也就是系鞋带后再拿回手机这一个过程。这时候就需要我们的一个老朋友就是堆栈(记住是堆栈,而不是堆也不是单纯的栈),之前说过它是一个类似弹夹的东西,也就是先入后出,后入先出的结构。他就可以帮助我们暂时的存储我们中断的事情,也就是例子中的瑶瑶。发生中断后,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等于低电平)这时候是下降沿过程,所以触发的方式就应该设置为下降沿
        }
    }  
    }
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值