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