Mittels volatile teilt man dem Compiler mit, dass er sich Optimierungen
auf dieser Variablen verkneifen soll.
Dies deshalb, weil es Wege gibt wie sich diese Variable verändern
kann, die dem Compiler verborgen bleiben und die er nicht
einsehen kann.
Kosmos hat das schon so in etwa angesprochen.
Worum gehts konkret?
Der Optimizer eines Compilers hat die Aufgabe, die Laufzeit
eines Programmes zu verringern. Dazu stehen im unter anderem
die Register einer CPU zur Verfügung. Die kannst du dir als
Speicherzellen in der CPU vorstellen, auf die das Programm
besonders schnell zugreifen kann. Nur: Variablen residieren
primär nicht in CPU-Registern sondern werden im Speicher
abgelegt. D.h. der Compiler muss darauf achten, dass er
die CPU Register wieder aus dem Speicher nachlädt, wenn
es eine Chance gibt, dass sich diese Variable verändert hat.
Bsp:
uint8_t a;
int main()
{
uint8_t b;
a = 0;
b = 5;
...
while( a < b )
b = b - 1;
}
aus Sicht des Compilers gibt es für a keine Möglichkeit, wie
sich der Wert von a innerhalb der while-Schleife jemals ändern
könnte. Für den Optimizer ergibt sich daher die Situation, dass
es ausreichen würde, den Wert von a vor der Schleife in ein
CPU Register zu laden und dort zu belassen. Für ihn ist es völlig
unnötig, jedesmal den Wert von a im Speicher aufzusuchen um den
Vergleich zu machen. Das würde nur unnötig Zeit kosten.
Nun: In dem Moment, in dem Interrupt Funktionen auf der Bühne
auftauchen, ändert sich die Situation: In einer ISR könnte man
sehr wohl den Wert von a ändern. Nur: Wenn der Compiler die main()
Funktion übersetzt, weiss er davon nichts. Innerhalb der while-
Schleife erfolgt kein Aufruf der Interrupt Routine. Daher
berücksichtigt der Compiler die Änderung von a innerhalb der ISR
schlicht und ergreifend nicht, wenn die while-Schleife übersetzt
wird.
Mittels
volatile uint8_t a;
kann man dem Compiler allerdings mitteilen: a wird auf Wegen
verändert, die du nicht erkennen kannst. Da darfst also auf
dieser Variablen keine wie immer gearteten Optimierungen durch-
führen weil du zu den falschen Schlussfolgerungen kommen wirst.
Noch extremer wäre dieser Fall:
int main()
{
a = 0;
while( a == 0 ) {
... Ausdrücke in denen a nicht verändert wird
}
}
Die Datenfluss-Analyse wird herausfinden, dass a innerhalb
der Schleife nicht verändert wird. Da a mit 0 vorbelegt wurde
wird der Vergleich zur Schleifensteuerung immer true ergeben.
Wenn der Vergleich aber sowieso immer true ergibt, braucht er
auch nicht gemacht werden. Der Compiler ersetzt also die
while-Schleife ganz einfach mit einer Endlosschleife. Nur:
Innerhalb der while-Schleife gibt es keine Möglichkeit dass
a seinen Wert ändern könnte. Aber die ISR kann das tun! Und
wieder: Der Compiler kann das so nicht erkennen, daher muss
man ihm mit einem volatile weiterhelfen.