#AVR中断系统与基本应用(ATMega16)
##一、中断的基本概念
###1.1 中断的基本概念
- 中断是指计算机自动响应的一个中断请求信号,暂时停止(中断)当前程序的执行,转而执行为外部设备服务 的型号(中断服务程序),并在执行完服务程序后自动返回原程序的过程。
- 具有的优势
- 实现实时处理
- 实现分时操作,提高MCU的处理效率
- 进行故障处理
- 待机状态的唤醒
###1.2 中断的处理过程
- 由于MCU处理完中断之后需要返回原程序,因此,要在执行中断之前,要将主程序中断处的地址,即断点处(实际上是程序计数器PC的当前地址值——即即将执行的主程序的下一条指令,即图中的k + 1处)保护起来,称为保护断点
- 除了保护断点,在程序执行之前,还要把有关的数据保护起来,称为中断现场保护,方便在返回主程序的时候继续执行,这一过程称为恢复现场和恢复断点
- 简单说,在响应中断的时候,MCU的硬件系统会将断点地址压进系统的堆栈保护,而在执行中断返回指令时,硬件系统又会自动将断点地址弹出到程序计数器PC中。
###1.3 中断源、中断信号和中断向量 - 中断源
- 一般可分为内部中断和外部中断
- 典型的中断有定时器溢出中断,ADC完成中断等
- 系统中的外部设备也可以作为中断源,这些中断源位于单片机外部,所以称之为外部中断源
- 中断信号
- 中断信号是指内部或者外部中断源产生的中断申请信号。这个中断信号往往是电信号的某种变化形式,通常有几种类型
- 脉冲的上升沿或者是下降沿(上升沿触发型或者是下降沿触发型)
- 高电平或者是低电平(电平触发型)
- 电平的变化(状态变化触发型)
- 单片机硬件系统会对这些触发源自动检测,一旦检测到信号,就会将相应的中断标志位置位 1 (在I/O空间的控制或状态寄存器中),通知CPU进行处理。
- 中断信号是指内部或者外部中断源产生的中断申请信号。这个中断信号往往是电信号的某种变化形式,通常有几种类型
- 中断向量
- 中断源发出的请求信号被CPU检测到之后,如果单片机的中断控制系统允许中断响应,则CPU会自动转移,执行一个固定的程序空间地址中的指令。这个固定的地址叫做中断的入口地址,也叫做中断向量
- 一般,一个单片机有若干个中断源,而每个中断源都有自己相应的中断向量
###1.4 中断的优先级和中断嵌套
- 中断的优先级,一般由单片机的硬件结构所决定,决定方式一般有两种:
- 某个中断的中断向量越小,其中断的优先级越高
- 通过软件对中断寄存器进行设置,改变优先级
- 注意:AVR不支持用户改变中断的优先级,C51单片机可以
- MCU只能同时响应一个中断,假如有多个中断同时申请时MCU已经在执行中断,MCU会暂停该中断的执行,执行优先级更高的那个中断,完成后进而执行未完成的中断,再响应其他较低优先级的中断,这就有导致中断嵌套
- 但是需要注意,一些单片机(如8051结构)的硬件能够自动处理中断嵌套,但是另一些单片机(如AVR ATMega16)却不可以自动处理中断嵌套,这需要通过软件实现中断嵌套的处理
###1.5 中断的响应条件和中断的控制 - 在有些设计中不允许打断现有程序的运行,因此单片机就需要单片机有一套中断屏蔽/允许的系统。
- 在单片机的I/O寄存器中,通常存在一些特殊的标志位用于控制使能和禁止(屏蔽)MCU对中断的响应,这些标志称之为中断屏蔽标志位或者是中断允许标志位
- 从中断控制的角度来讲,中断源还可以分为三类:
- 非屏蔽中断:也就是MCU对中断的请求是不可以屏蔽的,比如外部 (RESET)’ 引脚产生的复位信号,就是一个非屏蔽中断。
- 可屏蔽中断:可以通过用户设置使能或者禁止MCU响应中断,一旦发生中断请求,则MCU只能在中断使能的条件下响应中断。
- 软件中断:是指具有响应的软件中断指令,当MCU执行执行这条指令时,就可以进入软件中断服务,完成特定的功能,一般用于调试。一般单片机中都没有软件中断的指令,因此,在单片机系统中,如果必须使用软件中断的功能,则一般需要通过间接的方式来实现。
- MCU中断响应一个可屏蔽中断源(假定是 A 中断)中断请求的条件是:
- 响应 A 中断 = 全局中断允许标志 + 中断 A 允许标志 + 中断 A 标志
- 全局中断允许标志位为“ 1 ”(用户软件设置)
- 中断 A 允许标志位为“ 1 ”(用户软件设置)
- 中断 A 标志为“ 1 ”(符合中断条件时,由硬件自动设置或者由用户设置)
- 三者缺一不可!!!
- 响应 A 中断 = 全局中断允许标志 + 中断 A 允许标志 + 中断 A 标志
##二、ATMega16的中断系统
###2.1 ATMega16的中断源和中断向量
- AVR 一般具有数十个中断源,每个中断源都有自己的中断向量。默认情况下,AVR的程序存储器的最低端,即从Flash存储器地址0x0000开始存储中断向量,称之为中断向量区。
- AVR中断向量区的大小是不同的,由下式决定:
- 中断向量区大小 = 中断源个数 x 每个中断向量占据字数
- AVR中断向量区的大小是不同的,由下式决定:
- ATMega16中断向量区表:
向量号 | Flash空间地址(中断向量) | 中断源 | 中断定义说明 | 在GCC中的中断名SIG_NAME |
---|---|---|---|---|
1 | $000 | RESET | 外部引脚电平引发的复位、上电复位、掉电检测复位、看门狗复位和JTAG AVR复位 | |
2 | $002 | INT0 | 外部中断请求0 | SIG_INTERRUPT0 |
3 | $004 | INT1 | 外部中断请求1 | SIG_INTERRUPT1 |
4 | $006 | TIMER2 COMP | 定时器/计数器2比较匹配 | SIG_OUTPUT_COMPARE2 |
5 | $008 | TIMER2 OVF | 定时器/计数器2溢出 | SIG_OVERFLOW2 |
6 | $00A | TIMER1 CAPT | 定时器/计数器1事件捕捉 | TIMER1_CAPT_vect |
7 | $00C | TIMER1 COMPA | 定时器/计数器1比较匹配A | SIG_OUTPUT_COMPARE1A |
8 | $00E | TIMER1 COMPB | 定时器/计数器1比较匹配B | SIG_OUTPUT_COMPARE1B |
9 | $010 | TIMER1 OVF | 定时器/计数器1溢出 | SIG_OVERFLOW1 |
10 | $012 | TIMER0 OVF | 定时器/计数器0溢出 | SIG_OVERFLOW0 |
11 | $014 | SPI STC | SPI串行输入结束 | SIG_SPI |
12 | $016 | USART RXC | USART,接收结束 | SIG_UART_RECV |
13 | $018 | USART UDRE | USART,数据寄存器空 | SIG_UART_DATA |
14 | $01A | USART TXC | USART,发送结束 | SIG_UART_TRANS |
15 | $01C | ADC | A/D转换结束 | SIG_ADC |
16 | $01E | EE RDY | EEPROM就绪 | SIG_EEPROM_READY |
17 | $020 | ANA_COMP | 模拟比较器 | SIG_COMPARATOR |
18 | $022 | TWI | 两线串行接口 | SIG_2WIRE_SERIAL |
19 | $024 | INT2 | 外部中断请求2 | SIG_INTERRUPT2 |
20 | $026 | TIMER0 COMP | 定时/计数器0比较匹配 | SIG_OUTPUT_COMPARE0 |
21 | $028 | SPM_RDY | 保存程序寄存器内容就绪 | SIG_SPM_READY |
- 这21个中断中,包含1个非屏蔽中断(RESET),3个外部中断(INT0,INT1,INT2)和17个内部中断
###2.2 中断优先级的确定 - 在AVR单片机中,一个中断在中断向量区的位置决定了他的优先级。位于低地址的中断优先级高于位于高地址的中断。
###2.3 中断标志 - AVR具有两种不同机制的中断:
- 带有中断标志的中断(可挂起)
- 不带有中断标志的中断(不可挂起)
- 这里的挂起是指在中断嵌套的时候,能否等待 MCU 对齐响应
- 中断标志位一般在 MCU 响应之后会自动清除,或者在中断服务程序中通过读写专门数据寄存器的方式自动清除。
- 若用软件的方法清除,清除的方法是对其写 1 !!!
- 在 AVR 中,还有个别不带(不设置)中断标志,例如配置为低电平触发的外部中断即为此类型的中断。该中断只要满足外部输入低电平,就会一直向 MCU 发出中断申请,这种**外部低电平中断有其特殊性,他不会产生中断标志,因此不能挂起。如果等待时间过长而得不到响应,**则可能因为中断条件结束而失去一次服务的机会。
- 在退出中断后, AVR 至少要在执行一条指令才能去响应其他被挂起的中断!!!
###2.4 中断屏蔽与管理
- winAVR 平台中:
asm("sei"); //全局中断使能 asm("cli"); //全局中断禁止
- ICCAVR 平台中:
#asm("sei"); //全局中断使能 #asm("cli"); //全局中断禁止
- 注意两个平台上使用的区别!
##三、ATMega16的外部中断 - ATMega16有 INT0 , INT1 , INT2 这三个外部中断源,分别由芯片外部引脚PD2,PD3,PB2上的电平变化或状态作为中断触发源信号
###3.1 外部中断的触发方式和特点 - INT0,INT1,INT2的中断触发方式取决于用户程序对 MCU 控制寄存器 MCUCR 和 MCU 控制与状态寄存器 MCUCSR 的设定。
- 外部中断的 4 种中断触发方式:
触发方式 | INT0 | INT1 | INT2 | 说明 |
---|---|---|---|---|
上升沿触发 | Yes | Yes | Yes(异步) | |
下降沿触发 | Yes | Yes | Yes(异步) | |
任意电平变化触发 | Yes | Yes | —— | |
低电平触发 | Yes | Yes | —— | 无中断标志 |
###3.2 与外部中断相关的寄存器和标志位
-
在ATMega16中,除了寄存器SERG的全局中断允许标志位 I 外,与外部中断有关的寄存器有 4 个,其中有 11 个标志位。其作用分别是 3 个外部中断各自的中断标志位,中断允许标志位及用于定义外部中断的触发类型。
-
1) MCU 控制寄存器 MCUCR
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
$35($0055) | SM2 | SE | SM1 | SM0 | ISC11 | ISC10 | ISC01 | ISC00 |
读/写 | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W |
初始化值 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
INT0,INT1中断触发方式:
ISCn1 | ISCn0 | 中断的触发方式 |
---|---|---|
0 | 0 | INTn的低电平产生一个中断请求 |
0 | 1 | INTn的下降沿和上升沿都产生一个中断请求 |
1 | 0 | INTn的下降沿产生一个中断请求 |
1 | 1 | INTn的上升沿产生一个中断请求 |
- 2) MCU 控制和状态寄存器 MCUCSR
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
$34($0054) | JTD | ICS2 | —— | JTRF | WDRF | BORF | EXDRF | PORF |
读/写 | R/W | R/W | R | R/W | R/W | R/W | R/W | R/W |
初始化值 | 0 | 0 | 0 | RESET复位 | RESET复位 | RESET复位 | RESET复位 | RESET复位 |
INT2中断触发方式:
ISC2 | 中断的触发方式 |
---|---|
0 | INT2的下降沿产生一个异步中断请求 |
1 | INT2的上升沿产生一个异步中断请求 |
- 3) 通用中断控制寄存器 GICR
- 通用中断控制寄存器 GICR 的高 3 位为INT0,INT1,INT2的中断允许控制位,如果SERG寄存器的I为**“1”,而且GICF寄存器中相应的中断允许控制位为“1”,那当 INTn 中断触发时, MCU 就会响应中断。**
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
$3B($005B) | INT1 | INT0 | INT2 | —— | —— | —— | IVSEL | IVCE |
读/写 | R/W | R/W | R/W | R | R | R | R/W | R/W |
初始化值 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- 4) 通用中断标志寄存器 GIFR
- 通用中断标志寄存器 GIFR 的高 3 位为INT0,INT1,INT2的中断标志位。
- 当INT0,INT1,INT2引脚上的有效事件满足中断触发条件之后,INT0,INTF1,INTF2位会变为 1 。假如 MCU 响应中断服务,硬件会自动将 INTFn 位置位“0”
- 用户可以使用指令将 INTFn清 0 ,清除的方式是 INTFn 置位 “1”,将标志清 0
- 当INTF0(INTF1)设置为低电平触发时,标志位INTF0(INTF1)始终未 0 ,并不意味着不产生中断请求,而是低电平触发方式是不带中断标志类型的中断触发。
- 低电平触发时,中断请求将一直保持到引脚的低电平消失为止。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
$3A($005A) | INTF1 | INTF0 | INTF2 | —— | —— | —— | —— | —— |
读/写 | R/W | R/W | R/W | R | R | R | R | R |
初始化值 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
##四、外部中断简单实用实例
###4.1 WinAVR平台
- 代码实例:
/**
*作者:Dandri
*时间:2017/01/31
*MCU:ATMega16
*频率:8MHz
*功能:按下两次按键,LED灯亮一次
*/
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned char count = 0;//用于计数按下按键的次数
unsigned char flag = 0; //用于启动点灯的标志
//中断向量地址里的程序
#pragma interrupt_handler int0_isr:2 //首先使用#pragma interrupt_handler int0_isr:2声明中断,2表示中断向量号
void int0_isr(void)
{
count++;
if (count >= 2)
{
flag = 1;
count = 0; //计数器清0
}
}
//外部中断相关的寄存器初始化,因为接的是PD2,使用的是INT0
void int0_init()
{
GIFR = 0x40; //INTF0 标志位清零
MCUCR = 0X02; //INT0下降沿触发
GICR = 0X40; //INT0触发允许
}
//引脚初始化
void port_init()
{
DDRC = 0X01;
PORTC = 0X00;
}
//亮灯程序
void led()
{
if (flag == 1)
{
PORTC = 0X01;
flag = 0;
}
else
PORTC = 0X00;
}
int main()
{
port_init();
int0_init();
asm("sei"); //启用全局中断
while(1)
{
led();
}
return 0;
}