C ohne Makefile

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Während es unbedingt zu empfehlen ist, zum Übersetzen von mittelgroßen und großen Projekten Makefiles zu verwenden, kann es bei sehr kleinen Programmen oder für kurze Tests einfacher sein, das Übersetzen und Übertragen direkt per Kommandozeilenbefehl zu erledigen. Hier wird eine Methode vorgestellt, die es erlaubt, mit einer einzigen Datei auszukommen, die gleichzeitig als Batch-Datei fürs Übersetzen und Übertragen sowie als Quellcode-Datei verwendet wird.

Dieser Trick gelingt sehr gut unter Linux (hier: Ubuntu). Wahrscheinlich ist es ohne größere Probleme möglich, eine Windows-Variante zu erstellen (bitte im Artikel ergänzen).

Am besten kann die Funktionsweise an einer Beispiel-Quellcodedatei gezeigt werden (hier für AVR-Mikrocontroller):

# /*
M=atmega328p; F=${0:0:-2}; rm -f "$F".hex; avr-gcc -mmcu=$M -Os \
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
-Wall -Wstrict-prototypes -std=gnu99 -g "$F".c -o "$F".elf && \
avr-objcopy -O ihex -R .eeprom "$F".elf "$F".hex && \
avrdude -c usbasp -p $M -B 12 -U flash:w:"$F".hex:i; exit; */

#include <avr/io.h>

int main(void) {
  DDRB= 0xFF;
  while(1) {
    PORTB= 0x03;
    // ...
    }
  return 0;
  }

Voraussetzungen

Neben avr-gcc wird im Beispiel das weit verbreitete Programm avrdude verwendet. Natürlich kann auch jedes andere Programm zur Übertragung der am Ende erzeugten .hex-Datei verwendet werden. Die Kommandozeilen sind dann entsprechend anzupassen.

Vorbereitung

Die oben im Beispiel gezeigten Kopfzeilen (alles oberhalb von "#include") müssen in die eigene Quellcodedatei kopiert werden. Dort müssen sie ebenfalls ganz am Anfang stehen.

In der zweiten Zeile der Quellcodedatei muss der Name des verwendeten Mikrocontrollers auf den tatsächlich verwendeten Typ geändert werden. Der Compiler akzeptiert den Namen nur in Kleinbuchstaben, also zum Beispiel: attiny13a

Damit die Quelldatei als Programm startbar ist, muss einmalig das Ausführungsflag gesetzt werden. Unter Ubuntu kann das am einfachsten so erledigt werden: Rechtsklick auf das Symbol der Quelldatei, Menüpunkt "Eigenschaften", Reiter "Zugriffsrechte", Häkchen bei "Datei als Programm ausführen" setzen. In der Konsole geht das bei eigentlich allen Linuxen mit "chmod +x dateiname".

Arbeitsweise

Die Quelldatei mit den oben gezeigten speziellen Kopfzeilen kann ganz wie gewohnt editiert werden. Zum Übersetzen des Programms bewegt man sich per Kommandozeilenbefehl cd in dessen Verzeichnis und ruft es auf, als wäre es ein ganz normales Batch-Kommando. Unter Linux so:

./meinprogramm.c

Das Programm wird nun übersetzt, in eine .hex-Datei konvertiert und auf den Mikrocontroller übertragen.

Geht bei der Übersetzung etwas schief, weil das Programm nicht fehlerfrei übersetzt werden konnte, unterbleibt das Übertragen auf den Mikrocontroller, und die Terminal-Ausgabe zeigt die betreffenden Fehlermeldungen.

Disassemblieren

Mit diesem Befehlszeilenkommando kann man sich anzeigen lassen, welche Assembler-Befehle der C-Compiler erzeugt hat:

avr-objdump -D -m avr -S -j .text -j .data hier_der_quelldateiname.elf

C-Programmierer mit guten Assemblerkenntnissen werden hier so manche Überraschung finden. Teilweise lernt man neue Kniffe, die man danach selbst in der Assemblerprogrammierung einsetzen kann. In vielen Fällen wird man sich aber auch wundern, wie umständlich manche Programmabschnitte vom Compiler umgesetzt werden. Man kann sich selbst ein Bild davon machen, in welchen Fällen die Programmiersprache C Vorteile hat und in welchen Fällen man mit Assembler eher zum Ziel kommt.

Warum funktioniert es, Kommandozeilenbefehle und Quellcode in derselben Datei zu verwenden?

Für das Programmieren von Mikrocontrollern ist die Antwort darauf nicht wichtig, man kann auch einfach hinnehmen, dass es so ist. Für jeden, der sich intensiver mit den Hintergründen beschäftigen will, hier ein paar Sätze zur Erklärung.

Für die Parser von Compiler und Shell ist der jeweils andere Code "unsichtbar":

  • Der C-Compiler interpretiert alles, was zwischen /* und */ steht, als Kommentar und ignoriert den betreffenden Abschnitt. Das #-Zeichen zu Beginn der Datei wird in C als Einleitung für eine Präprozessor-Direktive verstanden. Da dem #-Zeichen aber in der gleichen Zeile keine solche Direktive folgt, wird es vom Compiler als "leere Direktive" angesehen und deshalb ignoriert.
  • Der Kommandozeileninterpreter betrachtet das #-Zeichen als Einleitung eines Kommentars und ignoriert daher den Rest der ersten Zeile; er macht gleich mit der zweiten Zeile weiter und arbeitet sich bis zum Kommando "exit" durch. Dort beendet er seine Arbeit und ignoriert deshalb den danach folgenden C-Quellcode.