Some things are “volatile”

When writing code for devices, the programmer will encounter some things that are volatile – variables which must be declared with the volatile keyword.  It is not avoidable and it is not an option.  Yet, I frequently encounter code written by experienced programmers that fail to include ‘volatile’ where needed in a variable declaration.

Very simply, the ‘volatile’ keyword must be applied to any variable whose value can change unexpectedly by activity independent of the nearby code.  The simplest example of this are memory-mapped peripheral registers.  These are hardware registers located in the peripheral modules of a microcontroller.  Many of these registers are modified by the hardware in response to activity of the peripheral.

Consider the code example below that executes in a loop until the timer register value exceeds the value ‘5’.  At some earlier point in the code (not shown), the timer hardware has been started causing the timer to count.  (For this illustration, I’ll leave out my usual additions to prevent looping forever.)

uint16_t timerReg; //declaration missing 'volatile'
// . . .
for(; timerReg <= 5;)
   //waiting while timer has not exceeded ‘5’
//Timer has exceeded 5; do something

Without ‘volatile’ as a variable qualifier for ‘timer’, the compiler may presume that the variable need only be read once, since the value is not changed by the code.  Unless the initial value is greater-than ‘5’, the code would loop forever.  This code may execute properly until the optimizer is enabled; some compilers, however,  may perform “trivial” optimizations even with the optimizer off.  The proper declaration of the timer register is…

volatile uint16_t timerReg;

Some programmers forget that without ‘volatile’ even “writes” may behave improperly.  Consider the trivial situation in which the programmer desires to transmit two characters by writing twice to the UART transmit register.

volatile static uint8_t uartTxReg;
// . . .
uartTxReg = char1;
uartTxReg = char2;
// . . .

Without the volatile qualifier for ‘uartTxReg’, many compilers will optimize and remove the assignment of ‘char1’ from code execution and only one character will be transmitted.

Interrupt code also can cause situations where the ‘volatile’ keyword is required for proper code execution.  Here is a simple example of code that waits for a static variable flag to be set when a new character is received and read by the UART Rx Interrupt.

static uint16_t newCharReceived; //missing ‘volatile’
static uint8_t newChar;
volatile uint8_t uartRxReg;

// interrupt code
void UartRxInterrupt( void)
   // Interrupt has occurred
   // Read character and set flag
   newChar = uartRxReg;
   newCharReceived = 1u;
void main( void)
   // . . .
   // loop waiting for new character received flag
   for( newCharReceived = 0u; newCharReceived == 0;)
      // do nothing
   // flag has been set, character has been received
   // . . .

Here again, without the ‘volatile’ qualifier, the compiler may generate code that checks the value of ‘newCharReceived’ only once not recognizing that the variable may be changed by other code.  (Some compilers may generate code that never reads the flag; reading is not needed since the variable was just set.)

Multitasking also creates “volatile” situations.  If the device is using a multitasking operating system, a file-scope or global variable should be declared as volatile if the variable can be modified by code in more than one task.  This ensures that the compiler always generates code to read the variable from memory.

Good practice dictates that all hardware registers be declared as volatile.  Also declare all static or global variables modified by an interrupt handler as volatile.  When using a multitasking operating system, all variables shared by more than one task should be declared volatile. (Please note: I am not advocating the use of global variables; I am simply stating that if you use them and they can be modified by an interrupt handler or another task, they must be declared volatile.)

Some things are volatile; the key to proper execution is for the programmer to recognize and declare them.

I recently found this more extensive article about the volatile keyword at “geeks for geeks”