Confusion about interrupts

Hello,

I was about to try, if interrupt triggers get stored in a queue when another isr is already running, but got some confusing results.

const byte led = 8;
volatile byte leden = false;
const byte in0 = 0;
const byte in1 = 1;
const byte in2 = 2;
volatile bool ledin = false;

void setup() 
{
  pinMode(in0, INPUT_PULLUP);
  pinMode(in1, INPUT_PULLUP);
  pinMode(in2, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(in0), isr0, FALLING);
  attachInterrupt(digitalPinToInterrupt(in1), isr1, FALLING);
  attachInterrupt(digitalPinToInterrupt(in2), isr2, FALLING);
}

void loop() 
{
}

void isr0()
{
  volatile byte count = 0;
  digitalWrite(led, HIGH);
  while(count < 200)
  {
    count++;
    delayMicroseconds(16383);
  }
}

void isr1()
{
  ledin =!ledin;
  digitalWrite(LED_BUILTIN, ledin); 
}

void isr2()
{
    digitalWrite(led, LOW);
}

As show in the sketch, I went in the ISRX, switched on a connected LED and waited 200*16838 µs, before ending the ISRX. In the meantime I triggered the other two interrupts.
Further i changed the ISR-Numbers to analyze the behavior.

Here the measurements with the used Pins.
LED_on LED_off LED_BUILTIN
1 0 1 2 --> LED_BI changed after 3s, the ED turned of after 7s
2 0 2 1 --> LED_BI changed after 7s, LED only turned off after 7s if interrupt was triggered >3s after LED was switched on
3 1 2 0 --> LED_BI changed after 3s, the LED only turned off, if the interrupt was triggered after LED_BI changed, otherwise never turned off
4 1 0 2 --> LED_BI changed after 3s, the LED only turned off, if the interrupt was triggered after LED_BI changed, otherwise never turned off
5 2 0 1 --> LED_BI changed and LED switched off, both after 7s
6 2 1 0 --> LED_BI changed and LED switched off, both after 7s

The Arduino Leoanardo I used, has the following Interupt-Pin configuration
Int 0 1 2 3 4
Pin 3 2 0 1 7

Why the behavior depends on the used Interrupts und therefor sometimes works and sometimes doesnt?
And why the delay is about 7s and not 200*16383s (~3s)?

Hope you can help me :slight_smile:

It's questionable whether or not delayMicroseconds() works as expected inside an ISR. Also, it's unnecessary for the variable 'count' to be volatile inside the ISR as it's local to that function.

Correct, count doesnt need to be volatile, but this wont explain that strange behavior.

As I understood, delayMicroseconds() doesnt use interrupts, so it should work fine in an isr. Do you have a source for your statement or is it out of experience?

The best way to confirm that understanding would be to look at the source code that implements delayMicroseconds() for the particular type of Arduino board you're using ... I'm just not sure.

Instruction overhead from the surrounding while loop would be my guess. Also note that declaring "count" as volatile will disallow the value from being cached (kept in a register) and instead it is always written to RAM which may cause poor performance.

byte count = 0;
digitalWrite(led, HIGH);
while(count++ < 200) delayMicroseconds(16383);
digitalWrite(led, LOW);

You should perform a verification like:

void isr0()
{
  volatile byte count = 0;
  digitalWrite(led, HIGH);
  while(count < 200)
  {
    count++;
    delayMicroseconds(16383);
  }
  digitalWrite(led, LOW);
}

before testing the actions of the other ISRs.

Leonardo pins 0 and 1 are used for unavailable during USB serial programming communications.
Typically, while (!Serial); is added in Setup() to wait for the USB to Serial chip to report that it's ready.

No, they are not. On 32U4 based boards Serial is handled by the internal USB functionality and RX and TX are Serial1.

I know ... I'm referring to the USB serial converter IC (ATMEGA32U4-XUM). When its in operation, pins 0 and 1 on the ATMEGA32U4-XUAU (Leonardo) are not available.

I noticed the strange pin choice. Definitely better to use different ones.

No, they aren't. Look at the schematic.

I know (I have a Leonardo) ... its just poor wording (fixed).
If pins 0 and 1 are available at the beginning of Setup(), that's news to me.

You can use them for whatever you want (digital I/O or Serial1) at the start of setup(). You are thinking of the USB port (Serial, not Serial1) that is not ready immediately. That is NOT on pins 0 and 1 like on an UNO or Nano.

If using the serial monitor (with a Leonardo only), you need to wait for it to connect.

Wouldn't the same issue occur (additional delay needed) before using interrupts on pins 0 and 1 at the beginning of setup?

Referring to the schematic, there's a direct connection of D0/RX and D1/TX between the 2 MCUs:

Maybe try another way to get that 3ish second delay? Trying to game the delayMicroseconds inside an ISR (that's why you chose 16383 and figured you'd just do that 200 times, right?) is likely not going to work out.

According to @nickgammon

Don't do delays in an ISR. Don't argue with this.
During an ISR interrupts are turned off. Thus the calculations for millis (and indeed micros) will be seriously out. You can't just hang around for a half a second in an ISR. Well, you can. But don't complain when everything else stops working.

on this page
https://forum.arduino.cc/t/using-delay-for-polling-and-delaymicroseconds-for-interrupts/285007/4

to which he provides some great info here

http://www.gammon.com.au/interrupts

Good luck.

I understood this to be a "sandbox experiment". At least I hope so. So the normal advisements don't apply. 'delayMicroseconds()' doesn't seem to use interrupts. So it should work in an ISR. It's really a simple code:

// busy wait
	__asm__ __volatile__ (
		"1: sbiw %0,1" "\n\t" // 2 cycles
		"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
	);

that only makes some small adustments to 'us' before running this loop.

I can see the cause of the confusion. The schematic shows two different versions of the same chip:

https://docs.arduino.cc/static/05f737fc664415f0e33201f8c2078672/schematics.pdf

I believe only one of the two packages (XUAU or XUMU) is populated on any one board. They have the same number of pins and roughly the same size so it is possible to design solder pads to fit either. They probably pick whichever is available.

1 Like

Looks like that topic is about another problem.

You only have to wait if you don't want to run the risk to miss the first messages that are printed by the Leonardo.

I'd like to run this quick test using my Saleae Logic Pro 8, but won't have access to my hardware for 3 or 4 days:

void setup() {
DDRD |= ~0X0F; //D3-D0 are outputs
PORTD |= 0X0F;  //D3-D0 high 
PORTD &= ~0X0F; //D3-D0 low
PORTD |= 0X0F;  //D3-D0 high 
PORTD &= ~0X0F; //D3-D0 low
PORTD |= 0X0F;  //D3-D0 high 
PORTD &= ~0X0F; //D3-D0 low
PORTD |= 0X0F;  //D3-D0 high 
PORTD &= ~0X0F; //D3-D0 low
PORTD |= 0X0F;  //D3-D0 high 
PORTD &= ~0X0F; //D3-D0 low
DDRD &= ~0X0F; //D3-D0 are intputs
PORTD |= 0X0F;  //D3-D0 input pullups enabled 
}

void loop() {
}

Should expect 5 pulses high/low, then resting high on pins 0,1,2 and 3.

Simulating this code on a nano (wokwi), only RX (D1) works as expected ...

If the first line turns off interrupts using cli();


Only D0 and D2 work as expected. Hmm, something wrong with my test ... need to investigate on real hardware with a Leonardo.

Did you mean to write this?
DDRD |= 0X0F; //D3-D0 are outputs
By inverting the 0x0F to 0xF0 you are setting D4-D7 to outputs.

1 Like