Interrupts, place in code, critical blocks

Hello everyone! That's my first post so please be forgiving.

So basically my quesiton is about interrupts. Yes, I know that interrupts stop the main code, execute ISR, go back to where it stopped and execute the rest of the code. Yes, I know that if I want to NOT execute the rest of the code, I should set the flag inside ISR. But what if I'm the most unlucky person in the world and the code looks like this

blah
blah
if (flag){
doSomething()
}

So imagine a situation where flag is set to TRUE mostly and interrupt changes it to FALSE, so the rest of the code (or even the smallest part of it) doesn't execute. What if I'm the aforementioned unlucky guy and interrupt EXACTLY between if and function doSomething()? The flag will be set to TRUE, then to FALSE but it's already past the verification.

One can say "Create smaller blocks". But that's not the solution, I can still be unlucky and in code like this

if (flag) digitalWrite(ledPin, HIGH);

where in interrupt function I set it to LOW, set flag to FALSE and stop executing the rest of the code, but was unlucky enough to hit an interrupt EXACTLY between verification and function.

Also what if the part of the code after interrupt is suuuper long and I don't want to execute it at all, because why? I could create more super small blocks with ifs, but that would make 100 lines code a 1000 lines code.

My apologies if it sounds confusing, I don't really know how to explain it better. Thanks in advance!

Not fully clear to me what are you asking. If you afraid about ISR occurrence in important part of code which should not be interrupted, it is easy. Just disable interrupts before and enable it after this part of code with cli()/sei() resp. noInterrupts()/interrupts() functions.

Daide: Hello everyone! That's my first post so please be forgiving.

So basically my quesiton is about interrupts. Yes, I know that interrupts stop the main code, execute ISR, go back to where it stopped and execute the rest of the code. Yes, I know that if I want to NOT execute the rest of the code, I should set the flag inside ISR. But what if I'm the most unlucky person in the world and the code looks like this

blah
blah
if (flag){
doSomething()
}

So imagine a situation where flag is set to TRUE mostly and interrupt changes it to FALSE, so the rest of the code (or even the smallest part of it) doesn't execute. What if I'm the aforementioned unlucky guy and interrupt EXACTLY between if and function doSomething()? The flag will be set to TRUE, then to FALSE but it's already past the verification.

One can say "Create smaller blocks". But that's not the solution, I can still be unlucky and in code like this

if (flag) digitalWrite(ledPin, HIGH);

where in interrupt function I set it to LOW, set flag to FALSE and stop executing the rest of the code, but was unlucky enough to hit an interrupt EXACTLY between verification and function.

Tough, you should have interrupted 100 ns earlier! The code is checking the flag when the variable is read, after then its not checking it again. If you want to check more often, check more often.

Also what if the part of the code after interrupt is suuuper long and I don't want to execute it at all, because why? I could create more super small blocks with ifs, but that would make 100 lines code a 1000 lines code.

Maybe, but if that's the behaviour you want, you have to code it...

My apologies if it sounds confusing, I don't really know how to explain it better. Thanks in advance!

I think what you are imagining is that there should be some way for the ISR to take over completely. For that you need threads/coroutines and a scheduler or similar.

I know that if I want to NOT execute the rest of the code, I should set the flag inside ISR.

That won't work unless you test the flag before each program statement which is, of course, not realistic.

The first question should be whether you need to use an interrupt in the first place. Can you please provide a real world example of your use case that requires an interrupt ?

Daide: What if I'm the aforementioned unlucky guy and interrupt EXACTLY between if and function doSomething()? The flag will be set to TRUE, then to FALSE but it's already past the verification.

Won't it get picked up in the next iteration of loop() ?

In your code snippet you do not indicate when the flag gets set back to false.

...R

@Budvar10

It's not about important code to not be interrupted. It's more about setting flag to FALSE in ISR, then executing code PAST the point that flag is checked (because I pressed button in wrong moment) with flag being seen as TRUE even though it's not.

@MarkT & UKHeliBob & Robin2

Let me post the code

int val;
int i;
int timeInSeconds = 5;
volatile boolean turnedOn = false;
#define LED 3
#define red 7
#define green 6
void setup() {
 Serial.begin(9600);
 pinMode(LED, OUTPUT);
 pinMode(red, OUTPUT);
 pinMode(green, OUTPUT);
 digitalWrite(LED, LOW);
 pinMode(5, INPUT);
 pinMode(2, INPUT_PULLUP);
 attachInterrupt(0, changeMode, RISING);
}

void loop() {
 digitalWrite(green, LOW); 
 digitalWrite(red, HIGH);
 while (turnedOn) {
   digitalWrite(red, LOW);
   digitalWrite(green, HIGH);
   val = digitalRead(5);
   //val = map(val, 0, 1023, 0, 5);
   Serial.println(val);
   if (val > 0) {
     digitalWrite(3, HIGH);
     i = timeInSeconds * 1000;
   }
   if (i > 0) i -= 20;
   if (i == 0) digitalWrite(LED, LOW);
   delay(20);
 }


}
/*void changeMode() {
 turnedOn = !turnedOn;
 digitalWrite(LED, LOW);
}*/
void changeMode() // mikalhart's code
{
 static unsigned long last_interrupt_time = 0;
 unsigned long interrupt_time = millis();
 // If interrupts come faster than 200ms, assume it's a bounce and ignore
 if (interrupt_time - last_interrupt_time > 200)
 {
   turnedOn = !turnedOn;
   digitalWrite(LED, LOW);
 }
 last_interrupt_time = interrupt_time;
}

Pardon me for amateur code.

So basically what happens and what I need is - the while loop goes on when turnedOn state is set as TRUE, that's when PIR sensor scans the area and lights up an LED for fixed amount of time. Now when I press button it should stop right there and never reenter the loop again. I know I can do it without an interrupt, but that's not the point of my post. I'm more interested about interrupts themselves, not the particular code. Like how do you ensure that if something is interrupted it won't happen right after interrupt checking statement (like if (flag) doSomething), but as it is intended - right before. Is it only matter of rebuilding code to not use interrupts this way?

Edit:

Actually I think you Budvar10 were right. Disabling interrupts before checking sequence and enabling it after would be a great solution, but what happens to the interrupt that happened when it was disabled? Does it get queued? Or is completely omitted? If it gets queued then my problem is basically solved.

Daide: It's not about important code to not be interrupted. It's more about setting flag to FALSE in ISR, then executing code PAST the point that flag is checked (because I pressed button in wrong moment) with flag being seen as TRUE even though it's not.

You have not responded to my Reply #4

...R

Does it get queued?

Not as such. If the interrupts are disabled then it gets ignored until they are enabled again for the edge triggered ones.

However the whole problem is in your head, the code does what the code does and if it matters that the timing of the interrupt is vital to one machine cycle that means you are writing the rest of the code incorrectly and it needs to be written as a state machine.

So basically what happens and what I need is - the while loop goes on when turnedOn state is set as TRUE, that's when PIR sensor scans the area and lights up an LED for fixed amount of time. Now when I press button it should stop right there and never reenter the loop again. I know I can do it without an interrupt, but that's not the point of my post. I'm more interested about interrupts themselves, not the particular code. Like how do you ensure that if something is interrupted it won't happen right after interrupt checking statement (like if (flag) doSomething), but as it is intended - right before. Is it only matter of rebuilding code to not use interrupts this way?

There is never something like immediate; everything takes time ;) Regardless wether you use interrupts or not.

Your while-loop takes about 100 us to execute (excluding the delay). If you take out the printing of val, it's 24 us.

How would you have written the code without interrupts? I'm quite sure you would have encountered exactly the same problem.

@Robin2

My apologies, I missed that. Yes it will if I rewrite the code to some better version with different mindset.

@Grumpy_Mike

I understand, I think I know how to rewrite it now.

@sterretje

I also think I’d encounter exactly the same problem and that’s why I thought about using interrupts, but everyone here is actually 100% right about the code just being bad, I’ll just rewrite it:)

Thanks everyone for replies and have a great Sunday!

Daide:
… but what happens to the interrupt that happened when it was disabled? Does it get queued? Or is completely omitted?

On the AVR exactly, each interrupt type has its own flag. During the time when the interrupts are disabled and some interrupt occurs the flag will keep this info and ISR will be executes imediately after enabling. Of course, if more than one interrupts of some sort occur in such situation the ISR will be executed just once. The ISRs are executed in order of their priority.