PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : globale Variable und Interrupt



a//b
24.04.2006, 10:43
Hi,

mein Interrupt muss auf ein Statusflag zugreifen, dass als globale Variable im Hauptprogramm definiert wurde. Die Interrupt-Routine soll in einer extra Datei ausgelagert werden.
Wie teile ich jetzt dem Interrupt mit, dass die Variable existiert? Beim Kompilieren beschwert sich der Compiler, dass die Var undefiniert ist. Ich kann die Variable ja schlecht in eine Headerdatei schreiben, wie ich das bei Funktionen machen würde, dann wird die Var ja gleich mehrmals definiert.

OK, ist irgendwie eher eine Frage zu C-Softwaredesign als zum AVR. Vielleicht hat trotzdem jemand einen Tipp für mich.

Gruß
Arne

SprinterSB
24.04.2006, 15:37
Doch ;-)

Die Definition der Vatiablen kommt in eine C-Datei (Modul)

type_t name1;
type_t name2 = initializer;

Die Deklaration kommt in den Header:

extern type_t name1;
extern type_t name2;

PasstScho
24.04.2006, 15:41
Hi,
Sollte da nicht noch volatile hin?

MfG Alex

SprinterSB
24.04.2006, 17:40
Nicht unbedingt. Kommt drauf an, was dein type_t ist:


typedef char volatile type_t;

:-b

Und das volatile ist schon in type_t! 8)

Sternthaler
24.04.2006, 18:13
Hallo,
etwas umständlicher mache ich es immer. Dadurch erreiche ich aber, dass ich den Header in allen Sourcen includen kann (Auch im Source, in dem ich die Variable definieren will).
Das geht bei mir so:
Im Code mit der main-Funktion steht folgendes:

#define MAIN
#include "header.h"
#undef MAIN

In allen anderen Sourcen lasse ich "define" und "undef" IMMER weg.

In der Datei header.h steht dann für alle global zu vereinbarenden Variablen dies hier:


#ifdef MAIN
#define EXTERN
#else
#define EXTERN extern
#endif

EXTERN int g_variable_1;
EXTERN char g_variable_2

Der Vorteil ist, dass ich die Variable niemals mit verschiedenen Type schreiben kann, da sie ja nur einmal in header.h aufgeführt ist. Somit bekommen ich nie einen Warning wegen falscher typangaben im Source bzw. im Header.

Für die 'Interruptfesten' Variablen füge ich dann natürlich auch das 'volatile' hinzu.

SprinterSB
24.04.2006, 18:50
Mit extern ist es kein Problem, wenn es mehrfach auftauch für die gleiche Variable. Es ist ja eine Deklaration (Bekanntmachung) und keine Definition (Oblekt anlegen)!

Bewährt hat sich in C/C++ folgendes:
source.h

#ifndef _SOURCE_H_
#define _SOURCE_H_

extern int var1;
extern int volatile var2;
extern void mache (int volatile * arg);

#define MAKRO(x) ((x)+1)

#endif /* _SOURCE_H_ */

source.c

#include "source.h"

int var1;
int volatile var2 = 42;

void mache (int volatile * arg)
{
*arg = MACHE (*arg);

var1 = var2;
}


Damit sind Makros und globale Variablen unf Funktionen überall benutzbar, wo der Header inkludiert wird.

Es ist dann auch kein Thema, wenn über verschlungene Umwege ein Header 2 mal inkludiert wird.

Beispiele sind im Wiki bei den C-Quellen zur genüge.

Sternthaler
24.04.2006, 23:25
@SprinterSB
ich denke, dass du in deinem Muster in source.h von den Zeilen
#ifndef _SOURCE_H_
#define _SOURCE_H_
.
#endif /* _SOURCE_H_ */
sprichst/schreibst.

Nicht das wir uns falsch verstehen.
Ich meinte nicht ein doppeltes includen, auch nicht über verschlungen Pfade (kommt ja doch manchmal vor), sondern das Problem der DEFINITION und DEKLARATION.
Ich stelle mit meinem #define MAIN (Immer NUR im Source mit der main-Funktion!) nur sicher, dass die globalen Variablen nur GENAU EINMAL dann definiert werden wenn der Source mit der main-Funktion diese Headerdatei includet.

Das #ifndef ... , welches du beschreibst, ist natürlich ebenso wichtig. Allerdings nur um keine Warnings wegen doppelten defines zu bekommen. Diese Warnings könnten aber auf alle Fälle ignoriert werden, da ja das selbe aus der gleichen Datei defined wird.

Ich wollte mit meinem Zeug zeigen, dass ich den nicht zu ignorierenden Warning vermeiden kann, wenn ich eben NICHT an 2 Stellen Variablen und deren Datentypen (mal als Definition bzw. als Deklaration) schreiben muss.

Frage an @SprinterSB: In deinem Beispiel Source.c hast du auch die Funktion mache () mit extern void angegeben.
Jetzt kommen ich in's schleudern. Ist das OK, oder ist es nicht sogar falsch hier ein extern anzugeben?

@a//b
Kleines Muster in einfacher Variante:
Datei mit main() und deiner Verarbeitung. (z.B.: test.c)

char volatile g_flag; // Hier DEFINIERT

void main ()
{
Init ();
g_flag = 0;

while (1)
{
if (g_flag == 1)
{
// Tu das was nötig ist, da der Interrupt das Flag gesetzt hat.
g_flag = 0;
}
}
}In deiner Datei mit der Interruptfunktion ist folgendes:
extern char volatile g_flag; // Nur DEKLARIERT

SIGNAL (????)
{
// Nun ist etwas passiert
g_flag = 1;
}

SprinterSB
25.04.2006, 00:02
ich denke, dass du in deinem Muster in source.h von den Zeilen
#ifndef _SOURCE_H_
#define _SOURCE_H_
.
#endif /* _SOURCE_H_ */
sprichst/schreibst.

[...]

Das #ifndef ... , welches du beschreibst, ist natürlich ebenso wichtig. Allerdings nur um keine Warnings wegen doppelten defines zu bekommen. Diese Warnings könnten aber auf alle Fälle ignoriert werden, da ja das selbe aus der gleichen Datei defined wird.

...und bei typedef fliegen wir aus der Kurve, nämlich wenn eine Struktur in mehreren Modulen verwendet werden soll. Ebenso bei inline-Funktionen. Drittens hakt es aus, wenn du einer Variablen einen Initializer geben willst:
extern int foo = 42; gibt einen Fehler! Da eine Zeile Code sparen zu wollen ist IMHO an der falschen Stelle gespart.


In deinem Beispiel Source.c hast du auch die Funktion mache () mit extern void angegeben.
Jetzt kommen ich in's schleudern. Ist das OK, oder ist es nicht sogar falsch hier ein extern anzugeben?
Nein. Wenn die Funktion static (aufs Modul begrenzt) sein sollte, würde sie nicht im Header stehen, sondern als
static void mache();
nur in dem Modul, wo sie gebraucht wird. Für globale (über mehrere Module hinweg) verwendbare Funktionen, gehören Prototypen in die jeweiligen Header. Und zwar im Header zur Quelle, welche die Funktion implementiert.

ogni42
25.04.2006, 08:51
sternthaler, Das Problem kommt doch nur daher, weil Du die Definition der Variablen in einem Header-File machst.

Common Sense bei C-Programmierung ist aber, die Definition von Variablen (und Funktionen) in einer C-Source, deren Deklaration (einschliesslich der Deklarationsspezifizierer z.B. extern) im Header zu machen.

Das spart die #ifdefs (mal ganz abgesehen von den Problemen, die Sprinter schon beschrieben hat).

Sternthaler
25.04.2006, 15:27
...und bei typedef fliegen wir aus der Kurve, nämlich wenn eine Struktur in mehreren Modulen verwendet werden soll.
Kann ich nicht nachvollziehen. Bei mir funktioniert folgendes ohne Probleme in einer in allen Sourcen includeten Headerdatei:
/************************************************** ****************************
Typendefinition fuer die Sensoren
*/
typedef struct {
unsigned char aktiv; // Startet/Stoppt die Sensoren
unsigned char taster; // Tasterwert mit PollSwitch() geholt
unsigned int batterie; // Batteriewert wie Batterie()
unsigned int linie [4]; // 0:Links-Dunkel 1:Rechts-Dunkel
// 2:Links-Hell 3:Rechts-Hell
unsigned int rad [2]; // Zaehlerwerte der Radencoder
} sens_t;

#ifdef MAIN
#define EXTERN
#else
#define EXTERN extern
#endif

/* Je nach Definition von EXTERN, werden die aufgefuehrten Variablen
declariert oder definiert.
Somit kann GENAU EIN SOURCE (Source mit der Main()-Funktion macht Sinn)
die Variablen 'erstellen', aber alle anderen Sourcen koennen wegen der
extern-Angabe auch auf die Variable zugreifen.
*/
EXTERN volatile sens_t sens;
EXTERN int g_kp;
EXTERN int g_ki;
EXTERN int g_kd;



Ebenso bei inline-Funktionen.
Da kann ich nicht mitreden, da ich bis jetzt noch nie inline-Code benutzt habe.


Drittens hakt es aus, wenn du einer Variablen einen Initializer geben willst:
Das ist richtig, hier muss das Programm selbst initialisieren.


Da eine Zeile Code sparen zu wollen ist IMHO an der falschen Stelle gespart.
Es geht mir nicht um die Zeile Code. Ich will nur sicherstellen, dass die Typen der ZWEI Zeilen Code IMMER identisch sind. Das erhalte ich über mein Konstrukt, da ich ja nur die eine Zeile habe.
Hier ist das Potenzial für Differenzen, und wenn es nur nachträglich geschieht, dass der Typ der Variablen nur in einem Source geändert wird. Vergisst man, dass es noch den Header gibt, dann bekommt man zwar ein Warning beim Compilieren, aber es ist trotzdem möglich 2 verschiedene Datentypen zu nutzen. OK, wer die Warnings nicht beachtet hat selber Schuld, und wer vergisst, dass es 2 Source-Stellen gibt, ist gut beraten mal Urlaub zu machen. Aber genau dies ist schon immer eine extreme Gefahr bei uns in der Firma gewesen. (Bei dem Krempel von uns handelt es sich so ca. um 4500 Sourcen.)



Nein. Wenn die Funktion static (aufs Modul begrenzt) sein sollte, würde sie nicht im Header stehen, sondern als
static void mache();
nur in dem Modul, wo sie gebraucht wird. Für globale (über mehrere Module hinweg) verwendbare Funktionen, gehören Prototypen in die jeweiligen Header. Und zwar im Header zur Quelle, welche die Funktion implementiert.
Ja, bei static ist mir das schon klar, aber in deinem Beispiel zum source.c hast du als arbeitende Funktion (keine deklaration) diese als extern angelegt. Da wollte ich mal nachfragen, ob bzw. was ich da nicht verstehe. #-o


@ongni42
Ne, ne, ich unterscheide ja genau, dass ich nur einmal die Definition mache. Regeln tut das der Code mit dem #define EXTERN. Entweder hat EXTERN keinen 'Inhalt' oder es steht dann zur Deklaration eben doch das 'extern' dahinter.


OK, ich denke das nun jeder genug hat über dieses Thema zu debatieren. Ich hoffe hier niemandem auf die Füße getreten zu haben und wünsche euch (und mir) weiterhin schönen, fehlerfreien Code.

ogni42
25.04.2006, 16:09
Das ist schon richtig sternthaler, dass das so funktioniert. Aber Deine Kollegen werden Dich hassen, wenn sie den Code später mal pflegen müssen, weil es nun mal unüblich ist, die Variablen im Header zu definieren.

Stell Dir einen Header als eine Art Dokumentation vor, die Dir, Deinen Kollegen und dem Compiler sagen, was es an Variablen, Datenstrukturen, Funktionen, etc. gibt. Wo die dann definiert sind, ist erst zur Linkzeit von Belang.

SprinterSB
25.04.2006, 16:21
@Sternthaler: Oops, ich dachte, das wären private Code-Conventions. Da wär sowas ja vertretbar.

Zudem musst du wissen, WELCHE Datei den Header includet. Aber wenn man in einem (großen) Projekt arbeitet, weiß man das nicht unbedingt. Nehmen war an, die Standard-Libs/Header wären so gestrickt, und du willst ein Modul source.c schreiben, das std-Funktionen/Objekte verwendet.

Weil du in source.c Lib-Functions verwendest, musst du in source.h die libfuncs.h includen, damit jemand, der dein Modul benutzt (für ihn ist dein Modul ne Black-Box) keine unaufgelösten Referenzen bekommt. libfuncs.h kann aber nicht wissen, wo es später stehen wird! Und die Compilezeit von libfuncs.c kann eine ganz andere sein, als die von source.c oder source.h! (Bibliotheken!). Du müsstest dann zur Linkzeit wissen, wie die Quelle aussieht!!!

Nix für ungut, immerhin willst du deine Brötchen mit dem Code verdienen ;-)

::Edit::

Zu dem "extern". Ich hab's rausgenommen. Ein Fehler ist's allerdings nicht. Man sieht dann aber in der Quelle direkt, ob das Ding static oder extern ist. Ebenso könnte ein "static" dort stehen.

Sternthaler
04.08.2006, 18:45
Nun doch noch mal ein Kommentar von mir.
(Es läßt mich doch nicht los, warum meine Kollegen mich noch nicht hassen bzw. dass es mit Lib's doch geht)


Ich fange mal mit den Lib's an:
Wir haben pro Lib jeweils ein eigenes Unterverzeichniss.
Somit ist bekannt, welche C-Sourcen und deren Header-Dateien immer zur Lib gehören.
Wenn nun für ein Programm eine dieser Libs mit eingebunden werden muss, dann gibt es ja tatsächlichdas Problem, welche dieser Heder-Dateien von meinem Programm alle includet werden müssen.
Die Lösung ist folgende:
Es gibt in dem Lib-Verzeichnis immer eine Heder-Datei, die nur die dort vorhandenen Header der Sourcen includet. Somit muss das eigendlich zu schreibende Programm nur diesen Header includen. (P.S: Header der C-Sourcen und sammelnder Header werden bei uns mit dem make im Lib-Verzeichnis erzeugt. Ist ein bei uns gebasteltes Programm. Natürlich geheim!)


Zum Thema hassende Kollegen:
Eigendlich sprechen noch alle mit mir und bei Feten geht auch keiner 'plötzlich' weg wenn ich komme. Kann also nicht so schlimm sein.
Eventuell liegt es daran, dass dieses Verfahren bei mir in der Firma schon so lange benutzt wird, dass sich da keiner mehr Gedanken drum macht (deshalb habe ich hier wohl auch die Probleme es darzustellen), oder es liegt daran, dass die verpönten globalen Variablen so in der Minderheit sind, dass schon lange keine mehr angelegt werden mussten und somit keiner so richtig weiss ob er damit (diesem Schema) Probleme hat.
Ausserdem sollen die nicht mich hassen, da ich zwar den ganzen Haufen verwalte und 'nur' für die Integration von Kundenwünschen zuständig bin, ich aber erst seit 15 Jahren in der Firma bin und diese Struktur schon übernehmen musste. (Hört sich nach altem Zeug an, ist aber unter anderem auch WEB-fähig ohne Java zu nutzen. Ist ja schliesslich auch nur C)