Ich habe (auch) mal ne Frage zu volatile. Eigentlich dachte ich, die
Sache verstanden zu haben. (Register und Speicher und so). Aber dann
sollte doch eigentlich folgendes Programm auch nichts ausgeben
(1,2,3...):
In diesem Fall überflüssig.
Die ISR ist der einzige Benutzer der Variablen und es besteht keine
Gefahr, dass zwei Benutzerfunktionen (hier ISR und main) unabhängig
voneinander ändernd auf die Variable zugreifen.
PS: Pass mit dem delay_ms() auf. Es kann OK sein, wenn du eine eigene
Funktion korrekt schreibst. Die Libraryfunktion _delay_ms(500)
funktioniert mit 500 nie, weil das Argument ist ausserhalb des erlaubten
Wertebereichs ist.
>Also doch noch nicht verstanden?
Fast :-)
Das cnt++ in der ISR wird immer ausgeführt, egal, ob volatile oder
nicht.
Der Unterschied würde sich im Hauptprogramm zeigen, aber da machst du ja
nichts mit cnt.
Wenn du allerdings im Hauptprogramm so was wie
1
while(cnt<17)
o.ä. hinschreibst, weiss der Compiler nicht, daß sich cnt ausserhalb
seines Sichtbereiches ändern kann, und könnte daher cnt einmal in ein
Register laden, und da den Vergleich ausführen, oder sogar merken, daß
sich cnt ja nie verändern kann, und es ganz wegoptimieren. volatile
sorgt dann dafür, daß vor jeder Operation auf cnt dessen aktueller Wert
neu aus dem Speicher geladen wird.
Oliver
Das mit delay_ms ist schon klar. Betrifft aber _delay_ms...
>Der Unterschied würde sich im Hauptprogramm zeigen, aber da machst du ja>nichts mit cnt.
Ist vielleicht nicht deutlich gewesen, aber dort übergebe ich cnt an die
Funktion zur Displayausgabe. Im Grunde:
1
lcd(cnt);// Ausgabe
Da hätte ich dann gedacht, daß er
1
lcd(0);// Ausgabe
d'raus macht.
Kann man mein beispiel irgendwie so umstricken, daß der Nutzen von
volatile deutlich wird?
volatile ist kein Nutzen sondern eine "Hilfskrücke" damit der Compiler
korrekten Code produziert ;-)
Das Problem:
Die ISR verändert cnt, wenn ein Interrupt auftritt. Das unterbrpchene
Hauptprogramm (main) weiss nicht, dass ein Interrupt aufgetreten ist und
sich cnt durch die ISR verändert hat.
Die (Teil-)Lösung:
Man kennzeichnet diese Variable als volatile und der C Compiler erzeugt
Code, der die Variable im Hauptprogramm z.B. nie puffert (z.B. eine
Kopie in einem Arbeitsregister hält) sondern immer frisch aus der
Variablen einliest.
Aber das hilft nur in besonderen Föllen, nämlich wenn der Zugriff im
Hauptprogramm innerhalb eines Taktes erfolgen kann, also nicht
unterbrechbar durch die ISR ist.
Wenn für den Zugriff mehrere Takte erforderlich sind, muss man die
Interrupt(e) zwischenzeitlich sperren oder abschalten, um einen atomaren
Datenzugriff sicherzustellen. Näheres im Artikel Interrupt
>while (cnt < 500) /* wait */;
Hm. Dann sollte das hier aber nicht bei 10 Enden:
1
while(cnt<10)
2
{
3
delay_ms(500);
4
PORTD^=(1<<PD0);// toggle PD0
5
}
Aber auch ohne volatile ist bei erreichen von 10 Ende und er macht das
darauf folgende... Auch wenn ich alle Zugriffe und Ausgaben von cnt aus
main eliminiere.
Verwirrt.
Florian wrote:
>>while (cnt < 500) /* wait */;>> Hm. Dann sollte das hier aber nicht bei 10 Enden:
Dein Denk-Ansatz ist falsch.
Es ist nicht so, dass beim Weglassen von volatile nichts
mehr gehen würde.
Es ist vielmehr so, dass es dir passieren kann, dass nichts
mehr geht, weil der Optimizer zuschlägt. Wie und wann der
Optimizer aber dein Programm verändert, bzw. welche Variablen
er in Register optimiert, liegt alleine im Ermessen des
Optimizers. Und das hängt natürlich auch vom konkreten
Programm ab.
BTW: Du hast den Optimizer doch eingeschaltet. Ohne
Optimizer passiert dir nämlich sowieso nichts, weil
der Compiler von Haus aus sowieso konservativen Code
erzeugt, der nicht auf volatile angewiesen ist.
Der Punkt bei dir ist, dass du eine Funktion rufst. Normalerweise
analysiert der Compiler dabei aber nicht, was die gerufene
Funktion tut, und daher muss er annehmen, dass der Aufruf von
delay_ms() den Seiteneffekt hat, dass sich cnt dadurch ändert.
Wenn du entweder die Optimierung so weit hoch schraubst, dass er
sich diese Funktion analysiert (dafür genügt es u. U. schon, dass
sie vor dem Aufruf im gleichen File `static' deklariert ist), oder
aber du stattdessen _delay_ms() benutzt (die Funktion ist inline),
dann wirst du die Notwendigkeit von volatile auch mit deinem Code
sehen können.
Ahh!
Jupp. Danke an alle!
Optimierung hatte ich natürlich an. Aber man kann ja nie wissen.
Es lag an delay_ms. Nachdem ich das ersetzt habe, durchläuft er die
Schleife nun endlos, solange ich nicht volatile nutze.
So macht's doch Spaß: die Sache in der Praxis nachvollziehen, die man
theoretisch gelernt hat.