Forum: Compiler & IDEs volatile bei cast


von Hubert (Gast)


Lesenswert?

Hi!

Hab diesen Code in einem älteren Beitrag gefunden:

struct test {
  byte b0:1;
  byte b1:1;
  byte b2:1;
  byte b3:1;
  byte b4:1;
  byte b5:1;
  byte b6:1;
  byte b7:1;
} __attribute__((packed));

#define LED_rot (*(volatile struct test*)&PORTD).b3
#define LED_gruen (*(volatile struct test*)&PORTD).b2


Könnt ihr mir erklären warum ich bei dem cast der struct das 
schlüsselwort volatile zufüge? macht das sinn obwohl meine struct nicht 
volatile ist? oder ist das weil der portd volatile ist? das versteh ich 
nicht ganz?

Danke im Voraus,

lg Hubert

von Εrnst B. (ernst)


Lesenswert?

Hubert schrieb:
> Könnt ihr mir erklären warum ich bei dem cast der struct das
> schlüsselwort volatile zufüge?

Weil der Port volatile ist. Du willst das "Datenlayout" von der 
Speicherstelle "PORTD" von "uint8_t" in "stuct test" Ändern, aber das 
"volatile" dabei nicht verlieren. Also muss das im Cast mit dabei 
stehen.

von Hubert R. (Firma: JKU Linz) (hubert_jku)


Lesenswert?

Super, dank dir für deine schnelle Antwort!

von Dosmo (Gast)


Lesenswert?

Ist Dir wirklich klar, was "volatile" bedeutet?

von Hubert R. (Firma: JKU Linz) (hubert_jku)


Lesenswert?

Ich denke schon, aber vll kannst du mir ja da noch nachhilfe geben? Bin 
um jede Hilfe und Erklärung dankbar!

von Karl H. (kbuchegg)


Lesenswert?

>>> FAQ: Was hat es mit volatile auf sich <<<

Kapitel 9:  Was hat es mit volatile auf sich?

von Dosmo (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
>>>> FAQ: Was hat es mit volatile auf sich <<<
>
> Kapitel 9:  Was hat es mit volatile auf sich?

Ein wirklich sehr schöner Artikel, Hut ab!

Ich persönlich mag ja "const volatile" am liebsten (für 
Hardwareregister, die read-only sind)...

von Dosmo (Gast)


Lesenswert?

Hubert schrieb:
> Könnt ihr mir erklären warum ich bei dem cast der struct das
> schlüsselwort volatile zufüge? macht das sinn obwohl meine struct nicht
> volatile ist? oder ist das weil der portd volatile ist? das versteh ich
> nicht ganz?

Der Struct "test" ist der Datentyp.
"Volatile" ist ein Qualifier. Ein Qualifier gibt Eigenschaften an.
Das sind zwei Paar Schuhe.

In Deinem Beispiel bedeutet dies:
1. Greife auf den Port als Datentyp "struct test" zu.
2. Beachte dabei die Eigenschaft "volatile" des Ports.

von Stefan E. (sternst)


Lesenswert?

Dosmo schrieb:
> Ein wirklich sehr schöner Artikel, Hut ab!

Allerdings hat er einen beliebten Schwachpunkt (IMO), nämlich eine 
einseitige Konzentration (oder gar Beschränkung) auf nur "eine 
Richtung". Wie viele der volatile-Erklärungen erweckt auch diese den 
Eindruck, dass es nur ein Problem gäbe, wenn sich etwas außerhalb des 
direkten Programmflusses ändern kann. In Wahrheit geht es aber darum, 
ob es benutzt wird. Ob dieses Benutzen nun ein Schreiben oder Lesen 
ist, spielt keine Rolle, auch beim "nur" Lesen gibt es die selben 
Probleme.

Ich habe mal den Artikel um einen kleinen Absatz ergänzt. Vielleicht 
kann ja mal der originale Autor (Karl-Heinz?) drüber schauen und es ggf. 
den eigenen Vorstellungen (oder Stil) entsprechend 
umformulieren/ergänzen.

von Oliver (Gast)


Lesenswert?

Dosmo schrieb:
> 2. Beachte dabei die Eigenschaft "volatile" des Ports.

Nicht direkt. Der mit & auf PORTD erzeugte Pointer wird in einen 
(volatile struct test*) -Pointer gecastet. Das PORTD selber volatile 
ist, wird da nicht "beachtet", sondern sinnvollerweise auch dem neuen 
Pointer hinzugefügt.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:

> Ich habe mal den Artikel um einen kleinen Absatz ergänzt.

Du meinst AVR-GCC-Tutorial: Interrupt-Routinen und Registerzugriffe?
Oder Interrupt: Interruptfeste Programmierung?

Das Thema "Interruptfeste Programmierung" ist unabhängig von AVR und 
GCC, m.E. würde sich der Abschnitt besser in letzterem Artikel machen; 
dafür dann dort gut ausgearbeitet und vom Tutorial aus verlinkt.

Ein Tutorial sollte zwar nicht eine Linksammlung darstellen, aber bei 
entsprechend wichtigen Themen wie hier, und solchen, die wie gesagt 
nicht avr-gcc spezifisch sind, finde ich eine eigenständige Besprechung 
vorteilhafter.

Das vermeidet zudem sich widersprechende Erklärungen und ist i.d.R 
vollständiger und konsistenter als 
Hier-Ein-Bisschen-Und-Da-Ein-Bisschen-Aber-Nirgends-Richtig-Erklärungen.

Und nochwas: Code wie PORTD |= 1 ist auch nicht IRQ-fest per Design, 
wird aber in mindestens 99.9% aller Tutorials und Forenbeiträge als 
solches verkauft...

von Stefan E. (sternst)


Lesenswert?

Johann L. schrieb:
> Du meinst AVR-GCC-Tutorial: Interrupt-Routinen und Registerzugriffe?
> Oder Interrupt: Interruptfeste Programmierung?

Nein, ich meinte den FAQ-Abschnitt, auf den Karl-Heinz oben verwiesen 
hat.

Allerdings hat auch in "Interrupt: Interruptfeste Programmierung" gerade 
heute jemand einen Absatz "Volatile Variablen" eingefügt, den ich 
ebenfalls etwas umformuliert habe, damit beide Richtungen berücksichtigt 
werden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Johann L. schrieb:
>> Du meinst AVR-GCC-Tutorial: Interrupt-Routinen und Registerzugriffe?
>> Oder Interrupt: Interruptfeste Programmierung?
>
> Nein, ich meinte den FAQ-Abschnitt, auf den Karl-Heinz oben verwiesen
> hat.

Ah, also gibt es mindestens noch eine dritte Stelle, die das gleiche Faß 
aufmacht...

> Allerdings hat auch in "Interrupt: Interruptfeste Programmierung" gerade
> heute jemand einen Absatz "Volatile Variablen" eingefügt, den ich
> ebenfalls etwas umformuliert habe, damit beide Richtungen berücksichtigt
> werden.

...was aber sehr missverständlich ist. Unter "Verwendung einer Variable" 
kann nämlich leicht verstanden werden, daß sie nicht verändert wird.

Ich verwende zB ein Programm wie avr-gcc, was aber nicht bedeutet, daß 
ich es verändere oder erweitere.

von Stefan E. (sternst)


Lesenswert?

Johann L. schrieb:
> Ah, also gibt es mindestens noch eine dritte Stelle, die das gleiche Faß
> aufmacht...

Nicht nur das. Ich habe gerade gesehen, dass es jetzt sogar innerhalb 
eines Artikels (Interrupt: Interruptfeste Programmierung) an zwei 
Stellen thematisiert wird, einmal unter "Wichtige Eigenschaften von 
ISRs", und in dem neuen Abschnitt.

Johann L. schrieb:
> ...was aber sehr missverständlich ist. Unter "Verwendung einer Variable"
> kann nämlich leicht verstanden werden, daß sie nicht verändert wird.

Aber darum geht es doch, dass das volatile auch dann nötig ist, wenn die 
Variable außerhalb des Programmflusses nicht verändert (sondern nur 
gelesen) wird.

PS: Oder meinst du das im Sinne von "nirgendwo verändert wird"? In 
meinem Sprachgebrauch ist das dann keine Variable mehr, sondern eine 
Konstante. ;-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Johann L. schrieb:
>> ...was aber sehr missverständlich ist. Unter "Verwendung einer Variable"
>> kann nämlich leicht verstanden werden, daß sie nicht verändert wird.
>
> Aber darum geht es doch, dass das volatile auch dann nötig ist, wenn die
> Variable außerhalb des Programmflusses nicht verändert (sondern nur
> gelesen) wird.

Ja, eben auch. Aber die Erwähnung von Veränderung ist um Zuge der 
Änderung rausgeflogen, so daß die entsprechenden Stelle(n) jetzt überaus 
missverständlich sind, weil sie nur von "Verwenden" reden.

Aber letzteres bedeutet im üblichen Sprachgebrauch eben "nicht 
Verändern, sondern nur Nutzen".

von Stefan E. (sternst)


Lesenswert?

Johann L. schrieb:
> Aber letzteres bedeutet im üblichen Sprachgebrauch eben "nicht
> Verändern, sondern nur Nutzen".

Und genau hier würde ich widersprechen. Was die Termini "verwenden" und 
"nutzen" alles einschließen, hängt vom Objekt ab.

Johann L. schrieb:
> Ich verwende zB ein Programm wie avr-gcc, was aber nicht bedeutet, daß
> ich es verändere oder erweitere.

Klar, "verändern" ist da ja auch nicht Teil des eigentlichen 
Verwendungszwecks, also ist es auch nicht bei "verwenden" mit 
eingeschlossen. Bei einer Variablen sieht das ganz anders aus, da ist 
das Verändern integraler Bestandteil ihres Verwendungszwecks. Ich würde 
bei einer Variablen jedenfalls nicht auf die Idee kommen, dass 
"verwenden" und "nutzen" gleichbedeutend wäre mit "read only".

Aber diese Diskussion ist natürlich müßig, denn wenn es für dich 
missverständlich ist, dann ist es das auch für andere, und bedarf einer 
"Änderung". ;-)

von Dosmo (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Dosmo schrieb:
>> Ein wirklich sehr schöner Artikel, Hut ab!
>
> Allerdings hat er einen beliebten Schwachpunkt (IMO), nämlich eine
> (...)

Hallo Stefan,

mich überrascht Deine Ergänzung ein wenig, jedenfalls habe ich so etwas 
noch nie gesehen (was ja nix heißen soll). Ich kann mir gar keinen 
Anwendungsfall dafür vorstellen.
Hast Du schon mal eine solche Anwendung gehabt, wo Du volatile in diese 
Richtung benutzt mußtest (also von der Hauptschleife zur ISR hin)?

von Stefan E. (sternst)


Lesenswert?

Dosmo schrieb:
> mich überrascht Deine Ergänzung ein wenig, jedenfalls habe ich so etwas
> noch nie gesehen (was ja nix heißen soll). Ich kann mir gar keinen
> Anwendungsfall dafür vorstellen.

Du hast also noch nie Daten interrupt-gesteuert per UART versendet?
Gut, in dem Fall wird ein fehlendes volatile für den Sende-Puffer 
vermutlich keine Auswirkungen haben.
Ein konkretes Beispiel, bei dem es sehr wahrscheinlich ohne volatile in 
die Hose gehen wird: Software-PWM per Timer-Interrupts und eine 
Fading-Schleife in main in der Form:
1
for (PWM_duty = 255; PWM_duty; PWM_duty--)
2
  _delay_ms(4);

von Klaus (Gast)


Lesenswert?

Stefan Ernst schrieb:
> for (PWM_duty = 255; PWM_duty; PWM_duty--)
>   _delay_ms(4);

Das geht doch kürzer:
1
_delay_ms(1024);
2
PWM_duty = 0;
;)

MfG Klaus

von Dosmo (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Du hast also noch nie Daten interrupt-gesteuert per UART versendet?
> Gut, in dem Fall wird ein fehlendes volatile für den Sende-Puffer
> vermutlich keine Auswirkungen haben.

Hä? Ein UART-Tx-Interrupt läuft doch nicht periodisch die ganze Zeit, 
auch wenn er nichts zu senden hat. D.h. es gibt in der ISR keine 
Abfrage, ob neue Daten anliegen und zu versenden sind.

Aber das mit dem Timer leuchtet mir ein. Da wär ich echt nicht auf die 
Idee gekommen, daß der Compiler in der ISR eine Abfrage optimiert. Aber 
jetzt wo Du sagst...

Tatsächlich sind bei mir alle Variablen, die zwischen Hauptschleife und 
ISRs ausgetauscht werden, als volatile deklariert, nach dem Motto 
"lieber zu viel als zu wenig".

von Stefan E. (sternst)


Lesenswert?

Dosmo schrieb:
> Hä? Ein UART-Tx-Interrupt läuft doch nicht periodisch die ganze Zeit,
> auch wenn er nichts zu senden hat. D.h. es gibt in der ISR keine
> Abfrage, ob neue Daten anliegen und zu versenden sind.

Es geht um die Daten selber, die werden in main geschrieben und in der 
ISR gelesen.

Dosmo schrieb:
> Aber das mit dem Timer leuchtet mir ein. Da wär ich echt nicht auf die
> Idee gekommen, daß der Compiler in der ISR eine Abfrage optimiert.

Nicht die Abfrage in der ISR, das Schreiben in main (siehe dazu auch den 
Kommentar von Klaus).

von Dosmo (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Nicht die Abfrage in der ISR, das Schreiben in main (siehe dazu auch den
> Kommentar von Klaus).
Oh ja, um die falsche Ecke gedacht. Ich benutz für sowas immer 
Methoden/Funktionen, wegen der Lesbarkeit, aber Du hast natürlich Recht!

von Klaus (Gast)


Lesenswert?

Stefan Ernst schrieb:
> for (PWM_duty = 255; PWM_duty; PWM_duty--)
>   _delay_ms(4);

Dosmo schrieb:
> Ich benutz für sowas immer
> Methoden/Funktionen, wegen der Lesbarkeit

und der Wartbarkeit, insbesondere wenn mehrere an dem Projekt arbeiten. 
Ich zitiere mal Kernighan and Plauger:

> Write clearly -- dont't sacrifice clarity for "efficency"

In einem Stück Code, daß ich irgendwie zu verantworten habe, will ich so 
etwas nicht sehen

MfG Klaus

von Stefan E. (sternst)


Lesenswert?

Klaus schrieb:
> Stefan Ernst schrieb:
>> for (PWM_duty = 255; PWM_duty; PWM_duty--)
>>   _delay_ms(4);
>
> Dosmo schrieb:
>> Ich benutz für sowas immer
>> Methoden/Funktionen, wegen der Lesbarkeit
>
> und der Wartbarkeit, insbesondere wenn mehrere an dem Projekt arbeiten.
> Ich zitiere mal Kernighan and Plauger:
>
>> Write clearly -- dont't sacrifice clarity for "efficency"
>
> In einem Stück Code, daß ich irgendwie zu verantworten habe, will ich so
> etwas nicht sehen

Ich weiß gar nicht, was das Ganze jetzt überhaupt soll.
Es ging nicht darum, irgendwas zu präsentieren, was irgendwie 
"empfehlenswert" wäre, sondern lediglich um ein Beispiel zur 
Demonstration obigen volatile-Sachverhalts.

von Dosmo (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Ich weiß gar nicht, was das Ganze jetzt überhaupt soll.
> Es ging nicht darum, irgendwas zu präsentieren, was irgendwie
> "empfehlenswert" wäre, sondern lediglich um ein Beispiel zur
> Demonstration obigen volatile-Sachverhalts.

Richtig.
Ich wollte auch auf was anderes raus, aber mich aber doof ausgedrückt:
Wenn ich mit Interrupts arbeite, dann kommen alle Daten, die zwischen 
ISR und main() ausgetauscht werden, in einen struct. Diesen struct 
deklariere ich volatile. Damit sind automatisch beide Flußrichtungen 
abgedeckt, obwohl mir bisher noch gar nicht bewußt war, daß ich 
überhaupt an die Flußrichtung main()->ISR denken muß.
Insofern danke für Deinen Input.

von Oliver (Gast)


Lesenswert?

Dosmo schrieb:
> obwohl mir bisher noch gar nicht bewußt war, daß ich
> überhaupt an die Flußrichtung main()->ISR denken muß.

Dosmo schrieb:
> Ist Dir wirklich klar, was "volatile" bedeutet?

So schliesst sich der Kreis...

Oliver

von Peter D. (peda)


Lesenswert?

Dosmo schrieb:
> Damit sind automatisch beide Flußrichtungen
> abgedeckt, obwohl mir bisher noch gar nicht bewußt war, daß ich
> überhaupt an die Flußrichtung main()->ISR denken muß.

Diese Flußrichtung gibt es auch nicht, das Main kann keine Variable 
ändern, während die ISR läuft.
Es reicht also völlig, wenn die ISR die Variable einmal einliest.

Die ISR kann allerdings genau dann zuschlagen, wenn das Main eine 
Variable ändert und das nicht atomar macht. Also z.B. von 2 Bytes (int) 
nur eines geändert hat. Dann liest die ISR Unsinn.

Für die ISR hat das volatile daher keine Bedeutung, es vergrößert nur 
oft den Code.
Für das Main ist bei Interruptvariablen das volatile Pflicht und bei 
Bedarf auch noch das atomic.

Elegant läßt sich das volatile mit einem Macro auf das Main beschränken:
1
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
2
3
main()
4
{
5
  a = IVAR(b);
6
  IVAR(c) = d;
7
}

Damit erhöht sich zugleich die Lesbarkeit. Jeder sieht sofort, daß diese 
Variable zu einem Interrupt gehört.

In der ISR hat das volatile nur bei Hardwarezugriffen Bedeutung, die 
müssen natürlich auch immer ausgeführt werden.


Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
>
> Diese Flußrichtung gibt es auch nicht, das Main kann keine Variable
> ändern, während die ISR läuft.
> Es reicht also völlig, wenn die ISR die Variable einmal einliest.

Steht irgendwo, daß die ISR atomar ist?

> Die ISR kann allerdings genau dann zuschlagen, wenn das Main eine
> Variable ändert und das nicht atomar macht. Also z.B. von
> 2 Bytes (int) nur eines geändert hat. Dann liest die ISR Unsinn.

Da hilft auch kein volatile.

> Für die ISR hat das volatile daher keine Bedeutung, es vergrößert nur
> oft den Code.

Siehe oben. Eine ISR ist nicht norwendig atomar.

Die meisten AVR-Anwendungen/ISRs sind zwar so geschrieben, auf größeren, 
echtzeitfähigen Systemen ist es aber als Kunstfehler anzusehen, 
Interrupts global über einen so langen Zeitraum zu sperren.

> Elegant läßt sich das volatile mit einem Macro auf das Main beschränken:
1
#define IVAR(x)         (*(volatile typeof(x)*)&(x))
2
3
main()
4
{
5
  a = IVAR(b);
6
  IVAR(c) = d;
7
}


Nein, das ist nicht elegant, es ist Hack.

> Damit erhöht sich zugleich die Lesbarkeit.

Wer Quellcode les- und verstehbar haben will, der kommentiert und 
dokumentiert ihn.

von Dosmo (Gast)


Lesenswert?

Oliver schrieb:
> Dosmo schrieb:
>> obwohl mir bisher noch gar nicht bewußt war, daß ich
>> überhaupt an die Flußrichtung main()->ISR denken muß.
>
> Dosmo schrieb:
>> Ist Dir wirklich klar, was "volatile" bedeutet?
>
> So schliesst sich der Kreis...
>
> Oliver

Das kommt halt davon, wenn man soviele Endlosschleifen in den ISRs 
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.