Hey Zusammen :) ich will versuchen mich mal an C zu gewöhnen, sitze nun seit 5h hier und versuche ein Ringpuffer zum Speichern von empfangenen CAN-Msg´s zu stricken... Dafür wollte ich ein Array aus Struct´s anlegen und mit der Nummer des Struct´s dann weiterarbeiten... innerhalb meiner .c-Datei läuft es wie gewünscht. Auf den Puffer wird aber auch aus Programmteilen zugegriffen die halt in anderen .c-Datein stehen und da sind meine Variablen angeblich nicht definiert. Aber das habe ich doch schon in der "Haupt .c-Datei" gemach tund damit sollten sie global sein, oder? selbst alle zusammengesuchten Tips von google und hier aus dem Forum brachten mich nicht weiter... was mache ich falsch? #include <avr/io.h> #include <stdint.h> #include <avr/interrupt.h> #include <mcp2515.h> // Erzeugen des Datentyps einer CAN-Botschaft // typedef struct { uint8_t IDh; uint8_t IDl; uint8_t EID8; uint8_t EID0; uint8_t DLC; uint8_t DATEN[8]; } can_msg_aufbau; // Erzeugen der Datenfelder für Sende-/Empfangsfächer // can_msg_aufbau empfangsfach[8]; can_msg_aufbau sendefach[8]; // Zähler für Ringspeicher // uint8_t rs_ef = 1; uint8_t rs_sf = 1; thx & greetz Danny
Du musst alle Variablen, die auch in anderen Quelldateien verwendet werden sollen, mittels einer "extern"-Deklaration auch in diesen bekannt machen. Der Compiler verarbeitet jede einzelne Quelldatei separat. Wenn Du z.B. eine main.c und eine xyz.c hast, die zum selben Projekt gehören, dann compiliert der Compiler die main.c und die xyz.c getrennt, d.h. beim Compilieren von xyz.c existiert der Code der main.c für ihn überhaupt nicht. Wenn Du eine Variable in main.c hast, die auch in xyz.c bekannt sein soll, dann muss diese Variable mit dem vorgestellten Qualifizierer in der xyz.c deklariert werden. Die sinnvollste Lösung (die auch fast immer verwendet wird), ist allerdings, zusätzlich zu jeder Source-Datei eine *.h-Datei zu erstellen und in dieser alle extern-Deklarationen von Variablen, die auch anderen Quelldateien zur Verfügung stehen sollen, durchzuführen. Diese *.h-Dateien werden dann in die betreffenden Sourcen eingebunden. Den Rest hatten wir heute schon: Beitrag "extern Variable"
@Johannes: vielen Dank für die schnelle Antwort.... aaaaber :) das mit extern habe ich gelesen und diverse male versucht, nur wie genau wende ich es in dem 2. .c-file an? bei einer "normalen" Variablen kein Thema, aber bei nem Array aus Struct´s? Ich habe es nicht hinbekommen und auch kein entsprechendes Codebeispiel gefunden... thx & greetz Danny
Das typedef muss natürlich auch in allen Sourcen bekannt sein. Das
sollte man in einer eigenen *.h unterbringen und ebenfalls in alle
Sourcen einbinden.
BTW:
> Array aus Struct´s?
Der (Deppen-)Apostroph hat vor dem s nichts zu suchen... Selbst im
Englischen heißt die Mehrzahl von "struct" einfach nur "structs".
mh ich trau mich fast nicht zu fragen, aber könntet ihr mir anhand meines Beipieles mal aufschreiben was in welches File müsste, ich dreh bald durch... :( ich habe ein Haupt.c und ein MCP.c aus beiden soll auf den ringpuffer zugegriffen werden ich habe nun alles versucht, mit extern, definieren im .h aber wird nichts... vermutlich nur falshc geschrieben oder so... naja... c-anfänger halt :) thx & greetz Danny
header.h ========
1 | #include <avr/io.h> |
2 | #include <stdint.h> |
3 | #include <avr/interrupt.h> |
4 | #include <mcp2515.h> |
5 | |
6 | // Erzeugen des Datentyps einer CAN-Botschaft
|
7 | //
|
8 | typedef struct |
9 | {
|
10 | uint8_t IDh; |
11 | uint8_t IDl; |
12 | uint8_t EID8; |
13 | uint8_t EID0; |
14 | uint8_t DLC; |
15 | uint8_t DATEN[8]; |
16 | } can_msg_aufbau; |
17 | |
18 | // DEKLARATION der Datenfelder für Sende-/Empfangsfächer
|
19 | //
|
20 | extern can_msg_aufbau empfangsfach[]; |
21 | extern can_msg_aufbau sendefach[]; |
22 | |
23 | // DEKLARATION der Zähler für Ringspeicher
|
24 | //
|
25 | extern uint8_t rs_ef; |
26 | extern uint8_t rs_sf; |
Haupt.c =======
1 | #include "header.h" |
2 | |
3 | // DEFINITION der Datenfelder für Sende-/Empfangsfächer
|
4 | //
|
5 | can_msg_aufbau empfangsfach[8]; |
6 | can_msg_aufbau sendefach[8]; |
7 | |
8 | // DEFINITION der Zähler für Ringspeicher
|
9 | //
|
10 | uint8_t rs_ef = 1; |
11 | uint8_t rs_sf = 1; |
12 | |
13 | /* ... Rest vom Programm ... */
|
MCP.c =====
1 | #include "header.h" |
2 | |
3 | /* ... Rest vom Programm ... */
|
Im Makefile muss dann eine so ähnliche Zeile auftauchen (SRC kann auch CSRC oder C_SRC o.ä. genannt sein): SRC = Haupt.c MCP.c
Prinzipiell willst du doch Folgendes erreichen main.c ******
1 | can_msg_aufbau empfangsfach[8]; |
2 | can_msg_aufbau sendefach[8]; |
MCP.C *****
1 | extern can_msg_aufbau empfangsfach[8]; |
2 | extern can_msg_aufbau sendefach[8]; |
Das sagt erst mal aus: in main.c existieren die Variablen empfangsfach und sendefach. In MCP.C wird (wegen dem extern) nur gesagt, dass es 2 Variablen gibt, names empfangsfach bzw. sendefach. Wie gesagt: diese beiden existieren irgendwo (naemlich in main.c. Das wissen aber nur wir, der Compiler weis das erst mal nicht). Dem Compiler ist es beim compilieren von MCP.C auch erst mal egal wo diese beiden Variablen liegen, er muss nur wissen * dass es sie gibt * dass daher in weiterer Folge in MCP.c ein Variablenname von empfangsfach kein Schreibfehler ist, sondern dass du tatsächlich eine Variable damit meinst * und natürlich welchen Datentyp sie haben. Insbesondere letzteres muss er natürlich wissen, damit er bei einer Arrayindiziegun aussrechnen kann, wo den das angesprochene Element innerhalb des Arrays liegt. Soweit so gut. Der Compiler muss aber noch mehr wissen. Wie ein Array aussieht, weiss er auch so. Aber wie der Datentyp can_msg_aufbau aussieht weiss er nicht. Das muss man ihm daher beschreiben. Einschub: Denk immer daran: main.c und MCP.c werden unabhängig voneinander compiliert! Wenn der Compiler MCP.c kompiliert, dann sieht er nur dass, was in diesem File drinnen steht. Was in main.c drinnen steht interessiert zu diesem Zeitpunkt nicht D.h. aber auch. Wenn der Compiler das hier compiliert main.c ******
1 | // Erzeugen des Datentyps einer CAN-Botschaft
|
2 | //
|
3 | typedef struct |
4 | {
|
5 | uint8_t IDh; |
6 | uint8_t IDl; |
7 | uint8_t EID8; |
8 | uint8_t EID0; |
9 | uint8_t DLC; |
10 | uint8_t DATEN[8]; |
11 | |
12 | } can_msg_aufbau; |
13 | |
14 | |
15 | // Erzeugen der Datenfelder für Sende-/Empfangsfächer
|
16 | //
|
17 | can_msg_aufbau empfangsfach[8]; |
18 | can_msg_aufbau sendefach[8]; |
ist alles in Butter. Da werden 2 Variablen definiert, die beide vom Datentyp Array von can_msg_aufbau sind. Wie ein can_msg_aufbau aussieht, ist im vorher mitgeteilt worden- Wenn der Compiler aber das hier compiliert: MCP.c *****
1 | extern can_msg_aufbau empfangsfach[8]; |
2 | extern can_msg_aufbau sendefach[8]; |
Dann hat er ein Problem. Zwar kennt er danach die beiden Variablen. Er hat auch erkannt das es sich dabei um Arrays handelt. Aber er weis nichts über den Datentyp can_msg_aufbau. Das würde er gerne tun und auch tun muessen, denn er muss ja schliesslich prüfen, ob sowas hier
1 | empfangsfach[2].EID8 |
überhaupt gültig ist. Dazu muss er aber wissen ob den eine can_msg_aufbau überhaupt ein Feld namens EID8 besitzt und an welcher Stelle in der Struktur diese steht. Nur, wenn der Compiler nur das hier hat (und denk dran, was in main.c steht interessiert nicht) MCP.C *****
1 | extern can_msg_aufbau empfangsfach[8]; |
2 | extern can_msg_aufbau sendefach[8]; |
dann kann er das schlicht und ergreifend nicht. Also muss man ihm das beibringen MCP.c ***** // Erzeugen des Datentyps einer CAN-Botschaft // typedef struct { uint8_t IDh; uint8_t IDl; uint8_t EID8; uint8_t EID0; uint8_t DLC; uint8_t DATEN[8]; } can_msg_aufbau; // Erzeugen der Datenfelder für Sende-/Empfangsfächer // extern can_msg_aufbau empfangsfach[8]; extern can_msg_aufbau sendefach[8]; [/C] Jetzt kann der Compiler alles notwendig aus MCP.c (und nur von dort) ablesen um mit den beiden Variablen klarzukommen. Jetzt kommt das grosse 'Aber'! Natürlich ist sowas Fehleranfällig. Du musst die Struktur can_msg_aufbau 2-mal beschreiben: Einmal in main.c und einmal in MCP.c. Das die beiden Beschreibungen exakt übereinstimmen sollten, brauche ich wohl nicht extra zu erwähnen. Das diese Übereinstimmung von niemandem (ausser dir) kontrolliert wird, brauche ich auch nicht extra zu erwähnen. Wer solls auch machen? Wenn der Compiler main.c compiliert kümmert ihn nur das was in main.c steht. Wenn der Compiler MCP.c compiliert kümmert ihn nur das was in MCP.c steht. Der Ausweg lautet: Ein Include File muss her. Die Strukturdefinition wird dorthin ausgelagert. Dann kann ich dieses Include File sowohl in main.c includieren als auch in MCP.c includieren. Beim compilieren ist dadurch die Strukturdefinition sowohl in main.c sichtbar, als auch in MCP.c. Und als Programmierer hast du den Vorteil, dass du sie nur einmal schreiben musst. Und da es sie in Textform nur einmal gibt, muessen daher die Definitionen in main.c und MCP.c per Definition übereinstimmen. structs.h *********
1 | typedef struct |
2 | {
|
3 | uint8_t IDh; |
4 | uint8_t IDl; |
5 | uint8_t EID8; |
6 | uint8_t EID0; |
7 | uint8_t DLC; |
8 | uint8_t DATEN[8]; |
9 | |
10 | } can_msg_aufbau; |
main.c ******
1 | #include "structs.h" // dadurch wird can_msg_aufbau eingeführt |
2 | // was wiederuum notwendig ist, damit
|
3 | // der Datentyp für ...
|
4 | |
5 | can_msg_aufbau empfangsfach[8]; |
6 | can_msg_aufbau sendefach[8]; |
7 | |
8 | // bekannt ist. Der Compiler kann daher im
|
9 | // Speicher eine entsprechende Anzahl Bytes
|
10 | // für die beiden Variablen reservieren
|
MCP.c *****
1 | #include "structs.h" // dadurch wird can_msg_aufbau eingeführt |
2 | // was wiederuum notwendig ist, damit
|
3 | // der Datentyp für ...
|
4 | |
5 | extern can_msg_aufbau empfangsfach[8]; |
6 | extern can_msg_aufbau sendefach[8]; |
7 | |
8 | // bekannt ist. Dadurch weiss der Compiler
|
9 | // wie die Datentypen aussehen und wie der
|
10 | // Speicher, der an anderer Stelle reserviert
|
11 | // wurde, organisiert ist.
|
@Stefan & Karl Heinz: Vielen Dank für die raschen Antworten und klasse Erklärungen! Ich bin zwar der Meinung das so mal getestet zu haben, aber dabei hab ich mich wohl an anderer Stelle verzettelt... nun klappts aber! :) thx & greetz Danny (der nun beruhigt schlafen kann ;)
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.