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
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.
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?
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
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*?
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?
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.
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.
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.
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.
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.
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.
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.
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?
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?
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?
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.
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.
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.
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.
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:)?
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.
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.
:-)
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.
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 :)
@ 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.
@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?
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?
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. :-)
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).
> 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?
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.
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.
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.