-1

I have been looking all of for an answer to this problem and have not been able to find anything. When my ISR is triggered, it goes through and does everything that it is supposed to perfectly fine, then before exiting and returning back to the main loop the ISR executes again. Once it has gone though the second time it then returns back to the main loop. This only happens when I use a 115V relay to operate the interrupt.

I am trying to detect when there is a power outage or when the power comes back on. I am using a Pin change interrupt to sense if the relay is closed or open. When the power goes out the relay will open and will trigger the ISR. If I connect this setup to a normal push button or switch everything works as needed and there is no problem, it is only when it is connected to the relay that there is a problem.

Here is the code that I have:(I know I don't need cli I have just been trying everything)

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);
            }
    }
}
JimmyB
  • 12,101
  • 2
  • 28
  • 44
brian
  • 43
  • 1
  • 7
  • Have you actually verified you're getting what you expect with a scope on the input pin? That interrupt is triggered by rising and falling edges, so if you're getting some kind of pulse, that would explain why you're getting two interrupts. – Matt Young Aug 22 '13 at 00:49
  • @MattYoung Yes there is a kind of pulse when the relay is closed and open, but I have not been able to come up with anything that will work to debounce the pulse. I was wondering if anyone had any ideas as to how that could be done since everything I tried has not worked. – brian Aug 23 '13 at 16:14
  • What is the pulse period and duty cycle? All of these 6 second delays are wasting tons of processor time, were they an attempt at debounce? – Matt Young Aug 23 '13 at 16:26

3 Answers3

1

I am using a Pin change interrupt to sense if the relay is closed or open.

Don't do that. Seriously. Do not try to hook mechanical switches to an interrupt pin to trigger ISRs.

If you insist on doing so, at least make sure the switch's signal is decently debounced in hardware before it hits the µC's input pin.

Besides, waiting of any kind (_delay_ms(6000);) is not something one wants to have in an ISR.

JimmyB
  • 12,101
  • 2
  • 28
  • 44
1

The reason your code fails: When the signal is bouncing, even if you wait 6 seconds in your interrupt, the flag is set again (while you are still in your ISR). The quick and dirty solution would be to reset the flag just before you exit the ISR.

Two (in my opinion) better solutions:

  • If you want to do this in software, just set a variable in the ISR routine which indicates a event. Then, in your main loop (or in a function), do the debouncing of the relais (10 to 100ms should be enough).

  • if you want to do this in hardware, just add a R C combination as a filter to the pin (the c to ground and the r to the relais. Just try some combinations like 100k and 10µF.

My personal recommendation: do both ;)

Annotations: - using a 6 second delay in an interrupt routine is really a bad practice. Better: Use a Timer which is called every say 10 ms and increase a counter if the flag is set and the relais indicate power loss. If the relais has power, reset the flag and counter to 0. If the counter reaches 600, this means the power outage is longer than 6 seconds.

Have fun!

ibschreiber
  • 141
  • 5
1

I had the problem myself, so I decided to answer it even though it is an old entry:

The reason why the interrupt fires twice is that the interrupt flag is not reset. This is a problem of some atmega types. You can solve the problem by one simple line:

Post this at the very end of you ISR interrupt function:

PCIFR |= (1<<PCIF2);

This will write a "1" into the interrupt-flag bit for the PCINT2 interrupt. If you are using other Interrupts, you have to set other flags to 1. Notice that the interrupt flag is inverted. So a 1 disables the interrupt, whereas a 0 triggers the interrupt.

Have a look into the datasheet: http://www.atmel.com/images/doc2545.pdf See Point 13.2.5:

Bit 2 - PCIF2: Pin change interrupt flag 2 - When a logic change on any PCINT23..16 pin triggers an interrupt request, PCIF2 becomes set (one). If the I-bit in SREG and the PCIE2 bit in PCICR are set (one), the MCU will jump to the corresponding Interrupt Vector. The flag is cleared when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it.

I hope this helped you and maybe some other people who have the same problem. Just search the datasheet for the name of the register and the bit corresponding to the interrupt flag and set it to 1.

Bye

a-wee-are
  • 11
  • 1