You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
}
原因分析
  1. 未清除外部中断挂起标志:AVR的外部中断触发后,即使关闭了中断使能,对应的中断标志位(EIFR寄存器的INTF0、INTF1位)也会被置为1并保持。当你在定时器中断结束时直接重新打开INT0、INT1的中断使能,已经置位的中断标志会立刻触发中断,这是中断触发两次的核心原因。
  2. 未做信号防抖:如果输入到中断引脚的信号存在抖动、毛刺,也会导致多次下降沿被检测到,触发多次中断。
修复方法
  • 在重新开启外部中断使能前,先手动清除中断挂起标志:在TIMER1_COMPA_vect中,给EIMSK赋值开启INT0、INT1之前,添加一行代码EIFR |= (1 << INTF0) | (1 << INTF1);,往这两个标志位写1即可清除挂起的中断请求。
  • 可选优化:给外部中断引脚增加硬件RC滤波,或者在进入外部中断服务函数后增加短时间的软件防抖判断,避免信号毛刺触发异常中断。

内容的提问来源于stack exchange,提问作者18avf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 17:54:04