Using 2 interrupts

Hi,

I’d like to use 2 interrupts with my project. The interrupts need to occur on falling edges. I found the below code.

#include <avr/sleep.h>                  
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>    // Needed to use interrupts

volatile int int0_flag;
volatile int int1_flag;

#define TRUE 1
#define FALSE 0


ISR(INT0_vect)
{
    int0_flag = TRUE;    
}


ISR(INT1_vect)
{
    int1_flag = TRUE;    
}

 
void setup()
{

   Serial.begin(9600);
   Serial.println("starting...");      

   pinMode(2, INPUT);
   pinMode(3, INPUT);

   digitalWrite(2, HIGH);
   digitalWrite(3, HIGH);

   EICRA = B00001010; // configure pin 2 and 3 for falling
   EIMSK = B00000011; // mask for INT1 and INT0

   int1_flag = FALSE;
   int0_flag = FALSE;  

   Serial.println("ready...");  
}

void loop()
{       
       if (int0_flag == TRUE)
       {
            Serial.println("INT0");   
            int0_flag = FALSE;
       } 
       if (int1_flag == TRUE)
       {
            Serial.println("INT1");  
            int1_flag = FALSE;
       }
}

Is this the correct way to handle 2 interrupts or is there a more reliable way?

How is below found?

  EICRA = B00001010; // configure pin 2 and 3 for falling
   EIMSK = B00000011; // mask for INT1 and INT0

Thanks

Why not just use attachInterrupt() ?

The Arduino IDE "knows" the names of all the Atmega registers and they are all explained in the Atmel datasheet for the MCU in your Arduino. You have not told us what Arduino you are using.

...R

I plan on using an Uno for now and then change to a Pro Mini board.

The main reason for the 2 interrupts is to wake up from sleep as shown below (this is stripped out version).

Would this way of using interrupts be very reliable?

#include <avr/sleep.h>                  
#include <avr/power.h>
#include <avr/wdt.h>


void setup()
{
    Serial.begin(9600);
    Serial.println("starting...");
    
    digitalWrite (2, HIGH);    // pull-up on button
    digitalWrite (3, HIGH);    // pull-up on button
    
    //enable watchdog to check for code lockups
    wdt_enable(WDTO_8S);
}


// interrupt service routine in sleep mode
void wake1 ()
{
  
}  // end of wake

// interrupt service routine in sleep mode
void wake2 ()
{
  
}  // end of wake


void sleepNow ()
{   
    wdt_disable();
    
    noInterrupts ();          // make sure we don't get interrupted before we sleep

    byte adcsra_save = ADCSRA;
    ADCSRA = 0;  // disable ADC
    power_all_disable ();   // turn off all modules
    set_sleep_mode (SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();
    attachInterrupt (0, wake1, LOW);   // allow grounding pin 2 to wake us
    attachInterrupt (1, wake2, LOW);   // allow grounding pin 2 to wake us
    interrupts ();
    sleep_cpu ();            // now goes to Sleep and waits for the interrupt
    detachInterrupt (0);     // stop LOW interrupt
    detachInterrupt (1);     // stop LOW interrupt
    
    ADCSRA = adcsra_save;  // stop power reduction
    power_all_enable ();   // turn on all modules 
}  // end of sleepNow


void loop()
{
    //reset watchdog timer
    wdt_reset(); 
    
    delay(1000);
    Serial.println("1");
    delay(1000);
    Serial.println("2");
    delay(1000);
    Serial.println("3");
    delay(1000);
    
    sleepNow();
    //while(1);
}

Thanks

I would have to spend a lot of time reading before I could answer Reply #2

Nick Gammon's interrupt Tutorial may be helpful

...R

Jeffro_Aus:
Would this way of using interrupts be very reliable?

No.

(this is the stripped down version of the answer).

No.
(this is the stripped down version of the answer).

Can you please elaborate?

Can you show the whole code?

Without seeing the whole picture there is no answer to your question.

A problem I see with your code snippet is the usage of

    detachInterrupt (0);     // stop LOW interrupt
    detachInterrupt (1);     // stop LOW interrupt

that is not a 'reliable' way to handle the disabling of interrupts.

The rest of the code at the moment only flashes a few leds, nothing serious at all.

Is there a 'reliable' way to handle the interrupts this way? If i only had one interrupt instead of 2, i don't see this as a problem.

To raise the stability of your program you should do only what has to be done and nothing more.

Each line of code introduces an additional point of error/failure.

So if you want to disable an interrupt, it’s a good idea to use the bit that is designed to mask it (only).
Setting this bit is one function of detachInterrupt, so it is used by people who are too lazy to look up the register/bit name.

If you never wanted to use that interrupt again, detachInterrupt would be ok (IMHO), but that is not the case.

Fiddling with vectors that are used by interrupts should be restricted to a minumum, if you are into reliability.
The time spent with interrupts disabled should be minimized, if you are into reliability.

Are you shure that detatchInterrupt may be called with interrupts disabled?
Will it return with interrupts disabled?

You don’t have to have an answer to these questions if you just don’t use it in that context.

Thanks for your detailed response.

Would it be better to use Pin Change interrupts to wake up from sleep instead of attaching/detaching interrupts like before?

Thanks

The type of interrupt you choose is in no way connected to the possible misuse of detachInterrupt.

You don't have to change the source and/or interrupt mechanism,
you should enable/disable the interrupts by masking them, if needed.

You don't have to change the source and/or interrupt mechanism

Ok, i can keep the current interrupt type.

you should enable/disable the interrupts by masking them, if needed.

This is the part i am not sure about.

Thanks

Jeffro_Aus:
This is the part i am not sure about.

So study Gammon Forum : Electronics : Microprocessors : Interrupts and the datasheet.

I decided to change my approach a little and use Pin Change Interrupts for D2 and D3. I’ve modified the code to allow for this.

How does this look now? (i need it to be very reliable)

#include <avr/sleep.h>                  
#include <avr/power.h>
#include <avr/wdt.h>

volatile int pressed = 0;

void setup() 
{
    // put your setup code here, to run once:
    Serial.begin(9600);
    Serial.println("starting...");
    delay(100);
    
    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    
    // pin change interrupt
    PCMSK2 |= bit (PCINT18)| bit (PCINT19);  // want pin D2 and D3
    PCIFR  |= bit (PCIF2);   // clear any outstanding interrupts
    PCICR  |= bit (PCIE2);   // enable pin change interrupts for D0 to D5
    
    //enable watchdog to check for code lockups
    wdt_enable(WDTO_8S);
}


  
ISR (PCINT2_vect)
{
   // handle pin change interrupt for D2/D3 
   pressed = 1;
}   


void sleepNow ()
{   
    wdt_disable();

    set_sleep_mode (SLEEP_MODE_PWR_DOWN);   // sleep mode is set here 
    byte adcsra_save = ADCSRA;
    ADCSRA = 0;  // disable ADC 
    power_all_disable ();   // turn off all modules
    sleep_enable(); 
    sleep_cpu ();            // now goes to Sleep and waits for the interrupt
    sleep_disable(); 
    ADCSRA = adcsra_save;  // stop power reduction
    power_all_enable ();   // turn on all modules 
}   


void loop() 
{
    // put your main code here, to run repeatedly:

    //reset watchdog timer
    wdt_reset(); 
  
    //print if button pressed
    if (pressed == 1)
    {
      pressed = 0;
      Serial.println("pressed"); 
    }

    //do something useful here

    
 
    delay(100);
    sleepNow(); 
}

Thanks

It looks a lot better. :slight_smile:

I would prefer pinMode(2, INPUT_PULLUP) to digitalwrite(2, HIGH) because it documents the intention.

If pressed is only a flag, make it a byte/char/bool.

I suppose the delay(100) before the sleepNow is testcode.

I suppose the delay(100) before the sleepNow is testcode.

Yes

I’m hoping when sleeping that we don’t have any lockups, although i am not sure if this is even possible.

I made the suggested modifications below.

#include <avr/sleep.h>                  
#include <avr/power.h>
#include <avr/wdt.h>

volatile bool pressed = false;

void setup() 
{
    // put your setup code here, to run once:
    Serial.begin(9600);
    Serial.println("starting...");
    delay(100);
    
    pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);
    
    // pin change interrupt
    PCMSK2 |= bit (PCINT18)| bit (PCINT19);  // want pin D2 and D3
    PCIFR  |= bit (PCIF2);   // clear any outstanding interrupts
    PCICR  |= bit (PCIE2);   // enable pin change interrupts for D0 to D5
    
    //enable watchdog to check for code lockups
    wdt_enable(WDTO_8S);
}


  
ISR (PCINT2_vect)
{
   // handle pin change interrupt for D2/D3 
   pressed = true;
}   


void sleepNow ()
{   
    wdt_disable();

    set_sleep_mode (SLEEP_MODE_PWR_DOWN);   // sleep mode is set here 
    byte adcsra_save = ADCSRA;
    ADCSRA = 0;  // disable ADC 
    power_all_disable ();   // turn off all modules
    sleep_enable(); 
    sleep_cpu ();            // now goes to Sleep and waits for the interrupt
    sleep_disable(); 
    ADCSRA = adcsra_save;  // stop power reduction
    power_all_enable ();   // turn on all modules 
}   


void loop() 
{
    // put your main code here, to run repeatedly:

    //reset watchdog timer
    wdt_reset(); 
  
    //print if button pressed
    if (pressed == true)
    {
      pressed = false;
      Serial.println("pressed"); 
    }

    //do something useful here

    
 
    delay(100);
    sleepNow(); 
}

Thanks