外部中断的触发方式和特点

INT0、INT1和INT2的中断触发方式取决于用户程序对MCU控制寄存器MCUCR和MCU控制与状态寄存MCUCSR的设定。其中,INT0和INT1都支持4种中断触发方式,INT2支持2种。下表是具体触发方式:

触发方式INT0INT1INT2说明
上升沿触发YesYesYes(异步) 
下降沿触发YesYesYes(异步) 
任意电平变化触发YesYes--- 
低电平触发YesYes---无中断标志

这4种触发方式还有一些不同的特点:

  • 低电平触发是不带中断标志类型的,即只要中断输入引起引脚PD2或PD3保持低电平,那么将一直会产生中断申请。
  • MCU对INT0和INT1引脚上的上升沿或下降沿变化的识别(触发),需要I/O时钟信号的存在,属于同步边沿触发的中断类型。
  • MCU对INT2引脚上的上升沿或下降沿变化的识别(触发)及低电平的识别(触发),是通过异步方式检测的,不需要I/O时钟信号的存在。
  • 如果使用低电平触发方式的中断源作为唤醒源,那么MCU从掉电模式中被唤醒时,电平拉低后仍需要维持一段时间才能将MCU唤醒。
  • 如果设置了允许响应外部中断的请求,那么即使引脚PD2、PD3和PB2设置为输出方式工作,引脚上的电平变化也会产生外部中断触发请求,这一特性为用户提供了使用软件产生中断的途径。

与外部中断相关的寄存器和标志位

在ATmega16中。除了寄存器SREG中的全局中断允许标志位1外,与外部中断有关的寄存器有4个,共有11个标志位。其作用分别是3个外部中断各自的中断标志位、中断允许控制位及用于定义外部中断的触发类型。

MCU控制寄存器MCUCR

MCU控制寄存器MCUCR的低4位为INT0(ISC01、ISC00)和INT1(ISC11、ISC10)中断触发类型控制位。MCUCR各位的定义如下:

76543210
$35($0055)SM2SESM1SM0ISC11ISC10ISC01ISC00
读/写R/WR/WR/WR/WR/WR/WR/WR/W
初始化值00000000

INT0和INT1的中断触发方式定义如下:

ISCn1ISCn0中断触发方式
00INTn的低电平产生一个中断请求
01INTn的下降沿和上升沿都产生一个中断请求
10INTn的下降沿产生一个中断请求
11INTn的上升沿产生一个中断请求

MCU对INT0、INT1引脚上电平值的采样在边沿检测前。如果选择脉冲边沿触发或电平变化中断的方式,那么在INT0、INT1引脚上的一个脉宽大于一个时钟周期的脉冲变换将触发中断,过短的脉冲则不能保证触发中断。如果选择低电平触发中断,那么低电平必须保持到当前指令执行完成才能触发中断。如果是低电平触发方式,则中断请求将一直保持到引脚上的低电平消失为止。

MCU控制和状态寄存器MCUCSR

MCU控制和状态寄存器MCUCSR中的第6位(ISC2)为INT2的中断触发类型控制位,MCUCSR定义如下:

76543210
$34($0054)JTDISC2-JTRFWDRFBORFEXTRFPORF
读/写R/WR/WRR/WR/WR/WR/WR/W
初始化值0005个RESET复位标志 

INT2的中断触发方式定义:

ISC2中断触发方式
0INT2的下降沿产生一个异步中断请求
1INT2的上升沿产生一个异步中断请求

通用中断控制寄存器GICR

通用中断控制寄存器GICR的高3位为INT0、INT1和INT2的中断允许控制位。如果SREG寄存器中的全局中断I位为“1”,而且GICR寄存器中相应的中断允许位置1,那么当外部INT0、INT1或INT2中断触发时,MCU将会响应相应的中断请求。GICR各位定义如下:

76543210
$3B($005B)INT1INT0INT2---IVSELIVCE
读/写R/WR/WR/WRRRR/WR/W
初始化值00000000

通用中断标志寄存器GIFR

通用中断标志寄存器GIFR的高3位为INT0、INT1和INT2的中断标志位。GIFR各位定义如下:

76543210
$3A($005A)INTF1INTF0INTF2-----
读/写R/WR/WR/WRRRRR
初始化值00000000

当INT2、INT1和INT0引脚上的有效事件满足中断触发条件后,INTF2、INTF1和INTF0位会变成“1”。如果此时SREG寄存器中的I位为“1”,GIFR寄存器中的INTn置1,则MCU响应中断请求。跳至相应的中断向量处开始执行中断服务程序,同时硬件自动将INTFn标志位清0。(用户可以使用指令将INTFn清除,清除的方式是写逻辑“1”到INTFn,将标志清0)

默认情况下,AVR的程序存储区的最低端,即从Flash地址的0x0000开始用于放置中断向量,称作中断向量区。
各种型号的AVR中断向量区大小由下式决定:
中断向量区大小 = 中断源个数 × 每个中断向量占据字数
原则上讲,在不使用中断时,中断向量区与程序存储区的其他部分没有什么区别,可以用于放置普通的程序。但在正式的系统应用中,为了提高系统的抗干扰能力,通常应该在中断向量的位置上放置一条中断返回指令RETI。对应使用了一部分中断的情况,则应在未使用的中断向量处放置这样的指令。在用汇编语言进行开发时,应该注意这一点。
ATmega16共有21个中断源,由于ATmega16片内的Flash为8K字,因此每个中断向量占据了2个字(4字节)。ATmega16的中断向量区:

向量号Flash空间地址中断源中断定义说明
1$000RESET外部引脚电平引发的复位、掉电复位
检测、看门狗复位和JTAG AVR复位
2$002INT0外部中断请求0
3$004INT1外部中断请求1
4$006TIMER2 COMP定时/计数器2比较匹配
5$008TIMER2 OVF定时/计数器2溢出
6$00ATIMER1 CAPT定时/计数器1事件捕捉
7$00CTIMER1 COMPA定时/计数器1比较匹配A
8$00ETIMER1 COMPB定时/计数器1比较匹配B
9$010TIMER1 OVF定时/计数器1溢出
10$012TIMER0 OVF定时/计数器0溢出
11$014SPI STCSPI串行传输结束
12$016USART RXCUSART,接收结束
13$018USART UDREUSART,数据寄存器空
14$01AUSATR TXCUSART,发送结束
15$01CADCA/D转换结束
16$01EEE_RDYEEPROM就绪
17$020ANA_COMP模拟比较器
18$022TW1两线串行接口
19$024INT2外部中断请求2
20$026TIMER0 COMP定时/计数器0比较匹配
21$028SPM_RDY保存程序存储器内容就绪

用到一点代码减少工作量:

_ = [print("|{}|$0{}|||".format(i+1,hex(i*2)[2:].upper().zfill(2))) for i in range(21)]

在这21个中断中,RESET是非屏蔽中断,INT0、INT1、INT2是外部中断,其余17个是内部中断。
RESET是AVR中唯一不可屏蔽的中断,当ATmega16由于各种原因被复位后,程序将跳转到复位向量(默认为0x000)处,在该地址处通常放置一条跳转指令,跳转到主程序处继续执行。
INT0、INT1和INT2是3个外部中断源,他们分别由芯片外部引脚PD2、PD3和PB2上的电平变化或状态触发。

AVR中断响应过程

中断响应的过程

AVR在响应中断请求时,MCU会用4个时钟周期自动、顺序完成以下任务:

  • 清0状态寄存器SREG中的全局中断允许标志位1,禁止响应其他中断。
  • 将被响应中断的标志位清0(仅部分中断有此操作)。
  • 将中断断点的地址压入堆栈,并将SP寄存器中的堆栈指针减2。
  • 自动将相应的中断向量地址压入程序计数器PC,即强行转入执行中断入口地址处的指令。

因此AVR中断响应时间最少为4个时钟周期。

中断返回的过程

AVR一旦执行中断返回RETI指令,MCU便开始了中断返回的过程。在中断返回过程中,AVR也是经过4个时钟周期自动按顺序完成以下任务:

  • 从栈顶弹出2字节的数据,将这两个数据压入程序计数器PC中,并将SP寄存器中的堆栈指针加2。
  • 置位状态寄存器SREG中的全局中断允许标志位1,允许响应其他中断。

中断现场的保护

AVR的中断响应和返回过程主要都是由硬件自动完成的,而在整个过程中用户程序的作用在于:

  • 中断入口处的指令,用于指引MCU转移到中断服务程序
  • 中断服务程序。完成中断服务的功能。
  • 中断返回指令。指引MCU从中断服务程序中返回。

保护和恢复中断现场通常会利用堆栈进行,同时在中断程序中的其他地方也可能要用到堆栈。使用堆栈保护数据时应该特别谨慎,必须保证数据进栈和出栈的数量一致,以确保在进入中断程序和中断程序结束时堆栈指针的值相同。这是因为中断返回时,CPU会认为此时栈顶保存的值是被保护的断点,而将其放入PC中。如果堆栈使用不当,将造成程序流程的严重错误。

写在前面:又是一篇超多文字的摘抄。


中断定义:指计算机自动响应一个中断请求信号,暂时停止(中断)当前程序的执行,转而执行为外部设备服务的程序(中断服务程序),并在执行完服务程序后自动返回原程序执行的过程。
单片机中断系统的优点:

  • 实现实时处理
  • 实现分时操作,提高了MCU效率
  • 进行故障处理
  • 待机状态的唤醒

中断处理过程

主程序:即MCU处在正常情况下运行的程序
中断源:产生申请中断信号的单元和事件
中断请求信号:由中断源向MCU所发出的申请中断信号
中断响应:MCU接收中断申请停止现行程序的运行而转向为中断服务
中断服务/处理程序:为中断服务的程序

现行程序被打断的地方称为断点,执行完中断处理程序后返回断点处继续执行主程序称为中断返回。这个处理过程称为中断处理过程。
在整个中断处理过程中,由于MCU执行完中断处理程序后仍然需要返回主程序,因此在执行中断处理程序之前,要将主程序中断处的地址,即断点处保存起来,称为保护断点。
断点的保护和恢复操作,是在系统响应中断和执行中断返回指令时由单片机内部硬件自动实现的。即在响应中断时,MCU的硬件系统会自动将断点地址压进系统的堆栈保存;而当执行中断返回指令时,硬件系统又会自动将压入堆栈的断点地址弹出到程序计数器PC中。

中断源、中断信号和中断向量

内部中断源:如定时器溢出中断、ADC完成中断
外部中断源:如作输入/输出设备、控制对象及故障源等。

中断信号是指内部或外部中断源产生的中断申请信号。这个中断信号往往是电信号的某种变化形式,通常有以下几种类型:

  • 脉冲的上跳沿或下降沿(上升沿触发型或下降沿触发型)
  • 高电平或低电平(电平触发型)
  • 电平的变化(状态变化触发型)

中断源发出的请求信号被CPU检测到之后,如果单片机的中断控制系统允许响应中断,则CPU会自动转移,执行一个固定的程序空间地址中的指令。这个固定地址称为中断入口地址,也叫做中断向量。

中断优先级和中断嵌套

中断优先级的概念是针对有多个中断源同时申请中断时,MCU如何响应中断,以及响应中断哪个中断而提出的。
一旦有多个中断请求信号,MCU先响应中断优先级高的中断请求,然后再逐次响应优先级次一级的中断。中断优先级也反映了各个中断源的重要程度,同时也是分析中断嵌套的基础。
中断优先级一般规则:

  • 某中断对应的中断向量地址越小,其中断优先级越高(硬件确定方式)
  • 通过软件对中断控制寄存器的设定,改变中断的优先级(用户软件可设置方式)
    中断嵌套通常的规则:当MCU正处于响应一个中断B的过程中,又产生一个中断A申请时,如果这个新产生的中断A优先级比正在响应的中断B优先级高,则应该暂停当前的中断B的处理,转入响应高优先级的中断A,待高优先级中断A响应处理完成后,再返回原来的中断B的处理;反之B结束后响应A(如果中断A条件还是成立的话)。

中断的屏蔽

在单片机的I/O寄存器中,通常存在一些特殊的标志位用于控制使能或禁止(屏蔽)MCU对中断响应处理,这些标志被称为中断屏蔽标志位或中断允许控制位。用户程序可以通过改变这些标志位的设置,在需要的时候允许MCU响应中断,而在不需要的时候将中断请求信号屏蔽(不是取消)。此时尽管产生了中断请求信号,MCU也不会响应中断请求。
按对中断源的控制角度讲,中断源还可以分为以下3类:

  • 非屏蔽中断
  • 可屏蔽中断
  • 软件中断

中断控制与中断响应条件

单片机检测到某一中断源产生符合条件的中断信号时,其硬件会自动将该中断源对应的中断标志位置1,这就意味着有中断信号产生并向MCU申请中断。但中断标志位的置1,并不代表MCU一定响应该中断。
要MCU响应中断要满足三个条件:

  1. 全局中断允许标志位为“1”
  2. 请求中断A的中断源自己的中断允许标志位为“1”
  3. 存在中断A

响应A中断 = 全局中断允许标志位 AND 中断A允许标志 AND 中断A标志

LCD的特点

  • 低电压,微功耗。工作电压为3~5V,每平方厘米液晶显示屏的耗电量在μA级。
  • 平板结构。易大量生产,物理体积小,占用空间多。
  • 寿命长
  • 光线柔和。液晶显示屏是被动发光器件,90%以上是外部物体对光的反射。被动显示适合人的视觉习惯,不会引起疲劳。
  • 无电磁辐射。液晶显示器不会产生电磁辐射,是绿色器件。

LCD显示器的分类

按显示器的使用和显示内容来分:

  • 字段式
  • 点阵字符式
  • 点阵图形式

16×2标准LCD字符显示器应用设计

硬件设计

图为16×2标准字符型LCD接口电路图。由于在CVAVR中必须按照一定的规定连接LCD才能使用CVAVR内部提供的LCD函数,所以原理图是按照CVAVR的规定设计的。
如果要使用CVAVR内部提供的LCD函数,则硬件连接必须按以下要求实现:

  • 与LCD的连接必须使用AVR的同一个8位的I/O端口,如PC(或者PA、PB、PD)
  • LCD采用4位并行传输方式(即仅用DB4~DB7),4位数据总线。
  • 具体连接定义如下:

    • 3根控制线:PC0-RS,PC1-R/W,PC2-E。
    • 4根控制线:PC4-DB4,PC5-DB5,PC6-DB6,PC7-DB7。

电路图如下:
Annotation.png

软件设计

代码如下(C):

/******
File name            :demo_6_8.c
Chip type            :ATmega16
Program type         :Application
Clock frequency      :4.000000MHz
Memory model         :Small
External SRAM size   :0
Data Stack size      :256
******/
#include<mega16.h>
#include<delay.h>
#asm
.equ __lcd_port = 0x15;//PORTC数据寄存器地址 CVAVR指定要用这一句
#endasm
/* [LCD]
1 GND - 9GND
2 +5V - 10 VCC
3 VLC - LCD HEADER Vo
4 RS - 1 PC0(M16)
5 RD - 2 PC1(M16)
6 EN - 3 PC2(M16)
11 D4 - 5 PC4(M16)
12 D5 - 6 PC5(M16)
13 D6 - 7 PC6(M16)
14 D7 - 8 PC7(M16) */
#include<lcd.h>

flash char dis_str[]="Hello World! This is a LCD display demo."
void main(void)
{
    char flash *str;
    str = dis_str;
    lcd_init(16);//激活2*16的LCD区域;初始化LCD
    while(1)
    {
        lcd_clear();//清屏
        lcd_putsf("It's demo_6_8.c");//显示这条消息
        lcd_gotoxy(str);//到lcd第二行?
        lcd_putsf(str);//显示这条消息
        if (*str++ == 0) str = dis_str;
        delay_ms(500);
    }
}

下面的演示程序,可以在LCD上显示用户定义的简单汉字“天天向上”。
程序代码:

/******-
File name            :demo_6_9.c
Chip type            :ATmega16
Program type         :Application
Clock frequency      :4.000000MHz
Memory model         :Small
External SRAM size   :0
Data Stack size      :256
******/
#include<mega16.h>
//Alphanumeric LCD Module functions
#asm
.equ __lcd_port = 0x15;
#endasm
#include<lcd.h>
typedef unsigned char byte;
/* table for the user defined character */
flash byte char0[8] = {//天的字型
0b0011111,
0b0000100,
0b0000100,
0b0011111,
0b0000100,
0b0000100,
0b0001010,
0b0010001,
}
flash byte char1[8] = {//向的字型
0b0000100,
0b0001000,
0b0011111,
0b0010001,
0b0011111,
0b0011011,
0b0011111,
0b0010001,
}
flash byte char2[8] = {//上的字型
0b0000100,
0b0000100,
0b0000111,
0b0000100,
0b0000100,
0b0000100,
0b0000100,
0b0011111,
}
/* function used to define user characters */
void define_char(byte flash *pc,byte_code)
{
    byte i,a;
    a = (char_code << 3) | 0x40;
    for (i=0;i<8;i++) lcd_write_byte(a++,*pc++);
}

void main(void)
{
    lcd_init(16);//初始化LCD
    define_char(char0,0);//定义字符0
    define_char(char1,1);//定义字符1
    define_char(char2,2);//定义字符2
    lcd_clear();
    lcd_putsf("demo_6_9.c");//第一行显示内容
    lcd_gotoxy(0,1);
    lcd_putsf("User define:");//第二行显示内容
    lcd_putchar(0);
    lcd_putchar(0);
    lcd_putchar(1);
    lcd_putchar(2);
    while(1);
}