Another Interrupt question

Hi guys;

I am sorry for the noob question here. Here goes... Do you have to "declare" the pin of the interrupt ?

So Do I add pinMode or no pinMode ?

Ex:

// My variable stuff

void setup()
{
  pinMode(2, INPUT); // <-- The interrupt pin
  /*
   Do I put that line  there if I need an interrupt ? YES or NO
                            
  */
  attachInterrupt(0, myinterrupt, RISING);
  // other init stuffs
}

void loop()
{
 // my main program
}

void myinterrupt()
{
  // interrupt stuff // No delay() please !!!
}

Sorry for the noob question here...

Do you have to "declare" the pin of the interrupt ?

The pin is defined in hardware. The interrupt number to use is defined in the attachInterrupt() function call.

So Do I add pinMode or no pinMode ?

Not required, but it won't hurt anything, either.

More generally, pins default to input after a reset.

Thank PaulS and Nick Gammon for answering, so it look like pinMode(interruptpin, INPUT); is an option.

use digitalWrite (interruptpin, HIGH);
to enable its pullup resisitor also, then a switch/logic level to ground can create an interrupt for you.

use digitalWrite (interruptpin, HIGH);
to enable its pullup resisitor also

I got that.

pinMode (intpin, INPUT);
digitalWrite( intpin, HIGH);

then a switch/logic level to ground can create an interrupt for you.

I don't get that. ???

attachInterrupt(0, myinterrupt, LOW);

When the pin goes low (as from a normally open switch being closed to ground, or some external device (output of an optocoupler for example) taking the pin to a low) an interrupt will be created.
Otherwise, the pullup resistor keeps it at a nice clean high level.

attachInterrupt(0, myinterrupt, FALLING); // would work also I guess.
attachInterrupt(0, myinterrupt, CHANGE); // would work also I guess.

CrossRoads:
attachInterrupt(0, myinterrupt, LOW);

Better to use FALLING. If you use LOW it keeps tripping until disabled. That can gobble up a serious amount of CPU time.

Depends on usage, like all things.
For my RF remote control, the program would start from the same spot after getting interrupted from sleep.

This function is called after determining that we're going to sleep:

void enterSleep()
{
  /* Setup pin2 as an interrupt and attach handler. */
  attachInterrupt(0, pin2Interrupt, LOW);

  /* the sleep modes
   SLEEP_MODE_IDLE - the least power savings
   SLEEP_MODE_ADC
   SLEEP_MODE_PWR_SAVE
   SLEEP_MODE_STANDBY
   SLEEP_MODE_PWR_DOWN - the most power savings
   */
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // setting up for sleep ...
  sleep_enable();                       // setting up for sleep ...
 
    // Disable ADC
  ADCSRA &= ~(1 << ADEN);

  // Power down functions
  PRR = 0xFF;

  sleep_mode();                         // now goes to Sleep and waits for the interrupt

this for the ISR:

void pin2Interrupt()
{
  /* This brings us back from sleep. */
}

and then this runs after waking up;

  /* The program will continue from here after the interrupt. */
  detachInterrupt(0);                 //disable interrupts while we get ready to read the keypad 
  
    // Power up functions
  PRR = 0x00;

  /* First thing to do is disable sleep. */
  sleep_disable();

You are slightly better off doing this:

void pin2Interrupt()
{
  detachInterrupt(0);                 //disable interrupts while we get ready to read the keypad 
}

The interrupt might be entered again before the detachInterrupt is done. And indeed getting into the detachInterrupt function (pushing stuff etc.) is likely to take a few instructions. You might not notice it (as the ISR doesn't do much) but I bet the interrupt count is higher than you are expecting, if you were to measure it.

Let me demonstrate the difference ...

This is detaching the interrupt the moment you return from sleep:

#include <avr/sleep.h>
volatile int counter;
void isr ()
{
  counter++;
}  // end of isr

void setup ()
{
  digitalWrite (2, HIGH);  // enable pull-up
  attachInterrupt (0, isr, LOW);
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  sleep_enable();          // enables the sleep bit in the mcucr register
  sleep_mode();            // now goes to Sleep and waits for the interrupt

  detachInterrupt (0);
   
  Serial.begin (115200);
  Serial.println ();
  Serial.println (counter);
}  // end of setup

void loop () {}

Now I start the serial monitor, and then ground pin 2. I see this:

11

Now detach the interrupt in the ISR:

#include <avr/sleep.h>
volatile int counter;
void isr ()
{
  counter++;
  detachInterrupt (0);
}  // end of isr

void setup ()
{
  digitalWrite (2, HIGH);  // enable pull-up
  attachInterrupt (0, isr, LOW);
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  sleep_enable();          // enables the sleep bit in the mcucr register
  sleep_mode();            // now goes to Sleep and waits for the interrupt
   
  Serial.begin (115200);
  Serial.println ();
  Serial.println (counter);
}  // end of setup

void loop () {}

Now when testing I see this:

1

So moving the detachInterrupt actually saved an extra 10 interrupts from firing! If the interrupt routine was doing anything else important, it is doing it 11 times rather than 1. Plus there is the overhead of servicing 11 interrupts rather than one.

Well, in my case I push 1 button, I get one action on the display.
Will keep in mind for next project.

@Nick Gammon

That is an interresting use of an interrupt, "interrupt and detach" ... Interesting. Can be usefull in some projects design, but to use the interrupt again you just simply re-activated the interrupt. In your example, the first program, you got 11, sound like bouncing, but the other version, it only one, some kind of "debounce" or "latch". .... Interesting

No it's not a bounce. It's expected behaviour from using the LOW interrupt.

The LOW interrupt keeps firing (while interrupts are enabled) after every instruction (and while the signal is low).

According to the datasheet:

When the AVR exits from an interrupt, it will always return to the main program and execute one more instruction before any pending interrupt is served.

So it limps along, executing the ISR for every single machine instruction until you detach the interrupt. I'm guessing from the number 11 being returned, that calling detachInterrupt, and it actually doing something useful, takes 10 instructions (we expected the count of 1 after all).

After the sleep it calls sleep_disable() which takes 3 instructions.

Then it takes two instructions to load a register with 0 and call detachInterrupt.

Then (inside detachInterrupt) this takes two instructions:

if(interruptNum < EXTERNAL_NUM_INTERRUPTS)

Then this takes two instructions:

    switch (interruptNum) {

Finally turning the interrupt off takes another.

That's a total of 10 instructions.

For detecting a pin going low you are better off using FALLING interrupt. That only fires once (ie. from HIGH to LOW transition). Handling debounces is another story.

Just to explain a bit, the LOW interrupt is the only one that wakes the processor from sleep, so you need to use it in this particular case. See the manual page 70:

Low level interrupt on INT0 and INT1 is detected asynchronously. This implies that this interrupt can be used for waking the part also from sleep modes other than Idle mode.

It can't detect FALLING, RISING, or CHANGE interrupts because the clock is stopped in sleep mode, thus it can't notice level transitions.

However as I pointed out, if you don't want a "sleepy" wake-up, detach the interrupt immediately in the ISR, to save having severely degraded performance after getting the interrupt.

Sorry I mis-understood you. In one of my project, I did use attachInterrupt(1,myint,LOW); and the display when crazy ( display a counting data ) , so I use FALLING and it's stable. Still, your idea "interrupt & detach" is interesting, it will be usefull in some projects design.