Forum: Mikrocontroller und Digitale Elektronik Das ewige Ding mit den globalen Variabeln.


von lucifer (Gast)


Lesenswert?

Hallo zusammen

Ich habe ein Problem: meine ISR soll eine Variable ändern, welchen auch 
eine andere Funktion verwendet. Gibt es hierzu eine nicht-globale 
Lösung?
Und welche Gründe gibt es, keine globalen Variabeln zu verwenden 
(Anwendungsbereich uC)? Ich kenne:

- alle anderen Funktionen können darauf zugreifen
- mühsam, wenn eine Funktion, die globale Variabeln verwendet, woanders 
wieder gebraucht wird

: Verschoben durch User
von U40 (Gast)


Lesenswert?

lucifer schrieb:
> Gibt es hierzu eine nicht-globale
> Lösung?

nimm statische variablen... die sind dann ausserhalb deiner 
übersetzungseinheit nicht sichtbar.  ISR und deine "andere" funktion 
muessen dann innerhalb derselben übersetzungseinheit stehen.

lucifer schrieb:
> Und welche Gründe gibt es, keine globalen Variabeln zu verwenden

gründe dafür pauschal festzulegen ist schwierig... es geht dabei einfach 
nur darum, dass anfänger dazu tendieren alles global zu machen, damit 
sie nicht über eine vernünftige programmstruktur nachdenken muessen. es 
wird dann eben schnell unübersichtlich, wann welche funktion irgendeine 
variable verändert... und wenn man dann nach nen halben jahr das 
programm warten muss, traut man sich nicht mehr irgendwelche variablen 
anzufassen, denn es könnte ja jederzeit eine andere funktion beim 
variablenzugriff dazwischenfunken, an die man sich nicht mehr erinnert. 
solange man also eine vernünftige programmstruktur hat, die einem die 
übersicht über die globalen zugriffe gewährleistet, spricht nichts gegen 
den einsatz von globalen geschichten.

von Tom (Gast)


Lesenswert?

Beispiel:
Welches Programm ist übersichtlich, leicht änderbar und schrittweise 
(z.B. durch fest eincodierte statt gemessene Werte) in Betrieb nehmbar:
1
int main(void)
2
{
3
/*
4
init etc.
5
*/
6
   while (1)
7
   {
8
       read_adc();
9
       convert_voltage();
10
       format_stuff();
11
       send_values();
12
   }
13
}

1
int main(void)
2
{
3
/*
4
init etc.
5
*/
6
   while (1)
7
   {
8
       int adc_value = read_adc(ADC_CHANNEL_TEMP_SENSOR);
9
       int temperature = voltage_to_temperature(adc_value);
10
       char out_string[10];
11
       format_temp(out_string, temperature);
12
       send_rs232(out_string);
13
   }
14
}

von lucifer (Gast)


Lesenswert?

Vielen Dank für die sehr informativen Antworten.
Mit anderen Worten sind globale Variabeln ok, wenn man sie gut überlegt 
einsetzt, so nach dem Credo "think before code"...

von Karl H. (kbuchegg)


Lesenswert?

lucifer schrieb:
> Vielen Dank für die sehr informativen Antworten.
> Mit anderen Worten sind globale Variabeln ok, wenn man sie gut überlegt
> einsetzt, so nach dem Credo "think before code"...

das musst du sowieso.

Es kommt auch immer darauf an.
Bei kleinen µC vernwedet man konzeptionell öfter globale Variablen.
Warum?
Weil die Ausstattung mit Speicher nicht sehr üppig ist.
D.h. hier muss man ein Auge darauf haben, wieviel Speicher bereits 
verbraucht wurde. Mit globalen Variablen geht das recht einfach, weil 
man einfach nur den Speicherverbrauch der globalen Variablen addieren 
muss, um zu wissen, wo man in etwa liegt.

Sind alle Variablen aber in main beheimatet bzw. hat man in einzelnen 
Funktionen größere Arrays, dann tauchen die in der Speicherstatistik 
nicht richtig auf. Bzw. wenn deine Tools versuchen den 
Laufzeit-Speicherverbrauch zu schätzen, dann ist das recht schwierig, 
weil ja a prioir nicht bekannt ist, welchen Weg der Programmfluss nehmen 
wird, da der ja sehr oft auch von unbekannten Eingangswerten abhängt.

Auf Desktop System, mit mehr Speicher, sind globale Variablen aus den 
genannten Gründen weitgehend verpöhnt, auch wenn man da teilweise aus 
Gründen der Einfachheit einige Werte global halten kann bzw. wird, wenn 
klar ist, dass diese Werte (wie zb Konfigurationswerte) praktisch 
überall quer durch das Programm benutzt werden. Es macht dann wenig 
Sinn, diese Werte quer durch komplexe Funktionshierarchien überall 
durchzuschleusen.

Aber selbst wenn man auf einem µC aus praktischen Gründen globale 
Variablen benutzt, heisst das nicht, dass man einfach auf Biegen und 
Brechen alles global macht. Das ist nicht besonders sinnvoll. In 
praktisch jeder Funktion gibt es Variablen wie zb Schleifenvariablen, 
die sinnvollerweise eben nicht global gemacht werden, sondern 
funktionslokal gehalten werden. Da das aber üblicherweise nicht viele 
sind und die auch nicht allzuviel Speicher verbrauchen, ist es kein 
großer Beinbruch, wenn die in der Speicherstatistik nicht auftauchen. 
Speicher der zur Laufzeit für Daten benutzt wird, darf man sowieso nicht 
zu 100% anfüllen, sondern muss sich eine kleine Reserve unter anderem 
auch für die Laufzeitumgebung halten.

: Bearbeitet durch User
von Fitzebutze (Gast)


Lesenswert?

Hi,

> Auf Desktop System, mit mehr Speicher, sind globale Variablen aus den
> genannten Gründen weitgehend verpöhnt, auch wenn man da teilweise aus
> Gründen der Einfachheit einige Werte global halten kann bzw. wird, wenn
> klar ist, dass diese Werte (wie zb Konfigurationswerte) praktisch
> überall quer durch das Programm benutzt werden. Es macht dann wenig
> Sinn, diese Werte quer durch komplexe Funktionshierarchien überall
> durchzuschleusen.
>

Die sind nicht nur verpönt, sondern meist schlicht verboten (dasselbe 
gilt für die static-Konstrukte innerhalb Funktionen), nämlich dann, wenn 
mehrere Threads eine Funktion durchlaufen. Bei lokal veränderten 
Variablen müssen diese zwingend ausschliesslich im Kontext des Threads 
(also auf dem Stack oder in einem spezifischen Register-Fenster) 
existieren, sonst knallts (und ist teils übelst zu debuggen)
Gewollt ist dann was globales nur noch zum Austausch zwischen den 
Threads (was dann auch entsprechend 'atomic' gehalten werden muss)

Manche Informatiker würden behaupten, dass genau eine einzige globale 
Variable existieren darf/muss. Vom technischen Aspekt dieser Behauptung 
abgesehen, macht es auf jeden Fall einen aufgeräumten Eindruck, wenn im 
Programm ein globaler struct mit allen Konfigurations-Variablen 
existiert, also zB:
1
struct my_context {
2
    int setting_a;
3
    ....
4
} g_context;

Wenn nötig (-> Anforderungen an OO/thread-Sicherheit) hat man dann das 
g_*-Konstrukt schnell wieder aus dem Code gesäubert.

von Besucher (Gast)


Lesenswert?

Heiner schrieb im Beitrag #3968584:
> das uebliche globale Variablen bashing...

Beside - ist dir jemals der Gedanke gekommen das es für "bashing" auch 
rationale Gründe geben könnte?

von FelixW (Gast)


Lesenswert?

> - alle anderen Funktionen können darauf zugreifen
> - mühsam, wenn eine Funktion, die globale Variabeln verwendet, woanders
> wieder gebraucht wird
- man es verpennt, dass die Variable NUR von DER ISR gesetzt werden darf

Lösung:
* ISR schreibt in eine lokal sichtbare Variable (statisch, nicht global)
* global sichtbare Funktion gibt den Wert der Variablen zurück
(Optimierung schmeißt den Funktionsaufruf wieder raus (geht das bei C?) 
und wenn du deinen Code portierst kann du ggf. eine Ersatzfunktion 
schreiben)

Header:
1
int getADCValue();

Source:
1
int ADCValue;
2
int getADCValue(){return ADCValue;}

von W.S. (Gast)


Lesenswert?

lucifer schrieb:
> Ich habe ein Problem: meine ISR soll eine Variable ändern, welchen auch
> eine andere Funktion verwendet.

..und genau DARÜBER solltest du dir Gedanken machen. Dein Problem ist 
also nicht die globale Variable an sich, sondern der geordnete 
störungsfreie Zugriff mehrerer unabhängiger Instanzen auf gemeinsame 
Ressourcen.

Da mußt du eben deine grauen Zellen anwerfen und dir ein sinnvolles 
Lösungsprinzip ausdenken. Z.B. daß eine ISR eben nicht direkt 
irgendwelche Systemparameter verstellt, sondern nur eine Botschaft ins 
System einspeist, die das Neusetzen solcher Ressourcen an zentraler 
Stelle veranlaßt. Stichwort: Entkopplung per Event-Handling

W.S.

von Noch einer (Gast)


Lesenswert?

> int getADCValue()

Die Gefahr von Namenskonflikten verändert sich dadurch nicht. Mit etwas 
Pech hat dann eine Lib eine global sichtbare Funktion getADCValue().

von Dungeon keeper (Gast)


Lesenswert?

Noch einer schrieb:
> Die Gefahr von Namenskonflikten verändert sich dadurch nicht. Mit etwas
> Pech hat dann eine Lib eine global sichtbare Funktion getADCValue().

Deswegen macht man z.B. Modul-Präfixe wie:

ADC_getADCValue();
Sensors_getADCValue()

was nebenbei die Lesbarkeit der Quelltexte erhöht, da der Kontext der 
Funktion dadurch sichtbar wird.

von Uwe Bonnes (Gast)


Lesenswert?

Fall der uC mit Threads laeuft, haben die Threads und ggf auch der Main 
Thread nur ein Stuck vom Stack zugeteilt bekomnen. Grosse Auto-Variable 
konnen dann den Stack zum Ueberlauf bringen, daher also mit XXXalloc() 
oder static allozieren

von Noch einer (Gast)


Lesenswert?

Auslesen der Temperatursensoren perfekt gekapselt... Und dann schlägt 
die Realität zu.

Ein ganz anderes Modul soll einen freien ADC-Kanal nutzen und das muss 
nun irgendwie in die Abfrage der Sensoren mit reingefrickelt werden.

von Walter (Gast)


Lesenswert?

FelixW schrieb:
> Header:int getADCValue();
> Source:int ADCValue;
> int getADCValue(){return ADCValue;}

Vorsicht, das funktioniert auf einem 8-Bit avr nicht.  Für den Zugriff 
auf ein int (ADCValue) muss der Interrupt gesperrt werden damit die 
beiden Bytes des int auch zusammengehören!
Und es fehlt ein volatile

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.