天天看点

51单片机之外部中断应用实例(电平触发、边沿触发)

  • 硬件:STC89C52RC
  • 开发工具:Keil uVision4

前言:8051是一款很经典的、历史悠久的单片机,作为一款入门级的单片机8051受到很多初学者的欢迎。89c52是8051系列的成员之一,拥有8K字节程序存储空间,512字节随机数据存储空间;I/O口控制端口、中断功能、定时器及串行接口。下面详细讲述外部中断功能的使用。

外部中断:单片机提供的系统紧急事件的输入控制。事件触发的方式包括输入信号的下降沿触发、低电平触发。当触发中断后,单片机会跳到某一个固定的地址去执行中断服务程序。

外部中断信号由INT0、INT1引脚传送进来,如图所示:

51单片机之外部中断应用实例(电平触发、边沿触发)

有关中断处理的相关控制寄存器如下:

  • 计时计数器控制寄存器 TCON
  • 中断允许控制寄存器 IE
  • 中断优先权寄存器 IP

寄存器并不算多,配置起来也不复杂。先对各个寄存器进行说明:

TCON寄存器:

“T”开头的是计数/定时器相关位,“I”开头的是外部中断相关位,我们需要看的是后者:

51单片机之外部中断应用实例(电平触发、边沿触发)

IE寄存器:

51单片机之外部中断应用实例(电平触发、边沿触发)

IP寄存器:

51单片机之外部中断应用实例(电平触发、边沿触发)

CPU接到中断信号发生时会停止当前的工作跳转到中断服务程序。那么当CPU同时接到多个中断信号时,该怎么选择?当CPU正在中断函数时又接受到另一个中断信号,该怎么处理?

关于中断的优先级有一下原则:

1、CPU同时接收到几个中断时,首先响应优先级最高的中断请求,低优先的进入队列等待;

2、正在进行的中断过程不能被新的同级或低优先级的中断请求所中断;

3、正在进行的低优先级中断服务,能被高优先级中断请求中断;

那么,IP寄存器的某一中断配置为1就成为高优先级。每一个中断在IP里面只占一位配置位(IP.x=0或OP.x=1),也就是说系统里只存在两种优先级,要么是高优先级,要么是低优先级。

如果,任何中断都不配置IP寄存器的优先级,也等同于系统上电时,默认的优先级顺序如下:

外部中断0 > 定时/计数器0 > 外部中断1 > 定时/计数器1 > 串行中断

关于外部中断的寄存器已经了解清楚了,接下来看代码设计:

外部中断0(下降沿触发)

/*-----------------------------------------------
  功能:外部中断0边沿触发
  现象:首先将P3.2口通过上拉电阻接到电源,保证在空闲时P3.2处于高电平;
       当外部中断信号输出口P3.2接到GND时,产生了一个下降沿信号,接到P0.0
       口的LED灯反转;若此后P3.2持续接到GND,LED只反转一次,这与电平触发
       有区别。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定义LED端口

void DelayMs(unsigned char t)     //大致延时1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT0_init(void) //外部中断0初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中断开
  EX0=1;         //外部中断0开
  IT0=1;         //边沿触发
}

main()
{
  INT0_init();
  while(1){
     //主循环
  }
}

//中断服务程序  interrupt 0 指明是外部中断0的中断函数
/*
interrupt 0  指明是外部中断0;
interrupt 1  指明是定时器中断0; 
interrupt 2  指明是外部中断1;
interrupt 3  指明是定时器中断1;
interrupt 4  指明是串行口中断;
*/
void ISR_Key(void) interrupt 0 using 1
{
 if(!INT0){
    DelayMs(10);       //防抖动
    if(!INT0){         
     LED=!LED;         //按下触发一次,LED取反一次
    }
 }
}
           

外部中断0(电平触发)

/*-----------------------------------------------
  功能:外部中断0电平触发
  现象:首先将P3.2口通过上拉电阻接到电源,保证在空闲时P3.2处于高电平;
       当外部中断信号输出口P3.2接到GND时,产生了一个低电平信号,接到P0.0
       口的LED灯反转;若此后P3.2持续接到GND,LED会反复反转,这与边沿触
       发有区别。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定义LED端口

void DelayMs(unsigned char t)     //大致延时1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT0_init(void) //外部中断0初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中断开
  EX0=1;         //外部中断0开
  IT0=0;         //电平触发
}

main()
{
  INT0_init();
  while(1){
     //主循环
  }
}

//中断服务程序  interrupt 0 指明是外部中断0的中断函数
/*
interrupt 0  指明是外部中断0;
interrupt 1  指明是定时器中断0; 
interrupt 2  指明是外部中断1;
interrupt 3  指明是定时器中断1;
interrupt 4  指明是串行口中断;
*/
void ISR_Key(void) interrupt 0 using 1
{
  if(!INT0){
    DelayMs(20);       //防抖动
    if(!INT0){         
     LED=!LED;         //按下触发一次,LED取反一次
    }
 }
}
           

外部中断1(下降沿触发)

/*-----------------------------------------------
  功能:外部中断1边沿触发
  现象:首先将P3.3口通过上拉电阻接到电源,保证在空闲时P3.3处于高电平;
       当外部中断信号输出口P3.3接到GND时,产生了一个下降沿信号,接到P0.0
       口的LED灯反转;若此后P3.3持续接到GND,LED只反转一次,这与电平触发
       有区别。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定义LED端口
void DelayMs(unsigned char t)     //大致延时1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT1_init(void) //外部中断0初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中断开
  EX1=1;         //外部中断1开
  IT1=1;         //边沿触发
}

main()
{
  INT1_init();
  while(1){
     //主循环
  }
}

/*
interrupt 0  指明是外部中断0;
interrupt 1  指明是定时器中断0; 
interrupt 2  指明是外部中断1;
interrupt 3  指明是定时器中断1;
interrupt 4  指明是串行口中断;
*/
void ISR_Key(void) interrupt 2 using 1
{
 if(!INT1){
    DelayMs(10);       //防抖动
    if(!INT1){         
     LED=!LED;         //按下触发一次,LED取反一次
    }
 }
}

#endif
           

外部中断1(电平触发)

/*-----------------------------------------------
  功能:外部中断1电平触发
  现象:首先将P3.3口通过上拉电阻接到电源,保证在空闲时P3.3处于高电平;
       当外部中断信号输出口P3.3接到GND时,产生了一个低电平信号,接到P0.0
       口的LED灯反转;若此后P3.3持续接到GND,LED会反复反转,这与边沿触
       发有区别。
------------------------------------------------*/

#include<reg52.h> 

sbit LED=P0^0; //定义LED端口
void DelayMs(unsigned char t)     //大致延时1mS
{
 unsigned short T=500;
 while(t--)
 {
     while(--T);
 }
}
void INT1_init(void) //外部中断1初始化
{
  LED=1;       //LED口初始值
  EA=1;          //全局中断开
  EX1=1;         //外部中断1开
  IT1=0;         //电平触发
}

main()
{
  INT1_init();
  while(1){
     //主循环
  }
}

/*
interrupt 0  指明是外部中断0;
interrupt 1  指明是定时器中断0; 
interrupt 2  指明是外部中断1;
interrupt 3  指明是定时器中断1;
interrupt 4  指明是串行口中断;
*/
void ISR_Key(void) interrupt 2 using 1
{
 if(!INT1){
    DelayMs(10);       //防抖动
    if(!INT1){         
     LED=!LED;         //按下触发一次,LED取反一次
    }
 }
}
           

以上四种模式,代码都是大同小异,比较一下就知道哪些是关键点了。

注意点:

  • 上面的程序已经是测试过没有问题的,如果出现led不反转,那么检测一下电路,有一些集成了很多元件的开发板里面电路复杂,几个外围元件可能共用一个IO口,容易被干扰,以至于达不到想要的效果。最好还是买一个最小系统的单片机,所有IO独立出来。
  • 在选择“电平触发”模式下,因为低电平的持续时间比较长(虽然只是按一下,对于单片机来说已经持续很长),会出现反复进入中断,导致LED不会反转,解决方法就是在进入第一次中断后,先把该中断关闭掉并且用while循环,直到中断信号引脚退出低电平状态再打开中断并退出while循环,这么做缺点就是会阻塞在中断里面,可能导致主函数里面的程序不能及时运行。
  • 上面的代码比较简单,需要根据实验出现的问题进一步优化。
  • 外部中断还可以应用到检测波形的周期、占空比、频率以及红外接收处理,有兴趣可以试一下。

仅供参考,错误之处以及不足之处还望多多指教。

继续阅读