Interrupts and Global Variable Access

Hi,

I'm having a problem understanding what is happening when it comes to detecting when an ISR has changed a global variable.

I wrote the little program below and I'm seeing some different behaviors.

Of the four test functions below only number two works as desired.

The others appear to be hanging in different ways.

Any help in understanding what is going on "behind the scenes" would be much appreciated.

I have attached the sketch file.

Thanks much.

Frederick

// ------------------------------
// start of file
// ------------------------------

// ------------------------------------------------------------------------------------------
// Comments:
//
// Tested on an Arduino UNO
//
// Trying to understand when a global variable can be accessed by BOTH an ISR and a program loop.
//
// My need is to have a function invoked from the main loop wait within the function code
// until the ISR has set a global variable to a certain value
//
// In my tests only #2 below works - the others appear to be hang different ways
//
// the ***/fff/=== in the print statements are just to make it easy to identify
// the source when watching the console
// ------------------------------------------------------------------------------------------

// ------------------------------
// defines
// ------------------------------

#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// ------------------------------
// setup
// ------------------------------

void setup() {
 Serial.begin(57600); 
 Serial.println("----------------------------------------"); Serial.println("setup started...");

 // setup timer 1 to generate an interrupt every second
 
 TCCR1A = 0x00; TCCR1B = 0x00; TCCR1C = 0x00; TCNT1  = 0; OCR1A  = 15625; TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);

 // enable interrupt
 
 sbi(TIMSK1, OCIE1A);

 Serial.println("setup done");
}

// ------------------------------
// global var so ISR and loop can access
// ------------------------------

int counter;

// ------------------------------
// timer compare interrupt handler
// ------------------------------

ISR (TIMER1_COMPA_vect) {
 if (counter > 0) { counter--;  Serial.print("=== compare interrupt: "); Serial.println(counter); }  
}

// ------------------------------
// function_1()
// ------------------------------

void function_1() {
 Serial.println("fff setting counter to 5"); counter = 5;
 do { delay(1); } while (counter > 0);
 Serial.println("fff counter is now 0");
}

// ------------------------------
// function_2()
// ------------------------------

void function_2() {
 if (counter == 0) { Serial.println("fff setting counter to 5"); counter = 5; }
 do { delay(1); } while (counter > 0);
 if (counter == 0) { Serial.println("fff counter is now 0"); }
}

// ------------------------------
// function_3()
// ------------------------------

void function_3() {
 Serial.println("fff setting counter to 5"); counter = 5;
 do { delayMicroseconds(1000); } while (counter > 0);
 Serial.println("fff counter is now 0");
}

// ------------------------------
// function_4()
// ------------------------------

void function_4() {
 if (counter == 0) { Serial.println("fff setting counter to 5"); counter = 5; }
 do { delayMicroseconds(1000); } while (counter > 0);
 if (counter == 0) { Serial.println("fff counter is now 0"); }
}

// ------------------------------
// loop
// ------------------------------

void loop() {
 static char key = '?';
 
 if (key == '?') { key = '0'; Serial.println("*** press 1 or 2 or 3 or 4"); }

 if (Serial.available() > 0) { 
   key = Serial.read(); Serial.print("*** key pressed: "); Serial.println(key);

   switch (key) {
     case '1': {
       function_1();
       break;
     }
     case '2': {
       function_2();
       break;
     }
     case '3': {
       function_3();
       break;
     }
     case '4': {
       function_4();
       break;
     }
     default: {
       Serial.println("*** invalid key pressed");
     }
   }

   key = '?'; // trigger prompt
 }
   
 delay(250);
}

// ------------------------------
// end of file
// ------------------------------

Test_Timer_Interrupts_1.zip (1.17 KB)

Hi,

Haven't studied your code carefully, but a quick glance suggests you should declare "counter" volatile:
https://www.arduino.cc/en/Reference/Volatile
best,
Michael

Here we go again (please edit your post)

type
** **[code]** **

paste your code after that
type
** **[/code]** **
after that

So it will look like

your code here

Hi,

Well those code tags are nifty.

Didn't know about them.

Thanks.

Frederick

ISR (TIMER1_COMPA_vect) {
 if (counter > 0) { counter--;  Serial.print("=== compare interrupt: "); Serial.println(counter); }  
}

You should not be using Serial.print from within an ISR. Set a flag in the isr, and print in loop based on the state of that flag.

What version of the IDE are you using? Somethings have change lately with the serial buffer. Your code does not hang for me with 1.6.9 and I get

setup started...
setup done
*** press 1 or 2 or 3 or 4
*** key pressed: 1
fff setting counter to 5
=== compare interrupt: 4
=== compare interrupt: 3
=== compare interrupt: 2
=== compare interrupt: 1
=== compare interrupt: 0
fff counter is now 0
*** press 1 or 2 or 3 or 4
*** key pressed: 2
fff setting counter to 5
=== compare interrupt: 4
=== compare interrupt: 3
=== compare interrupt: 2
=== compare interrupt: 1
=== compare interrupt: 0
fff counter is now 0
*** press 1 or 2 or 3 or 4
*** key pressed: 3
fff setting counter to 5
=== compare interrupt: 4
=== compare interrupt: 3
=== compare interrupt: 2
=== compare interrupt: 1
=== compare interrupt: 0
fff counter is now 0
*** press 1 or 2 or 3 or 4
*** key pressed: 4
fff setting counter to 5
=== compare interrupt: 4
=== compare interrupt: 3
=== compare interrupt: 2
=== compare interrupt: 1
=== compare interrupt: 0
fff counter is now 0
*** press 1 or 2 or 3 or 4

...and of course counter and the flag MUST be declared volatile.

Hi,

Yes that was the problem.

Complete slipped my mind.

Thanks very much.

Frederick