www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Vermeidung Globaler Variablen?


Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade dabei ein Programm mit dem AVR Studio für einen ATmega16 
in C zu schreiben. Jetzt bin ich auf ein Problem gestoßen, auf das mir 
keine Lösung einfällt.
Ich will in einer Interruptroutine, die alle 10ms zyklisch aufgerufen 
wird mehrere Pins abfragen. Wie kann ich die Werte aus der 
Interruptroutine an zurückgeben, ohne dabei globale Variablen zu 
benutzen? Ebensowenig weiß ich, wie ich Werte an die Interruptroutine 
übergeben kann. Kann mir da jemand weiterhelfen?

Grüße
Michael

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wieso willst du keine globalen variablen benutzen?
das ist doch völlig unproblematisch.

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die globalen Variablen (Achtung: volatile) sind doch völlig ok!

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
eine Interrupt-Routine kann prinzipiell keinen Wert zurückgeben. Wohin 
auch?
Und du kannst einer ISR auch keine Variablen übergeben (das würde den 
Sinn eines Interrupts negieren, ich habe hier was für dich, na komm 
schon, ich warte solange)
Aus welchem Grund willst du keine globalen Variablen? Sie wurden 
erfunden, um sie zu benutzen. Ok, man kann und sollte sparsam damit 
sein, ohne geht es meiner Meinung nach nicht.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
du kannst den globalen variablen etwas von ihrer 'globalität' nehmen, 
indem du sie 'static' deklarierst. dann sind sie nur lokal (in der 
übersetzungseinheit, in der sie angelegt wurden) vorhanden.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn dich die globalen Variablen völlig um den Verstand bringen: Es
geht tatsächlich auch ohne. Das folgende Beispiel soll aber nicht als
Empfehlung, sondern eher als Proof-of-Concept verstanden werden ;-)

Idee: Statt in globalen werden die Werte in den statischen lokalen
Variablen 'wert' der Zugriffsfunktionen argument() und ergebnis()
gespeichert.

#define SET 0
#define GET 1

// Interrupthandler:
  ...
  // Wert vom Hauptprogramm übernehmen
  wert1 = argument(GET, 0);
  ...
  // Rückgabewert ans Hauptprogramm übergeben
  ergebnis(SET, wert2);
  ...



// Hauptprogramm
  ...
  // Wert an Interrupthandler übergeben
  argument(SET, wert1);
  ...
  // Wert vom Interrupthandler übernehmen
  wert2 = ergebnis(GET, 0);


// Zugriffsfunktionen für Argument und Ergebnis mit gekapseltem Wert

uint8_t argument(uint8_t mode, uint8_t w) {
  static uint8_t wert;

  if(mode == SET)
    wert = w;
  else // GET
    w = wert;
  return w;
}

uint8_t ergebnis(uint8_t mode, uint8_t w) {
  static uint8_t wert;

  if(mode == SET)
    wert = w;
  else // GET
    w = wert;
  return w;
}
Das Ganze benötigt aber sinnloserweise zusätzlichen Programmspeicher.

Auch wenn dir deine Programmierlehrer und -lehrbücher anderes weis
zu machen versuchen:

            =================================
            Global ist schön, Goto ebenfalls!
            =================================

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das löst ja nicht das Problem.

Autor: mein Name ist Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@crazy horse

> eine Interrupt-Routine kann prinzipiell keinen Wert zurückgeben. Wohin
> auch?

Ich sammle über eine Interrupt-Routine A/D-Werte in ein globales
Variablen-Array das dann vom Hauptprogramm (FFT) ausgewertet wird.

> Und du kannst einer ISR auch keine Variablen übergeben (das würde den
> Sinn eines Interrupts negieren, ich habe hier was für dich, na komm
> schon, ich warte solange)

Ich habe eine Applikation, die ein LC-Display über SPI ansteuert
und das in zwei verschiedenen Modi und auch unterschiedlichen
Interruptzeiten. Das wird vom Hauptprogramm im fliegenden
Wechsel durch eine Variable umgeschaltet.
Allerdings hatte ich kein Problem damit, das als static globale
Variable zu lösen.

Vielleicht erläutert Michael noch den Grund, warum er keine
globale Variable einsetzen möchte.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> das löst ja nicht das Problem.

Welches Problem?

>> eine Interrupt-Routine kann prinzipiell keinen Wert zurückgeben. Wohin
>> auch?
>
> Ich sammle über eine Interrupt-Routine A/D-Werte in ein globales
> Variablen-Array das dann vom Hauptprogramm (FFT) ausgewertet wird.

Die ISR wird aber nicht vom Hauptprogram aufgerufen. Wo sollte dieses 
also den Rückgabewert herbekommen?

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
zunächst mal danke für die vielen Antworten.
@mein Name ist Gast:
Der Grund für diese Frage ist folgender: Ich bin/war der Meinung das es 
kein guter Programmierstiel ist, mit globalen Variablen zu arbeiten (C++ 
Programmierung für PC). Scheinbar ist es jedoch so, dass es bei C im 
Mikrocontrollerbereich durchaus üblich ist, mit globalen Variablen zu 
arbeiten.
Was ist besser:
Wenn ich alle Variablen global definiere, die von irgendeiner 
Interruptroutine einen Wert zurückliefern bzw. an eine Interruptroutine 
einen Wert übergeben oder wenn ich einen einzigen Zeiger auf ein Array 
global definiere (sozusagen auf einen Speicherbereich der für 
Interruptübergabeparameter reserviert ist)?

Grüße
Michael

Autor: sepp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Vielleicht erläutert Michael noch den Grund, warum er keine
>globale Variable einsetzen möchte.

Das würde ich auch gerne wissen.

Meiner Meinung nach führt in diesem Fall nichts an globalen Variablen 
vorbei.
Es kann sinn machen, diese als static zu deklarien oder wie yalu 
vorschlägt zu kapseln. Das wichtigste ist, das man den Zugriff auf die 
Variable schützt.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaube zwar, dass Michael, während ich diesen Text schrieb, gerade
ein Missverständnis aufgelöst hat, aber das muss ich jetzt trotzdem
los werden:

crazy horse schrieb:

> das löst ja nicht das Problem.

Doch.

Aber vielleicht haben wir Michaels Problem nur unterschiedlich
verstanden.

Meine Interpretation seines Problems:

> Ich will in einer Interruptroutine, die alle 10ms zyklisch
> aufgerufen wird mehrere Pins abfragen.

  In der Interruptroutine entstehen Daten.

> Wie kann ich die Werte aus der Interruptroutine an zurückgeben,

  Diese sollen in irgendeiner Weise dem Hauptprogramm zugänglich
  gemacht werden (da fehlt im Originalbeitrag wohl etwas).

> ohne dabei globale Variablen zu benutzen?

  Die bekannte Methode, Daten in der Interruptroutine in globale
  Variablen zu schreiben und vom Hauptprogramm wieder auszulesen,
  funktioniert zwar, hat aber den Schönheitsfehler, dass eben globale
  Variablen benutzt werden. Geht es auch ohne?

> Ebensowenig weiß ich, wie ich Werte an die Interruptroutine
> übergeben kann.

  Wie können umgekehrt Daten, die im Hauptprogramm entstehen, der
  Interruptroutine zugänglich gemacht werden?


In meinem Beitrag beschreibe ich deshalb einen Weg, wie ein bidirek-
tionaler Datenfluss zwischen Hauptprogramm und Interruptroutine
realisiert werden kann, ohne dabei globale Variablen zu benutzen.

Aus deinem Beitrag entnehme ich, dass du Michaels Frage so verstanden
hast, dass dieser Datenfluss in Form von Funktionsargumenten und
Funktionswerten erfolgen soll. Das geht natürlich nicht, weil die
Interruptroutine nicht vom Hauptprogramm als Funktion aufgerufen wird.


Und um noch einmal Missverständnisse auszuschließen: Es ist es meiner
Meinung nach durchaus legitim, zum Datenaustausch mit der
Interruptroutine globale oder (besser) übersetzungseinheitslokale (s.
Peter) Variablen zu verwenden. In C++ (wonach Michael aber nicht
gefragt hat) würde die Interruptroutine als Klassenmethode und die
Variablen zur Datenübergabe als Klassenvariablen implementiert.
Dadurch sind die Variablen noch etwas "weniger" global.

Autor: sepp (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Der Grund für diese Frage ist folgender: Ich bin/war der Meinung das es
>kein guter Programmierstiel ist, mit globalen Variablen zu arbeiten (C++
>Programmierung für PC). Scheinbar ist es jedoch so, dass es bei C im
>Mikrocontrollerbereich durchaus üblich ist, mit globalen Variablen zu
>arbeiten.

Auf einem PC ist der Stack sehr gross. Hier ist es kein so grosses 
Problem die ganzen Variablen lokal zu deklarieren. Auf einem uC mit 
kleinem Stack ist dies sehr problematisch. Da ist es besser ein paar 
Bytes an RAM zu verschwenden als ein Stack Overflow zu riskieren.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wenn ich alle Variablen global definiere, die von irgendeiner
> Interruptroutine einen Wert zurückliefern bzw. an eine
> Interruptroutine einen Wert übergeben oder wenn ich einen einzigen
> Zeiger auf ein Array global definiere (sozusagen auf einen
> Speicherbereich der für Interruptübergabeparameter reserviert ist)?

Viele globale Variablen sind unübersichtlich, insbesondere dann, wenn
sie quer über das ganze Programm verstreut sind.

Besser ist es, alle globale Variablen, die einen gemeinsamen Zweck
erfüllen (in diesem Fall der Datenaustausch mit der Interruptroutine)
zu einer Struktur (struct) zusammenzufassen. Das bringt mehr
Durchblick und die Anzahl der globalen Variablen wird auf 1 reduziert.

Autor: mein Name ist Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf Magnus
Das Interrupt-Programm setzt ein Flag, wenn die Daten
für das Hauptprogramm bereit liegen.
Ähnlich funktioniert auch eine Interrupt-Routine für
Tastatur-Abfrage. In der Interrupt-Routine empfangene
Daten werden in einem Puffer abgelegt und ein Zeichen-
zähler signalisiert dem Hauptprogramm, dass da etwas
angekommen ist.
Die Zahl der Beispiele ist unüberschaubar.
Fängt eigentlich mit jedem simplen interrupt-gesteuerten
UART-I/O an.

Autor: mein Name ist Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Michael,
es ist absolut gang und gäbe, Datenaustausch zwischen
Hauptprogramm und Interrupt-Routine über globale
Variable abzuwickeln. Da ist nichts anrüchiges dran.
Wie bereits oben vorgeschlagen, wird die Sache
zusätzlich übersichtlich und strukturiert (obwohl
nicht zwingend notwendig), wenn man solche Schnitt-
stellen in einem struct zusammen fasst.

Autor: mein Name ist Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Michael,
wenn Du die Datenübergabe partout nicht über globale
Variable vornehmen willst - obwohl nichts wirklich
dagegen spricht - kannst Du die Daten einer Funktion
(ne Art Push/Pop-Funktion) übergeben, welche wechsel-
seitig von Interrupt-Routine und Hauptprogramm bedient
wird. Mir fällt jetzt aber kein konkreter Fall ein,
bei dem man dadurch wirklich einen Vorteil gewinnt
der diesen Mehraufwand rechtfertigt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael wrote:
> Der Grund für diese Frage ist folgender: Ich bin/war der Meinung das es
> kein guter Programmierstiel ist, mit globalen Variablen zu arbeiten (C++
> Programmierung für PC).

Auf MCs ist das keine Frage des Stils, sondern der Effizienz.

Lokale Variablen  werden oft in Registern gehalten und sind damit 
schneller und benötigen weniger Code.
Auch wird nicht unnötig Speicher blockiert, d.h. viele lokale Variabeln 
können sich den Speicher teilen (Überlagerung).

Variablen mit begrenzter Lebenszeit sollten daher immer lokal sein.

Interrupts sind aber aktiv, solange das Programm läuft. Damit müssen 
auch die Variablen zur Parameterübergabe unendlich leben.


Peter

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und selbst die modernste OOP Sprache benötigt globale Variablen. Nur das 
sie dort besser "versteckt" werden. Der Irrtum besteht also darin das es 
heutzutage eine allgemeine Meinung ist das OOP ohne solche Variablen 
auskäme, das ist aber falsch.

Gruß Hagen

Autor: Olaf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> dagegen spricht - kannst Du die Daten einer Funktion
> (ne Art Push/Pop-Funktion) übergeben,

Das ist genau die Art wie ich das Problem loese. Zusaetzlich allerdings 
implementieren meine Funktionen noch eine Fifo und ich lese daraus erst 
wenn in der Fifo >1 Zeichen drin sind.

So stelle ich naemlich sicher das mein Hauptprogramm Daten liesst 
waehrend die Interruptfunktion die Daten veraendert, etwas das bei 
Datentypen die der Prozessor nicht in einem Befehl verarbeiten kann 
durchaus passieren kann.

Olaf

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Das ist genau die Art wie ich das Problem loese. Zusaetzlich allerdings
>implementieren meine Funktionen noch eine Fifo und ich lese daraus erst
>wenn in der Fifo >1 Zeichen drin sind.

Warum erst bei > 1 Zeichen ? Wenn der FIFO nur in eine Richtung geht, 
zb. ISR -> Anwendung oder Anwendung -> ISR, dann kannst du auch schon 
lesen bei > 0 Zeichen im FIFO, ohne Kollisionen zu provozieren.

Gruß Hagen

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael wrote:
> Der Grund für diese Frage ist folgender: Ich bin/war der Meinung das es
> kein guter Programmierstiel ist, mit globalen Variablen zu arbeiten (C++
> Programmierung für PC).

Na ja, Interrupt-Routinen sind auch kein guter Stil, aber manchmal gehts 
eben nicht ohne...

Der 'gute Stil' ist die Politcal Correctnes der Programmierer.

Leider ist das in keinem Fall ein Ersatz für den Einsatz des 
Verstandes...

Autor: Winfried (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da werden manchmal Schreckgespenster aufgebaut: Bloß keine globalen 
Variablen!

Viel wichtiger finde ich, die Problematik von globalen Variablen genau 
zu verstehen, um dann abschätzen zu können, wann sie gefährlich sind und 
wann sie Sinn machen.

Schlechter Programmierstil ist, wenn man globale Variablen hat, und 
tausend Programmteile verändert diese wild durcheinander. Irgendwann hat 
keiner mehr eine Übersicht, welche Variable durch was beeinflusst wird.

Eine sauberer globaler Übergabe-Speicher, um Information von 
Interruptroutinen an das Hauptprogramm zu übergeben oder umgekehrt, da 
sehe ich keine Probleme. Jedoch muss man auch hier aufpassen, dass nicht 
zu viele Programmteile darin rumfuschen, also die Daten beeinflussen.

Wichtig ist auch zu sehen, dass jede globale Variable eine Schnittstelle 
ist - eine Kommunikation zwischen Programmteilen, die damit eine Bindung 
eingehen. In der OO-Programmierung gilt die Leitidee, wenige und schmale 
Schnittstellen, die eindeutig erkennbar sind. Damit hält man sich eine 
Menge Probleme vom Hals.

Autor: Winfried (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wird ja immer obskurer: Wieso sind Interrupt-Routinen perse kein guter 
Programmstil? Das kann ich nicht nachvollziehen.

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Winfried wrote:
> Wird ja immer obskurer: Wieso sind Interrupt-Routinen perse kein guter
> Programmstil? Das kann ich nicht nachvollziehen.

Politsch korrekte Leute haben mit Ironie prinzipbedingt so ihre 
Probleme...

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
genau, da fehlt noch ne EU-Richtlinie, mit einer Deutsch-gründlichen 
Erweiterung, wie man zu programmieren hat. Mit allem Ausnahmeregelungen, 
je nach System, Systemanforderungen und verwendeten Programmiersprachen. 
Das gäbe wieder mal einen schönen Wust (mehrere Bände).

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die zahlreichen Antworten.
Werde das ganze mit einer globalen Struktur (struct), wie weiter oben 
vorgeschlagen, lösen.

Grüße
Michael

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.