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
wieso willst du keine globalen variablen benutzen? das ist doch völlig unproblematisch.
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.
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.
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.
1 | #define SET 0
|
2 | #define GET 1
|
3 | |
4 | // Interrupthandler:
|
5 | ...
|
6 | // Wert vom Hauptprogramm übernehmen
|
7 | wert1 = argument(GET, 0); |
8 | ...
|
9 | // Rückgabewert ans Hauptprogramm übergeben
|
10 | ergebnis(SET, wert2); |
11 | ...
|
12 | |
13 | |
14 | |
15 | // Hauptprogramm
|
16 | ...
|
17 | // Wert an Interrupthandler übergeben
|
18 | argument(SET, wert1); |
19 | ...
|
20 | // Wert vom Interrupthandler übernehmen
|
21 | wert2 = ergebnis(GET, 0); |
22 | |
23 | |
24 | // Zugriffsfunktionen für Argument und Ergebnis mit gekapseltem Wert
|
25 | |
26 | uint8_t argument(uint8_t mode, uint8_t w) { |
27 | static uint8_t wert; |
28 | |
29 | if(mode == SET) |
30 | wert = w; |
31 | else // GET |
32 | w = wert; |
33 | return w; |
34 | }
|
35 | |
36 | uint8_t ergebnis(uint8_t mode, uint8_t w) { |
37 | static uint8_t wert; |
38 | |
39 | if(mode == SET) |
40 | wert = w; |
41 | else // GET |
42 | w = wert; |
43 | return w; |
44 | }
|
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! =================================
@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.
> 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?
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
>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.
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.
>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.
> 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.
@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.
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.
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.
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
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
> 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
>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
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...
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.
Wird ja immer obskurer: Wieso sind Interrupt-Routinen perse kein guter Programmstil? Das kann ich nicht nachvollziehen.
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...
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).
Vielen Dank für die zahlreichen Antworten. Werde das ganze mit einer globalen Struktur (struct), wie weiter oben vorgeschlagen, lösen. Grüße Michael
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.