Interrupts - different behavior on Due vs Uno (bug?)

Hi All, first post having recently got a Due.

I've noticed a difference in the behaviour of interrupts on a Due, after comparison to an Uno (well actually a Duemilanove).

When I attach an interrupt to FALLING on a digital pin, and drive the pin as an OUTPUT between HIGH and LOW, I would expect the interrupt to trigger each time the output pin is set low. This happens on the Uno, but NOT on the Due, where the interrupt only seems to fire once.

Note that I've tested this both with and without a load attached to the output pin (an LED).

Simple test case posted below.

** UPDATE**
After a fruitless search through the SAM 3x datasheet, I came to the conclusion this was something specific in the arduino library. On a bit of a hunch, I replaced digitalWrite with digitalWriteDirect (thanks to Stimmer):

inline void digitalWriteDirect(int pin, boolean val){
  if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
  else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

And the counter now increases on the Due, just like it does in the Uno!

Does digitalWrite disable the interrupt, which is then missed? Why isn't it missed on the Uno? And surely digitalWrite shouldn't cause interrupts to be missed completely - just postpone them until it finishes?

Anyone have any ideas? Is this a bug in the Due core library?

Original test case below:

// "Self-driven" Interrupt comparison between Uno & Due
// Expected output (obtained on Uno)
// Pin high, count=0
// Pin low, count=1
// Pin high, count=1
// Pin low, count=2
// BUT...
// On due, count remains 1

int pin = 3;    // INT 1 on UNO
volatile int i = 0;

void test() { i++; }

void setup() {            
  Serial.begin(9600);   
  pinMode(pin, OUTPUT);
//  attachInterrupt(1, test, FALLING);    // Uno
  attachInterrupt(pin, test, FALLING);    // Due
}

void loop() {
  digitalWrite(pin, HIGH);
  Serial.print("Pin high, count="); Serial.println(i);
  delay(500);
  digitalWrite(pin, LOW);
  Serial.print("Pin low, count="); Serial.println(i);
  delay(500);
}

A follow up to the above (and another possible bug report?) -

Switching the pinMode() on a Due seems to detach the interrupts set up on the pin. You need to call attachInterrupt() again after changing e.g. from input to output mode.

Again this is different behaviour from the Uno, where there is no need to do this.

By modifying the loop in the code in my previous post (with the digitalWriteDirect patch), you can see that the count going up until three seconds elapse (at which point the pinMode is toggled) and it stops increasing.

void loop() {
  static int secs = 3;
  digitalWriteDirect(pin, HIGH);
  Serial.print("Pin high, count=");
  Serial.println(i);
  delay(500);
  digitalWriteDirect(pin, LOW);
  Serial.print("Pin low, count=");
  Serial.println(i);
  delay(500);
  secs--;
  if (secs == 0) {
      pinMode(pin, INPUT);
      pinMode(pin, OUTPUT);
  }

These differences with the Due break some existing code which should otherwise work. e.g. the PS2KeyboardExt2 library switches the pinMode to send commands to the keyboard to switch on the caps lock light. I've now got this library work on the Due, but its been a bit painful to get to this point, given the above.

I will release the code when I've got it a bit tidied up and tested with a couple of keyboards, if anybody is interested.

Hi Robin,

the arm doesn't clear the interrupt status register automatically (in contrast to the avr). You have to read it manually to get it cleared.

For example:

  unsigned long sreg = REG_PIOD_ISR;

Of course, you have to read the register according to the pin you are using ...

Regards
Philipp

Thanks Philipp, will test this out later today.

Is this a bug in the current Arduino 1.5 library (I appreciate it's still officially a beta) and if so, should I report it since the idea of using the arduino library is to abstract away platform differences like this? At the very least, it would save a lot of wasted time if added to the digitalWrite/pinMode/attachInterrupt documentation.

regards,

Robin

Hi Robin,

this behaviour is specific for the arm (in comparison to the avr), and is also described in the datasheet. Since it's the only option to get the detailed interrupt reason (eg. which pin from PIOA has triggered the int), it's a very usefull info. So it should be kept the way it is ...

Philipp

Philipp,

Perhaps you (or someone else?) could explain why reading the interrupt status register is a solution to the two examples I posted.

I can see the register being read in the arduino core interrupt code, which is then used to select the appropriate user callback to call-

So, unless I'm misunderstanding, this status register has already been read by the time my code executes.

Robin

Perhaps not related, but, I have a current problem with FioV3 boards.
When I call
Serial1.begin()
the falling interrupt handler attached to an input pin stop being run.

The same code works just fine on Uno
(without the Serial1 ofcourse)

Have not had the time do a detailed debug, but seems odd it is also problem with falling interrupt