• ATmega8仿真——外部中断的学习


    前面我们学习了ATmega8的I/O口作为通用数字输入/输出口来用时对LED数码管控制和扫描按键的应用;

    但ATmega8多数的I/O口都是复用口,除了作为通用数字I/O使用,还有其第二功能,这里我们学习PD2、PD3两端口的第二功能:外部中断。

    1.外部中断的特点:

      PD2端口是外部中断源0,PD3端口是外部中断源1。ATmega8的外部中断就是由这两个引脚触发的。

    *要注意的是:如果设置允许外部中断产生,即使是INT0和INT1引脚设置为输出方式,外部中断还是会触发的。

    外部中断的触发方式有三种可选性:

      (1)上升沿触发;

      (2)下降沿触发;

      (3)低电平触发。

    具体方式是由以下三个决定的:

      (1)MCU的控制寄存器MCUCR

      (2)MCU控制

      (3)状态寄存器MCUCSR

    *当允许外部中断且设置为低电平触发方式时,只要中断输入引脚保持低电平,就将一直触发产生中断;

    *而对于上升沿或者下降沿的中断触发,则需要I/O时钟信号的存在。

    要使用外部中断我们首先要了解几个寄存器:

      (1)AVR的状态寄存器SREG

      (2)MCU控制寄存器MCUCR

      (3)通用中断控制寄存器GICR

      (4)通用中断标志寄存器GIFR

    详细信息有:

    (1)AVR的状态寄存器SREG:

    SREG的每一位都是一个标志位,位7(全局中断允许位)——I位

    • 该位为1时全局中断使能允许,单独的中断使能则有对应的中断寄存器控制;
    • 该位为0时则不论单独允许位是否置1,所有中断都被禁止,系统将不响应任何中断。

    (2)MCU控制寄存器MCUCR:

    位0(ISC00)是外部中断0的中断方式控制位0;

    位1(ISC01)是外部中断0的中断方式控制位1;

    位2(ISC10)是外部中断1的中断方式控制位0;

    位3(ISC11)是外部中断1的中断方式控制位1;

    参考表与上图类似。

    (3)通用中断控制寄存器GICR:

    位6——INT0控制外部中断0使能

    位7——INT1控制外部中断1使能

     当状态寄存器SREG的I位(全局中断允许位)置1时,

    • INT0置1则外部引脚中断0使能;
    • INT1置1则外部引脚中断1使能。

    (4)通用中断标志寄存器GIFR:

    位6——INT0是外部中断0的标志位

    位7——INT1是外部中断1的标志位

    • 当INT0 引脚上的有效事件触发一个中断请求后,INTF0位会变成1。
    • 如果全局中断使能且外部中断0 使能,则MCU将跳至相应的中断向量处开始执行中断服务程序,同时硬件自动将INTF0 标志位清零。

    *当外部中断0被设置为低电平触发方式时,标志INTF0 位将始终为0。

    扩展:

    中断向量表:Atmega8共有18 个中断源,Flash程序存储器空间的最低位置(0x000—0x012)定义为复位和中断向量空间,也就是说把中断函数的地址保存在这里,当中断发生后就到这里找到对应函数的地址,然后去执行对应的函数。x向量表如下:

    在中断向量表中,处于低地址的中断向量对应的中断优先级高,所以系统复位RESET拥有最高优先;

    外部中断0高于外部中断1;系统复位REST不是中断。

    编程准备:

    用ICCAVR的编程,在C中只要用#pragma伪指令和中断向量说明中断服务程序入口地址即可:

      #pragma interrupt_handler <函数名>:<中断向量>

    例如要定义使用INT0中断服务程序:

      #pragma interrupt_handler int0_fun:2

      void int0_fun()

      {

        ......

      }

    2对应INT0的中断服务程序入口地址(由向量表中红色字体可知);

    同理,3对应INT1的中断服务程序入口地址。

    也可以让多个中断调用同一个函数,如:

      #pragma interrupt_handler int_fun:2

      #praama interrupt_handler int_fun:3

    表示外部中断0和中断1都调用int_fun函数。

    2.应用实例——中断计数器

    用两个按键作为两个外部中断的触发源,再接一个LED数码管用来显示两位数的数据,电路图如下:

    将外部中断0设置为下降沿触发(MCUCR的位1为1,位0为0),中断1设置为低电平触发(MCUCR的位3为0,位2为0);(MCUCR=0x02)

    调用同一个中断函数,在中断中做数值加1,然后在LED数码管中显示。

    代码如下:

     1 #include <iom8v.h>
     2 #include <macros.h>
     3 #include "Delay.h"
     4 
     5 unsigned char CountNum; //全局变量用于计数
     6 
     7 //指明中断程序入口地址
     8 #pragma interrupt_handler int_fun:2
     9 #pragma interrupt_handler int_fun:3
    10 void int_fun(void)
    11 {
    12     if(++CountNum>=100)
    13         CountNum -= 100;
    14 }
    15 
    16 //主函数,显示数据时先关闭中断,然后再打开
    17 void main()
    18 {
    19     unsigned char tempL,tempR;
    20     unsigned char num[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
    21     //初始化端口
    22     DDRB = 0XFF;
    23     PORTB = 0XFF;
    24     DDRC = 0X03;
    25     PORTC = 0XFF;
    26     DDRD = 0XFF;
    27     PORTD = 0XFF;
    28     
    29     //中断配置
    30     SEI(); //打开全局中断
    31     MCUCR = 0X02; //外部中断0设置为下降沿触发,中断1设置为低电平触发
    32     GICR = 0xC0; //打开INT0、INT1中断
    33     GIFR = 0xC0; //清除INT0、INT1中断标志位
    34     
    35     CountNum = 0; //初始化全局变量
    36     while(1)
    37     {//显示数据时关闭中断
    38         CLI(); //关闭全局中断
    39         
    40         //显示十位数
    41         tempL = CountNum/10;
    42         PORTC &= ~(2);
    43         PORTB = num[tempL];
    44         delay_ms(1);
    45         
    46         //显示个位数
    47         tempR = CountNum%10;
    48         PORTC &= ~(1); 
    49         PORTB = num[tempR];
    50         
    51         SEI(); //打开全局中断
    52         delay_ms(1);
    53     }
    54 }
    中断计数器

    3.中断触发键盘扫描

    按下键盘的任意一个按键就触发一个中断,然后在中断函数中来调用键盘处理函数。

    电路图中,比上一讲的实例中多了一个74S10的与非门,作用是任意一个按键按下都可以触发一个INT0中断。

    要实现的内容是:

    任意一个按键按下触发一个INT0中断,INT0设置为上升沿触发方式(MCUCR=0x03),在中断中做一个标志,表示有按键按下;

    然后在主函数中判断该标志位,有按键按下,消除抖动干扰,再做确认哪个按键按下,最后在LED数码管上显示按键的值。

     1 #include <iom8v.h>
     2 #include <macros.h>
     3 #include "Delay.h"
     4 
     5 unsigned char KeyDown;
     6 
     7 //按键扫描函数,返回按键的值
     8 //unsigned char ScanKey(void)函数的实现与上一实例类似
     9 
    10 //中断函数,设置一个标志,表示按键按下
    11 //指明中断程序入口地址
    12 #pragma interrupt_handler int_fun:2
    13 void int_fun(void)
    14 {
    15     KeyDown = 1; //在中断中仅设置一个标志
    16 }
    17 
    18 //主函数,扫描按键显示数据
    19 void main()
    20 {
    21     unsigned char temp,keynum;
    22     unsigned char num[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
    23     
    24     //初始化端口
    25     DDRB = 0xFF;
    26     PORTB = 0xFF;
    27     DDRC = 0x07;
    28     PORTC = 0x38;
    29     DDRD &= 0x0F;
    30     PORTD |= 0xFC;
    31     
    32     //中断配置
    33     SEI(); //打开全局中断
    34     MCUCR = 0x03; //INT0上升沿触发
    35     GICR |=0x40; //打开INT0中断
    36     GIFR = 0xC0; //清除INT0、INT1中断标志位
    37     
    38     KeyDown = 0; //初始化全局变量
    39     while(1)
    40     {
    41         PORTB = 0x40; //没有按键时,LED默认显示-
    42         if(KeyDown==1) //检测是否有按键按下
    43         {
    44             //关闭中断,恢复全局变量
    45             GICR &= 0x00;
    46             KeyDown = 0;
    47             delay_ms(5);
    48             
    49             //防抖动,再次判断是否有按键
    50             temp = PINC&0x38;
    51             if(temp==0x38) //没有按键
    52             {
    53                 GICR = 0x40; //打开INT0中断
    54                 continue;
    55             }
    56             
    57             //有按键
    58             keynum = ScanKey(); //获得按键值
    59             PORTB = num[keynum]; //LED显示按键值
    60             
    61             while(temp!=0x38) //等待按键释放
    62                 temp = PINC&0x38;
    63                 
    64             //退出前开启INT0中断
    65             GICR = 0x40;
    66             DDRC = 0x07;
    67             PORTC = 0x38;
    68         }
    69     }
    70 }
    中断触发键盘扫描
  • 相关阅读:
    Traefik2.X 版本 中 URL Rewrite 的使用
    图144 超音速客机
    ingressnginx 的使用 =》 部署在 Kubernetes 集群中的应用暴露给外部的用户使用
    在k8s集群中安装rookceph 1.8版本步骤
    Traefik 2.0 暴露 Redis(TCP) 服务
    Traefik 2.0 实现自动化 HTTPS
    Kubernetes的kubectl常用命令速记
    docker运行tomcat的war包程序,构建镜像
    使用Prometheus和Grafana监控RabbitMQ集群 (使用RabbitMQ自带插件)
    使用Prometheus和Grafana监控nacos集群
  • 原文地址:https://www.cnblogs.com/Christal-R/p/6928873.html
Copyright © 2020-2023  润新知