Forum: Compiler & IDEs *.h Dateien statt *.c Dateien includieren


von Michael J. (jogibaer)


Lesenswert?

Hallo,

wir hatten ja schon mal eine Diskussion darüber.

Jetzt will ich mein Programm umstellen, habe aber immer noch Probleme.

Als Beispiel mal ein völlig sinnloses Programm:

main.c
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#include "test.h"
5
6
int main(void)
7
8
9
  {  
10
  ausgabe ();
11
  return 0;
12
  }


test.c
1
#include "test.h"
2
3
4
void ausgabe (void)
5
6
  {
7
  PORTA = 255;
8
  }


test.h
1
#ifndef __TEST_H__
2
#define __TEST_H__
3
4
5
void ausgabe (void);
6
7
8
#endif

MAKEFILE

#makefile, written by guido socher

# CPU Type
MCU=atmega32

# Projektname
NAME=Bootloader

# Startadresse des Programms
# 0x0000  -> bei normalem Programmstart

# ( Achtung ! Laut Datenblatt Wortadressen !!)

# ATMega8
# 0x1C00   -> Bootloader von 1024 Byte
# 0x1800   -> Bootloader von 2048 Byte

# ATMega32
# 0x7C00   -> Bootloader von 1024 Byte


#START_ADR=0x7C00
START_ADR=0x0000


# verwendeter Compiler
CC=avr-gcc
LD=avr-gcc
OBJCOPY=avr-objcopy
# optimize for size:
CFLAGS= -mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues
#LDFLAGS=-Wl,-Map=$(NAME).map,--cref,--section-start=.flashloader=$(STAR 
T_ADR)  --mmcu=$(MCU)
LDFLAGS=-Wl,-Map=$(NAME).map,--cref,--section-start=.text=$(START_ADR) 
--mmcu=$(MCU)


#-------------------
all: $(NAME).hex
#-------------------
$(NAME).hex : $(NAME).out
  $(CC) $(CFLAGS) -o $(NAME).out $(LDFLAGS) main.o
  $(OBJCOPY) -R .eeprom -O ihex $(NAME).out $(NAME).hex

$(NAME).out : $(NAME).o
  $(CC) $(CFLAGS) -o $(NAME).out -Wl,-Map,$(NAME).map main.o

$(NAME).o : main.c
  $(CC) $(CFLAGS) -Os -c main.c

# you need to erase first before loading the program.
# load (program) the software into the eeprom:

load:    $(NAME).hex
    uisp  -dprog=dasa2 -dserial=/dev/ttyS0 -dpart=$(MCU) --erase
    uisp  -dprog=dasa2 -dserial=/dev/ttyS0 -dpart=$(MCU) --upload 
if=$(NAME).hex

boot:    $(NAME).hex
    avrdude -p m32 -c AVR109 -P /dev/ttyUSB0 -u  -U flash:w:$(NAME).hex

info:    $(NAME).hex
    avr-size $(NAME).out

asm:    $(NAME).hex
    avr-objdump -S $(NAME).out

readfuse:  $(NAME).hex
    uisp -dprog=dasa2 -dpart=$(MCU) -dserial=/dev/ttyS0 --rd_fuses

setquarz_m8:  $(NAME).hex
    uisp -dprog=dasa2 -dpart=$(MCU) -dserial=/dev/ttyS0 --wr_fuse_l=0x3f

clean:    $(NAME).hex
    rm -f *.o *.map *.out

#----------------------------------------------------------------------- 
-












Wenn ich test.c in main.c includiere, funktioniert es.
Wenn ich test.h in main includiere bekomme ich folgende Fehlermeldung:

avr-gcc -mmcu=atmega32 -Wall -Wstrict-prototypes -Os -mcall-prologues 
-Os -c main.c
avr-gcc -mmcu=atmega32 -Wall -Wstrict-prototypes -Os -mcall-prologues 
-o Bootloader.out -Wl,-Map,Bootloader.map main.o
main.o: In function `main':
main.c:(.text+0x8): undefined reference to `ausgabe'
make: *** [Bootloader.out] Fehler 1



Ich denke mal, das liegt an meinem Makefile.
Was muß ich wo abändern, damit es auch mit .h Dateien funktioniert ?


Danke
Jogibär

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ja, dein Makefile kann einfach nicht mit mehr als einer Sourcedatei
umgehen.

Nimm doch sowas wie Mfile, um dir ein Makefile generieren zu lassen.

_TEST_H_ ist übrigens dem Compiler und der Bibliothek vorbehalten.
Nenne das besser TEST_H.

von Michael J. (jogibaer)


Lesenswert?

Hallo Jörg,

danke für Deine Antwort.

Ich habe mal Dein Script heruntergeladen und laut Readme installiert.
Ein Makefile habe ich auch erzeugt.

Blos hat sich nichts geändert:



avr-gcc -c -mmcu=atmega128 -I. -gstabs   -Os -Wall -Wstrict-prototypes 
-std=gnu99  main.c -o main.o
avr-gcc -mmcu=atmega128 -I. -gstabs   -Os -Wall -Wstrict-prototypes 
-std=gnu99  main.o   --output main.elf     -lm
main.o: In function `main':
main.c:9: undefined reference to `ausgabe'
make: *** [main.elf] Fehler 1


Jogibär

von Karl H. (kbuchegg)


Lesenswert?

Michael Jogwich wrote:
> Hallo Jörg,
>
> danke für Deine Antwort.
>
> Ich habe mal Dein Script heruntergeladen und laut Readme installiert.
> Ein Makefile habe ich auch erzeugt.
>
> Blos hat sich nichts geändert:
>
>
>
> avr-gcc -c -mmcu=atmega128 -I. -gstabs   -Os -Wall -Wstrict-prototypes
> -std=gnu99  main.c -o main.o
> avr-gcc -mmcu=atmega128 -I. -gstabs   -Os -Wall -Wstrict-prototypes
> -std=gnu99  main.o   --output main.elf     -lm
> main.o: In function `main':
> main.c:9: undefined reference to `ausgabe'
> make: *** [main.elf] Fehler 1
>

Wenn du dir die Ausgaben mal ansiehst dann siehst du:

Es wird zwar main.c compiliert.
Es wird aber nirgends test.c compiliert

Weiters versucht zwar der Linker aus main.o (welches durch
compilieren von main.c erhalten wurde) zu einem Programm
zu linken, dabei wird aber ein test.o (welches durch compilieren
von test.c entstehen würde) nicht dazugelinkt. Daher fehlen
natürlich alle Funktionen die in test.c definiert wurden.

Dein Projekt besteht aus 2 Source code Dateien: main.c UND test.c
Beide muessen compiliert werden und beide muessen zum fertigen
Programm gelinkt werden.


   main.c                          test.c
   +-------+                       +--------+
   |       |                       |        |
   +-------+                       +--------+
       |                               |
    Compiler                       Compiler
       |                               |
   +--------+                      +----------+
   | main.o |                      | test.o   |
   +--------+                      +----------+
       |                               |
       +-------------+   +-------------+
                     |   |
                    Linker
                       |
                   +----------+
                   | main.hex |
                   +----------+

Wenn du mit makefiles nicht klarkommst (was absolut keine
Hexerei ist), bzw. Tools die dir ein Makefile erzeugen,
dann benutze AVR-Studio. Das kann das

http://www.mikrocontroller.net/articles/FAQ#Ich_hab_da_mehrere_.2A.c_und_.2A.h_Dateien._Was_mache_ich_damit.3F

von Michael J. (jogibaer)


Lesenswert?

Hallo,

achso.
Ich habe das bisher so verstanden, wenn ich die test.h include,
per make dann automatisch auch die test.c Datei compiliert wird.

Bei anderen Projekten habe ich gesehen, das die alle *.c dateien
im makefile extra angeben.
So kann ich das aber auch machen, oder ?


Zu AVRStudio -> ich habe Linux, und brauche AVRStudio auch nicht

Zum Programmieren benutze ich kate, das ist schön übersichtlich;
Eclipse habe ich mal versucht -> das Javazeug hat mir überhaupt
nicht gefallen und hatte nur seltsame Probleme


Jogibär

von Johannes M. (johnny-m)


Lesenswert?

Michael Jogwich wrote:
> Hallo,
>
> achso.
> Ich habe das bisher so verstanden, wenn ich die test.h include,
> per make dann automatisch auch die test.c Datei compiliert wird.
Nein. Es werden nur Sourcen, die dem Compiler bekannt sind, compiliert, 
und zwar völlig unabhängig voneinander.

> Bei anderen Projekten habe ich gesehen, das die alle *.c dateien
> im makefile extra angeben.
> So kann ich das aber auch machen, oder ?
Das musst Du sogar machen! Die einzige Aufgabe der .h-Datei ist es, 
Variablen und Funktionen, die in einer anderen Source-Datei definiert 
sind, in der aktuellen Source bekanntzumachen. Da eine Variable nur 
einmal deklariert werden darf, muss die Deklaration in der .h-Datei mit 
dem Schlüsselwort "extern" erfolgen, sonst gibts u.U. Fehlermeldungen 
wegen "multiple definition".

Die Source-Datei, in der die Variablen und Funktionen definiert sind, 
muss separat compiliert werden.

von Johannes M. (johnny-m)


Lesenswert?

Kleine Ergänzung zum Verständnis:
Der Compiler compiliert grundsätzlich alle Sourcen völlig unabhängig 
voneinander, d.h. wenn er eine .c-Datei compiliert, ist ihm das 
vollkommen wurscht, dass es möglicherweise noch andere Sourcen gibt. Er 
ist quasi blind für die "Umgebung". Wenn man jetzt aber in einem Modul 
Variablen oder Funktionen definiert hat, die man in anderen Modulen 
nutzen möchte, dann muss man dafür sorgen, dass der Compiler beim 
Compilieren des Moduls, in dem die Objekte verwendet werden sollen, auch 
von deren Existenz weiß. Und nur zu diesem Zweck ist die .h-Datei da. 
Sie enthält alle Variablen und Funktionen, auf die andere Module 
zugreifen können sollen.

Damit es aber keine Kollisionen beim Linken gibt (denn erst da werden 
alle compilierten Module zusammengefügt und zu einem lauffähigen 
Programm vereint), darf eine Deklaration jeweils nur ein einziges Mal 
auftauchen. Zur reinen Bekanntgabe einer Variable/Funktion in einem 
anderen Modul als dem, in dem sie deklariert wurde, gibt es das 
Schlüsselwort "extern". Deklariert man eine Variable/Funktion mit 
"extern", dann wird im Unterschied zu einer normalen Deklaration kein 
Speicherplatz reserviert, sondern lediglich dem Compiler mitgeteilt, 
dass es irgendwo außerhalb des aktuellen Moduls eine Variable des 
angegebenen Datentyps mit dem angegebenen Namen (bzw. eine Funktion mit 
dem angegebenen Datentyp, dem angegebenen Namen und den angegebenen 
Parametern mit den angegebenen Datentypen) gibt, so dass der Compiler 
die Namen, wenn sie im Quelltext auftauchen, entsprechend behandeln 
kann.

Wichtig ist: Eine Variable darf nur einmal im gesamten Programm 
deklariert werden. "extern"-Deklarationen darf man aber so oft einsetzen 
wie man will, weil sie ja wie gesagt keine Deklarationen im eigentlichen 
Sinn sind, sondern eben nur "Verweise".

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Johannes M. wrote:

>> Bei anderen Projekten habe ich gesehen, das die alle *.c dateien
>> im makefile extra angeben.
>> So kann ich das aber auch machen, oder ?
> Das musst Du sogar machen!

Muß man überhaupt nicht !


Man kann auch dem Compiler *.c angeben, dann expandiert er das zu allen 
C-Files, die im aktuellen Verzeichis stehen und linkt auch alles 
zusammen.

Siehe mein Batch im Anhang.

Ich finde das sehr bequem.

Man muß nicht bei jedem Projekt extra das Make anpassen. Nur wenn man 
ein anderes Target (MCU) nimmt, muß man das natürlich eintragen.

Bei Projekten unter 100.000 Zeilen sollte es auch nicht zu lange dauern, 
wenn alle Sourcen neu compiliert werden.


Peter

von Johannes M. (johnny-m)


Lesenswert?

Peter Dannegger wrote:
>> Das musst Du sogar machen!
>
> Muß man überhaupt nicht !
>
>
> Man kann auch dem Compiler *.c angeben, dann expandiert er das zu allen
> C-Files, die im aktuellen Verzeichis stehen und linkt auch alles
> zusammen.
OK, das ist wieder einer der PeDa-Tricks...;-) Klar kann man das so 
machen, es ist nur nicht die "übliche Vorgehensweise" (was nicht heißt, 
dass alles, was nicht üblich ist, schlecht ist).

Mir ging es mit dem Satz "Das musst Du sogar machen!" darum, noch mal 
darauf hinzuweisen, dass dem Compiler die Existenz aller relevanter 
Sourcen mitgeteilt werden muss, und zwar eigentlich natürlich unabhängig 
davon, wie man das letztendlich bewerkstelligt. Die Angabe der Sourcen 
im Makefile (bzw. an entsprechender Stelle in einer IDE, die dann selbst 
das makefile generiert) ist aber nunmal die übliche Variante...

Gruß

Johnny

von Peter D. (peda)


Lesenswert?

Johannes M. wrote:

> OK, das ist wieder einer der PeDa-Tricks...;-)

Ich hatte mir schon vorgenommen, die Geheimnisse des Make zu ergründen.
Aber nachdem Ich merkte, daß der AVR-GCC ja die DOS-Wildcards 
beherrscht, hab ich wieder die Lust verloren.

Und mit -xc gewöhnt man ihm auch die DOS-Großschreibung ab.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger wrote:

> Ich hatte mir schon vorgenommen, die Geheimnisse des Make zu ergründen.

Ich vermute, dass du eher FORTH oder Ada lernen wirst. ;-)  (OK, FORTH
kennst du vielleicht sogar noch aus DDR-Zeiten...)

> Aber nachdem Ich merkte, daß der AVR-GCC ja die DOS-Wildcards
> beherrscht, hab ich wieder die Lust verloren.

Macht er gar nicht.  Die aufrufende Shell expandiert die Wildcards,
und das war schon immer so.  Nur als MS-DOS die Wildcards übernommen
hat, hat es dank einer bekloppt minimalen Shell die Expansion der
Wildcards dem Programm angelastet.  Ist ja auch einfacher, wenn das
jedes Programm für sich selbst machen muss, statt es einmal zentral
im Kommandointerpreter zu machen...

(Falls jemand nicht glaubt, wer von wem hier abgeguckt hat, sei daran
erinnert, dass die Unix-Epoche am 1. 1. 1970 beginnt, während die
MS-DOS-Epoche am 1. 1. 1980 beginnt.)

Wer `make' aber nur als Batchdatei benutzen will, der braucht es auch
nicht.

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.