The compiler uses a complex optimizing system which follows the flow of a function in order to create the most efficient code possible. It will remove redundant, unused or unreachable code. However, many embedded applications often have pieces of code that test hardware registers or data objects that are modified by interrupt service routines. These data changes take place outside the flow of the function or even the entire program in the case of hardware modified registers. Code that uses such data objects may appear to be redundant or unreachable to the optimizer and are legitimately removed. The ANSI C keyword "volatile" was created to alert the compiler that that particular code was not redundant and should be kept. For more details please see "The const and volatile Type Qualifiers" section of the user's manual for more information.
e.g. Writing to a serial port may involve several writes to a data register.
unsigned char SCDR;
SCDR = 'h'
SCDR = 'e'
SCDR = 'l'
SCDR = 'l'
SCDR = 'o
The compiler will only generate the code for the letter 'o' because SCDR was never tested or used with any other value.
If you add the volatile keyword to the declaration and any prototypes that will tell the compiler to generate all reads and writes and the program will behave as expected. All Cosmic supplied peripheral include files include the volatile keyword for hardware registers.
volatile unsigned char SCDR;
extern volatile unsigned char SCDR;
Many users forget to use the volatile keyword on global variables that are modified by interrupt service routines. These variables are also modified outside of the scope of non interrupt functions that use them. This is effectively the same as variables modified by hardware.
Another common oversight is to forget to use the volatile on extern variable prototypes.