Forum: Mikrocontroller und Digitale Elektronik Durch volatile explodiert der Speicherbedarf des AVR


von Michael N. (much)


Lesenswert?

Hallo Leute,

ich bin gerade dabei ein Programm zu schreiben welches das Taktsignal 
für einen Schrittmotor alla Atmel Application Note AVR446 erzeugt. Ich 
verwende also den 16bit timer eines AVR (attiny2313).
Der Wert des Output Compare Registers des Timers wird in der ISR 
berechnet und gesetzt. dazu benötige ich einige Variablen aus der 
Berechnung des vorangegangenen Timer Interrupts. Daher hab ich jetzt 
globale Variablen für diese Werte (Datentyp = uint16_t) angelegt. Wenn 
ich dies variablen jetzt aber volatile mache explodiert mein Program- 
und Data-Speicher.

ohne volatile:
1
AVR Memory Usage
2
----------------
3
Device: attiny2313
4
5
Program:    1494 bytes (72.9% Full)
6
(.text + .data + .bootloader)
7
8
Data:         97 bytes (75.8% Full)
9
(.data + .bss + .noinit)

EINE variable als volatile
1
AVR Memory Usage
2
----------------
3
Device: attiny2313
4
5
Program:    4862 bytes (237.4% Full)
6
(.text + .data + .bootloader)
7
8
Data:        361 bytes (282.0% Full)
9
(.data + .bss + .noinit)

Zum einen kann ich nicht ganz nachvollziehen weshalb eine uint8_t 
Variable plötzlich so viel speicher verbraucht. Kann mir das evtl. 
jemand erklähren?
Zum zweiten möchte ich natürlich gerne wissen ob man das irgendwie 
optimieren kann.

PS: hier noch die betrefende Codestelle:
1
//================================================================= globals ===
2
uint16_t accelerationStop; //volatile uint16_t accelerationStop;
3
uint16_t decelerationStart;
4
uint16_t goal;
5
uint16_t currentStep; //volatile uint16_t currentStep;
6
uint16_t counterValue; //volatile uint16_t counterValue;
7
uint8_t refreshCounterValue; //volatile uint8_t refreshCounterValue;
8
uint8_t busy; //volatile uint8_t busy;
9
10
//===================================================================== isr ===
11
ISR(TIMER1_COMPA_vect) {
12
13
  if (refreshCounterValue) {
14
    if (currentStep <= accelerationStop) { //acceleration mode
15
      counterValue = counterValue  - ((2 * counterValue) / (4 * (currentStep + 1) + 1));
16
      counterValue = counterValue / 2;
17
    } else if ((currentStep) >= decelerationStart) { //deceleration mode
18
      accelerationStop++;
19
      counterValue = ((4 * accelerationStop * counterValue + counterValue) / (4 * accelerationStop - 1));
20
      counterValue = counterValue / 2;
21
      accelerationStop -= 2;
22
    }
23
24
    OCR1AH = (uint8_t) (counterValue >> 8);
25
    OCR1AL = (uint8_t) (counterValue & 0xFF);
26
27
    currentStep++;
28
    if (currentStep >= goal) {
29
      TIMSK &= ~(1 << OCIE1A);
30
      busy = 0;
31
    }
32
    refreshCounterValue = 0;
33
    PORTB &= ~(1 << PB3);
34
  } else {
35
    refreshCounterValue = 1;
36
    PORTB |= (1 << PB3);
37
  }
38
}

von Falk B. (falk)


Lesenswert?

Dein Problem liegt woanders. Poste VOLLSTÄNDIGEN Code als Anhang.

von Εrnst B. (ernst)


Lesenswert?

Michael N. schrieb:
> ich dies variablen jetzt aber volatile mache explodiert mein Program-
> und Data-Speicher.

Dann mach sie nicht volatile.

Volatile brauchst du nur, wenn du sowohl in der ISR als auch übers 
main() auf die Variable zugreifen willst. (Ansonsten: "static" in der 
ISR definieren)

Und auch da kann es helfen, nur einzelne Zugriffe "volatile" 
auszuführen, anstatt die Variable immer und generell dem Optimierer zu 
entziehen.

von Ingo (Gast)


Lesenswert?

1
accelerationStop * counterValue

ohne Cast nach 32Bit ganz schön mutig...
Am volatile liegt es wohl nicht, obwohl es natürlich den Code etwas 
größer macht...


Ingo

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Michael N. schrieb:
> Wenn
> ich dies variablen jetzt aber volatile mache explodiert mein Program-
> und Data-Speicher.

Alle globalen Variablen, die Du ausschließlich in der ISR (und sonst 
nirgendwo) benutzt, solltest Du in der ISR mit static statt volatile 
deklarieren. Alle volatile-Variablen, die Du tatsächlich global 
benötigst, solltest Du bei mehrfacher Verwendung innerhalb der ISR einer 
lokalen Variablen zu Beginn der Funktion zuweisen und dann mit dieser in 
der ISR arbeiten. Am Ende der ISR kannst Du den Wert der lokalen 
Variablen wieder zurüspeichern.

Aber das kann nicht alles sein. Poste bitte den vollständigen Code.

von Yvonne J. (laserlight)


Lesenswert?

schau mal nach einem "inline"... das wäre für mich eher ein Grund...

von Michael N. (much)


Angehängte Dateien:

Lesenswert?

Hab jetzt mal den Code gezippt (siehe Anhang).

Ingo schrieb:
> ohne Cast nach 32Bit ganz schön mutig...

Stimmt. Hab das jetzt nur so schnell runterprogrammiert. Das Sollte ich 
auch noch mal an anderen Stellen im Code kontrollieren.

von Peter D. (peda)


Lesenswert?

Michael N. schrieb:
> EINE variable als volatileAVR Memory Usage
> ----------------
> Device: attiny2313
>
> Program:    4862 bytes (237.4% Full)
> (.text + .data + .bootloader)
>
> Data:        361 bytes (282.0% Full)
> (.data + .bss + .noinit)

Da wird die unopimierte Float-Lib hinzugelinkt, die braucht viel Flash 
und 264 Byte SRAM (361 - 97 = 264).
Du hast irgendwo float drin, was aber der Compiler schon ausrechnen kann 
(Konstante).
Das volatile verhindert das Ausrechnen.
Volatile nicht mit der Gießkanne verteilen, sondern nur dort, wo nötig.
Z.B. nicht bei Konstanten.


Peter

von Michael N. (much)


Lesenswert?

Hab jetzt wie vorgeschlagen auch mal an anderer Stelle gesucht und bin 
glaub ich fündig geworden. Der Übeltäter scheint folgender 
Codeausschnitt zu sein:
1
counterValue = counterValue * 0.676;

Die Formeln hab ich wohl zu sorglos heruntergetippt. Werd noch mal alle 
genau unter die Lupe nehmen müssen.

Frank M. schrieb:
> Alle globalen Variablen, die Du ausschließlich in der ISR (und sonst
> nirgendwo) benutzt, solltest Du in der ISR mit static statt volatile
> deklarieren.

Das kann ich aber nur machen wenn ich die Variablen nach dem verlassen 
der ISR nicht mehr benötige, was in meinem Fall ja nicht der Fall ist da 
ich sie ja beim nächsten Aufruf der ISR wieder verwende, oder hab ich da 
etwas falsch verstanden?

von Chris (Gast)


Lesenswert?

Michael N. schrieb:
> Das kann ich aber nur machen wenn ich die Variablen nach dem verlassen
> der ISR nicht mehr benötige, was in meinem Fall ja nicht der Fall ist da
> ich sie ja beim nächsten Aufruf der ISR wieder verwende, oder hab ich da
> etwas falsch verstanden?

Dafür auch das static davor. Somit bleiben sie erhalten auch beim 
verlassen der ISR.

von Rolf Magnus (Gast)


Lesenswert?

Es liegt daran:
1
  counterValue = (F_CNTR * sqrtf((2 * ALPHA) / WP_MAX));
2
  counterValue = counterValue / 2;
3
4
  //set counter
5
  OCR1AH = (uint8_t) (counterValue >> 8);
6
  OCR1AL = (uint8_t) (counterValue & 0xFF);
7
8
  //multiply first counter value with correction value
9
  counterValue = counterValue * 0.676;

Ohne volatile kann der Compiler die erste Zuweisung mit einer 
Konstanten, die Division und die float-Multiplikation zusammenfassen und 
zur Compilezeit ausrechnen. Für die Werte von OCR1AH/AL kann auch 
enstprechend gleich eine Konstante eingesetzt werden. Wenn du die 
Variable volatile machst, müssen aber alle Aktionen, die counterValue 
lesen, ändern und wieder schreiben, wirklich zur Laufzeit durchgeführt 
werden, insbesondere die float-Multiplikation, die dann ein Linken an 
die ganze float-Library nach sich zieht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Michael N. schrieb:
> Das kann ich aber nur machen wenn ich die Variablen nach dem verlassen
> der ISR nicht mehr benötige, was in meinem Fall ja nicht der Fall ist da
> ich sie ja beim nächsten Aufruf der ISR wieder verwende, oder hab ich da
> etwas falsch verstanden?

Ja, da hast Du was falsch verstanden. static-Variablen bleiben auch nach 
dem Return der Funktion am Leben, d.h. sie behalten ihren Wert - genauso 
wie globale Variablen.

Dafür ist static da: Lebenszeit "unendlich", aber nicht global 
zugreifbar.

Dir fehlt essentielles C-Wissen...

von Falk B. (falk)


Lesenswert?

Da sind noch weitere Problemfälle drin, z.B. busy. Volatile will 
verstanden und richtig angewendet sein, siehe Interrupt. Und das 
Thema der Fließkommarechnung eliminiert man mit Festkommaarithmetik.

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.