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
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.
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 | }
|
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"...
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
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.
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?
> - 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;} |
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.
> int getADCValue()
Die Gefahr von Namenskonflikten verändert sich dadurch nicht. Mit etwas
Pech hat dann eine Lib eine global sichtbare Funktion getADCValue().
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.
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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.