How to force interrupt to restart main loop instead of resuming? (timing Fissue)

For the last two days i wrote a program that in basic terms generates a fairly accurate user adjustable pulse signal (both frequency and duty cycle adjustable). It basically uses the micros() function to keep track of time in order to pull low or high the 4 digital output channels.

These 4 channels need to have a phase difference of 90 degrees (think a 4cyl engine) always. In order for the user to change settings an ISR is implemented which returns a flag to the main loop to re-initialise the program. This flag is defined as a boolean 'set4'. When it is false a 'while' statement in the main loop will run the outputs. When it is true an 'if' statement will perform the necessary recalculations and reset the flag so that the 'while' statement will resume.

The program works perfectly with the initial values. Phase is perfect. However when the ISR is called and comes back to the main loop, from how i understand it resumes the program in the 'while' statement from where was originally interrupted, until it finishes and re-checks the flag 'set4' to see it is now true and it should stop. Then, even though the 'if' statement afterwards resets and re-calculates all the necessary variables the phase between these 4 output channels is lost. Tested manually i see depending on which time the ISR is called it will give different results, usually having all 4 output channels synchronised together! This happens even though i might don't change any values (thus the 'if' routine resets the variables to exactly the same ones when you first power up the arduino!). However, if i comment out this routine and just leave the line which resets the flag 'set4' the program will continue normally like nothing never happened!

I'm pretty sure that this is somehow caused because of the micros() timer because the loop will be resumed from where the ISR was called. I've tried to do it differently by checking and disabling for interrupts using cli() and sei() but i couldn't get it to work because it will just freeze when the arguments for cli() are true. The only solution that i can think of (i've tried everything, spend the whole day searching and trying out stuff) is to force the ISR to resume from the start of the loop so that the program may initialize properly. Another solution that comes to mind is to maybe reset the micros() timer somehow..but this would mess up the ISR i believe.

To help you visualise what is going on here's a snip of my code (please don't mind the 'Millis" name in the micros variables and any missing curly brackets since it is not pure copy-paste :p):

void loop()
    {
        while(!set4)
        {
        currentMillis = micros();
        currentMillis2 = micros();
        currentMillis3 = micros();
        currentMillis4 = micros();

          if(currentMillis - previousMillis >= interval) {
            // save the last time you blinked the LED 
            previousMillis = currentMillis;   

            // if the LED is off turn it on and vice-versa:
            if (ledState == LOW)
            {
              interval = ONTIME;
              ledState = HIGH;
            }
            else
            {
             interval = OFFTIME; 
             ledState = LOW;
            }

            // set the LED with the ledState of the variable:
            digitalWrite(ledPin, ledState);
            }
    .
    .
    //similar code for the other 3 output channels
    .
    .
    }
    if (set4){
    //recalculation routine - exactly the same as when declaring the variables initially  
      currentMillis = 0;
      currentMillis2 = 0;
      currentMillis3 = 0;
      currentMillis4 = 0;

      //Output states of the output channels, forced low seperately when the ISR is called (without messing with the 'ledState' variables)
      ledState = LOW;
      ledState2 = LOW;
      ledState3 = LOW;
      ledState4 = LOW;

      previousMillis = 0;
      previousMillis2 = 0;
      previousMillis3 = 0;
      previousMillis4 = 0;

     //ONTIME is the HIGH time interval of the pulse wave (i.e. dwell time), OFFTIME is the LOW time interval
     //Note the calculated phase/timing offset at each channel

      interval = ONTIME+OFFTIME;
      interval2 = interval+interval/4;
      interval3 = interval+interval/2;
      interval4 = interval+interval*3/4;

      set4=false;

    }
}

Any idea what is going wrong? Kind regards, Ken

Please post (or attach) all your code.

http://snippets-r-us.com/

The whole point of an interrupt, is to resume the interrupted program at the same place it was interrupted from. Otherwise, the logic of that program would be ruined.

If you want to abort that process and start over, you can get the interrupt to set a flag to do so. It looks like you are trying to do this.

Key things.

(1) the variable you are using has to be declared "volatile".

(2) if you have a loop while( set4 ) { do a lot of stuff } then it only checks set4 each time it comes to the beginning or end of the while loop. It doesn't check it "continuously".

(3) There is nothing stopping you from writing code like this:

while ( set4 )
{
     // do something
     if ( !set4) break ;

     //  do something else
     if ( !set4 ) break ;
}

(4) If this while loop is not the only thing in your loop( ) function, and you want to go immediately to the end of your loop function, because the state of set4 has changed, you could use goto. Or you could use "return" which will exit from loop( ) and it will start again.

  while(!set4)
        {
        ...
        }

  if (set4){

Why have this “if”? What else can be the reason you are not doing the “while”?

currentMillis = micros();
 currentMillis2 = micros();
 currentMillis3 = micros();
 currentMillis4 = micros();

This runs the risk that they don't all have the same value. Better to read micros() once and copy it to the other variables

currentMillis = micros();
 currentMillis2 = currentMillis;
 currentMillis3 = currentMillis;
 currentMillis4 = currentMillis;

And I wonder why it is necessary to save the same number 4 times? Isn't once sufficient?

...R

Well, they might change, as you said. ;)

currentMillis = micros();

Ah ...

currentMicros = micros();

[quote author=Nick Gammon date=1430296309 link=msg=2209488]

currentMillis = micros();

Ah ... [/quote] Well spotted.

...R

Thanks for the replies guys! The whole problem was reset variable problem that was answered in another forum:

The problem is here:

  previousMillis = 0;
  previousMillis2 = 0;
  previousMillis3 = 0;
  previousMillis4 = 0;

All if statement will be true on the next loop.

Try with:

  previousMillis = micros();
  previousMillis2 = micros();
  previousMillis3 = micros();
  previousMillis4 = micros();

That's what you get for trying to work out problems at 5am..

michinyon: The whole point of an interrupt, is to resume the interrupted program at the same place it was interrupted from. Otherwise, the logic of that program would be ruined.

If you want to abort that process and start over, you can get the interrupt to set a flag to do so. It looks like you are trying to do this.

Key things.

(1) the variable you are using has to be declared "volatile".

(2) if you have a loop while( set4 ) { do a lot of stuff } then it only checks set4 each time it comes to the beginning or end of the while loop. It doesn't check it "continuously".

(3) There is nothing stopping you from writing code like this:

while ( set4 )
{
     // do something
     if ( !set4) break ;

    //  do something else     if ( !set4 ) break ; }




(4) If this while loop is not the only thing in your loop( ) function, and you want to go immediately to the end of your loop function, because the state of set4 has changed, you could use goto. Or you could use "return" which will exit from loop( ) and it will start again.

My program runs and the interrupt is used to fully stop the main loop so that the user can adjust the settings in an oled screen. After all flags are set it will resume the normal program with the new settings. It is important that i don't have a stuck or excessive dwell time. That's why the interrupt should first stop the main loop completely.

1) I use volatile variables for the interrupt parts of the programs 2)+3) Yes i could do it this way but i was stuck on this problem so left it as was in order to debug and not introduce more problems - If it works why change it? 4) i will look into that, i'm a very novice programmer

[quote author=Nick Gammon date=1430284487 link=msg=2209328]

  while(!set4)
        {
        ...
        }

  if (set4){

Why have this "if"? What else can be the reason you are not doing the "while"? [/quote]

Not any particular reason, was like when i was trying to find out the issue above and i didn't bother to change it :p

Robin2: currentMillis = micros(); currentMillis2 = micros(); currentMillis3 = micros(); currentMillis4 = micros();

This runs the risk that they don't all have the same value. Better to read micros() once and copy it to the other variables

currentMillis = micros();
 currentMillis2 = currentMillis;
 currentMillis3 = currentMillis;
 currentMillis4 = currentMillis;

And I wonder why it is necessary to save the same number 4 times? Isn't once sufficient?

...R

I did try both ways, or even moving the position of them but i need to confirm with an oscilloscope to check it there are any timing issues. Actually i did have it like that before but i had another problem and i had to change it back to this in order to debug that....

[quote author=Nick Gammon date=1430296309 link=msg=2209488]

currentMillis = micros();

Ah ...

currentMicros = micros();

[/quote]

Yes i know..initially the program was using millis() but was too small for my needs so i changed it to micros() but was too late when i was writing this to clean it up..

That's why the interrupt should first stop the main loop completely.

You are completely misunderstanding what an interrupt is meant to be used for.

Think of serial data arriving. That has to be handled NOW, or the data will be lost. So, the interrupt stops the main code, collects the serial data, stashes it, and resumes the main code, right where it left off.

Think of the timer ticking. The value that millis() returns needs to be updated. So, the interrupt happens, the main code is suspended, the value is updated, and the main code resumes, right where it left off.

You need to determine some other approach. An interrupt is NOT the right answer.

PaulS:
You are completely misunderstanding what an interrupt is meant to be used for.

Think of serial data arriving. That has to be handled NOW, or the data will be lost. So, the interrupt stops the main code, collects the serial data, stashes it, and resumes the main code, right where it left off.

Think of the timer ticking. The value that millis() returns needs to be updated. So, the interrupt happens, the main code is suspended, the value is updated, and the main code resumes, right where it left off.

You need to determine some other approach. An interrupt is NOT the right answer.

No i understand that, but look at the whole thing and tell me if you are happy then:
https://www.youtube.com/watch?v=fdODx-q1_0c

St0RM53: No i understand that, but look at the whole thing and tell me if you are happy then: https://www.youtube.com/watch?v=fdODx-q1_0c

Just to play Debby Downer, I can promise you from experience that looking at spark plugs firing is no way to tell whether they are firing correctly. You have to check the pulses on a scope, there's simply no other way to test it with any degree of confidence.

noisymime: Just to play Debby Downer, I can promise you from experience that looking at spark plugs firing is no way to tell whether they are firing correctly. You have to check the pulses on a scope, there's simply no other way to test it with any degree of confidence.

I know..i verified this with a 7MHz oscilloscope i have (4 channel), but lowering the frequency you can definitely check it with the eye (at least the leds you see on the breadboard). I was rushing to make this video and forgot to connect the oscilloscope. Either way for the frequencies i'm simulating an the simplicity of my main loop, millis() on a 16Mhz arduino gives a really accurate and precise result. We are talking here 1ms is the lowest sub-division i i need :p