B001-Atmega16-中断(GCC-AVR)-(ques=3)

来源:互联网 发布:网络嗅探器win7 编辑:程序博客网 时间:2024/05/24 01:43

编译器    :AVR Studio 4.19 + avr-toolchain-installer-3.4.1.1195-win32.win32.x86
芯片型号:ATmega16
芯片主频:8MHz


-------------------------------------------------------------------------------------------------------------------------------------

待解决问题数量 = 3


-------------------------------------------------------------------------------------------------------------------------------------

AVR-GCC中断概述

1、需要包含头文件interrupt.h、它定义了中断函数:#define ISR(vector, [attributes]) 

2、中断函数一般的格式:

       ISR(interrupt_number)

      {

      }

      这里缺省的attributes默认为ISR_BLOCK,表示进入中断后禁止全局中断,这也就禁止了中断嵌套

3、中断号定义在iom16.h中,中断向量地址越低,优先级越高

4、中断标志位会在执行中断程序的时候被自动清0,所以不需要手动清0


-------------------------------------------------------------------------------------------------------------------------------------

中断配置的步骤:

1、配置中断源的触发方式
2、允许中断
3、清除中断标志位
4、开启总中断


-------------------------------------------------------------------------------------------------------------------------------------

AVR-GCC中断的写法

如定时器2的中断的例子:

#include <avr/interrupt.h>

……

……

ISR(TIMER2_OVF_vect)
{
    PORTA ^= (1 << PA0);
}

完整的定时器2 中断测试详见这篇文章的第一步(普通模式):http://blog.csdn.net/manon_des_source/article/details/51564339


-------------------------------------------------------------------------------------------------------------------------------------

不同编译器的中断写法:

中断是编译器自己实现的,在不同的编译器中,中断函数的定义和写法不一样:

1、旧版GCC-AVR使用signal.h中的SIGNAL(SIG_INTERRUPT0)
2、ICC-AVR的写法是:#pragma interruput_handler TIM1_OVF: 6


-------------------------------------------------------------------------------------------------------------------------------------

伪中断BADISR_vect

作用:

1、程序中需要使用ISR(BADISR_vect){ },用来捕获未定义中断函数的中断

2、如果某个被允许的中断产生了中断请求,但程序中又没有定义它的中断函数,那么系统可能会执行到错误的程序

      |<-------------- 待测试 question-001

3、没有包含interrupt.h的文件中的ISR()都不是中断函数、因为编译器找不到定义,将其视为int (int)类型的普通函数,这种ISR请求可以被BADISR_vect这个伪中断捕获


-------------------------------------------------------------------------------------------------------------------------------------

测试准备:

1、在Drv_Timer2.c中允许定时器2溢出中断,并定义定时器2 中断函数如下:

同时屏蔽中断头文件interrupt.h


ISR(TIMER2_OVF_vect)被编译器视为int (int)类型的普通函数

这样,定时器2 允许中断,但又没有自己的中断函数


2、在main.c中定义伪中断BADISR_vect如下(包含中断头文件interrupt.h):


……


3、在上面两个函数内都放置断点,进入调试模式

测试结果:

1、每次按下F5、在定时器2 溢出时,中断都会跳转到ISR(BADISR_vect),而不是ISR(TIMER2_OVF_vect)


同时PA1引脚的电平翻转,而PA0引脚无变化。


2、取消在Drv_Timer2.c中对中断头文件interrupt.h的屏蔽,重新进入调试。

      此时、每次按下F5、在定时器2 溢出时,中断都会跳转到ISR(TIMER2_OVF_vect),而不是ISR(BADISR_vect)


同时PA0引脚的电平翻转,而PA1引脚无变化。


3、这个测试说明,伪中断BADISR_vect 确实捕捉到了未定义中断函数的中断求情。


-------------------------------------------------------------------------------------------------------------------------------------

伪中断捕捉多个中断

1、第1步:T1CTC控制300us中断一次,T2CTC控制200us中断一次。

                     在比较匹配中断中翻转PA1PA3引脚、他们的波形应该和比较匹配引脚OC1AOC2的波形一致。

                     可以使用比较匹配中断输出方波、以测试CTC的时间 ( 比较匹配引脚来输出的方波也可以测试 ) 。

                     伪中断BADISR_vect中控制PA7引脚输出方波。

测试代码:

// ==========================================================================================================// 主函数// ==========================================================================================================#include <avr/io.h>#include <avr/interrupt.h>#include "Drv_Timer.h"#include "system.h"#include "config.h"// ==========================================================================================================//  伪中断BADISR_vect// // ==========================================================================================================ISR(BADISR_vect){    PORTA ^= (1 << PA7);}// ==========================================================================================================// main函数// ==========================================================================================================int main(void){    // ------------------------------------------------------------------------------------------------------    // 关全局中断    cli();    // 系统初始化    sys_init();    // PA[7,3,1]初始化为输出0    DDRA   =   (1 << DDA1) | (1 << DDA3) | (1 << DDA7);    PORTA &= ~((1 << PA1 ) | (1 << PA3 ) | (1 << PA7 ));    // 定时器1 初始化:CTC模式、COM1A启用(取反)、COM1B不启用、8预分频    Drv_Timer1_init(T1_WGM_CTC, T1_COM_MODE_TOGGLE, T1_COM_MODE_NONE, T1_CLK_SOURCE_CLK_8);    // 设置TCTN1=0、OCR1A=300、OCR1B=0、ICR1=0    Drv_Timer1_set_TCNT1_OCR1A_OCR1B_ICR1(0, 300, 0, 0);    // 使能OCF1A中断    Drv_Timer1_INT_Enable(INT_MODE_OCF1A, ENABLE);    // 定时器2 初始化:CTC模式、COM2启用(取反)、8预分频    Drv_Timer2_init(T2_WGM_CTC, T2_COM_MODE_TOGGLE, T2_CLK_SOURCE_CLK_8);    // 设置TCTN2=0、OCR2=200    Drv_Timer2_set_TCNT2_OCR2(0, 200);    // 使能OCF2中断    Drv_Timer2_INT_Enable(INT_MODE_OCF, ENABLE);    // PD[7,5]初始化为输出0(比较匹配引脚)    DDRD   =   (1 << DDA5) | (1 << DDD7);    PORTD &= ~((1 << PA5 ) | (1 << PD7 ));    // 开全局中断    sei();    // ------------------------------------------------------------------------------------------------------    while(1)    {    }    return 0;}

示波器输出如下:


      CH1OCR2中断输出的方波,CH2OCR1A中断输出的方波。

      可以看到、T1每300us中断一次,T2每200us中断一次,说明时间输出OK

      OC1A引脚和OC2引脚的波形和上面2个中断中PA1PA3的波形一致。

      而伪中断BADISR_vect中控制的PA7引脚就没有波形输出。

2、屏蔽上面的2个中断。

 

      这样、就只有比较匹配引脚输出波形,而中断中的引脚就没有波形输出。

      而PA7有引脚输出。

3、在伪中断BADISR_vect中放置断点,进入DEBUG。


测试结果:

1、第1次进入中断时、PA7引脚翻转,OC2引脚翻转,OCR1A引脚不变,说明是OC2发生比较匹配。

2、第2次进入中断时、PA7引脚翻转, OC2引脚不变,OCR1A引脚翻转,说明是OC1A发生比较匹配。

3、这说明伪中断BADISR_vect可以捕捉多个中断。


-------------------------------------------------------------------------------------------------------------------------------------

中断挂起测试

1、一个中断源发出中断请求后、这个中断应该被挂起,进入等待响应队列。

      如果全局中断打开,且没有比该中断优先级更高的中断,那么这个中断就应该被立即执行。

      否则、该中断需要继续留在等待队列,除非认为清除它的中断标志来删除该中断。

测试准备:

1、假如现在只有INT0中断,且关闭全局中断SREG.I = 0,等待500ms后、INT0中断到来,等待500ms,开全局中断SREG.I = 1。

      这个时候,INT0会得到执行么,会进入INT0中断服务函数么?

      |<---- 待测试-question-002

      如果INT0可以被执行,虽然INT0被延后了500ms才被执行,但说明INT0在这500ms内是被挂起的,没有被忽略。


-------------------------------------------------------------------------------------------------------------------------------------

中断嵌套

1、使用ICP1做红外接收的话,需要ICP1中断的优先级最高,而且要允许中断嵌套。

测试准备:

1、只使用TOV0中断和INT0中断。

2、在TOV0中断服务函数中写入死循环:

ISR(TIMER0_OVF_vect,ISR_NOBLOCK){    while(flag_INT) { }}

3、、在INT0中断中写入退出循环的代码:

volatile uint8_t flag_INT = 0;ISR(INT0_vect<span style="font-family: Arial, Helvetica, sans-serif;">, ISR_NOBLOCK</span>){    flag_INT = 0;}

3、在这两个中断中放入断点,进入DEBUG。

测试结果:

1、

|<----待测试-question-003

-------------------------------------------------------------------------------------------------------------------------------------

中断向量表

代码:

功能:使用外部中断1控制PORTB0的亮灭。

在如下代码中可以看到、上电加载完毕后,程序跳转到复位向量,而外部中断1发生时、程序跳转到外部中断1的中断向量

; sample.asm; 运行环境  atmelage16 8M晶振;  .include "m16def.inc" ;包含atmelage16定义文件.org $0000  ;中断向量表地址; ===========================================================================================================; 中断向量表; 地址范围:[0x000,0x029]、包含了21个中断、每个中断向量占用2个字节; BOOTRST未被编程=1,GICR.IVSEL=0时,复位向量地址=0x0000,ISR向量的起始地址=0x0002(见中文数据手册P44.Table19); ===========================================================================================================jmp RESET           ;复位中断jmp EXT_INT0        ;外部中断0jmp EXT_INT1        ;外部中断1jmp TIMER2_COMP     ;定时器2比较中断jmp TIMER2_OVF      ;定时器2溢出中断jmp TIMER1_CAPT     ;定时器1捕捉中断jmp TIMER1_COMPA    ;定时器1比较匹配A中断jmp TIMER1_COMPB    ;定时器1比较匹配B中断jmp TIMER1_OVF      ;定时器1溢出中断jmp TIMER0_OVF      ;定时器0溢出中断jmp SPI_STC         ;SPI传输完成中断jmp USART_RXC       ;USART接收完成中断jmp USART_UDRE      ;USART数据寄存器空中断jmp USART_TXC       ;USART发生结束中断jmp ADC_OVER        ;ADC转换结束中断jmp EEPROM_READY    ;EEPROM就绪中断jmp ANA_COMP        ;模拟比较器中断jmp TWI_INTERFACE   ;TWI接口中断jmp EXT_INT2        ;外部中断2jmp TIMER0_COMP     ;定时器2比较匹配中断jmp SPM_READY       ;保存程序存储器内容就绪中断.org $002A ;主程序入口地址,跳过中断向量区; ===========================================================================================================; 主程序; ===========================================================================================================main:; -----------------------------------------------------------------------------------------------------------; PORTB0设置为输出1ldi r20,    $01out DDRB,   r20out PORTB,  r20; -----------------------------------------------------------------------------------------------------------; 设置INT1中断; PORTD3/INT1设为输入、输出高、使能上拉ldi r20,    $F7out DDRD,   r20ldi r20,    $FFout PORTD,  r20; 使能全局总中断in  r20,    SREGsbr r20,    $80out SREG,   r20; 设置INT1为低电平触发in  r20,    MCUCRcbr r20,    $0Cout MCUCR,  r20; 使能INT1in  r20,    GICRsbr r20,    $80out GICR,   r20; -----------------------------------------------------------------------------------------------------------; 死循环loop_main:jmp loop_main;ret    ;main函数不是rcall指令进来的、不需要返回; 中断服务程序; ===========================================================================================================; 复位中断服务函数; ===========================================================================================================RESET:; -----------------------------------------------------------------------------------------------------------; 设置堆栈指针SPldi r20,high(RAMEND);high获取RAMEND高字节的立即数,将立即数装入r20out sph,r20 ;将获取到的堆栈高字节立即数送堆栈寄存器SP高字节中ldi r20,low(RAMEND) ;low获取RAMEND低位立即数,将立即数装入r20out spl,r20 ;将获取到的堆栈低字节立即数送堆栈寄存器SP低字节中; -----------------------------------------------------------------------------------------------------------; 跳转到main函数rjmp mainreti; 外部中断0EXT_INT0:reti; ===========================================================================================================; 外部中断1; ===========================================================================================================EXT_INT1:push r20push r21in  r20,    PINBldi r21,    0x01eor r20,    r21out PORTB,  r20pop r20pop r21reti;定时器2比较中断TIMER2_COMP:reti;定时器2溢出中断TIMER2_OVF:reti;定时器1捕捉中断TIMER1_CAPT:reti;定时器1比较匹配A中断TIMER1_COMPA:reti;定时器1比较匹配B中断TIMER1_COMPB:reti;定时器1溢出中断TIMER1_OVF:reti;定时器0溢出中断TIMER0_OVF:reti;SPI传输完成中断SPI_STC:reti;USART接收完成中断USART_RXC:reti;USART数据寄存器空中断USART_UDRE:reti;USART发生结束中断USART_TXC:reti;ADC转换结束中断ADC_OVER:reti;EEPROM就绪中断EEPROM_READY:reti;模拟比较器中断ANA_COMP:reti;TWI接口中断TWI_INTERFACE:reti;外部中断2EXT_INT2:reti;定时器2比较匹配中断TIMER0_COMP:reti;保存程序存储器内容就绪中断SPM_READY:reti

测试结果:

1、复位时:程序跳转到RESET向量


2、外部中断1发生时、程序进入外部中断1的中断向量:




0 0
原创粉丝点击