First, a definition of "volatile" is needed. A volatile variable is one that can change unexpectedly. Consequently, the compiler can make no assumptions about the value of the variable. In particular, the optimizer must be careful to reload the variable every time it's used instead of holding a copy in a register. Examples of volatile variables are:

Non-automatic variables referenced within an interrupt service routine

This is probably the most obvious use of the volatile keyword. A global or static variable used in an interrupt will appear to change unexpectedly at the task level, so the volatile keyword is needed to inform the compiler that this will happen.

But at the interrupt level, the variable may not have to be treated as volatile if a higher level interrupt won't be changing the value. If so, the variable value can be read into an automatic variable, any operations on it performed, and the updated value stored back into the variable before returning from the interrupt. This will reduce code size and execution time in the interrupt service routine.

Variables shared by multiple tasks in a multi-threaded application

This is probably the most misused of all the items here. Developers usually don't think to add the volatile keyword to a global variable because "The variable isn't used in an interrupt so I don't need it here." The result can be pandemonium as the variable gets written and then reset seemingly at random as another task writes a previous value to it.

With variables that are written by only one task and read by multiple others, instead of declaring the variable volatile, it may be better to use a lock semaphore to gain exclusive access until the writing task is finished with it.

Hardware registers in peripherals (for example, status registers)

This is another place either missed by developers or misused by declaring an entire structure of hardware registers as volatile, the latter being the most prevalent (at least to me).

When adding hardware register definitions to an application, each register should be examined. If even one bit can change unexpectedly, the entire register must be declared volatile. The register must also be declared volatile if it's write only since the optimizer may optimize out the first write in a two-write operation, and optimize out all writes when a subsequent read isn't done. So what registers don't need to be declared volatile? Configuration registers where settings are written and not expected to change.

Delay loop variables in a highly optimized function

Although an RTOS should be used to perform delays, shorter delays on the order of microseconds need to be in a loop with a counter. The compiler will optimize the loop away if it sees that no useful operations are contained within it. Declaring the loop counter volatile will cause the compiler to leave the loop in the code.

Variables that shouldn’t be optimized out if not used



Probably the biggest use of these kind of variables is for debugging. Set a variable to a value at certain points of the code and then see what the value is in the debugger when the program fails. Like the delay loop variables, declaring them volatile will keep them in the code.