Forum: Mikrocontroller und Digitale Elektronik Struktur von C-Programmen für Mikrocontroller


von Uwe S. (tribe589)


Lesenswert?

Hallo liebe Mikrocontroller Gemeinde,

ich habe ein Projekt in MPLAB X IDE v2.35 erstellt. Ich schreibe gerade 
an einem kleinem Programm das CAN Botschaften empfängt und sendet.

Der Mikrocontroller ist der dsPIC30F4011.

Ich habe mein Projekt in mehrere C-Dateien "geteilt", hierdurch soll die 
Übersicht besser erhalten bleiben.

Problem: Wenn ich in einer C-Datei eine Funktion schreibe bzw definiere 
und in einer anderen verwende, dann werden Warnungen ausgeben beim 
kompelieren.
Soweit ich das richtig verstanden habe, muss ich die Funktion in der 
Datei, wo ich Sie genutzt habe, nochmal deklarieren.

Jetzt habe ich mir gedacht, dass ich die Funktionen einfach in einer 
Headerdatei deklariere und den Header in allen C-Dateien include. Jedoch 
werden dann wieder Warnungen ausgeben. Wodran liegt das? Was mache ich 
falsch?

Und eine Frage nochmal direkt zu der Deklaration. Ich habe gedacht ich 
kopiere einfach den "Kopf" der Funktion nochmal in das jeweilige Datei 
bzw in die Header-Datei und schließe mit ; ab.
z.B.
void CAN_Send600(unsigned char variable);
Jedoch zeigt der Kompiler manchmal Fehler, (genau Formulierung habe ich 
gerade nicht).
dann hilft es die Deklaration so programmieren:
void CAN_Send600();

Jetzt sind meine Fragen: Wie ist die saubere Vorgehensweise, um ein 
Projekt sauber aufzubauen? Wie spielen die Dateien zusammen? Beispiel 
Aufbau?
Mich fragt sich auch in welcher Reihenfolge die Dateien abgearbeitet 
werden. Ich habe eine main.c, eine function.c, eine interrupt.c, eine 
header.h.
Welche c-Datei wird zuerst abgearbeitet? Wie ist die Reihenfolge? Die 
Dateien function.c und interrupt.c sind nicht in der main.c irgendwie 
included oder ähnliches (Die Header Datei schon).

Ich freue mich schon auf eure Hilfe, damit ich mein Projekt anständig 
aufbauen kann und keine Warnungen mehr angezeigt werden :)

von Toni Tester (Gast)


Lesenswert?

Uwe S. schrieb:
> Jetzt habe ich mir gedacht, dass ich die Funktionen einfach in einer
> Headerdatei deklariere und den Header in allen C-Dateien include.

Genau so macht man das.

> Jedoch werden dann wieder Warnungen ausgeben. Wodran liegt das?
> Was mache ich falsch?

Wir können leider nicht hellsehen. Welche Warnungen werden ausgegeben? 
Quellcode?
Hast du schon selbst versucht, die Warnungen zu lesen, zu verstehen und 
zu beheben?

Uwe S. schrieb:
> Jedoch zeigt der Kompiler manchmal Fehler, (genau Formulierung habe ich
> gerade nicht).

Schlecht - wie sollen wir da helfen? Meine Glaskugel ist defekt.

> dann hilft es die Deklaration so programmieren:
> void CAN_Send600();
Das ist was völlig anderes als
1
void CAN_Send600(unsigned char variable);

Schön, wenn es zufällig geht - richtig wird es dadurch trotzdem nicht.

Uwe S. schrieb:
> Wie ist die saubere Vorgehensweise, um ein
> Projekt sauber aufzubauen? Wie spielen die Dateien zusammen? Beispiel
> Aufbau?
> Mich fragt sich auch in welcher Reihenfolge die Dateien abgearbeitet
> werden. Ich habe eine main.c, eine function.c, eine interrupt.c, eine
> header.h.
Typischerweise werden nur die C-Dateien compiliert - in der vorgegebenen 
Reihenfolge (Bei mir im Makefile - keine Ahnung, wie das bei MPLAB ist). 
H-Dateien werden durch den Präprozessor eingebunden (#include -> 
Google).

> Welche c-Datei wird zuerst abgearbeitet? Wie ist die Reihenfolge? Die
> Dateien function.c und interrupt.c sind nicht in der main.c irgendwie
> included oder ähnliches (Die Header Datei schon).
S. o.: Präprozessor macht Textersetzung: statt #include wird der 
Dateiinhalt der H-Datei eingefügt.
Die C-Dateien hast du wohl zum Projekt hinzugefügt, d. h. MPLAB wird 
diese alle compilieren und am Ende zusammen linken. Reihenfolge - keine 
Ahnung.

von Toni Tester (Gast)


Lesenswert?

Nachtrag: Jede C-Datei wird einzeln compiliert, d. h. "sieht" von den 
anderen nichts. Das Ergebnis wird dann zusammen gelinkt.
Die Reihenfolge der Compilierung muss daher egal sein; ist sie das 
nicht, ist etwas grob faul.

von Pit (Gast)


Lesenswert?

Hallo,

den Text in Deinen Header Dateien solltest Du in ein #ifndef / #endif 
einkapseln:
1
#ifndef __MY_HEADER_DATEI_NAME
2
#define __MY_HEADER_DATEI_NAME
3
4
//Hier Deine Definitionen ...
5
6
7
#endif //__MY_HEADER_DATEI_NAME

Dadurch erreichst Du, dass der Header nur einmal eingelesen wird und 
verhinderst somit Mehrfachdefinitionen und einiges an Fehlermeldungen.

von Sebastian W. (wangnick)


Lesenswert?

Uwe S. schrieb:
> Ich habe mein Projekt in mehrere C-Dateien "geteilt", hierdurch soll die
> Übersicht besser erhalten bleiben.

Löblich.

> Problem: Wenn ich in einer C-Datei eine Funktion schreibe bzw definiere
> und in einer anderen verwende, dann werden Warnungen ausgeben beim
> kompelieren.
> Soweit ich das richtig verstanden habe, muss ich die Funktion in der
> Datei, wo ich Sie genutzt habe, nochmal deklarieren.

Genau. Der Grund ist, dass der Compiler für unbekannte Funktionen keine 
Typprüfung machen kann.

> Jetzt habe ich mir gedacht, dass ich die Funktionen einfach in einer
> Headerdatei deklariere und den Header in allen C-Dateien include.

Genau richtig.

> Jedoch werden dann wieder Warnungen ausgeben. Wodran liegt das?
> Was mache ich falsch?

Dass kann man so generell nicht beantworten. Manchmal liegt es daran, 
dass sich Headerdateien gegenseitig inkludieren. Dann hilft das 
Konstrukt
[c]
#ifndef _modul_h
#define _modul_h
... Hier hinein den eigentlichen Inhalt der Headerdatei
#endif //_modul_h
[/c}

> Und eine Frage nochmal direkt zu der Deklaration. Ich habe gedacht ich
> kopiere einfach den "Kopf" der Funktion nochmal in das jeweilige Datei
> bzw in die Header-Datei und schließe mit ; ab.
> z.B.
> void CAN_Send600(unsigned char variable);
> Jedoch zeigt der Kompiler manchmal Fehler, (genau Formulierung habe ich
> gerade nicht).
> dann hilft es die Deklaration so programmieren:
> void CAN_Send600();

Das erstere Herangehen (mit expliziten Parametertypen) ist vorzuziehen. 
Wenn es Fehlermeldungen gibt, dann diese studieren und beheben.

> Jetzt sind meine Fragen: Wie ist die saubere Vorgehensweise, um ein
> Projekt sauber aufzubauen? Wie spielen die Dateien zusammen? Beispiel
> Aufbau?

Da gibt es viele Erfahrungswerte, z.b.:

*) Module möglichst schichten, also so, dass ein Modul B keine 
Funktionen eines Moduls A benutzt, wenn das Modul A schon Funktionen von 
B benutzt.

*) Schnittstellen so klein wie möglich.

*) So wenig globale Variable wie möglich.

Und natürlich unendlich viel Literatur ...

> Mich fragt sich auch in welcher Reihenfolge die Dateien abgearbeitet
> werden. Ich habe eine main.c, eine function.c, eine interrupt.c, eine
> header.h.
> Welche c-Datei wird zuerst abgearbeitet? Wie ist die Reihenfolge? Die
> Dateien function.c und interrupt.c sind nicht in der main.c irgendwie
> included oder ähnliches (Die Header Datei schon).

Die Reihenfolge ist als undefiniert definiert, also als etwas, auf dass 
man sich nicht verlassen darf.

> Ich freue mich schon auf eure Hilfe, damit ich mein Projekt anständig
> aufbauen kann und keine Warnungen mehr angezeigt werden :)

Zeig mal die Warnungen.

LG, Sebastian

von TriHexagon (Gast)


Lesenswert?

Pit schrieb:
> Hallo,
>
> den Text in Deinen Header Dateien solltest Du in ein #ifndef / #endif
> einkapseln:
> #ifndef __MY_HEADER_DATEI_NAME
> #define __MY_HEADER_DATEI_NAME
>
> //Hier Deine Definitionen ...
>
> #endif //__MY_HEADER_DATEI_NAME
>
> Dadurch erreichst Du, dass der Header nur einmal eingelesen wird und
> verhinderst somit Mehrfachdefinitionen und einiges an Fehlermeldungen.

Du meinst wohl Deklaration, nicht Definition.

Deklaration:
1
void foobar(void);

Definition:
1
void foobar(void)
2
{
3
//...
4
}

von Uwe S. (tribe589)


Lesenswert?

Vielen Dank für die Vielen Antworten :), Ich habe meine Fehler jetzt 
gefunden, manchmal übersieht man eine kleinigkeit die Große unterschiede 
macht :)

Nochmal zurück auf Pit bzw TriHexagon.
Wenn ich eine neue Datei erstelle, ist automatisch dieser Code schon da:

#ifndef DEKLARATIONEN_H
#define  DEKLARATIONEN_H

#ifdef  __cplusplus
extern "C" {
#endif


 !!! Hier kommt der selbst geschriebene Code hin? !!!


#ifdef  __cplusplus
}
#endif

#endif  /* DEKLARATIONEN_H */


Ist das richtig das der eigene Code dahin kommt, wo ich geschrieben habe 
"Hier kommt der ..."? und nicht ans Ende der Datei?

Was hat es mit der cplusplus zeile auf sich?
Was sagen diese Elementeaus?

von Olaf B. (Firma: OBUP) (obrecht)


Lesenswert?

Hallo Uwe,

die Deklarationen innerhalb der Header-Datei kommen tatsächlich an die 
Stelle wo Du
  "!!! Hier kommt der selbst geschriebene Code hin? !!!"
geschrieben hast.

Dieser Abschnitt hier wird als Header-Guard bezeichnet. Soll verhindern, 
das Header mehrfach eingebunden werden:
1
#ifndef DEKLARATIONEN_H
2
#define  DEKLARATIONEN_H
3
  ...
4
#endif  /* DEKLARATIONEN_H */

Der Block
1
#ifdef  __cplusplus
2
extern "C" {
3
#endif
4
  ...
5
#ifdef  __cplusplus
6
}
7
#endif
soll verhindern, dass Namen von Funktionen in C, der in C++-Code 
eingebunden wird durch Name-Mangling nach C++ Konvention aufgelöst wird. 
Stichwort Überladung.

Name Mangling bedeutet, dass Symbole, die der Compiler erzeugt (z.B. 
Funktionen, globale Variablen, etc.) im Compileroutput nicht den 
gleichen Namen haben wie im Quelltext.

mfg

Olaf

von Sebastian W. (wangnick)


Lesenswert?

Uwe S. schrieb:
> Wenn ich eine neue Datei erstelle, ist automatisch dieser Code schon da:
>
> #ifndef DEKLARATIONEN_H
> #define  DEKLARATIONEN_H
>
> #ifdef  __cplusplus
> extern "C" {
> #endif
>
>  !!! Hier kommt der selbst geschriebene Code hin? !!!
>
> #ifdef  __cplusplus
> }
> #endif
>
> #endif  /* DEKLARATIONEN_H */
>
> Ist das richtig das der eigene Code dahin kommt, wo ich geschrieben habe
> "Hier kommt der ..."? und nicht ans Ende der Datei?
>
> Was hat es mit der cplusplus zeile auf sich?
> Was sagen diese Elementeaus?

Uh, oh.

1. "DEKLARATIONEN" solltest du durch den Dateinamen der jeweiligen 
Header-Datei ersetzen. Wenn dieser Codeschnipsel genau so in zwei 
Header-Dateien auftaucht, dann wird der Inhalt der zweiten inkludierten 
Header-Datei ja ausgeblendet, weil in der ersten schon #define 
DEKLARATIONEN_H steht.

2. __cplusplus ist genau dann vom Compiler vordefiniert wenn er den Code 
als C++ übersetzt. extern "C" {...} führt dann dazu, dass der Code 
innerhalb von {...} quasi als C-Code übersetzt wird. Das ist nur dann 
wichtig wenn man C und C++ mischt. Details hat Olaf ja schon erklärt. 
Wenn du nur C oder nur C++ benutzt dann lass diesen Teil besser weg.

LG, Sebastian

von TriHexagon (Gast)


Lesenswert?

Um das ganze mal mit einem realem Beispiel zu untermauern: 
https://github.com/Kazade/kazmath

Hier sind die Deklarationen (Header Datei):
https://github.com/Kazade/kazmath/blob/master/kazmath/vec2.h

Und hier die Definitionen/Implementierung (Quellcodedatei):
https://github.com/Kazade/kazmath/blob/master/kazmath/vec2.c

Willst du nun in einer anderen Quellcodedatei diese Funktionen oder 
Typen nutzen, dann solltest du die Headerdatei inkludieren. Ansonsten 
gibts Warnungen, kompiliert aber trotzdem. Warum auch immer das erlaubt 
ist (typisch C :P).

von Uwe S. (tribe589)


Lesenswert?

Vielen Dank ihr habt mir sehr weiter geholfen! :)

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
Noch kein Account? Hier anmelden.