第10讲 外部中断

在前面的章节里我们介绍了AVR单片机的开发环境,学习了芯片的内部结构、I/O口驱动和片内功能的设置方法。本章将从AVR单片机的中断系统入手,重点学习外部中断及其编程方法。

7.1

中断系统

中断是处理器必备的功能之一。所谓中断,就是当一个特殊的事件发生后,处理器暂时停止当前的工作,转而去处理刚刚发生的特殊事件,当该事件处理完毕后,再返回到暂停的位置继续原来的工作。通过中断,能让处理器在执行一个任务的同时,监视和处理其他几项任务,达到一心二用或一心多用的目的,从而提高处理器应对多任务及突发事件的能力。与其他处理器一样,AVR单片机也有自己独特的中断系统,包含了中断的控制、产生以及处理的全部过程。通过中断系统,使AVR单片机可以及时地处理诸如外部引脚电平变化、定时器溢出、A/D转换结束、串口收发完成等引发的事件。

7.1.1

中断的类型

按照中断源类型的不同,ATmega32单片机的中断源可以分为21种,具体情况详见表7-1。

7.1.2

中断的控制

所有的中断都有一个共同的全局中断允许位“I”,它位于ATmega32单片机的状态寄存器SREG的最高位。该位用于控制所有类型中断的使能和禁止。此外,每一种类型的中断都有相应的标志位和允许位。标志位用于表明该类中断是否产生,允许位用于控制是否执行该中断的服务程序。

不仅如此,ATmega32单片机不同类型的中断还有自己独立的中断程序入口地址,分别对应着1~21个不同的中断向量号。当某一个中断发生时,相应的中断标志位会置1,表示该类中断已经发生。此时如果全局中断允许位置1且该类中断的允许位也同样置1,即表示该中断得到允许,CPU就会跳转到该中断向量对应的地址处开始执行相应的中断服务程序。中断服务程序执行后,相应的中断标志位会通过硬件自动清零,表明该中断已经得到响应。

中断向量号的顺序也决定了不同中断的优先级顺序。中断向量号越小,表明其中断的优先级越高。从表7-1中我们可以看出,RESET中断源具有最高的优先级,其次是INT0(外部中断0)中断源,依此类推。当两个中断同时发生时,CPU会优先响应中断优先级高的中断。

7.1.3

中断的处理

当中断发生后,相应的中断标志位会硬件置1。此时,若位于全局中断控制位和相应的中断允许位均为1,则该中断得到允许,单片机将跳转到相应的中断向量处开始执行中断服务程序,中断标志位随即硬件清零。中断服务程序执行完毕后,单片机返回原程序断点继续原来的任务进程,这一过程即为单片机处理中断的过程。这里需要说明的是,ATmega32单片机的中断响应时间最少需要4个时钟周期,且中断返回同样需要4个时钟周期,这一点在对时间非常敏感的应用中需要特别注意。

在Atmel Studio 6.1编译环境下,中断服务程序需要有特定的头文件支持,这个头文件与AVR系列单片机的通用头文件“io.h”位于相同的文件夹下,其名称为“interrupt.h”。包含AVR中断控制头文件可以使用如下代码:

#include 
//
包含AVR 系列单片机头文件
#include 
//
包含AVR 中断控制头文件
在程序中包含了“interrupt.h”中断控制头文件后,就可以在程序中使用中断服务函数了。中断服务函数不需要声明,也没有返回值,它可以位于程序中的任何位置。在同一个C程序中,可以有多个中断服务函数存在,但其中断源代码必须是不同的。中断服务函数的格式是固定的,例如INT0的中断服务函数可以用以下的方法编写:
ISR(INT0_vect)
//INT0
中断服务函数
{
...
}
在以上代码中,“ISR”是中断服务函数的统一名称,圆括号中的内容是“中断源代码”+“_vect”。我们已经知道,AVR单片机的不同中断源有着不同的中断入口地址和中断向量号,不同的中断源还有着不同的中断源代码,这些代码在表7-1中已经列出。在Atmel Studio 6.1编译环境下,就是使用不同的中断源代码后面加“_vect”这一固定格式来区分不同的中断服务函数的。在中断服务函数的函数体中,用于编写处理相应中断请求的语句。
不同中断源的中断服务函数是不同的,例如,定时器0比较匹配中断服务函数的编写方法如下:
ISR(TIMER0_COMP_vect)
//
定时器0
比较匹配中断服务函数
{
...
}
7.2
外部中断
ATmega32单片机的INT0、INT1与INT2引脚具有外部中断功能。当这些引脚上发生电平变化时,就会触发单片机产生中断。INT0和INT1中断可以配置成由低电平触发或边沿触发,而INT2中断只能被配置成边沿触发。使用低电平触发外部中断时,只要触发电平存在,中断就会持续发生。电平触发中断的一个最大的特点就是它的检测不需要有I/O时钟的支持,我们知道,当单片机处于睡眠模式时,I/O时钟是停止的,通过电平方式触发中断,可以将MCU从睡眠模式唤醒。
另外,ATmega32单片机对外部中断的检测与引脚的数据方向无关,无论引脚被配置成输入或是输出状态,只要引脚电平发生相应变化,都会产生外部中断。这个特点也可以用来产生软件中断,例如我们可以将INT2引脚设置为输出,当我们用软件使其交替输出高低电平时,就会产生相应的INT2中断。
7.2.1
外部中断的控制寄存器
与其他中断一样,外部中断有着自己的允许位和标志位,它们都存在于相应的寄存器中。
1.SREG寄存器
该寄存器是ATmega32单片机CPU的状态寄存器,包含了中断的全局使能位和多个算术指令标志位。
SREG寄存器:CPU的状态寄存器
bit 7I:全局中断使能位。该位置1时使能全局中断,但不同类型的中断仍受各自的中断允许位控制。I位清零时无论各自的中断允许位如何,所有类型的中断都将被禁止。
bit 6-0暂不介绍。
2.GICR寄存器
该寄存器是通用中断控制寄存器,包含了中断向量选择位及多个外部中断使能位。
GICR寄存器:通用中断控制寄存器
bit 7INT1:外部中断1使能位。该位置1时使能外部中断1。
bit 6INT0:外部中断0使能位。该位置1时使能外部中断0。
bit 5INT2:外部中断2使能位。该位置1时使能外部中断2。
bit 4-2未使用。
bit 1IVSEL:中断向量选择位。该位清零时,中断向量位于FLASH存储器的起始地址。IVSEL位置1时,中断向量转移到BOOT区的起始地址处。通常情况下,该位应保持清零状态。
bit 0IVCE:中断向量修改使能位。该位置1后,在4个时钟周期内,允许修改中断向量地址,4个时钟周期后,该位硬件清零。
3.GIFR寄存器
该寄存器是通用中断标志寄存器,包含了多个外部中断标志位。
GIFR寄存器:通用中断标志寄存器
bit 7INTF1:外部中断1标志位。该位置1时表示产生了INT1中断,如果此时SREG的I位以及GICR寄存器的INT1位均为1,单片机将跳转到相应的中断向量处执行中断服务程序。中断服务程序执行后,该位自动清零,也可用软件对该位写1来将其清零。
bit 6INTF0:外部中断0标志位。该位置1时表示产生了INT0中断,如果INT0中断得到允许,单片机将跳转到相应的中断向量处执行中断服务程序。中断服务程序执行后,该位自动清零,也可用软件对该位写1来将其清零。
bit 5INTF2:外部中断2标志位。该位置1时表示产生了INT2中断,如果INT2中断得到允许,单片机将跳转到相应的中断向量处执行中断服务程序。中断服务程序执行后,该位自动清零,也可用软件对该位写1来将其清零。
bit 4-0未使用。
4.MCUCR寄存器
该寄存器是ATmega32单片机的控制寄存器,包含了多个中断触发控制位及MCU功能位。
MCUCR寄存器:CPU的控制寄存器
bit 7-4暂不介绍。
bit3-2ISC11:ISC10:外部中断1触发方式控制位,具体设置详见表7-2。
bit1-0ISC01:ISC00:外部中断0触发方式控制位,具体设置详见表7-2。
5.MCUCSR寄存器
该寄存器是ATmega32单片机的控制与状态寄存器,包含了INT2中断触发控制及多个复位状态标志位。
MCUCR寄存器:CPU的状态寄存器
bit 7暂不介绍。
bit 6ISC2:外部中断2触发方式控制位。该位置1时INT2中断由上升沿触发,清零时INT2中断由下降沿触发。INT2不能由低电平触发。
bit 5未用位。
bit 4-0暂不介绍。
7.2.2
外部中断的编程应用
为了测试外部中断,我们将INT0配置成由下降沿触发,四位数码管用于显示INT0中断的次数。打开Atmel Studio 6.1软件,新建名为“int0”的项目文件,保存在chapter7文件夹下,软件会将int0.c源文件自动添加到新建的int0项目中。编辑int0.c源文件,具体代码详见代码清单7-1。
代码清单7-1
INT0中断测试
/*
*
int0.c *
INT0
中断测试+
数码管显示
*
Created: 2013/8/23
18:11:42
*
Author: GAO */
#include 
//
包含AVR 头文件
#include 
//
包含AVR 中断控制头文件
#define F_CPU 16000000UL //
定义系统时钟
#include 
//
包含延时函数头文件
unsigned char table0[]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f }; //
共阴无点
unsigned char table1[]={0xbf,0x86,0xdb,0xcf,0xe6, 0xed,0xfd,0x87,0xff,0xef }; //
共阴有点
void display(unsigned int NUM); //
数码管显示函数声明
void int0_init(void); //INT0
中断初始化函数声明
unsigned int INTNUM; //
定义全局变量存储外部中断次数
int main(void)
{
DDRA=0xFF; //
设数码管段驱动端为输出
DDRB=0xF0; //
设数码管位驱动端为输出
DDRD=0x0F; //
将PORTD 低四位置为输出, 仍可产生外部中断
//
此时引脚为低电平,需导线触发Vcc 产生下降沿
int0_init(); //
INT0
模式初始化
while(1)
{
display(INTNUM); //
数码管显示中断次数
}
}
/**********
数码管显示函数**********/
void display(unsigned int NUM)
{
unsigned char NUM4,NUM3,NUM2,NUM1; NUM1=NUM%10; NUM2=NUM%100/10; NUM3=NUM%1000/100; NUM4=NUM/1000; PORTA=table0[NUM1]; PORTB=0x10; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTA=table0[NUM2]; PORTB=0x20; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTA=table0[NUM3]; PORTB=0x40; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTA=table0[NUM4]; PORTB=0x80; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms }
/**********INT0
中断初始化函数**********/
void int0_init(void)
{
SREG=0x80; //
开全局中断
MCUCR=0x02; //INT0
下降沿触发
GICR=0x40; //
使能INT0
中断
}
/**********INT0
中断服务函数**********/
ISR(INT0_vect)
{
INTNUM++; //
进入中断INTNUM 自加
if(INTNUM>9999)
{
INTNUM=0; }
}
/**********
结束**********/
将以上代码编译后,下载到AVR系统板中。程序运行后,使用杜邦线短接单片机的INT0引脚与Vcc引脚,数码管的数值会有变化,此数值反映出了产生INT0中断的次数,具体如图7-1所示。
外部中断也可以由软件控制产生。将外部中断引脚设置为输出状态,并使能外部中断,引脚上输出电平的变化将触发中断。在Atmel Studio 6.1软件中新建名为“int2”的项目文件,保存在chapter7文件夹下,软件会将int2.c源文件自动添加到新建的int2项目中。编辑int2.c源文件,具体代码详见代码清单7-2。
代码清单7-2
INT2软件中断测试
/*
*
int2.c *INT2
软件中断测试+
数码管显示
*
Created: 2013/8/23
23:01:42
*
Author: GAO */
#include 
//
包含AVR 头文件
#include 
包含AVR 中断控制头文件
#define F_CPU 16000000UL //
定义系统时钟
#include 
//
包含延时函数头文件
unsigned char table0[]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f }; //
共阴无点
unsigned char table1[]={0xbf,0x86,0xdb,0xcf,0xe6, 0xed,0xfd,0x87,0xff,0xef }; //
共阴有点
void display(unsigned int NUM); //
数码管显示函数声明
void int2_init(void); //INT2
中断初始化函数声明
unsigned int INTNUM; //
定义全局变量存储外部中断次数
int main(void)
{
DDRA=0xFF; //
设数码管段驱动端为输出
DDRB=0xFF; //
设数码管位驱动端为输出
//
将PORTB 低四位置为输出,用于产生软件中断
int2_init(); //INT2
模式初始化
while(1)
{
display(INTNUM); //
数码管显示中断次数
PORTB|=0x04; //INT2
引脚置1
,数码管显示函数会将该位清零
//INT2
引脚上输出电平的交替变化会产生软件中断
}
}
/**********
数码管显示函数**********/
void display(unsigned int NUM)
{
unsigned char NUM4,NUM3,NUM2,NUM1; NUM1=NUM%10; NUM2=NUM%100/10; NUM3=NUM%1000/100; NUM4=NUM/1000; PORTA=table0[NUM1]; PORTB=0x10; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTA=table0[NUM2]; PORTB=0x20; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTA=table0[NUM3]; PORTB=0x40; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms PORTA=table0[NUM4]; PORTB=0x80; _delay_ms(2); //
延时2ms PORTA=0x00; PORTB=0x00; _delay_ms(1); //
延时1ms }
/**********INT2
中断初始化函数**********/
void int2_init(void)
{
SREG|=0x80; //
开全局中断
MCUCSR|=0x40; //INT2
上升沿触发
GICR|=0x20; //
使能INT2
中断
}
/**********INT2
中断服务函数**********/
ISR(INT2_vect)
{
INTNUM++; //
进入中断INTNUM 自加
if(INTNUM>9999)
{
INTNUM=0; }
}
/**********
结束**********/
再次将以上代码编译后,下载到AVR系统板中。程序运行时数码管的数值会不断递增,此数值反映出了产生INT2软件中断的次数,具体如图7-2所示。
外部中断是AVR单片机中除复位外级别最高的中断,也是单片机处理外部电平变化的一个重要的功能,特别是当外部中断被配置成通过电平方式触发时,可以将单片机从睡眠模式中唤醒,而且这一功能不需要时钟的支持,这在一些低功耗的控制方面会有很高的应用价值。

 


评论:

AVR单片机基础教程

作者:高显生   共18讲

AVR系列单片机是8位单片机中第一个真正的RISC结构单片机,它采用了大型快速存取寄存器组、快速的单周期指令系统以及单级流水线等诸多先进技术,使得AVR单片机具备了高达1MIPS/MHz的运行处理能力。