Accessing variables in ISR

Hello,

Just wondering how do access variables "safely", that are changing inside the ISR? (My ISR is on a 1 ms period)
Is it just declaring the variable volatile enough? what would happen if I am in the middle of a read operation that accesses the variable inside the ISR, and then the ISR kicks in, pausing my read operation and... well giving false data?
Is there a way to safely access variables inside ISR?

Thank you,

You also need to turn interrupts off breifly to take a local copy of the variable inside loop, example and background here -

Duane B

what would happen if I am in the middle of a read operation that accesses the variable inside the ISR, and then the ISR kicks in, pausing my read operation and... well giving false data?

There's only a problem if the ISR modifies the variable being read.

I don't think you need to worry about single-byte variables (although they need to be volatile still) because access to them is atomic. Multiple bytes (eg. int) or related variables would need to be protected by putting noInterrupts() and interrupts() around them.

I would also be cautious if the main code modified a variable in a non-atomic way (eg. changing more than one bit).

Thank you for all your input.
The link that DuaneB posted was very helpful and explained it all. Thanks for that.

Thank you Nick for your comment. However in the project that I am doing, the timing of the ISR is very critical and I cannot stop it no matter what. Is there any way around that. Is there a way to let the ISR do its job (read sensors to volatile variables (ints)) and these variables can be read in the main loop? ( I am not writing to them, just read them in main loop)

Thank you,

I don't know how much of a delay calling the ISR would be acceptable to you. Bearing in mind that the processor is running quite fast and you may only need to make a couple of variable assignments, the delay would be relatively short.

If you can't afford to delay the ISR at all and need the ISR to make non-atomic writes which can be read by the main code, you can implement either a circular queue or swinging buffers.

FardinB:
Thank you Nick for your comment. However in the project that I am doing, the timing of the ISR is very critical and I cannot stop it no matter what. Is there any way around that. Is there a way to let the ISR do its job (read sensors to volatile variables (ints)) and these variables can be read in the main loop? ( I am not writing to them, just read them in main loop)

No-one said anything about stopping the ISR. If you have multi-byte variables, then in the main loop you need to protect them by putting noInterrupts() in front of attempts to access them.

If timing is critical, put all the relevant variables into a struct, and then make a quick copy of it. eg.

myStructType copyOfVariables;

noInterrupts ();
memcpy (&copyOfVariables, &isrVariables, sizeof copyOfVariables);
interrupts ();

Bear in mind the ISR won't fire immediately, necessarily, anyway. For example, the millis() interrupt may be processing its stuff.

Yes, there are a few ways to do what you want:

  1. Double read: this is the most widely used. Essentially, it reads the variable until two consecutive reads yield the same value.
  2. Write flag: your isr will set a flag just before it writes to that variable. Your read routine will test that flag before reading the variable and resets the flag after a read.

The above doesn't alter the timing of your isr at all.

After it writes the variable, I would say.

That's a good idea, and one I've used before.

The ISR checks first if the flag is set, and if so does nothing. If not set, it changes as many variables as it wants and then sets the flag last. eg.

volatile byte updatedFlag = false;
volatile int var1;
volatile int var2;

ISR (foo)
  {
  // do nothing if loop hasn't picked up previous value
  if (updatedFlag)
    return; 

  var1 = whatever;
  var2 = whatever;
 
  updatedFlag = true;
  } // end of ISR

void loop ()
  {
  int copyVar1;
  int copyVar2;

  if (updatedFlag)
    {
    copyVar1 = var1;
    copyVar2 = var2;
    updatedFlag = false;  // let ISR know it can update variables again
    }

  // work with copy of variables here ...

  }   // end of loop