mikrocontroller.net

Forum: Compiler & IDEs Wie Messwerte im Programm verfügbar machen ohne globale Variablen


Autor: Mike Litoris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi!

Ich habe folgende Situation:
Ich lese auf meinem Mikrocontroller zyklisch mehrere Sensoren ein. Diese 
müssen dann für mehrere Funktionen zur Verfügung stehen:
- Schreiben auf SD Karte
- Steuern von LEDs
- Regeln eines Motors

Bis jetzt habe ich das immer so gemacht, dass die Messwerte in globale 
Variablen geschrieben wurden, diese können dann von den einzelnen 
Funktionen abgerufen werden.

Ich muss dazu sagen, dass ich absolut kein Profi der Programmierung bin. 
Ich "habe gehört", dass globale Variablen aufgrund Speicherverbrauch und 
Sicherheit zu vermeiden sind.

Wie löst man das intelligenter? Ist die Übergabe mit Pointern hier 
richtig? Ich bin mir leider auch nicht sicher, wie man sowas am besten 
angeht...

Grüße und Danke!

Autor: PfuschenderAutodidakt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mike Litoris schrieb:
> Wie löst man das intelligenter? Ist die Übergabe mit Pointern hier
> richtig? Ich bin mir leider auch nicht sicher, wie man sowas am besten
> angeht...

Ich verwende für sowas Pointer auf Strukturen.
Es gibt z.B. ein struct "Messwerte". Das ist im Header der Funktion 
deklariert (Ausschließlich als type, nicht als struct!).

Jede Funktion, die die Messwerte benötigt, benötigt halt dann den header 
und bekommt einen pointer darauf.

Beispiel (LED-Streifen):

//im header:
typedef struct
{
    uint8_t     ch_red;                 //channel setting : which is connected to what on the LPC6803: 
                                        //0 = OUT0, 1= OUT1, 2 = OUT2, other = invalid
    uint8_t     ch_green;               // ""
    uint8_t     ch_blue;                // ""
    uint16_t    NrOfLED;                //Nr. Of Pixels in series
    uint8_t     stripe_nr;              //number of stripe
    bool        active;                 //stripe active / inactive
    rgb         col;                    //color
    rgb         data[stripe_max_len];   //pixel data for stripe1
}LED_CFG;

//Funktion:
void set_Stripe(LED_CFG *cfg, uint8_t brightness);

Das Struct wird einmal im main als Variable deklariert, und nur der 
Pointer herumgereicht.

Im Beispiel hat jeder LED-Streifen ein eigenes Struct vom Type LED_CFG.

Structs lassen sich beliebig verschachteln. Man kann sich auf die Art im 
main() eine Struktur deklarieren, die alle Daten enthält, und in der 
alle Funktionsblöcke logisch gegliedert sind.
z.B. kann man der Struktur "Messung" eine Substruktur "ADC" eingliedern, 
die eine Substruktur "ADC-settings" enthält.
Die Übersicht ist leichter zu behalten, als bei einzelnen Variablen.

Den Speicherverbraucht sieht man auch sofort.

Wichtig ist: Eine Funktion bekommt ausschließlich die Substruktur, die 
sie WIRKLICH benötigt! Mehr führt zum Verlust der Übersicht, und zu 
schlechter Wiederverwendbarkeit des Codes!

Ob das so gut ist, weiß ich aber auch nicht, denn ich bin auch nur 
Autodidakt.
Ich bin damit bisher gut gefahren, vor allem bei richtig komplexen 
Projekten mit hunderten oder tausenden Variablen. Man benötigt keine 
statics mehr, das ist auch hilfreich.

Autor: Sebastian S. (amateur)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin bisher davon ausgegangen, das eine Variable immer ihren Platz 
benötigt. Egal ob lokal oder global.

Soweit mir bekannt ist auch das alignment gleich.

Bei der unnötigen Verwendung von Zeigern gilt:
Mehr Programmspeicher (Zeiger holen + Daten holen)
etwas langsamer, da mindestens zwei Schritte.

Autor: Teddy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian S. schrieb:
> Ich bin bisher davon ausgegangen, das eine Variable immer ihren
> Platz
> benötigt. Egal ob lokal oder global.
>
> Soweit mir bekannt ist auch das alignment gleich.
>
> Bei der unnötigen Verwendung von Zeigern gilt:
> Mehr Programmspeicher (Zeiger holen + Daten holen)
> etwas langsamer, da mindestens zwei Schritte.

Der Speicherbereich einer lokalen Variable außerhalb der Main-Fkt wird 
immer nach beenden der Fkt immer freigegeben, bei Globalen dagegen 
nicht.

Autor: Carl D. (jcw2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian S. schrieb:
> Ich bin bisher davon ausgegangen, das eine Variable immer ihren Platz
> benötigt. Egal ob lokal oder global.
>
> Soweit mir bekannt ist auch das alignment gleich.
>
> Bei der unnötigen Verwendung von Zeigern gilt:
> Mehr Programmspeicher (Zeiger holen + Daten holen)
> etwas langsamer, da mindestens zwei Schritte.

Je nach (unbekanntem) μC ist da kein Unterschied.
Und wenn wie beim AVR8 absolute Addressierung vorhanden ist, dann muß 
die Adresse im Befehl stehen, was (AVR8) 16Bit extra macht. Bei ARM gibt 
es keine absoluten Adressen, immer nur relativ zu einem Register, also 
egal ob globale Variablen oder Zeiger auf Strukturen.

Autor: Sebastian S. (amateur)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Teddy
Das es "Probleme" mit der Lebensdauer und der Sichtbarkeit gibt ist mir 
bekannt. Ich bezog mich auch nur auf die verwendete Größe.

Autor: Alex D. (allu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mike Litoris schrieb:
> Bis jetzt habe ich das immer so gemacht, dass die Messwerte in globale
> Variablen geschrieben wurden, diese können dann von den einzelnen
> Funktionen abgerufen werden

Gute Lösung ! Und ist auch schneller.
Früher als der Ram-Speicherplatz knapp war, ok, aber heute...

Autor: Programmiersprachentheaterintendant (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich verwende für sowas Pointer auf Strukturen.

> Das Struct wird einmal im main als Variable deklariert, und nur der
> Pointer herumgereicht.

Schön und übersichtlich, leider keinen Deut anders als /globale 
Variablen/ weil immernoch... global und ungeschützt weil jeder beliebige 
Code komplett in der Struktur rumwühlen kann bis zum umfallen.

Du suchst nach eingeschränkte Sichtbarkeit (C) und Kapselung 
(allgemein).

Ein erster, einfacher Ansatz f. C ist die strukturierte Variable 
ausschliesslich in einer Implementationsdatei zu haben und nur per 
Funktionen welche via Headerdatei zugänglich gemacht werden bedient 
werden.

Bevor jemand wg. Overhead nölt: richtig geschrieben[TM] ist der nur zur 
Compiletime, bringt zusätzlichen Schutz und kostet exakt Null zur 
Laufzeit.

Autor: Alex D. (allu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Programmiersprachentheaterintendant schrieb:
> Bevor jemand wg. Overhead nölt: richtig geschrieben[TM] ist der nur zur
> Compiletime, bringt zusätzlichen Schutz und kostet exakt Null zur
> Laufzeit.

Aha, danke, ich dachte bisher jedesmal neu anlegen und freigeben würde 
Zeit benötigen.

: Bearbeitet durch User
Autor: Achim S. (achs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
globale Variablen sind böse, wenn
- von verschiedenen Stellen wild darauf geschrieben wird
- von parallelen Threads davon gelesen wird, ohne Konsistenz zu 
beachten.
- in C viele davon verwendet werden, da es nur einen Namespace gibt. 
Also dutzende von Variablen wie MyValue1 oder Temp.

Wenn eine Variable (z.B. ein Init-Flag oder ein i) lokal sein kann, oder 
zumindest static in einer Datei, dann sollte man das auch gerne tun.

Und als Anfänger hilft es ungemein, globale Variablen zu meiden.

Für den Profi sind Konventionen oder const/volatile-Qualifier für 
globale Variablen wirksamer als eine Pseudokapslung mit Pointer oder 
Getter/Setter.

Autor: Dumdi D. (dumdidum)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Programmiersprachentheaterintendant schrieb:
> Schön und übersichtlich, leider keinen Deut anders als globale Variablen
> weil immernoch... global und ungeschützt weil jeder beliebige Code
> komplett in der Struktur rumwühlen kann bis zum umfallen.

Naja, hilft da nicht einfachr 'static' vor der Struktur? Ich finde die 
Idee mit der Pointeruebergabe eingentlich ganz gut.

Autor: PfuschenderAutodidakt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dumdi D. schrieb:
> Programmiersprachentheaterintendant schrieb:
>> Schön und übersichtlich, leider keinen Deut anders als globale Variablen
>> weil immernoch... global und ungeschützt weil jeder beliebige Code
>> komplett in der Struktur rumwühlen kann bis zum umfallen.
>
> Naja, hilft da nicht einfachr 'static' vor der Struktur? Ich finde die
> Idee mit der Pointeruebergabe eingentlich ganz gut.

Jeder Codeteil bekommt nur den Zeiger auf den Zweig der Struktur, den er 
benötigt. Der Codeteil KANN andere Teile der Struktur gar nicht 
bekommen, er bekommt den nötigen Type gar nicht. Sonst wäre mein Konzept 
ziemlich unsinnig.

Ich mache das hirachisch.

Nehmen wir an:
- Der ADC hat ein ADC_Config struct
- Die Messfunktion ein "MESS" struct

wenn man Messung() aufruft, bekommt die einen Pointer auf "MESS".
Messung ruft init_ADC() auf, das bekommt einen Pointer auf den Zweig 
"ADC_Config", und nicht mehr.

Das C-File, das init_ADC() enthält, kennt "MESS" gar nicht, weil der 
nötige Header gar nicht inkludiert ist.

Warum das sinvoll ist:
Will man init_ADC() in einem neuen POrojekt verwenden, will man den 
Header von "MESS" nicht mitziehen.

Autor: Programmiersprachentheaterintendant (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Alex D. schrieb:
> Programmiersprachentheaterintendant schrieb:
>> Bevor jemand wg. Overhead nölt: richtig geschrieben[TM] ist der nur zur
>> Compiletime, bringt zusätzlichen Schutz und kostet exakt Null zur
>> Laufzeit.
>
> Aha, danke, ich dachte bisher jedesmal neu anlegen und freigeben würde
> Zeit benötigen.

[X] Du sollst nicht die Kerzen vom Kristbaum fressen.
[  ] Ich hab ausschliesslich von lokalen Variablen geschrieben.
[  ] Du hast Kapselung verstanden.

Autor: Dumdi D. (dumdidum)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PfuschenderAutodidakt schrieb:
> Der Codeteil KANN andere Teile der Struktur gar nicht bekommen, er
> bekommt den nötigen Type gar nicht.

Ich finde Deine Idee prima. Theoretisch kann jedoch auf eine globale 
Variable ohne static natuerlich zugegriffen werden, aber das ist nur 
relevant falls die Programme in einem Team entwickelt werden.

Autor: PfuschenderAutodidakt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dumdi D. schrieb:
> PfuschenderAutodidakt schrieb:
>> Der Codeteil KANN andere Teile der Struktur gar nicht bekommen, er
>> bekommt den nötigen Type gar nicht.
>
> Ich finde Deine Idee prima. Theoretisch kann jedoch auf eine globale
> Variable ohne static natuerlich zugegriffen werden, aber das ist nur
> relevant falls die Programme in einem Team entwickelt werden.

Man ist nicht gezwungen, die Struktur global anzulegen.
Man kann sie einfach im main "lokal" deklarieren. Oder woanders. Nur 
muss man sicherstellen, dass der Speicher reserviert bleibt - also 
entweder als static, oder im main loop (das man ja nie verlässt).
Oder sogar nur mit malloc(sizeof(type));

Eines sollte klar sein:
Im Prinzip ist durch die Deklarierung als "typedef" nur eine Maske 
definiert, die über einen Speicherbereich gelegt werden kann.
Konkret Speicher belgt man erst dann, wenn man eine Variable des Typs 
anlegt.

Autor: Sheeva P. (sheevaplug)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PfuschenderAutodidakt schrieb:
> Man ist nicht gezwungen, die Struktur global anzulegen.

Spätestens wenn ein Interrupt auf die Daten zugreifen muß, wirst Du wohl 
nicht darum herum kommen, die Strutur oder Teile davon global anzulegen.

Autor: Nop (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mike Litoris schrieb:

> Bis jetzt habe ich das immer so gemacht, dass die Messwerte in globale
> Variablen geschrieben wurden, diese können dann von den einzelnen
> Funktionen abgerufen werden.

Das ist auch sauber so. Die Variablen werden von einer einzigen Stelle 
aus geschrieben und können von überall her gelesen werden. Single 
producer, multiple consumer. Die Hauptbaustelle ist hier natürlich 
atomic access, das mußt Du schon sicherstellen.

Autor: PfuschenderAutodidakt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sheeva P. schrieb:
> PfuschenderAutodidakt schrieb:
>> Man ist nicht gezwungen, die Struktur global anzulegen.
>
> Spätestens wenn ein Interrupt auf die Daten zugreifen muß, wirst Du wohl
> nicht darum herum kommen, die Strutur oder Teile davon global anzulegen.

Naja, man kann dem Interrupt einen Pointer setzen. Selbstverständlich 
nur einen auf die Substruktur, die benötigt wird. Der Pointer muss 
global sein, das stimmt, aber man braucht ihn nicht projektglobal 
überall als "extern ..." durchzuschleifen.

In solchen Fällen habe ich der Initfunktion dann den Pointer übergeben, 
diese setzt ihn dann auf den übergebenen Wert.
Man kommt aber erstaunlich oft ohne Daten in einer ISR aus.

Hier ist es wichtig, nur die "lokale" Substruktur zu verwenden. Z.B. das 
Strukturelement "ADC" in ADC.c welches in ADC.h deklariert ist, und 
nicht mehr.
Würde man die Struktur verwenden, die die substruktur ADC enthältlt, 
müsste man der Funtkion alle verwendeten Typen bekanntgeben. Das macht 
Code unübersichtlich.

Autor: temp (Gast)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Mike Litoris schrieb:
> Ich "habe gehört", dass globale Variablen aufgrund Speicherverbrauch und
> Sicherheit zu vermeiden sind.

Viele Leute haben viele Meinungen und geben oft genug Dünnschiss von 
sich. Es gibt keine globale Regel an die man sich halten sollte oder 
muss. Für alles gibt es Gründe dafür oder dagegen. Und wenn du hier im 
Forum mitliest, wirst du oft genug merken wie die Weltanschauungen 
aufeinanderprasseln. C gegen C++ oder asm, Tasteneinlesen per GPIO 
Interrupt oder Polling, Debugger oder printf, goto u.s.w. Am besten du 
schaust dir Codeschnipsel im Netz an und bildest dir deine eigene 
Meinung ob eine bestimmte Aufgabenstellung für dich elegant oder nicht 
gelöst wurde. Am wenigsten solltest du Leuten und ihrer Meinung 
vertrauen die für alles festgeklopfte Meinungen haben und darauf wie die 
Moralapostel herumreiten. Meistens schreien die leider am lautesten...

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.