Need Interrupt Help

I’m hoping that someone can point out where I am going wrong! I have four ISR’s attached to A0, A1, A7, and D0. The issue arises when I attach both A1, and A7. I can only get one of them to fire if I attach both as shown in the code below. If I attach A1 interrupt last, it will be the one to fire and A7 will not… If I attach A7 interrupt last, it will be the one to fire and A1 will not. Note that the other ISR’s always fire (A1, D0).

Thanks!

    // BUTTON PINS
int reading = 0;   
int mem = A1;
int clear = A0;     
int power = A7;     

volatile bool readingflag = false;
volatile bool memflag = false;
volatile bool clearflag = false;
volatile bool powerflag = false;

void setup(){

    Serial.begin(9600);
 
    attachInterrupt(clear,clearPress,CHANGE);
    attachInterrupt(power,powerPress,CHANGE);
    attachInterrupt(mem,memoryPress,CHANGE);
    attachInterrupt(reading,readingPress,CHANGE);

}

void loop(){

    if(readingflag){
    
        Serial.println("READING!");
        readingflag = false;
    
    }
  
    if(memflag){
    
        Serial.println("MEM!");
        memflag = false;
    
    }
  
    if(clearflag){
    
        Serial.println("CLEAR!");
        clearflag = false;
    
    }
    
    if(powerflag){
    
        Serial.println("POWER!");
        powerflag = false;
    
    }

}

void memoryPress(){

    memflag = true;
}

void readingPress(){

    readingflag = true;
}

void clearPress(){

    clearflag = true;
}

void powerPress(){

    powerflag = true;
}

It was mentioned to me that pin assignments (with Spark) are not 100% Arduino-like and that one should designate pins with either the 'D' or the 'A'

? have you tried:

int reading = D0;

Thanks… Just tried it but no luck.

Can anyone try to duplicate this scenario? I have pull down resistors on on each interrupt pin (about 3k) and have a button connecting each interrupt pin to the 3v3 rail when pressed. I have duplicated this on a couple sparks but it would be nice to verify this.

one more suggestion… try to debounce the interrupts:

void powerPress()
{ 
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 200)
  {
    powerflag = true;
  }
  last_interrupt_time = interrupt_time;
}

I ran into something a while ago, maybe it is related. There is something beyond my understanding about handling interrupts, so I can’t give you good advice. But I would try to use different pins for interrupts, maybe some other combination will work. Maybe some pins conflict when interrupt is attached to both of them.

I had interrupts working great when attached to pins D2, D3, D4, A0 and A1.

And I would also recommend some software debouncing, as @BulldogLowell recommends. But you might keep it for later, when interrups work well. I did mine in main loop instead of ISR handler (to keep code in ISR minimal). Something like this, although it doesn’t work well when interrupt events are independent, as any interrupt blocks everything else. Good enough for user input though. Just use more debounceTime variables if you need it.

// software debounce
#define             DEBOUNCE_TIMEOUT    200     // software debounce timeout
unsigned int debounceTime      = 0;

void loop() {
    unsigned int now = millis();
    if (now > debounceTime) {
        if (readingflag) {
            debounceTime    = now + DEBOUNCE_TIMEOUT;
            readingflag     = false;
            Serial.println("READING!");
        }

        // ... other flags handling follows
    }
    else {
        // just reset anything interrupt might trigger
        readingflag = false;
        memflag     = false;
        clearflag   = false;
        powerflag   = false;
    }
}

// Your ISRs here, unchanged...

And the topic with the problem I encountered:

Thanks guys! I understand what you are saying about debounce.. I do have it in my actual code and it works great. It looks exactly like the code @BulldogLowell posted. The code I posted is a simple sketch to prove the point that there is something funny going on with the A1 and A7 interrupt pins. However, I'm not so proud that I wouldn't give the debounce a go again.. I will try both and let you know!

@lami It works great with this combination (A0,A2,A7,D0). In other words changing from A1 to A2. Unfortunately, I jumped the gun and ordered PCB's based ton the (A0,A1,A7,D0) combo :fearful:

Thanks guys!

Sorry, I meant to say

"If I attach A7 interrupt last, it will be the one to fire and A1 will not."

That totally stinks that you pre-ordered your PCBs. I’m guilty of that; have done it MANY times.

I never order PCBs without first prototyping everything and wiring everything up and testing everything first to make sure I know about things like this.

Fortunately for you, since the Spark Core is a DIP package, you might be able to salvage the boards with some trace cutting and bodge wiring. That way you can get the working pins wired up and keep things working.

@jasonp, @naikrovek, I remember reading about this interrupt issue before but never investigated it. From what I see, all interrupts share a common ISR which then jumps to the user-defined function. Also, there are only 5 unique GPIO IRQ channels being allocated. Not sure what this means but I will look into the implications and report back. The issue should either be documented or fixed, IMO. :smiley:

3 Likes

@naikrovek Yes I’m getting quite familiar with that feeling! A hack might be in the works but I’m still hoping I see a software fix first.

@peekay123 THANKS!!

I am trying to implement a simple standby and timed ‘sleep’ mode on my project and I want to allow a user to ‘wake’ the device by pushing a button. I have tried in my larger project code to make this work and it isn’t. I have copied the example in the Firmware documentation as a simple test and this does not work reliably. Can anybody help and spot what I am doing wrong here. ledPIN off at startup then once I press the button attached to the wake PIN the led stays on with a slight flicker every 2 seconds. This suggests a lot of interrupts were queued up - is there a way to avoid this ‘repeat key’ issue.

void blink(void);
int ledPin = A5;
int wake = A6;
volatile int status = LOW;

void setup()
{
  pinMode(ledPin, OUTPUT);
  attachInterrupt(wake, blink, RISING);
  status = LOW;
}

void loop()
{
  if (status == HIGH)
  {
        digitalWrite(ledPin, HIGH);
        delay(2000);
        digitalWrite(ledPin, LOW);
        status = LOW;
    }
}

void blink()
{
    if (status == LOW) status = HIGH;
}

You probably want to debounce the button. Have a read: https://www.arduino.cc/en/Tutorial/Debounce :slight_smile:
Need Interrupt Help

Sorry for these questions, but only to make sure :blush:

How have you wired your button?
Are you using an external pull-down resistor?
Are you actually closing to 3V3 (3V3 -> switch -> A6 -> pull-down -> GND)?

Instead of an ext pull-down you may also try to add this before you attach the interrupt.

  pinMode(wake, INPUT_PULLDOWN);

Interrupts of one priority level are usually not queued.
I don’t think it’s a bounce problem, the delay(2000) should take care of that.
I’d rather suspect a floating pin to cause the trouble.


When you talk of sending your device to sleep, do you actually mean System.sleep()/Spark.sleep()?
In this case your code might need to be “completely” different.

1 Like

ScruffR

I mentioned ‘sleep’ in inverted commas because I am simply turning off the wifi and display which is enough power saving for my application and not using the System.sleep() function.

I have wired as GND - 1K Ohms - LED - contact switch to 3.3V - 10K - pin. To have a limit on the current and an led to witness the key press. Off course, what this does not do is pull the input down to ground in the first place hence putting in the pinMode(wake, INPUT_PULLDOWN); as you suggested has made this work - thanks.

I can’t find an explanation for the volatile int data type in the firmware documentation. Is this necessary for this code to work properly?

The volatile qualifier is required to ensure that each time the variable is requested, its value will be retrieved from its original location rather than reusing its its last known value (e.g. internal registers).
By “looking” at the standard code flow, the optimizer might assume the value of a given variable won’t change between two statements and hence just reuse the old value, but if the same variable is altered inside an interrupt service routine, that assumtion is not valid and hence the “optimization” has to be prevented.
That’s what volatile tells the optimizer/compiler.

For further background
https://en.wikipedia.org/wiki/Volatile_(computer_programming)

1 Like

@ScruffR
Further question to this answer. I notice in the Firmware documentation it suggests that mills() will not return a value in the Interrupt Routine. I am trying to provide for a multi-function single button to control the device - a simple push to ‘wake’ the device and once woken a longer push to place it back in ‘standby’. Can this be done in the interrupt handling routine using millis()?

The reason why you could not use millis() inside an ISR is (was) that the counter didn’t change while processing the ISR.
So even if you had an ISR that ran for several milliseconds (which is really bad programming BTW) a call to millis() at the end of the ISR would still return the same value as a call at the beginning.

Although this behaviour is meant to change (or has changed already), I’d rather vote against a use you outlined there.

ISRs should only be as short as absolutely necessary.

You can however do this

volatile uint32_t msLastISR;
...
void ISR()
{
  msLastISR = millis();
}

And check inside loop() how long the button stays pressed once msLastISR != 0 (don’t forget to set it back to 0 when the button gets released).

On the other hand, if you are only checking for a long push, you would not need to go through the bother of using a “high speed” interrupt. Just do it conventionally inside loop() (or a dedicated function called via loop()).