[Solved] volatile keyword usage in ISR function in micro-controller programing


Of course you do.
volatile isn’t a prerogative of an ISR, it has a specific definition in the C11 standard:

An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects.
Therefore any expression referring
to such an object shall be evaluated strictly according to the rules of the abstract machine,
as described in 5.1.2.3.

Furthermore, at every sequence point the value last stored in the
object shall agree with that prescribed by the abstract machine, except as modified by the
unknown factors mentioned previously.
What constitutes an access to an object that
has volatile-qualified type is implementation-defined.

So whenever you control flows in a manner that cannot be deduced from the sources (like when an interrupt occurs), the compiler cannot know that a variable may have changed meanwhile.
You have to use volatile to tell it that such variable is subject to change at any moment.


If that’s too abstract,
consider this toy code for the AVR microcontroller:

unsigned char STATUS;

void ISR_SUB()
{
  STATUS = 0x80;
}

void ISR ()
{
  ISR_SUB();
}

int main()
{
  unsigned char i=1;

  while (STATUS & 0x80)
  {
    STATUS |= i;
  }

  return 0;
}

This gets compiled into this assembly code

main:
        lds r24,STATUS             ;r24 = STATUS 
        sbrs r24,7                 ;Skip next inst if bit7 of r27 is set
        rjmp .L4                   ;Jump to the end
.L6:
        ori r24,lo8(1)             ;OR r24 with 1
        sbrc r24,7                 ;Do the test again, break loop if bit7 set
        rjmp .L6                   ;Jump back to the loop

        sts STATUS,r24             ;STATUS = r24
.L4:
        ldi r24,lo8(0)
        ldi r25,hi8(0)
        ret

As you can see, the variable STATUS is read once and updated in the register r24, so the loop will never end!
Now look at what happens when we use volatile

main:
        rjmp .L8
.L6:
        lds r24,STATUS           ;Now status is load in r24 at each iteration ...
        ori r24,lo8(1)           ;... updated and ...
        sts STATUS,r24           ;... stored back
.L8:
        lds r24,STATUS
        sbrc r24,7
        rjmp .L6
        ldi r24,lo8(0)
        ldi r25,hi8(0)
        ret

This time STATUS is read and updated at every iteration, as requested.

Note on synchronisation

Many thanks to @Olaf for pointing out the necessity of this section.

I made no claims above that volatile is a sufficient condition for whatever the OP is trying to implement (there is simply not enough context to made any claim).
The way this answer is to be interpreted is that volatile is a necessary condition (as the simple counter-example above shows).

The code shown, as said, is a toy example meant to show a simple issue that can arise without volatile.
It is not meant to be working code because actually I will not work.

When dealing with concurrent flows of execution synchronisation is mandatory and for C this can be achieved using the stdatomic.h header and functions.

Being this a uC question, stdatomic.h may not be present or no synchronisation may be required (this is rare).
Just to avoid any misunderstanding: if you have stdatomic.h then use it (it will eventually compile to nothing but it made the code portable).

The example above contains a RMW operation (the |=) that is not atomic can thus cancel the update made by the ISR.


So yes, you do need (at least) volatile.

16

solved volatile keyword usage in ISR function in micro-controller programing