1

I'm noticing a crash when testing out some interrupt code for a project I'm working on.

I've returned to the example provided with the library PciManager and I'm still experiencing the same issue. There doesn't appear to be any patten to when the crash appears, and on occasion I've been playing for several minutes and not seen a crash. NOTE I had to add a file (Task.h) from the developers other project (SoftTimer) to get it to compile.

here is the code:

#include <PciManager.h>
#include <PciListenerImp.h>

#define INPUT_PIN 3

PciListenerImp listener(INPUT_PIN, onPinChange);

int i = 0;

void setup() {
  Serial.begin(9600);
  PciManager.registerListener(INPUT_PIN, &listener);
  Serial.println("Ready.");

}

void onPinChange(byte changeKind) {
  i++;
  Serial.print("pci : ");
  Serial.print(i);
  Serial.print(" : ");
  Serial.println(changeKind);
}

void loop() { }

I have a wire connected to pin 3 and 3.3v which I'm pulling out and then connecting again. Some times when disconnected it continually interrupts, I was expecting this. Sometimes it doesn't which I found strange, the crash appears to happen more in the latter case.

Here is a trace from the serial monitor

Ready.
pci : 1 : 0
pci : 2 : 1
pci : 3 : 0
pci : 4 : 1
pci : 5 : 0
pci : 6 : 1
pci : 7 : 0
pci : 8 : 1
pci : 9 : 0
pci : 10 : 1
pci : 11 : 0
pci : 12 : 1
pci : 13 : 0
pci : 14 : 1
pci : 15 : 0
pci : 16 : 1
pci : 17 : 0
pci : 18 : 1
pci : 19 : 0
pci : 20 : 1
pci :

I've run this on a shrimp and on a freeduino diecimila compatible board both resulting in the same effect.

Anyway down to question; is the issue the way I'm interrupting? i.e. not stopping the floating pin? or is it a problem with the library, or is it a fundamental problem with pin change interrupts.

3 Answers 3

5

Your onPinChange() function is called with interrupts disabled.

For you what it means is this function should:

  • last as short as possible (because NO interrupts are handled during its execution, that includes timers that are used by several Arduino core libraries)
  • not use Serial functions as these functions are slow and sometimes wait on interrupts (that could thus mean wait forever)

You can read further advice about these so-called Interrupt Service Routines (ISR) here and there.

If you want to trace all calls to onPinChange() then you will have to use some kind of circular buffer as a global volatile variable, which will be written into by onPinChange() and read from by loop().

Accessing that buffer from loop() should be protected against concurrent write by ISR.

Here is sample code I have just written (disclaimer, I have not checked that it compiles but I think it should work correctly) that uses such a circular buffer as described above, and ensures all shared variables accesses are protected against concurrent write by ISR:

#include <PciManager.h>
#include <PciListenerImp.h>

// Structures and function to manage circular buffer of changes
struct Change {
    int i;
    byte kind;
};

// We allow max SIZE changes in the buffer; if onPinChange() is called more often
// than loop(), then at one time the buffer will be overwritten.
const int SIZE = 100;
volatile Change changes[SIZE];
volatile int start = 0;
volatile int end = 0;

// Get the next change recorded by onPinChange()
Change* nextChange() {
    static Change change;
    Change* result = 0;
    // Disable interrupts as we are accessing variables modified by onPinChange
    cli();
    if (start != end) {
        change = changes[start];
        start = (start + 1) % SIZE;
        result = &change;
    }
    // Re-enable interrupts before leaving the function
    sei();
    return result;
}

#define INPUT_PIN 3

PciListenerImp listener(INPUT_PIN, onPinChange);

// No change necessary in setup()
void setup() {
    Serial.begin(9600);
    PciManager.registerListener(INPUT_PIN, &listener);
    Serial.println(Ready.);
}

void loop() {
    // Get the next change recorded by onPinChange (if any)
    Change* change = nextChange();
    if (change != 0) {
        // One change occurred since last time, trace it to Serial
        Serial.print("pci : ");
        Serial.print(change->i);
        Serial.print(" : ");
        Serial.println(change->kind);
    }
}

void onPinChange(byte changeKind) {
    // i does not have to be visible outside this function
    static int i = 0;
    i++;
    // Write information to the end of the circular buffer
    Change& change = changes[end];
    change.i = i;
    change.kind = changeKind;
    // Prepare for the next call (write to the next item of the buffer)
    end = (end + 1) % SIZE;
}
3
  • Using Serial.print inside the ISR did cause crashes on my latest project too. By setting the baudrate to a higher value (Serial.begin(115200)), the number of crashes was reduced to almost zero.
    – Gerben
    Commented Aug 9, 2014 at 12:51
  • 3
    @Gerben, yes increasing baud rate will make crashes appear less often, but they still can appear, as 115200 bps means that one character will need ~0.1ms to be transmitted, that's 1600 clock cycles on an UNO, just for one character! Really, I always stay away from Serial in ISR, whatever baud rate I use!
    – jfpoilpret
    Commented Aug 9, 2014 at 13:00
  • Just so o understand properly it's calling onPinChange while it's executing the code in onpinchange Commented Aug 9, 2014 at 13:37
5

I'm going to include this answer even though I think there's a very little chance that it is the problem because I just wasted ten hours on a project before finding it and it's an easy check so it won't take much time either way.

On AVR chips, if you have enabled interrupts and one occurs and you don't have an ISR for it, it will call the default ISR which resets the board. You can test if this is happening by redefining the default ISR by creating a function ISR(BADISR_vect){}; I would put a serial print in there and run it again and see if it's happening. If you don't want to do serial (for obvious reasons) just toggle an LED.

I also agree it's probably a bad idea to be doing this on a floating pin. I'd use the internal pull-up resister to keep it high then connect a wire between that pin and ground to simulate a button.

1
  • I just spent the last 8 hours trying to figure this out. Thanks for preventing me from turning that into 16 hours.
    – Cate
    Commented Jun 21, 2019 at 5:01
3

Note that disconnecting a wire on an input pin is also not going to give you the results you expect. A disconnected input pin's value will float, and you'll get a semi-random mixture of 1s and 0s. You should either connect an external pull-up/pull-down resistor, or activate the internal pull-up resistor when you configure the pin (by setting the pin to INPUT_PULLUP).

I prefer using INPUT_PULLUP, since that saves wiring an external resistor. Use INPUT_PULLUP, then wire the pin through a pushbutton switch to ground.

Then look for a high-to-low transition on the pin to signify a button press.

4
  • Right, floating inputs have undeterministic values that can change all the time; in addition, they draw more energy due to these fast changes.
    – jfpoilpret
    Commented Aug 9, 2014 at 12:55
  • As I said in the question when it bounced round all over the place it didn't crash as much; recording thousands of interrupts when it didn't bounce it was more likely to crash like after 20 in the listing provided. I know I need pull it up or down I'm just confused by the crash. Commented Aug 9, 2014 at 13:19
  • Just realised I added extra debugging in after a crash so probably made my problem worse Commented Aug 9, 2014 at 13:29
  • 3
    Don't use Serial commands in an interrupt service routine, even for debugging. It will hang your device.
    – Duncan C
    Commented Aug 9, 2014 at 23:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.