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
intmain(void)
7
8
9
{
10
ausgabe();
11
return0;
12
}
test.c
1
#include"test.h"
2
3
4
voidausgabe(void)
5
6
{
7
PORTA=255;
8
}
test.h
1
#ifndef __TEST_H__
2
#define __TEST_H__
3
4
5
voidausgabe(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
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.
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
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
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
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.
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".
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
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
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
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.