Forum: Compiler & IDEs volatile überflüssig?


von Florian (Gast)


Lesenswert?

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...):
1
uint8_t cnt = 0;
2
3
ISR (INT0_vect)
4
{
5
  cnt++;
6
}
7
8
int main()
9
{
10
  DDRD &= ~(1 << DDD2);        // PD2 als Eingang
11
  DDRD |= (1 << DDD0);        // PD1 als Ausgang
12
13
  sei();
14
  MCUCR = (1 << ISC00)|(1 << ISC01);  // steigende Flanke INT0 => IRQ
15
  GICR = (1 << INT0);          // IRQ an INT0 aktivieren
16
  
17
  // Startwert ausgeben
18
  
19
  while (1)
20
  {
21
    // Zählerstand ausgeben
22
    delay_ms (500);
23
    PORTD ^= (1 << PD0);    // toggle PD0
24
  }
25
  
26
}

Egal aber, ob ich cnt als volatile oder nicht deklariere, es wird 
inkrementiert. Also doch noch nicht verstanden?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

>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

von Florian (Gast)


Lesenswert?

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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der Nutzen von volatile wird halt insbesondere deutlich, indem
man wirklich eine Warteschleife darauf ansetzt.  Also
1
while (cnt < 500) /* wait */;

oder sowas.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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

von Florian (Gast)


Lesenswert?

>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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

Dann schalte mal die Optimierung an (-Os)...

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Florian (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.