-1

我一直在寻找这个问题的答案,但一无所获。当我的 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);
            }
    }
}
4

3 回答 3

1

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

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

在 ISR 中断函数的最后发布:

PCIFR |= (1<<PCIF2);

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

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

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

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

再见

于 2014-01-25T00:25:55.170 回答
1

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

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

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

  • 如果您想在硬件中执行此操作,只需在引脚上添加一个 RC 组合作为滤波器(c 接地,r 连接继电器。尝试一些组合,如 100k 和 10µF。

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

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

玩得开心!

于 2013-09-11T22:43:02.600 回答
1

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

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

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

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

于 2013-08-27T13:29:11.310 回答