外部中断应用实例

1999天前 · AVR单片机 · 189次阅读

用按键控制的1位LED数码管显示系统

硬件电路

图为硬件电路图。其中LED数码管的控制显示连接与前面例6.4(没有做摘抄)相同,PA口工作于输出方式,作为LED数码管的段码输出,LED数码管的位信号接地,因此这个1位的LED数码管工作于静态显示方式。图中使用了两个按键K1、K2,按键的一端分别与PD2(INT0)、PD3(INT0)连接。INT0和INT1作为外部中断的输入,采用电平变化的下降沿触发方式,当K1(K2)按下时,会在PD2(PD3)引脚上产生一个高电平到低电平的跳变,触发INT0或INT1中断。
系统的功能还是控制一个8段数码管显示0~F 16个十六进制的数字。为系统上电时,显示0。K1键的作用是加1控制键,按一下加1,到F之后又从0开始,K2则是减1键,到0后从F开始。
QQ截图20181106112019.png

软件设计

首先在CVAVR中使用C语言编写程序。再次建议读者使用CVAVR中的程序生成向导功能来帮助建立整个程序的框架,并采用芯片初始化部分的语句,可以省掉过多地查看器件手册和考虑寄存器设置值的时间等。
程序如下:

/******-
File name            :demo_7_1.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>
flash unsigned char led_7[16] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
unsigned char counter;

//INT0 中断服务程序
interrupt [EXT_INT0] void ext_int0_isr(void)
{
    if (++counter >= 16) counter = 0;
}

//INT1 中断服务程序
interrupt [EXT_INT1] void ext_int1_isr(void)
{
    if (counter) --counter;
    else counter = 15;
}

void main(void)
{
    PORTA = 0xFF;
    DDRA = 0xFF;
    GICR |= 0xC0;//使能INT0、INT1中断
    MCUCR = 0x0A;//INT0、INT1下降沿触发
    GIFR = 0xC0;//清除INT0、INT1中断标志位
    counter = 0;//计数单元初始化为0
    #asm("sei");//使能全局中断
    while(1)
    {
        PORTA = led_7[counter];//显示计数单元
    };
}

上面的程序,就是利用CVAVR的程序生成向导功能进行配置,然后在它生成的程序框架基础上完成的。程序中定义了一个计数变量counter,执行一次INT0中断服务程序,counter加1,而执行一次INT1中断服务程序,counter减1.在主程序中,只显示counter的值。INT0、INT1初始化为电平变化的下降沿触发。
下面这个程序为汇编程序,思想方法与上面的C语言程序相同,可参考。

*******
;AVR汇编程序实例:demo_7_1.asm
;使用INT0、INT1控制LED数码管显示
;ATmega16,4MHz
;*******
.includeb "m16def.inc"
.def temp = r23;临时变量
.def counter = r24;计数变量
;中断向量区配置,Flash空间为$000~$028
.org         $000
        jmp    RESET                ;复位处理
        jmp EXT_INT0            ;TRQ0中断向量
        jmp EXT_INT1            ;TRQ1中断向量
        reti                    ;Timer2 比较中断向量
        nop
        reti                    ;Timer2 溢出中断向量
        nop
        reti                    ;Timer1 捕捉中断向量
        nop
        reti                    ;Timer1 比较A中断向量
        nop
        reti                    ;Timer1 比较B中断向量
        nop
        reti                    ;Timer1 溢出中断向量
        nop
        reti                    ;Timer0 溢出中断向量
        nop
        reti                    ;SPI 传输结束中断向量
        nop
        reti                    ;USART TX 结束中断向量
        nop
        reti                    ;UDR 空中断向量
        nop
        reti                    ;USART RX 结束中断向量
        nop
        reti                    ;ADC 转换结束中断向量
        nop
        reti                    ;EEPROM 就绪中断向量
        nop        
        reti                    ;模拟比较器中断向量
        nop        
        reti                    ;两线串行接口中断向量
        nop        
        reti                    ;IRQ2 中断向量
        nop        
        reti                    ;定时器0比较中断向量
        nop
        reti                    ;SPM就绪中断向量
        nop
.org        $02A                ;上电初始化程序
RESET:
        ldi r16,high(RAMEND)
        out SPH,r16
        ldi r16,low(RAMEND)
        out SPL,r16                ;设置堆栈指针为RAM的顶部
        ser temp
        out ddra,temp            ;设置PORTA为输出,段码输出
        out porta,temp            ;设置PORTA输出全1
        ldi temp,0x0a
        out mcucr,temp            ;INT0、INT1下降沿触发
        ldi temp,0xc0
        out gicr,temp            ;使能INT0、INT1中断
        out gifr,temp            ;清除INT0、INT1中断标志位
        clr counter
        sei                        ;使能中断
MAIN:
    clr r0
    ldi zl,low(led_7 * 2)
    ldi zh,high(led_7 * 2)        ;Z寄存器取得7段码组的首指针
    add zl,counter                ;加上要显示的数字
    adc zh,r0                    ;加上低位进进位
    lpm                            ;读对应7段码到R0中
    out porta,r0                ;LED段码输出
    rjmp MAIN                    ;循环显示
EXT_INT0:
    in temp,sreg
    push temp                    ;中断现场保护
    inc counter                    ;计数单元加1
    cpi counter,0x10            ;与16比较
    brne EXT_INT0_RET            ;小于16,转中断返回
    clr counter                    ;计数单元清0
EXT_INT0_RET:
    pop temp                    ;中断现场恢复
    out sreg,temp                ;中断返回
    reti
EXT_INT1:
    in temp,sreg
    push temp                    ;中断现场保护
    dec counter                    ;计数单元减1
    cpi counter,0xFF            ;与255比较
    brne EXT_INT1_RET            ;未到255,转中断返回
    ldi counter,0x0F            ;计数单元设置为15
EXT_INT1_RET:
    pop temp                    ;中断现场恢复
    out sreg,temp                ;中断返回
    reti
led_7:                            ;7段码表
    .db 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07
    .db 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71

思考与实践

//TODO 明天把问题部分摘抄下来,回答无能为力
Q:平稳地按下K1和K2,观察显示的变化
Q:修改程序,将INT0、INT1的中断触发方式分别改成电平变化的上升沿触发中断,以及电平变化触发中断和低电平触发中断,然后运行程序使显示数据加1或减1变化,与使用电平变化的下降沿触发中断的情况做比较,有何不同?
Q:不管使用哪种中断触发方式,经常会产生按键控制不稳定的现象,例如显示为5时,按下K1键一次,应该显示6,但时间显示7或8甚至更高,这是为什么?
Q:查看在CVAVR开发环境中编写编译demo_7_1.c的过程中,CVAVR都生成了哪些文件?这些文件的内容是什么?文件的扩展名是什么?有何作用?
Q:查看CVAVR生成的demo_7_1.lst的内容,问:CVAVR对中断向量是如何处理的?counter变量分配在什么地方?在CVAVR中,如何对中断现场进行保护和恢复?是使用硬件堆栈进行保护的吗?
Q:整理出编译demo_7_1.c生成的汇编代码,体会宏的使用,并与demo_7_1.asm进行比较。
Q:CVAVR在编译demo_7_1.c生成的汇编代码中还做了哪些工作?
Q:参考CVAVR的help,尝试在CVAVR中采用嵌入汇编语言的方式编写INT0和INT1的中断服务程序。

👍 0

none

最后修改于1998天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

狗头

原神

小黄脸

目录

avatar

未末

迷失

126

文章数

275

评论数

7

分类