Forum: Compiler & IDEs Umgang mit Volatile Variablen im Code, beispiel "atoi"


von Kevin B. (tewger)


Lesenswert?

Hallo,

ich habe eine Frage zum Umgang mit einer Volatile Variable(hier: 
"Clear_off" und "Clear_on"), dazu siehe der Codeausschnitt:
1
ISR(INT2_vect){
2
  cli();
3
  if (modus==1)
4
  {
5
    if (status_blink_on)
6
    {
7
      anz_blink-=1;
8
      status_blink_on = false;
9
      PORTA |= (1<<PA7);
10
      Interrupt_config_TCS34725(true,0x01,
11
                  (atoi(clear_off)>>8),((uint8_t)atoi(clear_off)),
12
                  0xFF,0xFF);
13
      Interrupt_clear_TCS34725();
14
    }
15
    else {  ...

Die Variablen "Clear_off" und "Clear_on" können in der UART Interrupt 
Routine geändert werden, daher auch die Deklaration als Volatile. Der 
Compiler reagiert darauf mit der Warnung:

"passing Argument 1 of 'atoi' discards 'volatile' qualifier from pointer 
target type"

Wie würdet ihr diese Warnung am elegantesten lösen?

Vielen Dank im Vorraus

von Peter II (Gast)


Lesenswert?

Kevin B. schrieb:
> Die Variablen "Clear_off" und "Clear_on" können in der UART Interrupt
> Routine geändert werden, daher auch die Deklaration als Volatile.
da du selbser in der ISR bist, brauchst du kein volatile.

und das CLI sollte auch nicht drin stehen.

von Kevin B. (tewger)


Lesenswert?

aber die beiden Variablen können ja in einer anderen ISR geändert 
werden? Bzw. wie würde die Lösung aussehen, wenn es sich nicht um einen 
ISR handeln würde?

von Werner (Gast)


Lesenswert?

Welchen Inhalt hat denn die Variable? Wie breit ist sie?

Schon mal dran gedacht, dass bei Zahlen von 0-9 atoi nichts anderes als 
x-48 ist?
Vielleicht kommst du um atio() drum herum?

Werner

von Kevin B. (tewger)


Lesenswert?

Liegt im Bereich von 0-65.535 , also Integer

von Peter II (Gast)


Lesenswert?

Kevin B. schrieb:
> aber die beiden Variablen können ja in einer anderen ISR geändert
> werden?
ja, spielt aber keine rolle. Weil es nur eine ISR gleichzeitig geben 
kann.

> Bzw. wie würde die Lösung aussehen, wenn es sich nicht um einen
> ISR handeln würde?

atoi( (char*)clear_off );

ist denn clear_off wirklich eine ein volatile char*?

von Kevin B. (tewger)


Lesenswert?

wie meinst du das Peter, ob es wirklich ein Volatile ist?

Clear_off kann über die UART Interrupt Routine geändert werden, daher 
ist dies für mich Volatile. zwischen zwei Aufrufen von ISR2 kann diese 
Variable ja geändert worden sein. Oder?

von Peter II (Gast)


Lesenswert?

Kevin B. schrieb:
> wie meinst du das Peter, ob es wirklich ein Volatile ist?

ob es wirklich ein char* ist wollte ich wissen?`

der Name deutet nicht an das es ein String ist.

von Kevin B. (tewger)


Lesenswert?

ja es ist ein string. Clear off ist ein Teilstring aus einem UART 
String. Die Funktion braucht dies aber als Integer.

von Eric B. (beric)


Lesenswert?

Solche Konvertierungen macht man üblicherweise außerhalb des ISRs, da
zum einen diese Konvertierungen "lange" dauern können und zum anderen 
Konvertierungsfehler auftreten können, die man innerhalb der ISR nicht 
behandeln kann weil das zu lange dauern würde.

von Klaus (Gast)


Lesenswert?

Kevin B. schrieb:
> aber die beiden Variablen können ja in einer anderen ISR geändert
> werden? Bzw. wie würde die Lösung aussehen, wenn es sich nicht um einen
> ISR handeln würde?

Zunächst einmal sagt der Standard, dass das Ergebnis solcher Art 
Zugriffe undefiniert ist. Siehe auch: http://blog.regehr.org/archives/28

Allerdings wäre es die einfachste, stringente und im Sinne der einer 
einfachen Programm-Struktur beste Möglichkeit, den String an sich 
vollständig auszuwerten, sobald er gelesen wurde - natürlich im 
Hauptprogramm. Dann wäre das Ergebnis auch nicht volatile - nur der 
String beim Input.

Auf jeden Fall, die Warnung beseitigen. Und auf keinen Fall volatile 
Problme mit einem cast beseitigen. Das gibt blaue Flecken.

von Klaus (Gast)


Lesenswert?

Es ist zwar an sich nicht "grundfalsch" aber mit gutem Grund eine oft 
hervorgehobene Faustregel, möglichst wenig Operationen in der ISR zu 
machen und keine Funktionsaufrufe. Ob atoi etc. nun inline ist, ist 
dabei ein nebensächliches Problem.

Aus ebensolchen Gründen wird empfohlen und als "guter Stil" angesehen, 
wie Eric schon erwähnte, Auswertungen im Hautprogramm zu machen.

Wenn Du diese beiden Regeln beachtet hättest, hättest Du auch das 
Problem nicht. Verstehe mich bitte recht. Ich sage das, weil ich 
hervorheben möchte wie nützlich diese Faustregeln tatsächlich sind.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Kevin B. schrieb:
> "passing Argument 1 of 'atoi' discards 'volatile' qualifier
> from pointer target type"

atio erwartet ein const char*, d.h. das Ziel des Zeigers ist 
nicht-volatile.

Das Problem mit volatile ist, dass du nicht weisst, wie atoi auf den 
String zugreift: Bei volatile macht es i.d.R einen Unterschied, in 
welcher Reihenfolge und wie oft entsprechende Objekte (Variablen, 
Array-Elemente, Strukturkomponenten, Pointer-Targets) zugegriffen 
werden!

Wenn all dies hier keine Rolle spielt, dann ist ein Cast auf const char* 
angezeigt.

Falls es eine Rolle spielt, benötigst du ein eigenes, darauf angepasstes 
atoi.

von Markus F. (mfro)


Lesenswert?

Klaus schrieb:
> Auf jeden Fall, die Warnung beseitigen. Und auf keinen Fall volatile
> Problme mit einem cast beseitigen. Das gibt blaue Flecken.

Bezweifle ich.

Hier ein "char *" hinzuschreiben, macht nichts kaputt. Das volatile 
kriegt man von der Variable eh' nicht weg (genausowenig, wie man eins 
"hincasten" kann), man macht sie lediglich zuweisungskompatibel.

Es mag allerdings andere Fälle geben, wo das eine Rolle spielt.

von Kevin B. (tewger)


Lesenswert?

ich habe mich etwas falsch ausgedrückt: Der String wird zwar in UART 
Interrupt Routine eingelesen, allerdings erst in einer normalen Sub 
verarbeitet. In dieser wird dann auch die Globale Volatile Variable 
Clear_off und Clear_on gesetzt.

von Peter II (Gast)


Lesenswert?

Kevin B. schrieb:
> ich habe mich etwas falsch ausgedrückt: Der String wird zwar in UART
> Interrupt Routine eingelesen, allerdings erst in einer normalen Sub
> verarbeitet. In dieser wird dann auch die Globale Volatile Variable
> Clear_off und Clear_on gesetzt.

und warum wandelst du nicht dort gleich in ein int um?

von Kevin B. (tewger)


Lesenswert?

Peter II schrieb:
> Kevin B. schrieb:
>> ich habe mich etwas falsch ausgedrückt: Der String wird zwar in UART
>> Interrupt Routine eingelesen, allerdings erst in einer normalen Sub
>> verarbeitet. In dieser wird dann auch die Globale Volatile Variable
>> Clear_off und Clear_on gesetzt.
>
> und warum wandelst du nicht dort gleich in ein int um?

Gute Frage... ich schau mir das mal an. Aber ist das Volatile trotzdem 
notwendig?

von Peter II (Gast)


Lesenswert?

Kevin B. schrieb:
> Aber ist das Volatile trotzdem
> notwendig?

ohne den Kompletten code zu sehen, kann man das schwer sagen.

bei char* ist schon mal die Frage muss der Inhalt oder der Zeiger 
Volatile sein?

Ändert sich also nur der Inhalt vom Zeiger oder ändert der Zeiger sich 
selber?

von Klaus (Gast)


Lesenswert?

Markus F. schrieb:
> Klaus schrieb:
>> Auf jeden Fall, die Warnung beseitigen. Und auf keinen Fall volatile
>> Problme mit einem cast beseitigen. Das gibt blaue Flecken.
>
> Bezweifle ich.

Und was konkret lässt Dich daran zweifeln?

Im Standard steht es jedenfalls so. Abschnitt: 6.7.3. Type qualifiers.

Ich mag das falsch deuten. Aber jedenfalls ist das konkreter als 
unbegründete Zweifel mit angefügter Behauptung - bei allem Respekt.

von Kevin B. (tewger)


Lesenswert?

der komplette Code ist leider etwas zu viel, um Ihn hier einzufügen. 
Aber es ändert sich lediglich der Inhalt vom Zeiger

von Peter II (Gast)


Lesenswert?

Kevin B. schrieb:
> der komplette Code ist leider etwas zu viel, um Ihn hier einzufügen.
> Aber es ändert sich lediglich der Inhalt vom Zeiger

es gibt hier keine Größenbegrenzung.

wenn sich nur der Inhalt von Zeiger ändert, braucht man zu 99% eh kein 
volatile weil so ein Atmel gar kein Platz hat um so viele Daten zu 
cachen.

von Klaus (Gast)


Lesenswert?

Markus F. schrieb:
[...]
> Das volatile
> kriegt man von der Variable eh' nicht weg (genausowenig, wie man eins
> "hincasten" kann), man macht sie lediglich zuweisungskompatibel.


Das ist so auch nicht korrekt. Prominentes Beispiel ist der Software 
UART-Code hier im Forum von Peter Dannegger.

Mag ja sein, das wir uns beide vergallopiert haben. Aber ein wenig 
Konkreteres wäre doch wirklich hilfreich.

von Klaus (Gast)


Lesenswert?

Kevin B. schrieb:
[...]

> Gute Frage... ich schau mir das mal an. Aber ist das Volatile trotzdem
> notwendig?

Das volatile ist überall dort notwendig, wo sich Variablen unabhängig 
vom Hauptprogramm (also main und allen von dort aufgerufenen Funktionen 
und von denen wiederrum aufgerufenen Funktionen) ändern kann. Oder 
positiv aber nicht erschöpfend: Wenn sich, durch Interrupts oder sonst 
allgemein Hardware, ein Variableninhalt ändern kann.

von Kevin B. (tewger)


Lesenswert?

Klaus schrieb:
> Kevin B. schrieb:
> [...]
>
>> Gute Frage... ich schau mir das mal an. Aber ist das Volatile trotzdem
>> notwendig?
>
> Das volatile ist überall dort notwendig, wo sich Variablen unabhängig
> vom Hauptprogramm (also main und allen von dort aufgerufenen Funktionen
> und von denen wiederrum aufgerufenen Funktionen) ändern kann. Oder
> positiv aber nicht erschöpfend: Wenn sich, durch Interrupts oder sonst
> allgemein Hardware, ein Variableninhalt ändern kann.

Die Variablen ändern sich ja nicht in der Sub, sondern in einer 
Unterroutine, die sich aber auf die Interruptroutine bezieht. Wozu zählt 
das dann:)?

von Peter II (Gast)


Lesenswert?

Klaus schrieb:
> Das volatile ist überall dort notwendig, wo sich Variablen unabhängig
> vom Hauptprogramm (also main und allen von dort aufgerufenen Funktionen
> und von denen wiederrum aufgerufenen Funktionen) ändern kann. Oder
> positiv aber nicht erschöpfend: Wenn sich, durch Interrupts oder sonst
> allgemein Hardware, ein Variableninhalt ändern kann.

das stimmt so auch nicht.

Wen eine Variabel nur in 2 verschieden ISR verwendet wird, braucht man 
auch kein Volatile.

von Klaus (Gast)


Lesenswert?

Kevin B. schrieb:
> Klaus schrieb:
>> Kevin B. schrieb:
>> [...]
>>
>>> Gute Frage... ich schau mir das mal an. Aber ist das Volatile trotzdem
>>> notwendig?
>>
>> Das volatile ist überall dort notwendig, wo sich Variablen unabhängig
>> vom Hauptprogramm (also main und allen von dort aufgerufenen Funktionen
>> und von denen wiederrum aufgerufenen Funktionen) ändern kann. Oder
>> positiv aber nicht erschöpfend: Wenn sich, durch Interrupts oder sonst
>> allgemein Hardware, ein Variableninhalt ändern kann.
>
> Die Variablen ändern sich ja nicht in der Sub, sondern in einer
> Unterroutine, die sich aber auf die Interruptroutine bezieht. Wozu zählt
> das dann:)?

Nun, wenn gilt, was ich schrieb: "main und allen von dort aufgerufenen 
Funktionen und von denen wiederrum aufgerufenen Funktionen"

dann fällt eine Funktion, die innerhalb einer ISR aufgerufen wird nicht 
darunter.

Allerdings ist Deine Terminologie mißverständlich:
Was meinst Du mit "beziehen"? Das kann ein Aufruf-Bezug sein oder ein 
Datenbezug.

Übrigens:
Wenn Du nicht willst, dass ich Dir einen Zimtstern vom Weihnachtsteller 
klaue, dann gewöhne Dir das "Sub" ab. Das ist hier nicht perl sondern C. 
:-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wenn eine Variable von unterschiedlichen IRQ-Ebenen aus verwendet wird, 
muss sie i.d.R volatile sind, und der Zugriff muss i.d.R. atomar 
erfolgen.

In AVR-Programmen hat man üblicherweise nur 2 IRQ-Ebenen (eine unterhalb 
von main, eine unterhalb einer ISR), die nicht-unterbrechbar sind.

Für andere, komplexere Architekturen ist das üblicherweise anders, und 
auch mit AVR kann man mit mehr als 2 IRQ-Ebenen arbeiten.

von Kevin B. (tewger)


Lesenswert?

Klaus schrieb:
> Kevin B. schrieb:
>> Klaus schrieb:
>>> Kevin B. schrieb:
>>> [...]
>>>
>>>> Gute Frage... ich schau mir das mal an. Aber ist das Volatile trotzdem
>>>> notwendig?
>>>
>>> Das volatile ist überall dort notwendig, wo sich Variablen unabhängig
>>> vom Hauptprogramm (also main und allen von dort aufgerufenen Funktionen
>>> und von denen wiederrum aufgerufenen Funktionen) ändern kann. Oder
>>> positiv aber nicht erschöpfend: Wenn sich, durch Interrupts oder sonst
>>> allgemein Hardware, ein Variableninhalt ändern kann.
>>
>> Die Variablen ändern sich ja nicht in der Sub, sondern in einer
>> Unterroutine, die sich aber auf die Interruptroutine bezieht. Wozu zählt
>> das dann:)?
>
> Nun, wenn gilt, was ich schrieb: "main und allen von dort aufgerufenen
> Funktionen und von denen wiederrum aufgerufenen Funktionen"
>
> dann fällt eine Funktion, die innerhalb einer ISR aufgerufen wird nicht
> darunter.
>

Nein, die Funktion wird ja nicht in der ISR aufgerufen. Die ISR setzt 
ein Flag(String complete) und in der Main wird allein dieses Flag 
abgefragt und dann wird die Routine zur Stringverarbeitung aufgerufen-
1
  while(1)
2
  {  //led wird in interrupt routine getoggelt
3
    if (uart_str_complete ==1)
4
    {
5
      uart_str_complete = 0;
6
      uart1_auswerten();
7
    }

in dieser Routine uart1_auswerten wird dann folgendes gemacht :
1
...
2
clear_off=atoi(part2);
3
clear_on=atoi(part3);

(die Variablen sind jetzt integer und keine Strings, so wie es Peter 
vorgeschlagen hat)

> Allerdings ist Deine Terminologie mißverständlich:
> Was meinst Du mit "beziehen"? Das kann ein Aufruf-Bezug sein oder ein
> Datenbezug.
>

Nein ein Datenbezug, part2 wurde aus dem empfangenen String extrahiert.

> Übrigens:
> Wenn Du nicht willst, dass ich Dir einen Zimtstern vom Weihnachtsteller
> klaue, dann gewöhne Dir das "Sub" ab. Das ist hier nicht perl sondern C.
> :-)
sorry :)

von Klaus (Gast)


Lesenswert?

@ Kevin

Na, dann ist ja jetzt alles klar, oder?


Vielleicht mag ja Johann mal etwas epischer von "Ebenen" erzählen. Ich 
habe nur so eine ungefähre Ahnung, was er mit "unterhalb" meinen könnte. 
Es wird aber wohl Sinn haben, da er sonst recht beschlagen ist.

von Kevin B. (tewger)


Lesenswert?

@Klaus und alle anderen:

Ja es ist jetzt einiges klarer, Vielen Dank!

Jetzt nur noch die Frage: Die beiden Variablen Volatile oder ist es 
nicht notwendig?

Theoretische Frage: Wenn die Optimierung ausgeschaltet ist, hat das 
Volatile auch keinen Sinn mehr oder?

von Markus F. (mfro)


Lesenswert?

Klaus schrieb:
> Im Standard steht es jedenfalls so. Abschnitt: 6.7.3. Type qualifiers.

Du hast recht. Das steht da so. Undefined bahaviour. Streng genommen 
kann der Compiler also meinen Rechner sprengen, wenn ich das volatile 
hier wegcaste.

Nichtsdestotrotz hätte ich in der Praxis eher wenig Bedenken, das in 
diesem Fall (Aufruf einer eher trivialen Standardfunktion) trotzdem zu 
tun. Was kann passieren (ja, ich weiß, alles)?

Die Wahrscheinlichkeit, daß der Compiler hier nicht mehr aktuelle Werte 
in Registern hält geht gegen null, daß er den ganzen Aufruf wegoptimiert 
ist eher unwahrscheinlich.

Die Alternative wäre, ein eigenes atoi() zu erfinden (das ein volatile 
Argument akzeptiert) oder den String umzukopieren, beides eher 
unpassend.

Vielleicht kann ich Gnade erwarten, wenn ich gestehe, daß ich C gelernt 
habe, bevor die ISO9899 erfunden wurde?

von Klaus (Gast)


Lesenswert?

Markus F. schrieb:
> Klaus schrieb:
>> Im Standard steht es jedenfalls so. Abschnitt: 6.7.3. Type qualifiers.
>
> Du hast recht. Das steht da so. Undefined bahaviour. Streng genommen
> kann der Compiler also meinen Rechner sprengen, wenn ich das volatile
> hier wegcaste.
>
> Nichtsdestotrotz hätte ich in der Praxis eher wenig Bedenken, das in
> diesem Fall (Aufruf einer eher trivialen Standardfunktion) trotzdem zu
> tun. Was kann passieren (ja, ich weiß, alles)?
>
> Die Wahrscheinlichkeit, daß der Compiler hier nicht mehr aktuelle Werte
> in Registern hält geht gegen null, daß er den ganzen Aufruf wegoptimiert
> ist eher unwahrscheinlich.

Consider Murphys Law :-)

[...]
> Vielleicht kann ich Gnade erwarten, wenn ich gestehe, daß ich C gelernt
> habe, bevor die ISO9899 erfunden wurde?

Kannst Du. :-)

von Klaus (Gast)


Lesenswert?

Kevin B. schrieb:
> @Klaus und alle anderen:
>
> Ja es ist jetzt einiges klarer, Vielen Dank!
>
> Jetzt nur noch die Frage: Die beiden Variablen Volatile oder ist es
> nicht notwendig?

Was meinst Du? Die Informationen solltest Du eigentlich jetzt haben.

> Theoretische Frage: Wenn die Optimierung ausgeschaltet ist, hat das
> Volatile auch keinen Sinn mehr oder?

Räuspel. Das käme im Einzelfall auf den Compiler an. Es bleibt aber die 
Tatsache: Entweder wird eine Variable in der ISR verändert, dann nimmst 
Du volatile, oder eben nicht. Mit der Optimierung hat das nichts zu tun. 
Das ist ungefähr so, wie beim "bei Rot über die Strasse gehen". Ob da 
nun ein Auto ist oder nicht, spielt gar keine Rolle. Inkorrekt ist es in 
jedem Fall.

(Das ist nicht so letzte Wahrheit, aber für einen Anfänger auf jeden 
Fall seeeeeeeeeeeeeeeeeeeeeeeeehr ratsam).

von Klaus (Gast)


Lesenswert?

> Mit der Optimierung hat das nichts zu tun.

Mmmh. Naja. So richtig korrekt ist auch nicht. Natürlich geht es darum, 
ob man dem Compiler erlaubt davon auszugehen, dass sich ein Wert nicht 
geändert haben kann oder nicht. Falls nicht, dann "optimiert" er - oder 
auch nicht.

Die Optmierungen sind im Detail nicht festgelegt und es gibt auch immer 
nur eine arbiträte Zuordnung zwischen den Optionen "-O1", "O2" etc. und 
dem was tatsächlich im Compiler geschieht. Was beim GCC geht, kann Keil 
schon ganz übel nehmen oder umgekehrt.

Betrachte das mal von dem Gesichtspunkt, den ich oben so lakonisch 
genannt habe: Es gibt eine konkrete Tatsache. Die ist: Eine Variable 
wird in der ISR verändert oder nicht. Das ist so und Du kannst daran 
nicht rütteln. Genauso ist "volatile" mindestens ein "Qualifier" der das 
aussagt. Wenn es das aussagt, dann sollte man es auch verwenden um 1. 
Redundanz zu haben (beim Leser und ggf. bei dem einen oder anderen 
Compiler) und 2. Portabel zu bleiben. Compiler ändern sich recht schnell 
- schneller als die Standards. Und volatile wird voraussichtlich noch 
lange existent sein. Lies mal meinen Link. Da wird auch die 
Entstehungsgeschichte erklärt.

Und warum weglassen? Nur weil Willys Marzipancompiler ohnehin nichts als 
gegeben annimmt?

von Klaus (Gast)


Lesenswert?

Man kann das, was ich schrieb noch kürzer ausdrücken (ich habe nur oft 
keine Zeit kurz und knapp zu schreiben):

Ohne volatile ist es dem Compiler überlassen, ob er den Wert neu lädt 
oder nicht (durchaus auch abhängig von den Optionen - aber das ist nicht 
standardisiert).

Mit volatile muss der Compiler grundsätzlich immer den Wert neu laden.

von (prx) A. K. (prx)


Lesenswert?

Klaus schrieb:
> Vielleicht mag ja Johann mal etwas epischer von "Ebenen" erzählen. Ich
> habe nur so eine ungefähre Ahnung, was er mit "unterhalb" meinen könnte.
> Es wird aber wohl Sinn haben, da er sonst recht beschlagen ist.

Beim AVR arbeitet man normalerweise mit 2 Ebenen:
- Hauptprogramm
- Interrupt-Handler
und kein Interrupt unterbricht einen laufenden Interrupt.

Bei diversen 8-Bit PICs hat man hingegen bereits 3 Ebenen:
- Hauptprogramm
- niedrig priorisierter Interrupt-Handler
- hoch priorisierter Interrupt-Handler
wobei hoch priorisierte Interrupts niedrig priorisierte ISRs 
unterbrechen können.

Sobald Variablen in mehr als einer solchen Ebene verwendet werden kann 
volatile erforderlich sein, da sonst höher priorisierter Code nicht 
zwingend den letzten Stand sieht oder niedrig priorisierter Code 
Änderungen im höher priorisierten Code nicht mitkriegt.

DMA kann man in diesem Sinn als weitere höchst priorisierte Ebene 
betrachten.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> Beim AVR arbeitet man normalerweise mit 2 Ebenen:
> [...]

Sehr gut erklärt. Daraus folgt auch direkt, dass man beim AVR durchaus 
globale Variablen in zwei unterschiedlichen ISRs nutzen kann, ohne diese 
als volatile definieren zu müssen.

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.