中断服务程序执行两次 ATmega88

Posted

技术标签:

【中文标题】中断服务程序执行两次 ATmega88【英文标题】:Interrupt Service Routine Executes Twice ATmega88 【发布时间】:2013-08-20 19:03:21 【问题描述】:

我一直在寻找这个问题的答案,但一无所获。当我的 ISR 被触发时,它会通过并执行它应该完美无缺的一切,然后在退出并返回主循环之前,ISR 再次执行。一旦它通过第二次,它就会返回到主循环。这只发生在我使用 115V 继电器操作中断时。

我正在尝试检测何时停电或何时恢复供电。我正在使用引脚更改中断来检测继电器是关闭还是打开。当电源熄灭时,继电器将打开并触发 ISR。如果我将此设置连接到普通按钮或开关一切都按需要工作并且没有问题,那么只有当它连接到继电器时才会出现问题。

这是我拥有的代码:(我知道我不需要 cli 我一直在尝试一切)

ISR(PCINT2_vect)

    cli();

    sbi(PORTC,5);
    _delay_ms(6000);
    cbi(PORTC,5);
    for(delay_counter=0;delay_counter<2;delay_counter++)
    
        _delay_ms(6000);
    
    sbi(PORTC,5);
    _delay_ms(6000);
    if(bit_is_set(PIND,2))
    
        lcd_clrscr();
        lcd_puts("Sending SMS");
        usart_print("at");
        USART_Transmit('\r');
        _delay_ms(6000);
        for(i=0;i<=1;i++)
            
                usart_print("at*smsm2m=");
                USART_Transmit('"');
                for(j=0;j<11;j++)
                
                    USART_Transmit(Alert_Numbers[i][j]);
                
                usart_print(" Power has been lost");
                USART_Transmit('"');
                USART_Transmit('\r');
                _delay_ms(6000);
            

            lcd_clrscr();
            lcd_puts("SMS Sent");
            _delay_ms(6000);
            lcd_clrscr();
            lcd_puts("Status:NO POWER");
            cbi(PORTC,5);
        

        else if(bit_is_clear(PIND,2))
        
            lcd_clrscr();
            lcd_puts("System Reset");
            _delay_ms(6000);
            _delay_ms(6000);
            usart_print("at");
            USART_Transmit('\r');
            _delay_ms(6000);
            for(i=0;i<=1;i++)
            
                usart_print("at*smsm2m=");
                USART_Transmit('"');
                for(j=0;j<11;j++)
                
                    USART_Transmit(Alert_Numbers[i][j]);
                
                usart_print(" Pump regained power");
                USART_Transmit('"');
                USART_Transmit('\r');
                _delay_ms(6000);
            

            lcd_clrscr();
            lcd_puts("POWER ON");
            _delay_ms(6000);
            lcd_clrscr();
            lcd_puts("Status: Good");

        
        else
        

        




int main(void)
    /*Initializations*/
    DDRC = 0x20; // PORTC,5 is now output
    sbi(PORTC,5);
    USART_Init(51);
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();

    /*Set interrupts*/
    DDRD  = 0b11111011;   // set PD2 to input
    PORTD = 0b00000100;   // set PD2 to high
    PCICR |= (1 << PCIE0);
    PCMSK0 |= (1 << PCINT0);
    PCICR |= (1<<PCIE2);
    PCMSK2 |= (1<<PCINT18);
    sei();


    lcd_clrscr();
    lcd_puts("Status: Good");

    /*Main Program Loop: NOP*/
    while(1)
    
            lcd_clrscr();
            lcd_puts("MAIN LOOP");
            for(delay_counter=0;delay_counter<3;delay_counter++)
            
                    _delay_ms(6000);
            
    

【问题讨论】:

您是否真的验证过您通过输入引脚上的示波器获得了您期望的结果?该中断是由上升沿和下降沿触发的,因此如果您收到某种脉冲,那就可以解释为什么您会收到两次中断。 @MattYoung 是的,当继电器关闭和打开时会有一种脉冲,但我无法想出任何可以消除脉冲的方法。我想知道是否有人对如何做到这一点有任何想法,因为我尝试的一切都没有奏效。 什么是脉冲周期和占空比?所有这些 6 秒的延迟都在浪费大量的处理器时间,它们是在尝试去抖动吗? 【参考方案1】:

我正在使用引脚更改中断来检测继电器是否关闭或 打开。

不要那样做。严重地。不要尝试将机械开关连接到中断引脚来触发 ISR。

如果您坚持这样做,至少确保开关的信号在硬件到达 µC 的输入引脚之前得到适当的去抖动 .

此外,任何形式的等待 (_delay_ms(6000);) 都不是人们希望在 ISR 中拥有的东西。

【讨论】:

【参考方案2】:

您的代码失败的原因:当信号反弹时,即使您在中断中等待 6 秒,标志也会再次设置(当您仍在 ISR 中时)。快速而肮脏的解决方案是在退出 ISR 之前重置标志。

两个(在我看来)更好的解决方案:

如果您想在软件中执行此操作,只需在 ISR 例程中设置一个变量来指示事件。然后,在您的主循环(或函数)中,对继电器进行去抖动(10 到 100 毫秒应该足够了)。

1234563 >

我的个人建议: 两者都做;)

注释: - 在中断例程中使用 6 秒延迟确实是一种不好的做法。更好:使用每隔 10 毫秒调用一次的计时器,如果设置了标志并且继电器指示断电,则增加一个计数器。如果继电器有电,则将标志位和计数器复位为0。如果计数器达到600,则表示断电时间超过6秒。

玩得开心!

【讨论】:

【参考方案3】:

我自己也有这个问题,所以我决定回答它,即使它是一个旧条目:

中断触发两次的原因是中断标志没有复位。这是某些 atmega 类型的问题。你可以通过简单的一行来解决这个问题:

将此贴在 ISR 中断函数的最后:

PCIFR |= (1<<PCIF2);

这会将“1”写入 PCINT2 中断的中断标志位。如果您正在使用其他中断,则必须将其他标志设置为 1。请注意,中断标志已反转。所以 1 禁用中断,而 0 触发中断。

查看数据表: http://www.atmel.com/images/doc2545.pdf 见第 13.2.5 点:

Bit 2 - PCIF2:引脚变化中断标志 2 - 当任何 PCINT23..16 引脚上的逻辑变化触发中断请求时,PCIF2 置位 (一)。如果 I 位在 PCICR 中的 SREG 和 PCIE2 位是 设置(一),MCU将跳转到 相应的中断向量。标志被清除 当中断程序被执行时。 或者,可以通过向其写入逻辑标志来清除标志。

我希望这对您以及可能遇到同样问题的其他人有所帮助。只需在数据表中搜索寄存器名称和中断标志对应的位并将其设置为1即可。

再见

【讨论】:

以上是关于中断服务程序执行两次 ATmega88的主要内容,如果未能解决你的问题,请参考以下文章

AVR开发 Arduino方法 中断子系统

使用 UDRE 和 ATmega328P 的中断驱动 USART

不是问题的问题为什么复位中断服务程序里面直接调用的main函数,难道所有程序都在复位中断里面执行的?

不是问题的问题为什么复位中断服务程序里面直接调用的main函数,难道所有程序都在复位中断里面执行的?

中断服务程序大家都是怎么用的

attachInterrupt()函数