Eine gute Erklärung von VOLATILE kann unter https://blog.regehr.org/archives/28 gefunden werden. Hier ein kleiner Auszug:
For every read from a volatile variable by the abstract machine, the actual machine must load from the memory address corresponding to that variable. Also, each read may return a different value. For every write to a volatile variable by the abstract machine, the actual machine must store to the corresponding address. Otherwise, the address should not be accessed (with some exceptions) and also accesses to volatiles should not be reordered (with some exceptions).
Der generelle Gebrauch wird unter diesen Links gut beschrieben:// NEGATIVE EXAMPLE // Main void main() { foo = 0; for(;;) { while(!foo); // could be replace by while(true) foo = 0; // -> endless loop } } // ISR int foo; // globally shared variable void isr() { // ... foo = 1; } // BAD PROGRAMMING STYLE // Main /* The function would need to load the variable from the original location in case of external function declaration. It doesn't know the preceding program code. Thus no optimizations can be made. Anyway in case the compiler optimization is more sophisticated it may fail. */ void wait() { while(!foo); } // static void main() // inline void wait() /* In case of inline or static function declaration the compiler already knows all preceding program code and could optimize something. */ void main() { foo = 0; for(;;) { wait(); foo = 0; } } // ISR int foo; // globally shared variable void isr() { // ... foo = 1; }
// volatile integer volatile int foo; int volatile foo; // pointer to volatile integer volatile int * pFoo; int volatile * pFoo; // volatile pointer to volatile integer volatile int * volatile pFoo; int volatile * volatile pFoo; // pointer to volatile struct // separate volatile declaration for each variable typedef struct { } foo_s; foo_s volatile *pFoo = 0x10000; // disadvantage: have to be placed within // function declarations too void doSmth(foo_s volatile *pFoo); // each variable declared volatile typedef volatile struct { } foo_s; foo_s *pFoo = 0x10000; // advantage: automatically added at each declaration void doSmth(foo_s *pFoo); // volatile struct member typedef struct { volatile int a; int b; } foo_s; typedef struct { int volatile a; int b; } foo_s; // advantage: all other accesses can be optimized // volatile const variable const volatile int * p; // usage: the program isn't allowed to store to it, // however the register value may change from HW side
Beispiel für einen non-volatilen Buffer-Zugriff zw. ISR und Main
Bei einem Austausch über Double-Buffering passiert der Zugriff immer nur von einer Seite (Synchronisation vorausgesetzt). Das ermöglicht Compiler-Optimierungen, da ein direkter Lese/Schreib-Zugriff nicht mehr immer notwendig ist.
// Main void main() { int * local; collect = &a; process = &b; new_data = 0; IE(); // interrupts enabled for(;;) { while(!new_data); local = process; new_data = 0; // read/write access x = *local; y = *local; // could be optimized *local = x/2; } } // ISR int a,b; // double buffering int * volatile collect; int * volatile process; int volatile new_data; // sync variable void isr() { int * local = collect; // ... *local = y; *local += 2; // could be optimized // ... // swap buffer and inform main // (main should already be within while-loop) if (process == &a) { process = &b; collect = &a } else { process = &a; collect = &b; } new_data = 1; }
Beispiel für einen volatilen Zugriff von non-volatile Variablen
Eine Variable, welche nur in der ISR verwendet wird, kann grundsätzlich non-volatile deklariert werden. Wird sie aber während der Initialisierung in der Main-Routine beschrieben, so muss sichergestellt werden, dass der Zugriff volatile passiert (direkter Zugriff an genau diesem Zeitpunkt) und die ISR noch nicht aufgerufen werden kann. Weiterführendes über volatile-casts kann unter https://bytes.com/topic/c/answers/221923-cast-volatile nachgelesen werden
// Main void main() { *((int volatile *)&foo) = 1; IE(); // interrupts enabled // ... } // ISR int foo = 0; // globally shared variable void isr() { if (foo == 0) { foo = 1; } else { foo = 0; } // ... }
Unterschiede bei der Kernel-Programmierung:
https://web.archive.org/web/20160304053622/https:/www.kernel.org/doc/Documentation/volatile-considered-harmful.txt