Using ATOMIC_BLOCK

Hello, I use interrupts to read state of sensors (switches) connected to ports.
In main loop I use ATOMIC_BLOCK(ATOMIC_RESTORESTATE) to avoid changing some variables by interrupts.
My question is: does it block or queue interrupts? If interrupt occurs while in critical section of ATOMIC_BLOCK(ATOMIC_RESTORESTATE) will it be ignored or will it be handled right after exiting critical section?

Regards
Tomek

ATOMIC_BLOCK is only supported on AVR chips. It basically turns off interrupts and then restores them so it will delay any interrupt that occurs during that part of the code

If interrupts are turned off then I would expect them to be ignored rather than delayed

1 Like

It depends what you mean. If an interrupt happens when interrupts are disabled, the corresponding ISR not be executed until interrupts are enabled.

You can miss multiple interrupts when they are off, but you can also count on catching one. If it happened.

a7

It might be better to express what happens as

"When interrupts are disabled, should one or more interrupt events occur then the most recent event will be regarded as pending until interrupts are reenabled at which point it will be acted upon"

Of course, that means that a different strategy is required if you really want to ignore all interrupt events until you decide to act on them

If we talk about AVR, the each interrupt has a flag so the "queue" is just 1 interrupt of each kind. It means that the appropriate ISR will be executed just once at one or more occurrence of specific interrupt during the period with disabled interrupts.

However, there is a second type of interrupts which are triggered as long as the interrupt condition is present - external pin level change interrupt.

ATOMIC_BLOCK is C/C++ concept to disable/enable interrupts. ATOMIC_RESTORESTATE means
that the interrupts will be enabled by restoring previously saved value of the status register. In this case, the next ISR of any pending interrupt can be started immediately after this statement, In contrast of sei instruction where the next instruction after sei is executed before any pending interrupt.

interrupts aren't the best approach for reading switches which bounce resulting in several interrupt events

interrupts are intended to capture short lived events (pulse) and process events immediately. interrupts should do as little as necessary

using byte size variables can avoid the need for critical sections

I use electronic switches with hysteresis, bouncing is not a problem. Sometimes I need multi-byte variables, so critical section is needed. I just want to know if interrupts are queued (how many) or dropped while occur in critical section.

You've already received an answer to that.

The idea was: make some interrupts from port 17, while program is in critical section between noInerrupts() and interrupts().

volatile int Counter = 0;
int t;

void IRAM_ATTR ISR() { 
  Counter++;
}

void setup() {
  Serial.begin(115200);
  pinMode(17, INPUT_PULLUP);
  attachInterrupt(17, ISR, RISING);
}

void loop() {
  Serial.println("Be ready... Make some interrupts");
  noInterrupts();
  for (t = 0; t < 10; t++)  {
    Serial.println(t);
    delay (1000);
  }
 interrupts();
 Serial.print("Finished, Counter is: "); 
 Serial.println(Counter);
}

I expected three scenarios:

  1. Counter is not incremented, because interrupts took place in critical section and was dropped.
  2. Counter was incremented by 1, because only one interrupt was queued.
  3. Counter was incremented many times, because all interrupts were queued.

Connecting pin 17 to ground, I see Counter is few hundreds (bouncing) - it suggest that interrupts are queued and called after critical section.
But... I removed Interrupts() from code - interrupts should be disabled but I still see that Counter is incremented... How is it possible?
So I still doesnt know if interrupts are dropped or qued, but I see that noInterrupts() doesn't works :slight_smile:

Wrong!
Even there is a possibility to perform nested interrupts, if interrupts are globally disabled the program can wait forever at Serial.println because it uses interrupts, in case the buffer will be full.
Do you think there is something time critical on serial output and delay?

Which processor are you using?

1 Like

Exactly! Good question.

LOLIN D32 module

It doesn't wait - t is printed in loop. Maybe Serial.println enables interrupts?

LOLIN D32, it is ESP32. I'm out.

Some time in the past, the code was modified to allow printing while interrupts were disabled, so that code did not get eternally stuck when the transmit buffer was full and someone (foolishly) tried to print within an ISR. If I recall correctly, the status registers are polled instead of being interrupt driven, at least until there is sufficient room in the transmit buffer to queue whatever is being printed.

What surprises me is that delay() works, since it generally relies upon the millis() count, which requires interrupts. The ESP32 may have saved you there, at least for the ESP8266 yield() is called during delay(), which kicks the code out to the multitasking background processes, which might have interrupts enabled. The fact that the ESP32 is dual-core makes this much more complex.

Delay and print in this program is only to stay in critical section and inform about it. Main question i why Counter is incremented while noInterrupt(). Delay or print must turn interrupts back on.

I'm lost...

volatile int Counter = 0;
long t;
double x;
void IRAM_ATTR ISR() { 
  Counter++;
}

void setup() {
  Serial.begin(115200);
  pinMode(17, INPUT_PULLUP);
  attachInterrupt(17, ISR, RISING);
}

void loop() {
  Serial.println("Be ready...");
  noInterrupts();
  for (t = 0; t < 300000; t++)  {
    x = x+sin(t);
  }
 Serial.print("Finished, Counter is: "); 
 Serial.println(Counter);
}

In this program interrupts are off. When connect pin 17 to ground between "Be ready" and "Finished", Counter is incremented. WHY?

How much is counter incremented? If that is the entire code, the for loop should get completely optimized away by the compiler, since you never actually use x for anything.

You would probably get better answers in an ESP32 specific forum, there are some issues posted on the github about nointerrupts(), and warnings of the hazards of using it in a multi-tasking environment. The function to disable / enable interrupts was not present in some of the older board packages https://github.com/espressif/arduino-esp32/pull/7226