2013-08-20 44 views
-1

我一直在寻找所有这个问题的答案,一直没能找到任何东西。当我的ISR被触发时,它会通过并做所有它应该完全正常的事情,然后在退出并返回到主循环之前,ISR再次执行。一旦它第二次走完,它会返回到主循环。这只发生在我使用115V继电器来操作中断时。中断服务程序执行两次ATmega88

我试图检测何时发生停电或电源何时回复。我正在使用引脚变化中断来检测继电器是闭合还是断开。当电源熄灭时,继电器将打开并触发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); 
      } 
    } 
} 
+0

你有没有实际验证你得到你所期望与输入引脚上的范围是什么?这个中断是由上升沿和下降沿触发的,所以如果你得到某种脉冲,这就解释了你为什么得到两个中断。 –

+0

@MattYoung是的,当继电器闭合并且打开时有一种脉冲,但是我还没有能够想出任何可以消除脉冲的东西。我想知道如果有人有任何想法可以做到这一点,因为我尝试过的一切都没有奏效。 – brian

+0

什么是脉冲周期和占空比?所有这些6秒的延迟都浪费了大量的处理器时间,他们是否尝试了去抖? –

回答

0

我使用的是引脚变化来检测是否中继器关闭或打开 。

不这样做。认真。不要试图将机械开关挂到中断引脚来触发ISR。

如果你坚持这样做,在至少确保开关的信号是体面硬件它击中μC的输入引脚之前。

此外,任何种类的等待(_delay_ms(6000);)都不是在ISR中想要的。

0

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

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

  • 如果你想这样做的软件,只需设置在ISR例程表示事件的变量。然后,在你的主循环(或函数)中,去掉relais(10到100ms就足够了)。

  • ,如果你想这样做硬件,只需添加一个RC组合作为过滤器的引脚(的C接地,将R的继电器。只要尝试像100K和10μF的一些组合。

我个人的建议: 一举两得;)

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

玩得开心!

1

我有自己的问题,所以我决定即使回答它,虽然它是一个古老的条目:

之所以中断闪光两次是中断标志不复位。这是一些atmega类型的问题。

后这个在您的尽头ISR中断功能:您可以通过一个简单的线条解决问题

PCIFR |= (1<<PCIF2); 

这写的PCINT2中断“1”进入中断标志位。如果您正在使用其他中断,则必须将其他标志设置为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。

再见