Forum: Compiler & IDEs Schon wieder volatile -_O


von Joachim B. (jojo84)


Lesenswert?

Guten Abend!

Ich hoffe, daß einer von euch mir helfen kann. Ich dachte ja, daß ich 
die Anwendung von "volatile" verstanden hätte, aber das ist wohl nicht 
der Fall :( .

Ich habe hier ein Programm für eine Stromregelung. Das läuft auf einem 
Attiny45. Ich gebe eine PWM auf einen Step-Up-Wandler und beim Overflow 
vom PWM-Timer startet der ADC.
Abgetastet wird zum einen der Strom, und zum anderen ein Poti 
(Strom-Sollwert, immer abwechselnd).

Innerhalb der ISR rufe ich erst eine Routine zum Filtern der Messwerte 
auf und dann den Regler.
Ich hab fast alle Variablen (erstmal) global angelegt, also so Dinge wie 
Stromsoll- und istwert, Momentanwerte, auch die beiden Arrays für die 
gefilterten Werte...
Jetzt kommt das Putzige: die lokalen Variablen, in meiner Filterroutine 
muß ich volatile machen, damit es funktioniert! Und ich begreife 
wirklich nicht, warum!
Die ISR sieht so aus:
1
ISR(ADC_vect)
2
{
3
  PORTB |= (1<<PB0);
4
5
  if((ADMUX & 0x0F) == poti_mux)
6
  {
7
    poti_momentanwert = (ADCW * 3) >> 2;
8
    poti_gefiltert = filterung8(poti_momentanwert, filter_array_poti, (unsigned char*) &filter_position_poti);
9
        
10
    if(mode == 0)
11
    {
12
      strom_sollwert = poti_gefiltert;
13
    }
14
15
    ADMUX &= ~(0x0F);
16
    ADMUX |= strom_mux;
17
  }
18
19
  else if((ADMUX & 0x0F) == strom_mux)
20
  {
21
    PORTB |= (1<<PB0);
22
23
    OCR0A = (ADCW >> 2);
24
    strom_momentanwert = (355L * (ADCW - i_offset)) >> 10;
25
    strom_gefiltert = filterung8(strom_momentanwert, filter_array_strom, (unsigned char*) &filter_position_strom);
26
27
    OCR0B = stromregler();
28
29
    ADMUX &= ~(0x0F);
30
    ADMUX |= poti_mux;
31
  }
32
33
  PORTB &= ~(1<<PB0);
34
35
  TIFR |= (1<<TOV0);
36
}

Und die Filterung so:
1
unsigned int filterung8(unsigned int input, unsigned int array[], unsigned char *position)
2
{
3
  volatile unsigned int filterwert = 2;
4
  volatile unsigned int min = 0xFFFF;
5
  volatile unsigned int max = 1;
6
7
  array[*position] = input;
8
  (*position)++;
9
10
  if((*position) > 9)
11
  {
12
    (*position) = 0;
13
  }
14
15
  for(unsigned char x = 0; x < 10; x++)
16
  {
17
    if(array[x] > max)
18
    {
19
      max = array[x];
20
    }
21
22
    else if(array[x] < min)
23
    {
24
      min = array[x];
25
    }
26
27
    filterwert += array[x];
28
  }
29
30
  filterwert -= (min + max);
31
  filterwert >>= 3;
32
33
return filterwert;
34
}

Ich würd mich echt freuen, wenn mir das einen verklickern könnte

Danke schonmal,

Gruß

von Andreas B. (Gast)


Lesenswert?

Joachim B. schrieb:
> Jetzt kommt das Putzige: die lokalen Variablen, in meiner Filterroutine
> muß ich volatile machen, damit es funktioniert!

Was heißt "damit es funktioniert" oder in welcher Form äußert sich das 
Nichtfunktionieren?

Und wie sind die globalen Variablen definiert? Aus dem Ausschnitt lässt 
sich nicht viel folgern.

von Laszlo H. (mobius)


Lesenswert?

volatile in lokalen variablen? Ne, DAS ist nicht üblich und DAS ist auch 
nicht dein problem / Fehler.

Les dir mal durch, wie lange lokale Variablen existieren. Sollte 
eigentlich in jedem C-Buch das etwas von sich hält in den ersten 
Kapiteln zu finden sein. Die kurze Antwort, bis zu der schließenden 
geschwellten Klammer. D.h. deine Variablen verlieren am Ende der 
Funktion ihre Werte und beim nächsten Eintritt haben sie wieder die 
Default-Werte. Und dann funzt dein Program natürlich nicht.

Was du suchst ist das Stichwort "static". Damit veränderst du den 
Gültigkeitbereich von Variablen (bzw. wenn es sich um globale Variable 
handelt, ihre Sichtbarkeiten). Am Besten liest du das aber auch in einem 
C-Buch nach (ist schon spät und ich bin müde).

gruß
Mobius

von Oliver (Gast)


Lesenswert?

Ohne kompletten Sourcecode ist das schwierig.

Ich vermute, dass du die entscheidenden globalen Variablen nicht 
volatile deklariert hast. Welche das sind, musst du selber rausfinden. 
Lies hier nochmal nach:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Datenaustausch_mit_Interrupt-Routinen

Oliver

von Joachim B. (jojo84)


Lesenswert?

Hi und danke erstmal für die Antworten!

Andreas B. schrieb:
> Was heißt "damit es funktioniert" oder in welcher Form äußert sich das
> Nichtfunktionieren?

Stimmt, etwas genauer hätte ich das Problem beschreiben können :) . 
Hatte nur Angst euch da evtl. auf eine falsche Fährte (?) zu locken...

Mit "damit es funktioniert" meine ich folgendes: wenn ich die 
Optimierung ausschalte dann funktioniert die Regelung ohne Probleme 
(stabiler, als ich dachte :) ). Das heißt der Soll-Istwert-Vergleich 
generiert das PWM-Tastverhältnis und die Istwerte von Strom und vom Poti 
werden in das dafür vorgesehene Array gespeichert. Zumindest nehme ich 
das mal recht stark an, dann "es funktioniert" ja.
Wenn ich nun die Optimierung einschalte passiert bei der PWM nichts 
sinnvolles mehr. Da kann ich am Poti drehen, bis ich schwarz werden, die 
PWM ist entweder bei 0% oder bei 50% (Begrenzung per SW). Da die 
Begrenzung auch funktioniert scheint die Routine für die Regelung auch 
halbwegs korrekt durchlaufen zu werden (hier findet ja die Begrenzung 
statt). So kam ich darauf, daß der Fehler wohl bei der Filterung der 
Werte drinsteckt... Und dann hab ich irgendwann das mit den "lokalen 
volatiles" gemacht...

Laszlo H. schrieb:
> Les dir mal durch, wie lange lokale Variablen existieren. Sollte
> eigentlich in jedem C-Buch das etwas von sich hält in den ersten
> Kapiteln zu finden sein. Die kurze Antwort, bis zu der schließenden
> geschwellten Klammer.

Danke für den Hinweis, ich bin ja bei Weitem kein Profi, aber das war 
wirklich schon bekannt :) .
Die Sache ist aber die, daß ich die lokalen Variablen in der 
Filter-Funktion ja für nichts anderes brauche. Meine Arrays für die 
Ist-Werte sind global und die minimalen und maximalen Werte innerhalb 
des Arrays will ich mir doch gar nicht dauerhaft merken. Und da hilft 
mir auch kein "static" weiter. Schließlich sind die Extremwerte ja nach 
jeder neuen Messung anders.

> D.h. deine Variablen verlieren am Ende der
> Funktion ihre Werte und beim nächsten Eintritt haben sie wieder die
> Default-Werte. Und dann funzt dein Program natürlich nicht.

s.o. ... genau das brauche ich aber. Jedes mal, wenn einer neuer 
Messwert vorliegt soll ja erneut nach den Extremwerten gesucht werden. 
Da macht das Laden mit den Default-Werten (1 und 0xffff) schon Sinn. 
"filterwert" ist oben noch mit "2" angelegt. Das hab ich nur zum 
ausprobieren gemacht, weil ich dachte, daß das dann nicht so strickt 
wegoptimiert wird, wie mit "0". Brachte aber nix...

Oliver schrieb:
> Ich vermute, dass du die entscheidenden globalen Variablen nicht
> volatile deklariert hast. Welche das sind, musst du selber rausfinden.

Hab ich ja versucht, aber da kam ich nicht weiter. Ich habe nach und 
nach JEDE glaobale Variable als volatile deklariert, aber das bracuhte 
alles nichts! darum schreibe ich ja hier, weil ich selbst nicht 
weiterkam. Alle Variablen sind global (außer die in der Regelung und in 
der Filterung).
OHNE Optimierung funktioniert die Regelung wie erwartet auch OHNE 
volatile bei den Filter-Variablen.
MIT Optimierung hilft kein "volatilisieren" der globalen Variablen, nur 
der lokalen in der Filterung...

> Ohne kompletten Sourcecode ist das schwierig.
Ich bereite den mal so auf, daß ihr was damit anfangen könnt :)

Freue mich über noch mehr Vorschläge!

Gruß

von JW (Gast)


Lesenswert?

naja, wer lokale Variablen als "volatile" declariert, der hat leider 
"volatile" und das Problem, das damit gelöst werden soll nicht 
verstanden.
"volatile" bedeutet: "He Compiler, diese Variable kann sich ändern, ohne 
daß Du bei der Analyse sagen könntest wo". Das passiert z.B. wenn ein 
Interrupt eine globale Variable andert. Eine parallel laufende 
Hauptschleife darf diese Variable dann nicht in ein Register plazieren, 
sonst sieht sie die Änderung durch die ISR nämlich nicht.
Weiter Vorschläge erst bei komplettem SourceCode, den die PWM wird ja 
wohl von "stromregler()" bestimmt, den wir noch gar nicht kennen.
Vielleicht mal ""stromregler() { return FIXED_DEBUG_WERT; }"
Oder aber: .lss mit/ohne Optimierung vergleichen.

von Joachim B. (jojo84)


Angehängte Dateien:

Lesenswert?

Oh, sorry, stimmt! Stromregler() hab ich vergessen, das sollte 
eigentlich mit rein...

Im Anhang mal die vollständige Datei. Ich finds echt nett, daß sich 
einige schon Gedanken gemacht haben. Wenn ich selbst ne Lösung hätte 
wäre ich ja nicht hier... :( .

Wie schon gesagt, ich hatte nach und nach jede einzelne globale Variable 
mal volatilisiert, auch mal alle auf einmal. Das halb aber nichts... :( 
.

JW schrieb:
> naja, wer lokale Variablen als "volatile" declariert, der hat leider
> "volatile" und das Problem, das damit gelöst werden soll nicht
> verstanden.

naja, das hab ich ja nicht von vern herein so gemacht. Ich hatte an 
anderen Stellen auch nachgelesen, wo z.B. stand, daß lokale Variablen eh 
nicht volatilisiert werden müßten. Ist ja nicht so, daß ich da nicht 
schon selbst versucht habe hiner zu kommen ;) ...
Mir kommt das auch bescheuert vor, aber das war eben das, was half! Da 
mein Hauptprogramm ja nichts tut, außer auf den ADC-Interrupt zu warten, 
hatte ich ursprünglich gar keine volatile-Variable, weil ja 
schließlich kein Variable an anderer Stelle als im Interrupt benutzt 
wird.
Ich hätte ja gefühlsmäßig alle Variablen, die in der ISR verändert 
werden, volatile gemacht. Aber ich sagte ja schon... das brachte 
nichts...

Danke nochmal und Gruß!

von Walter (Gast)


Lesenswert?

wie schnell kommen denn die Interupts,
sind deine Interruptroutinen schnell genug?

von Joachim B. (jojo84)


Lesenswert?

Ähm... der Timer für die PWM läuft mit Systemtakt -> 16 MHz. Da ich mit 
symmetrischer 8-Bit-PWM arbeite würde also der Overflow-Interrupt alle 
32 µs (31,2 kHz) auftreten. So schnell ist aber der ADC und die ISR 
nicht. Der ADC läuft mit 250 kHz, er würde also theoretisch ca. 19 kSps 
schaffen. Der ADC bekäme also nur ungefähr jeder zweiten Timer-Overflow 
mit.
Aber was spielt das für eine Rolle? Die Interrupts können sich doch gar 
nicht in die Quere kommen. Ich benutze ja nur einen einzigen: den des 
ADC, dessen Wandlungen durch den Timer-Overflow getriggert werden. Eine 
erneute Triggerung ist ja erst möglich, wenn ich das Flag, was den 
Interrupt ausgelöst hat, resette...

von (prx) A. K. (prx)


Lesenswert?

Funktioniert in der optimierten Version überhaupt irgendwas oder geht 
der gleich ab in den Nirvana? Du hast ja einen Testpin in der ISR, der 
müsste das anzeigen.

Ich würde raten, übersetzbaren Code reinzustellen. Damit man das durch 
den Compiler jagen kann ohne selber rumbasteln zu müssen. Und bei der 
Gelegenheit verrätst du und dann hoffentlich auch mal den Controller und 
welche Version des Compilers du verwendest.

von (prx) A. K. (prx)


Lesenswert?

PS: Der Code sollte nicht nur übersetzbar sein, sondern natürlich auch 
noch das Problem enthalten ;-).

von Joachim B. (jojo84)


Lesenswert?

Hi und danke nochmal für die bisherigen Bemühungen!

Also ich hab mir nochmal ein paar Gedanken gemacht und auch einen 
Verdacht. Das werde ich aber nochmal selbst auseinandernehmen und 
ausprobieren. Wenn es das dann war, dann werd ich den kompletten Code 
nochmal posten. Hab ja eigentlich nix zu verbergen... Könnte ja 
vielleicht einer was mit anfangen ;) .

ach ja,
@ A. K.
den Controllertyp hab ich doch im ersten Post schon preisgegeben :) ...

Gruß!

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.