C-Programmierung mit AVR-GCC/ Exkurs: Makefiles



Das Kompilieren von Hand ist eine mühevolle Arbeit. Es gibt aber auch die Möglichkeit das zu automatisieren. Mithilfe des Tools make lassen sich die Quelldateien automatisch kompilieren und auch auf den Mikrocontroller programmieren.

Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebungen à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: 'Makefile' ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung für Windows auch im WinAVR Unterverzeichnis utils/bin enthalten ist.

Im Unterverzeichnis sample einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt (lokale Kopie Stand Sept. 2004). Wahlweise kann man auch mfile von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Plattformen lauffähig.

Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.

Ist im Makefile alles richtig eingestellt, genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.com) als Parameter an "make" übergeben werden. Das Programm make sucht sich "automatisch" das Makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.

Kommando
make all Erstellt aus den im Makefile angegebenen Quellcodes eine hex-Datei (und ggf. auch eep-Datei).
make program Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR.
make clean löscht alle temporären Dateien, also auch die hex-Datei

Diese Aufrufe können in die allermeisten Editoren in "Tool-Menüs" eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.

Üblicherweise sind folgende Daten im Makefile anzupassen:

  • Controllertyp
  • Quellcode-Dateien (c-Dateien)
  • Typ und Anschluss des Programmiergeräts


Seltener sind folgende Einstellungen durchzuführen:

  • Grad der Optimierung
  • Methode zur Erzeugung der Debug-Symbole (Debug-Format)
  • Assembler-Quellcode-Dateien (S-Dateien)

Die in den folgenden Unterabschnitten gezeigten Makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).

Der Controller wird mittels AVRDUDE über ein STK200-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die section .eeprom definiert (siehe Abschnitt Speicherzugriffe), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden.

Controllertyp setzen Bearbeiten

Dazu wird die "make-Variable" MCU entsprechend dem Namen des verwendeten Controllers gesetzt. Eine Liste der von avr-gcc und der avr-libc unterstützten Typen findet sich in der Dokumentation der avr-libc.

# Kommentare in Makefiles beginnen mit einem Doppelkreuz 
...

# ATmega8 at work
MCU = atmega8
# oder MCU = atmega16 
# oder MCU = at90s8535
# oder ...
...

Quellcode-Dateien eintragen Bearbeiten

Der Name der Quellcodedatei, welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung .c.

...
TARGET = superprog
...

Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. Include-Files (C)) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!

...
SRC = $(TARGET).c uart.c lcd.c 1wire.c 
...

Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.

SRC = $(TARGET).c uart.c 1wire.c
# lcd-Code fuer Controller xyz123 (auskommentiert)
# SRC += lcd_xyz.c
# lcd-Code fuer "Standard-Controller" (genutzt)
SRC += lcd.c

Programmiergerät einstellen Bearbeiten

Die Vorlagen sind auf die Programmiersoftware AVRDUDE angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z. B. stk500.exe, uisp, sp12).

...
# Einstellung fuer STK500 an com1 (auskommentiert)
# AVRDUDE_PROGRAMMER = stk500
# com1 = serial port. Use lpt1 to connect to parallel port.
# AVRDUDE_PORT = com1    # programmer connected to serial device

# Einstellung fuer STK200-Dongle an lpt1
AVRDUDE_PROGRAMMER = stk200
AVRDUDE_PORT = lpt1
...

Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.

...
# auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep

# nicht auskommentiert: EERPOM-Inhalt wird mitgeschrieben
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
...

Anwendung Bearbeiten

Das erstellte Makefile und der Code müssen im gleichen Ordner sein, auch sollte der Dateiname nicht verändert werden.

Die Eingabe von make all im Arbeitsverzeichnis mit dem Makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die superprog.hex und superprog.eep werden mit make program zum Controller übertragen. Mit make clean werden alle temporären Dateien gelöscht (="aufgeräumt").

Sonstige Einstellungen Bearbeiten

Optimierungsgrad Bearbeiten

Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (OPT = 0). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist OPT = s die optimale (sic) Einstellung, damit wird kompakter und oft auch der "schnellste" Maschinencode erzeugt. Beim Update auf eine neue Compilerversion ist zu beachten, dass diese möglicherweise intern andere Optimierungsalgorithmen verwendet und sich dadurch die Größe des Machinencodes etwas ändert, ohne dass man im Quellcode etwas geändert hat.

Als Orientierungswerte die Größe des Maschinencodes bei verschiedenen Optionen für einen nicht näher spezifizierten relativ kleinen Testcode bei Verwendung einer nicht näher spezifizierten Compilerversion.

  • -O0 : 12'217 Byte
  • -O1 : 9'128 Byte
  • -O2 : 1'670 Byte
  • -O3 : 3'004 Byte
  • -Os : 1'695 Byte

In diesem Testfall führt die Option -O2 mit zum kompaktesten Code, dies allerdings hier nur mit 25 Bytes "Vorsprung". Es kann durchaus sein, dass nur wenige Programmerweiterungen dazu führen, dass Compilieren mit -Os wieder in kompakteren Code resultiert.

Siehe dazu auch:

Debug-Format Bearbeiten

Unterstützt werden die Formate stabs und dwarf-2. Das Format wird hinter DEBUG = eingestellt. Siehe dazu Abschnitt Eingabedateien zur Simulation.

Assembler-Dateien Bearbeiten

Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (großes S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten, lautet die Zeile: ASRC = softuart.S

Taktfrequenz Bearbeiten

Neuere Versionen der WinAVR/Mfile Vorlage für Makefiles beinhalten die Definition einer Variablen F_CPU (WinAVR 2/2005). Darin wird die Taktfrequenz des Controllers in Hertz eingetragen. Die Definition steht dann im gesamten Projekt ebenfalls unter der Bezeichnung F_CPU zur Verfügung (z. B. um daraus UART-, SPI- oder ADC-Frequenzeinstellungen abzuleiten).

Die Angabe hat rein "informativen" Charakter, die tatsächliche Taktrate wird über den externen Takt (z. B. Quarz) bzw. die Einstellung des internen R/C-Oszillators bestimmt. Die Nutzung von F_CPU hat also nur Sinn, wenn die Angabe mit dem tatsächlichen Takt übereinstimmt.

Innerhalb neuerer Versionen der avr-libc (ab Version 1.2) wird die Definition der Taktfrequenz (F_CPU) zur Berechnung der Wartefunktionen in delay.h genutzt. Diese funktionieren nur dann korrekt, wenn F_CPU mit der tatsächlichen Taktfrequenz übereinstimmt. F_CPU muss dazu jedoch nicht unbedingt im makefile definiert werden. Es reicht aus, wird aber bei mehrfacher Anwendung unübersichtlich, vor #include <util/delay.h> (veraltet: #include <avr/delay.h>) ein #define F_CPU [hier Takt in Hz]UL einzufügen. Bei Nutzung von delay.h ist darauf zu achten, dass die Optimierung des Compilers nicht ausgeschaltet ist, sonst wird sehr viel Code erzeugt und die Wartezeit stimmt nicht mit der gewünschten Zeitspanne überein. Vgl. dazu den entsprechenden Abschnitt der Dokumentation.

Eingabedateien zur Simulation in AVR-Studio Bearbeiten

Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage so genannter coff-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) "direkt" vom Compiler erzeugt wird.

Vorgehensweise bei dwarf-2
  • Im Makefile bei DEBUG:
    DEBUG=dwarf-2
  • make all (evtl. vorher make clean)
  • Die erzeugte elf-Datei (im Beispiel oben superprog.elf) in AVR-Studio laden
  • AVR-Simulator und zu simulierenden Controller wählen, "Finish"
  • Weiteres siehe AVR-Studio Online-Hilfe
Vorgehensweise bei extcoff
(sollte nur noch in Ausnahmefällen genutzt werden)
  • Im Makefile bei DEBUG:
    DEBUG=stabs
  • make extcoff (evtl. vorher make clean)
  • Die erzeugte cof-Datei (im Beispiel oben superprog.cof) in AVR-Studio laden
  • AVR-Simulator und zu simulierenden Controller wählen, "Finish"
  • Weiteres siehe AVR-Studio Online-Hilfe


Beim Simulieren scheinen oft "Variablen zu fehlen". Ursache dafür ist, dass der Compiler diese "Variablen" direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.

Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, einem Nachbau davon (BootICE, Evertool o. ä.) oder dem ATMEL JTAGICE MKII "im System" zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. "In-System-Emulation" mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.

Die Verwendung von Makefiles bietet noch viele weitere Möglichkeiten, einige davon werden im Anhang Zusätzliche Funktionen im Makefile erläutert.