Forum: Mikrocontroller und Digitale Elektronik Deklaration von Variablen (volatile) bei Manipulation in ISR


von Frank (Gast)


Lesenswert?

N'Abend zusammen!

Ich wollte mal eine simple Frage loswerden:

Ich habe eine .c und eine .h für das Einlesen eines ADCs geschrieben.
Die .c beeinhaltet ein struct, welches verschiedene Variablen für die 
Kommunikation und auch z.B. das ADC-Ergebnis beeinhaltet. Zugriff auf 
die Variablen erfolgt durch Funktionen, welche in der .h deklariert 
sind.

Also z.B. "ergebnis = hole_adc_regebnis();"

Die State-Machine für das holen der Daten ist ebenfalls in der .c 
enthalten und wird über die ISR des Empfangsmoduls gesteuert.

Die Frage ist jetzt, ob ich alle Variablen, die während der Ausführung 
der State-Machine durch die ISR als volatile deklarieren muss.

Falls das unklar ist, nochmal anders:
1
#include "adc_funktionen.h"
2
...
3
uint16_t ergebnis;
4
...
5
main
6
{
7
  ...
8
  ergebnis = hole_adc_ergebnis();
9
  ...
10
}
11
12
ISR
13
{
14
  statemachine();
15
}

Die.h-Datei:
1
adc_funktionen.h
2
...
3
void statemachine( void );

Die .c-Datei:
1
#include "adc_funkctione.h"
2
...
3
struct adc
4
{
5
  uint16_t neues_ergebis;
6
  ...
7
}
8
9
struct adc adc_daten;
10
11
...
12
13
void statemachine( void )
14
{
15
  ...
16
  adc_daten.neues_ergebnis = Schieberegisterinhalt;
17
  ...
18
}

Muss die Variable, welche jetzt in der Funktion statemachine(), welche 
widerum durch die ISR aufgerufen wird, als volatile deklariert werden?

Gruß & danke!

von Oliver J. (skriptkiddy)


Lesenswert?


von Stefan F. (sfrings)


Lesenswert?

Ja, muss sein.

Zugriff auf Variablen ist nicht Thread Safe, es se denn, sie sind als 
"volatile" deklariert.

Alle Variablen, die Du sowohl in der ISR als auch im Hauptprogramm 
verwendest, verwendest Du in zwei Threads. Wenn Du sie nicht als 
volatile deklarierst, riskierst Du, dass der Optimizer zu viel 
optimiert. Beispiel:
1
int i=0;
2
3
ISR {
4
  i++;
5
}
6
7
main {
8
  while (1) {
9
    if (i>12) { 
10
      tuwas;
11
    }
12
  }
13
}

Während die ISR bei irgendeinem Ereignis die Variable i um eins erhöht, 
testes das Hauptprogramm in einer Endlos-Schleife, ob i>12 ist und macht 
dann irgend etwas. Ohne volatile könnte der Optimizer folgenden Code 
generieren:
1
ISR {
2
  Kopiere Variable i in Register R1 und R2
3
  Erhöhe Register R1 um eins
4
  Erhöhe Register R2 um eins, wenn R1 übergelaufen ist
5
  Kopiere Register R1 und R2 in Variable i
6
}
7
8
main {
9
  Kopiere Variable i in Register R3 und R4
10
  :marke
11
  Wenn R3 > 12 oder R4 > 0, dann tuwas
12
  Springe zu :marke
13
}

Variablem werden manchmal in Register kopiert, weil der bestimmte 
Operationen in Registern schneller sind. Manche Operationen können auch 
nur mit Registern durchgeführt werden.

Lass uns mal einfach annehmen, dass bei Deinem Prozessor ein direkter 
Vergleich zwischen einer Variablen (also RAM) und einer Konstanten nicht 
geht. Aber der Prozessor kann ein Register mit einer Konstanten 
vergleichen.

Schau Dir den obigen Pseudo-Code an. Wenn die ISR zwölf mal ausgeführt 
wurde, dann hat die Variable i den Wert 12. Aber das Register R3/R4 hat 
immer noch den Wert 0, die main Funktion wird also niemals das tun, was 
sie sollte.

Der Optimizier geht von einem Single-Threaded Programm aus. Er geht 
davon aus, dass Funktionen niemals untrbrochen werden. Deswegen erzeugt 
er (wenn Du Pech hast) nicht funktionierenden Maschinen-Code. Durch die 
"volatile" Deklaration sagst Du dem Optimizer, dass er diese Variable 
ganz besonders vorsichtig behandeln muss, weil sie jederzeit verändert 
werden kann, auch an Stellen, wo der Optimizier es nicht erwartet. Mit 
"volatile" würde der Pseudo-Code so aussehen:

1
ISR {
2
  Sperre andere Interrupts
3
  Kopiere Variable i in Register R1 und R2
4
  Erhöhe Register R1 um eins
5
  Erhöhe Register R2 um eins, wenn R1 übergelaufen ist
6
  Kopiere Register R1 und R2 in Variable i
7
  Erlaube andere Interrupts
8
}
9
10
main {
11
  :marke
12
  Sperre alle Interrupts
13
  Kopiere Variable i in Register R3 und R4
14
  Erlaube alle Interrupts
15
  Wenn R2 > 12 oder R4 > 0, dann tuwas
16
  Springe zu :marke
17
}

Das Sperren anderer Interruptsin der ISR kann entfallen, wenn der 
Mikrocontroller konstruktions-bedingt nur einen Interrupt-Level kennt 
(also Interrupts nicht wiederum durch andere unterbrochen werden 
können).

Im Hauptprogramm dient das Sperren der Interrupts nun dazu, zu 
verhindern, dass die beiden Bytes der int Variable sich verändern, 
während sie in die Register R3 und R4 kopiert werden. Weiterhin wird die 
Variable nun bei jedem Schleifendurchlauf erneut in die Register 
kopiert, so dass R3 jetzt tatsächlich irgendwann auch mal den Wert 12 
enthalten kann.

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.