Es gibt bereits eine sehr gute Anleitung für das Installieren vom avr-gcc [1] [2] ..., aber sie sind veraltet. Ich werde im folgenden nur das beste von diesen Artikel übernehmen.
Zusammenfassung:
Der AVR 8-Bit RISC Mikrocontroller von Atmel ist ein sehr verbreiteter Mikrocontroller. Es ist nur ein Chip mit EEPROM, RAM, Analog zu Digital Umwandler, einer Menge digitaler Ein- und Ausgabezeilen, Timern, UART für RS 232 Kommunikation und vielen anderen Dingen.
Das beste ist jedoch, dass es dafür unter Linux eine vollständige Programmierumgebung dafür gibt: Man kann diesen Mikrocontroller in C++ programmieren, mit dem GCC Compiler.
Die Anleitung ist in mehrere Teile gegliedert. Im ersten beiden Teil wird die Installation erklärt. Im dritten wird gezeigt, wie man damit denn nun seine Programme für den AVR erstellt. Im vierten Teil stehen noch ein paar typische Fehlermeldungen und die Lösungen. Und im letzten ein paar weiterführende Links.
Fertige Pakete
Leider gibt es zu diesem Zeitpunkt (04.2023) keine aktuellen AVR GCC Pakete in den Repositories der Distributionen. Es führt also kein Weg am compilieren vorbei. Wenn die Repositories aktualisiert werden, werde ich das hier vermerken.
Ausnahme bildet das Paket avrdude, dass mit Version 6.3 (2016, Debian 11 bullseye) zwar auch schon etwas älter ist, aber dennoch wird es funktionieren. In der nächsten Debian Version, welche wahrscheinlich im Juni 2023 veröffentlicht wird, ist ein aktuelleres avrdude mit der Version 7.1 dabei. Auf er github Seite von avrdude [7] gibt es Informationen, wie man jetzt schon die neuste Version bekommt.
Avrdude lässt sich mit dem folgenden Befehl aus den Repositories installieren:
# apt-get install avrdude
Eure Voraussetzungen
Ihr müsst grundsätzliche Linux Kenntnisse mitbringen und den Willen sich durch die Anleitung durch zu beißen. Es kann an der ein oder andere Stelle Probleme geben, dann Ruhe bewahren und meist findet man die einfache Lösung schnell von selbst.
AVR GCC compilieren
Also los. Wir brauchen folgende Software:
binutils-2.40.tar.bz2
gcc-13.1.0.tar.gz
avr-libc-2.1.0.tar.bz2
Um bestehende AVR GCC Installationen nicht zu überschreiben, werden die Programme nach ~/bin/avrgcc installiert.
Software Voraussetzungen
Um einem Compiler zu compilieren ist Voraussetzung, dass schon ein Compiler installiert ist - logisch.
Welche Pakete dafür zu installieren sind, variiert etwas zwischen den unterschiedlichen Linux Distributionen. Aber für Debian basierte System ist es im wesentlichen das Paket build-essential sowie das Paket texinfo für binutils.
# apt-get install build-essential texinfo
binutils compilieren
Das binutils Paket enthält alle nötigen low-level Utilities, um Objektdateien zu bauen. Es beinhaltet einen AVR assembler (avr-as), Linker (avr-ld), library handling tools (avr-ranlib, avr-ar), Programme, zum Erzeugen von Objektdateien, die auf das EEPROM des Microcontrollers (avr-objcopy) geladen werden können, disassembler (avr-objdump) und utilities wie avr-strip und avr-size.
Und so wird's gemacht:
$ tar xjf binutils-2.40.tar.bz2
$ cd binutils-2.40/
$ mkdir build
$ cd build
$ ../configure --target=avr --disable-nls --prefix $HOME/bin/avrgcc/
$ make
$ make install
Die Kompilation dauert etwa drei Minuten.
Die binutils ist nun in dem Verzeichnis $HOME/bin/avrgcc/ installiert. Um sie auch aufrufen zu können, muss das Verzeichnis der PATH Umgebungsvariable hinzugefügt werden. Dies geschieht mit folgenden Kommando:
$ export PATH=$HOME/bin/avrgcc/bin/:$PATH
Nun testen wir, ob auch alles klappt. Mit dem Programm 'witch' wird überprüft ob eines der binutils Programme, genauer gesagt das Programm avr-objcopy, erreichbar ist.
$ which -a avr-objcopy
/home/kater/bin/avrgcc/bin/avr-objcopy
/usr/bin/avr-objcopy
Da ich zwei Installationen des avr gcc habe, eine alte Systeminstallation mit dem GCC 5 und die neue mit dem GCC 12, werden bei mir zwei Zeilen ausgegeben. Wichtig ist, dass die neue Installation, also der Path /home/kater/bin/avrgcc/ ganz oben steht, denn dieser wird als erstes durchsucht.
Um das avrgcc Verzeichnis dauerhaft in der PATH Variablen zu seichern, tragt ihr die export Zeile an das Ende der .bashrc Datei ein, die sich im Homeverzeichnis befindet.
AVR GCC compilieren
avr-gcc ist unser eigentlicher Compiler. Diesen zu compilieren ist der aufwendigste Teil, da es am längsten dauert und beim Compilieren am meisten schief gehen kann.
Die für den GCC notwendigen Pakete wie gmp, mpfr, mpc und isl müssen nicht von Hand installiert werden. Dies kann durch das Skript contrib/download_prerequisites erledigt werden. Am besten führt ihr folgende Befehle Schritt für Schritt aus und achtet auf mögliche Fehlermeldungen die kommen könnten. Nicht einfach blind die Befehle copy&pasten!
$ tar xzf gcc-13.1.0.tar.gz
$ cd gcc-13.1.0/
$ ./contrib/download_prerequisites
$ mkdir build
$ cd build/
$ ../configure --target=avr --disable-nls --enable-languages=c,c++ --disable-libssp --with-double=64 --with-long-double=64 --prefix $HOME/bin/avrgcc/
$ make
$ make install
Das Kompilieren hat bei mir etwa 80 Minuten gedauert. Genug Zeit um gemütlich den nächsten Absatz zu lesen ;)
Die Optionen von configure bedeuten folgendes
--target=avr Wir wollen für die Zielplattform AVR compilieren -- logisch
--disable-nls Native Language Support (NLS) brauchen wir nicht. Es ist sinnvoller die Compiler Meldungen auf englisch zu googlen, dann bekommt man schneller die passende Antwort.
--enable-languages=c,c++ Unsere Zielsparache ist C++
--disable-libssp libraries for stack smashing protection - Ist sicher einen gute Sache, auch auf einem AVR bestimmt wertvoll, ich mus mal nachschauen warum das in den anderen Anleitungen deaktiviert wurde...
--prefix $HOME/bin/avrgcc/ Der Compiler soll in der Homeverzeichnis nach bin/avrgcc installiert werden und nicht in die Systemverzeichnisse.
--with-double=64 --with-long-double=64 Den Floatingpoint Typ (long)double mit 64bit compilieren und nicht mit 32.
Der letzte Punkt bedarf etwas Erklärung. Wir alle wissen, dass (32/64bit) Gleitkommaberechnungen auf einem 8bit Controller nicht unbedingt die beste Idee ist, da dies sehr viele Instruktionen erzeugt und der Speicherplatz sehr beschränkt ist. Es ist technisch möglich, aber man will es in der Praxis vermeiden. Wahrscheinlich ist das der Grund, warum der double-precision Typ für AVR auch auf 32bit eingestellt ist, statt 64bit. Anders sieht das bei Berechnungen zur Kompilierzeit aus. Diese werden auf einem normalen Computer erledigt, der im Vergleich zum AVR unendlich viel Speicherplatz hat und auch millionenfach schneller ist. Seit C++11 mit constexpr können bequem in normalen Code Berechnungen zur Kompilierzeit ausgeführt werden, es ist kein Gefummelt mit Makros mehr nötig. Und die Möglichkeiten werden mit jeder neuen C++ Version stetig verbessert. Zum Beispiel gibt es in C++23 (GCC13) das Keyword "consteval" welches garantiert, dass eine Funktion nur zur Kompilierzeit ausgeführt wird. Genau das Richtige für Mikrocontroller! Es spricht also nichts dagegen Berechnungen, die zur Kompilierzeit ausgeführt werden, mit 64bit double zu berechnen.
Hier ein Beispiel das Probleme mit 32bit double Typen aufzeigt: die UBRR Berechnung für den USART. Wie genau die Berechnung aussieht ist dabei egal, wichtig ist nur, dass die Eingangswerte, die CPU Frequenz (z.B 20000000Hz) und die Baudrate (z.B. 1000000), als 32bit integer vorliegen müssen, da die Zahlen für 16bit Typen zu groß sind. Nun kann eine 32bit Ganzzahl nicht verlustfrei in eine 32bit Gleitzahl konvertiert werden, was in einer Warnung (oder Fehler je nach Einstellung) resultiert. Für diesen Fall wissen wir, dass der Verlust an Ziffern kein Problem ist, da wir die möglichen CPU Frequenz und Baud Rate kennen, aber wenn nicht mal dieses eine Beispiel zu 100% mit 32bit double funktioniert, wie soll das erst mit komplizierten Berechnungen funktionieren?!
Aus diesem Grund habe ich double, wie gewohnt, auf 64bit eingestellt. Wenn man auf dem AVR unbedingt Gleitkommaberechnugen braucht, kann man den Typ float nutzen und alles ist wie immer.
So, nach diesen Gedanken sollte die Kompilation fertig sein :)
Zum Abschluss starte ich den Compiler und lasse mir die Versionsnummer ausgeben
$ avr-gcc --version
avr-gcc (GCC) 13.1.0
Weiter geht es mit der Installation der avr-libc
avr-libc compilieren
Die Compilation der avr-libc gestaltet sich ähnlich. Folgende Befehle sind auszuführen:
$ tar xjf avr-libc-2.1.0.tar.bz2
$ cd avr-libc-2.1.0/
$ mkdir build
$ cd build/
$ ../configure --build=`./config.guess` --host=avr --prefix=$HOME/bin/avrgcc/
$ make
$ make install
Die Kompilation dauert etwa vier Minuten.
Danach ist die Installation komplett und wir können mit dem Beispiel Projekt weiter machen.
Ein kleines Testprojekt
Die Hardware
Wir beginnen mit einer kleinen Testschaltung, die du dann später erweitern kannst. Unser Testprogramm, so wie es hier dargestellt ist, bringt einfach eine LED zum Blinken. Man kann fertige Experimentierboards nutzen oder sich eine kleine Schaltung selbst zusammen löten. Das Beispiel ist für den ATmega8 geschrieben, kann aber leicht auf andere Typen angepasst werden.
Schaltplan und Pinbelegung
Aufgebaute Schaltung
Ein externer Kristall an dem Mikrocontroller ist für dieses Beispiel nicht notwendig, da der eingebaute Oszillator benutzt wird der von Werk aus auf 1MHz eingestellt ist.
Die Software
Ein guter Start für die Programmierung von Mikrocontroller ist das AVR-GCC Tutorial [6] und, natürlich, das Datenblatt des verwendeten Kontrolles.
Viel besser als alle Theorie ist ein richtiges Beispiel. Wir schreiben ein kleines Programm, das unsere LED blinken lässt. Nicht sehr nützlich, aber sehr gut für den Anfang.
Ihr könne es hier runterladen: avrblink10zip Das zip enthält den folgenden Quelltext und eine Makefile.
/*
* blink.cpp
* ATmega8 mit 1 MHz
* PORTB wird ueber ein Timer alle 0.263s ein- und ausgeschaltet. Das entspricht 3.81Hz
*
* Der Takt beträgt 1Mhz. Der Timer Prescaler ist auf 1024 eingestellt und ein 8 Bit Timer
* läuft nach 256 Increments über. Also
* 1000000Hz / 1024 / 256 = 3.81Hz
*/
#include <avr/io.h>
#include <avr/interrupt.h>
// Overflow Interrupt Routine von Timer 0
ISR(TIMER0_OVF_vect) {
// PORTB inventieren
PORTB =~ PORTB;
}
int main() {
// PORTB als Ausgang schalten
DDRB = 0xFF;
// Alle Ausgaenge auf 0 schalten
PORTB = 0x00;
// Prescaler von Timer0 auf 1024 stellen
TCCR0 |= (1 << CS02) | (1 << CS00);
// Timer 0 Overflow Interrupt aktivieren
TIMSK |= (1 << TOIE0);
// Interrupts einschalten
sei();
// Endlosschleife. Hier ist kein weiterer Code nötig.
// Das Ein und Ausschalten der LED geschiet in der Overflow Interrupt Routine
for(;;);
}
Die reichlichen Kommentare im Quelltext kommentieren ihn hoffentlich ausreichend.
Kommen wir zum Makefile. Ich hab es extra super super super einfach gehalten, da das Ziel dieses Artikel nicht ist, Makefiles zu schreiben, sondern schnell das Beispiel erfolgreich zu bestreiten.
all:
avr-g++ -O1 -Wall -Wextra -Wconversion blink.cpp -mmcu=atmega8 -o blink.elf
avr-objcopy -O ihex blink.elf blink.hex
load:
avrdude -p m8 -c avr911 -P /dev/ttyUSB0 -U flash:w:blink.hex:i
clean:
rm -f *.o *.hex *.elf
Das Makefile besteht aus drei Targets "all", "load" und "clean". Zum compiliere des Quelltest, zum hochladen auf den Mikrocontroller und zum entfernen der compilat Datein. Die Angabe des Mikrocontroller Typs "atmega8" geschiet direkt mit der Compiler Option "-mmcu" und nicht über eine extra Makefile Variable, um die Sache einfacher zu halten. Wenn das Projekt wächst kann das Makefile entsprechend erweitert werden. Achtet unbedingt achten, daß die Einrückungen im Makefile explizit durch TABs erfolgen und nicht durch Leerzeichen! Das ist nervig, aber das ist nunmal so...
Die Optionen "-Wall -Wextra -Wconversion" schalten viele Warnings ein. Auch solche die Anzeigen wenn einen implizite conversion statt findet. Also z.B. 16bit auf 8bit Integer was auf einem 8Bit Mikrocontroller nicht unbedingt das ist, was man eigentlich programmieren wollte. So kann man viele Stellen finden die möglicherweise Fehler enthalten und potentiell ein paar Instrutionen sparen. Denn das Debuggen auf einem Mikrocontroller ist schwer bis garnicht möglich.
Allgemeine Fehlermeldungen
Fehlermeldungen sollten eigentlich keine vorkommen. Wenn doch, vergewissert euch, dass ihr euch genau an die Anleitung gehalten und nichts falsch abgeschrieben habt!
Hier ein paar typische Fehlermeldungen und ihr Lösungen:
Permission denied
Ihr habt nicht die Rechte, um diesen Befehl auszuführen. In dieser Anleitung müssen nur die Befehle zum installieren mit apt-get als root (also mit su oder sudo) ausgeführt werden.
programmer is not responding
Es kann keine Verbindung zum Programmer aufgenommen werden: Defektes Kabel, Programmer an der falschen Schnittstelle angeschlossen (default: /dev/ttyS0 statt z.B. /dev/ttypUSB0), kein Strom am Programmer etc.
Fehlermeldungen beim compilieren von AVR GCC
/usr/bin/ld: .libs/hwasan.o: relocation R_X86_64_PC32 against undefined symbol `__ehdr_start' can not be used when making a shared object; recompile with -fPIC
Uh... Ich habe etwas rumgefrage und gegoogelt und am wahrscheinlichsten ist es, dass das Linux einfach zu alt ist. Ich bekam den Fehler bei einem ubuntu 19.04.
Links
[1] https://rn-wissen.de/wiki/index.php/Avr-gcc_und_avrdude_installieren by Me 2006, GCC
[2] https://web.archive.org/web/20080611141419/http://www.tldp.org:80/linuxfocus/English/November2004/article352.shtml Guido Socher 2008, GCC 3.4, Example with ATmega8 (Englisch)
[3] http://www.linuxfocus.org/Deutsch/November2004/article352.shtml Selber Artikel in Deutsch
[4] https://web.archive.org/web/20080611143624/http://www.tldp.org/linuxfocus/English/March2002/article231.shtml Guido Socher 2002, GCC 3.0, Example with AT90S4433 (Englisch)
[5] http://roboblog.fatal-fury.de/?p=4076 GCC 8 compilieren
[6] https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
[7] https://github.com/avrdudes/avrdude/
[8] https://gcc.gnu.org/install/