www.mikrocontroller.net

Forum: Compiler & IDEs volatile überflüssig?


Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...):
uint8_t cnt = 0;

ISR (INT0_vect)
{
  cnt++;
}

int main()
{
  DDRD &= ~(1 << DDD2);        // PD2 als Eingang
  DDRD |= (1 << DDD0);        // PD1 als Ausgang

  sei();
  MCUCR = (1 << ISC00)|(1 << ISC01);  // steigende Flanke INT0 => IRQ
  GICR = (1 << INT0);          // IRQ an INT0 aktivieren
  
  // Startwert ausgeben
  
  while (1)
  {
    // Zählerstand ausgeben
    delay_ms (500);
    PORTD ^= (1 << PD0);    // toggle PD0
  }
  
}


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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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

Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
lcd(cnt);    // Ausgabe

Da hätte ich dann gedacht, daß er
lcd(0);    // Ausgabe
d'raus macht.
Kann man mein beispiel irgendwie so umstricken, daß der Nutzen von 
volatile deutlich wird?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

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

oder sowas.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>while (cnt < 500) /* wait */;

Hm. Dann sollte das hier aber nicht bei 10 Enden:
  while (cnt < 10)
  {
    delay_ms (500);
    PORTD ^= (1 << PD0);    // toggle PD0
  }

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann schalte mal die Optimierung an (-Os)...

Oliver

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.