AVR Timer与硬件中断配置问题:外部中断为何会触发两次?
问题现象
- 首次检测到硬件中断下降沿时启动Timer,运行8次后停用该定时器,停止定时器时硬件中断会再次触发,导致定时器被立即重启。
- 附图中可忽略蓝色信号:紫色信号为Timer1翻转引脚输出的波形,绿色信号为硬件中断触发时翻转引脚的波形。
- 预期功能:首次下降沿触发中断后,按照指定周期翻转对应引脚电平。
- 疑问:为什么硬件中断会触发两次?

相关代码
#define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> volatile int cnt = 0; uint8_t data[8]; int main(void) { // TIMER 1 DDRB |= (1 << PORTB0); // Set LED as output DDRB |= (1 << PORTB1); // Set LED as output DDRB |= (1 << PORTB2); // Set LED as output DDRD &= ~(1 << PORTD2); // Set LED as INPUT PORTD |= (1 << PORTD2); // PULLUP resistor TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TIMSK1 &= ~(1 << OCIE1A); // Disable CTC interrupt //TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt OCR1A = 1200; // Set CTC compare value to 15873Hz at 16MHz AVR clock, with a prescaler of 1 //TCNT0 = 0; TCCR1B |= ((1 << CS10)); // Start timer at Fcpu/1 // Interrupt EICRA |= (1 << ISC01) | (1 << ISC11); //ENABLE INT0 and INT1 ON falling EDGE EIMSK |= (1 << INT0); //ENABLE INT0 EIMSK |= (1 << INT1); //ENABLE INT0 sei(); // Enable global interrupts while (1) { } } ISR(TIMER1_COMPA_vect) { cnt = cnt + 1; PORTB ^= (1 << PORTB0); // D8 if (cnt > 7) { TIMSK1 &= ~(1 << OCIE1A); // stop CTC interrupt EIMSK |= (1 << INT0); //Enable INT0 EIMSK |= (1 << INT1); //Enable INT0 return; } } ISR(INT0_vect) //External interrupt_zero ISR { EIMSK &= ~(1 << INT0); //Disable INT0 PORTB ^= (1 << PORTB2); // Toggle the LED TCNT1 = 0; cnt = 0; TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt } ISR(INT1_vect) //External interrupt_zero ISR { PORTB ^= (1 << PORTB2); // Toggle the LED EIMSK &= ~(1 << INT1); //Disable INT0 TCNT1 = 0; cnt = 0; TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt }
原因分析
- 未清除外部中断挂起标志:AVR的外部中断触发后,即使关闭了中断使能,对应的中断标志位(EIFR寄存器的INTF0、INTF1位)也会被置为1并保持。当你在定时器中断结束时直接重新打开INT0、INT1的中断使能,已经置位的中断标志会立刻触发中断,这是中断触发两次的核心原因。
- 未做信号防抖:如果输入到中断引脚的信号存在抖动、毛刺,也会导致多次下降沿被检测到,触发多次中断。
修复方法
- 在重新开启外部中断使能前,先手动清除中断挂起标志:在
TIMER1_COMPA_vect中,给EIMSK赋值开启INT0、INT1之前,添加一行代码EIFR |= (1 << INTF0) | (1 << INTF1);,往这两个标志位写1即可清除挂起的中断请求。 - 可选优化:给外部中断引脚增加硬件RC滤波,或者在进入外部中断服务函数后增加短时间的软件防抖判断,避免信号毛刺触发异常中断。
内容的提问来源于stack exchange,提问作者18avf
相关产品推荐
相关产品推荐

