Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter fuer schwierige und grosse Gaerten im Test

(Signal/Interrupt-Name vertippt oder Leerzeichen zu viel)
(ISR mit eigenem Prolog/Epilog: Framepointer, used)
 
(285 dazwischenliegende Versionen von 16 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Die GNU Compiler Collection (GCC) unterstützt das Zielsystem (Target) [[Avr|AVR]] für die Sprachen C und C++. GCC ist ein sehr leistungsfähiger [[Compiler]], und er kann als die wichtigste freie Software überhaupt bezeichnet werden. Immerhin sind das freie [[Betriebssystem]] <tt>Linux</tt> und viele andere Programme &#150; auch gcc und die Toolchains selbst &#150; mit gcc generiert.  
+
'''avr-gcc''' ist ein freier C-[[Compiler]], mit dem C-Programme zu ausführbaren Programmen übersetzen werden können, die auf [[Microcontroller]]n der [[AVR]]-Familie lauffähig sind.
 +
An Sprachen versteht avr-gcc sowohl C als auch [[#C%2b%2b|C++]].
 +
Neben Standard-C bzw. ANSI-C versteht avr-gcc auch GNU-C, das etwas mehr Möglichkeiten und kleinere Spracherweiterungen bietet.
 +
 
 +
avr-gcc kann auch dazu verwendet werden, um C/C++ Programme nach Assembler zu übersetzen oder um Bibliotheken zu erstellen, die später in unterschiedlichen Projekten verwendet werden können.
 +
 
 +
Wie bei allen aus der UNIX-Welt kommenden Programmen ist das Kommando-Interface von avr-gcc die Shell bzw. die Kommandozeile, über die Optionen, Parameter, Einstellungen und die Namen der zu übersetzenden Dateien angegeben werden.  
  
<!--
 
Im Gegensatz zu [[Bascom]] ist <tt>avr-gcc</tt> ein reiner Compiler, bringt also keine umfangreiche Bibliothek an Funktionalitäten mit. Jedoch finden sich einige low-level Funktionen zum Lesen aus dem [[Flash]] oder Lesen/Schreiben des [[EEPROM|EEPROMs]] etc. in der <tt>avr-libc</tt>.
 
-->
 
 
=How to Read=
 
=How to Read=
  
Dieser Artikel bespricht <tt>avr-gcc</tt>. Er ist kein Tutorial und kein AVR-Handbuch &#150; das würde den Umfang des Artikels bei weitem sprengen.
+
Dieser Artikel bespricht avr-gcc Version 3.x. Er ist kein C-Tutorial und kein AVR-Handbuch &ndash; das würde den Umfang des Artikels bei weitem sprengen.  
  
Der Artikel ist ein Handbuch zu <tt>avr-gcc</tt>. Er bespricht zum Beispiel, wie man <tt>avr-gcc</tt> anwendet und
+
Der Artikel ist ein Handbuch zu avr-gcc. Er bespricht zum Beispiel, wie avr-gcc angenwendet wird und Besonderheiten von avr-gcc-C, die nicht zum Sprachumfang von C gehören.
Besonderheiten von <tt>avr-gcc</tt>-C,
+
Dazu zählen die Definition von [[#Interrupts|Interrupt]] Service Routinen ([[ISR|ISRs]])  
die nicht zum Sprachumfang von C gehören.
+
Dazu zählen die Definition von Interrupt Service Routinen (ISRs)  
+
 
oder wie man Daten ins [[EEPROM]] legt.
 
oder wie man Daten ins [[EEPROM]] legt.
  
Zeile 20: Zeile 21:
 
Dafür enthält dieser Artikel zusammen mit dem AVR-Handbuch das Rüstzeug,  
 
Dafür enthält dieser Artikel zusammen mit dem AVR-Handbuch das Rüstzeug,  
 
bietet aber keine Lösungen für konkrete Aufgaben.
 
bietet aber keine Lösungen für konkrete Aufgaben.
 +
 +
Neben diesem Artikel gibt es den Unterartikel [[avr-gcc/Interna|Interna von avr-gcc]] wo Dinge wie die Registerverwendung, Attribute, Builtins und Sections von avr-gcc dargestellt werden. Zudem findet sich dort ein Überblick über die Arbeitsweise von gcc mit den Schritten
 +
* Precompilieren
 +
* Compilieren
 +
* Assemblieren
 +
* Linken
 +
Ein weiterer Unterartikel widmet sich dem Thema [[Inline-Assembler in avr-gcc]].
  
 
In den [[:Kategorie:Quellcode C|C-Codebeispielen]]
 
In den [[:Kategorie:Quellcode C|C-Codebeispielen]]
befindet sich das ausführlichere Beispiel "[[Hallo Welt für AVR (Blinky)|Blinky]]",
+
befindet sich das ausführlichere Beispiel "[[Hallo Welt für AVR (LED blinken)]]",
das nur eine [[LED]] blinkt und zeigt,  
+
das nur eine [[Diode#Lumineszenzdiode|LED]] blinkt und zeigt,  
wie ein kleines Projekt mit <tt>avr-gcc</tt> compiliert werden kann.
+
wie ein kleines Projekt mit avr-gcc compiliert werden kann.
 +
 
 +
Es gibt ein [[C-Tutorial]], das jedoch noch unvollständig und teilweise feherhaft ist (Stand 02/2006). Darüber hinaus gibt es ein [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial C-Tutorial bei www.mikrocontroller.net].
  
 
=Benutzer-Schnittstelle=
 
=Benutzer-Schnittstelle=
  
Die Benutzer-Schnittstelle von GCC ist die Kommandozeile einer Shell, Console bzw. Eingabeaufforderung.  
+
Die Benutzer-Schnittstelle von avr-gcc ist &ndash; wie für alle Programme, die aus der UNIX-Welt kommen &ndash; die Kommandozeile einer Shell, Console bzw. Eingabeaufforderung.  
  
Im einfachsten Fall sieht ein Aufruf von <tt>avr-gcc</tt> also so aus:
+
Im einfachsten Fall sieht ein Aufruf von avr-gcc also so aus:
 
  > avr-gcc
 
  > avr-gcc
 
Dabei das '<tt>></tt>' nicht mittippen, und ein ENTER am Ende der Zeile drücken.
 
Dabei das '<tt>></tt>' nicht mittippen, und ein ENTER am Ende der Zeile drücken.
 
Die Antwort bei korrekter Installation ist dann
 
Die Antwort bei korrekter Installation ist dann
 
  avr-gcc: no input files
 
  avr-gcc: no input files
Was bedeutet: das Programm <tt>avr-gcc</tt> wurde vom Betriebssystem gefunden und konnte/durfte gestartet werden. Dann gibt <tt>avr-gcc</tt> eine Fehlermeldung aus und beendet die Ausführung, weil er keine Eingabedatei(en) bekommen hat &#150; was ja auch stimmt. Soweit ist also alles in Butter.
+
Was bedeutet: das Programm avr-gcc wurde vom Betriebssystem gefunden und konnte/durfte gestartet werden. Dann gibt avr-gcc eine Fehlermeldung aus und beendet die Ausführung, weil er keine Eingabedatei(en) bekommen hat &#150; was ja auch stimmt. Soweit ist also alles in Butter.
  
GCC war immer Kommandozeilen-orientiert und wird es auch immer bleiben, denn das hat gute Gründe:
+
Um eine C-Datei <tt>foo.c</tt> mir avr-gcc optimiert zu einem lauffähigen elf-Programm <tt>foo.elf</tt> für einen [[ATmega32]] zu compileren, würde man angeben
* ein Compiler ist ein Compiler (und keine grafische Bedienschnittstelle)
+
> avr-gcc -Os -mmcu=atmega32 foo.c -o foo.elf
* die Plattformabhängigkeit wird auf ein Minimum reduziert
+
Hat man seine Quellen auf zwei oder mehre Dateien verteilt, geht es analog:
* es gibt die Möglichkeit, GCC per Skript oder [[make]] zu starten
+
> avr-gcc -Os -mmcu=atmega32 foo.c foo2.c -o foo.elf
* GCC kann durchaus in eine Umgebung integriert werden: in einen Editor oder in eine GUI wie neuere Versionen von AVR-Studio, etc. Der GCC-Aufruf kann sogar von einem Server-Socket oder einer Web-Application heraus erfolgen, welche ein C-Programm empfängt, es von GCC übersetzen lässt, und das Resultat zurückschickt oder sonst was damit anstellt.
+
* Lizenzgründe: eine Umgebung, die GCC integriert, kann durchaus proprietär oder nicht quelloffen sein und muss nicht der [[Freie Software|GPL]] unterliegen.
+
  
=Allgemeine Charakteristika von avr-gcc=
+
Will man nur eine Objekt-Datei erstellen (nur compilieren, nicht linken), dann geht das wie folgt. Das kann günstig sein bei grösseren Projekten, wenn man das Projekt neu erzeugen will, aber nur in einer Quelldatei was geändert hat. Oder wenn das Objekt in einer Bibliothek landen soll.
 +
> avr-gcc -Os -c -mmcu=atmega32 foo.c -o foo.o
  
==Groß- und Kleinschreibung==
+
Die ausführbare Gesamtdatei <tt>foo_all.elf</tt> erhält man dann, indem alle Objekte zusammenlinkt:
C unterscheidet generell zwischen Groß- und Kleinschreibung, sowohl bei Variablen- und Funktionsnamen, bei Sprungmarken als auch bei Makros, und je nach Betriebssystem auch bei Pfad- und Dateinamen/Dateierweiterungen.
+
> avr-gcc -mmcu=atmega32 foo.o foo2.o foo3.o -o foo_all.elf
  
==Größe des Typs int==
+
Um die ausführbare Datei in das oft verwendete Intex-HEX-Format umzuwandeln (einmal fürs Programm, einmal für ein Abbild des [[EEPROM]]s) gibt man an:
Der Standard-Typ <tt>int</tt> ist 16 Bit groß
+
> avr-objcopy -O ihex -j .text -j .data                        foo_all.elf  foo_all.hex
 +
> avr-objcopy -O ihex -j .eeprom --change-section-lma .eeprom=1 foo_all.elf  foo_all_eeprom.hex
  
==Größe von Pointern==
+
----
Ein Pointer (Zeiger) ist 16 Bit groß
+
  
==Endianess==
+
[[GCC]] war immer Kommandozeilen-orientiert und wird es auch immer bleiben, denn das hat gute Gründe:
<tt>avr-gcc</tt> implementiert Datentypen als little-endian, d.h. bei Datentypen, die mehrere Bytes groß sind, wird das niederwertigste Byte an der niedrigsten Adresse gespeichert. Dies gilt auch für Adressen und deren Ablage auf dem [[Stack]] sowie die Ablage von Werten, die mehrere Register belegen.
+
* ein Compiler ist ein Compiler (und keine grafische Bedienschnittstelle)
 
+
* die Plattformabhängigkeit wird auf ein Minimum reduziert
==Binäre Konstanten==
+
* es gibt die Möglichkeit, avr-gcc per Skript oder [[make]] zu starten
Einige Versionen von <tt>avr-gcc</tt> ermöglichen die Verwendung binärer Konstanten für 8-Bit-Werte:
+
* avr-gcc kann durchaus in eine Umgebung integriert werden: in einen Editor oder in eine GUI wie neuere Versionen von AVR-Studio erfolgreich beweisen, etc. Der avr-gcc-Aufruf kann sogar von einem Server-Socket oder einer Web-Application heraus erfolgen, welche ein C-Programm empfängt, es von avr-gcc übersetzen lässt, und das Resultat zurückschickt oder sonst was damit anstellt.
:<pre>unsigned char value = 0b00000010;</pre>
+
* Lizenzgründe: eine Umgebung, die avr-gcc integriert, kann durchaus proprietär oder nicht quelloffen sein und muss nicht der [[Freie Software|GPL]] unterliegen. Wieder ist AVR-Studio ein Beispiel.
:Davon sollte man absehen, denn zum einen hat man schnell eine 0 zu wenig oder zu viel getippselt, es ist kein Standard-C und man hat die leserlichere Alternative
+
:<pre>unsigned char value = (1<<1);</pre>
+
 
+
==Registerverwendung==
+
;R0: ein temporäres Register, in dem man rumwutzen darf
+
;R1: enthält immer den Wert 0
+
;R2 &#150; R17, R28, R29: allgemeine Register, die durch einen Funktionsaufruf nicht verändert bzw wieder auf den ursprünglichen Wert restauriert werden
+
;R18 &#150; R27, R30, R31: können durch Funktionsaufrufe verändert werden
+
; R28 &#150; R29 (Y-Reg): enthält den Framepointer, sofern benötigt
+
 
+
=Ablauf der Codegenerierung=
+
 
+
Die Code-Erzeugung durch avr-gcc geschieht in mehreren, voneinander unabhängigen Schritten. Diese  Schritte sind für den Anwender nicht immer erkennbar, und es auch nicht unbedingt notwendig, sie zu kennen. Für ein besseres Verständnis der Code-Generierung und zur Einordnung von Fehlermeldungen ist eine Kenntnis aber hilfreich.
+
 
+
==Übersichts-Grafik==
+
 
+
[[Bild:Avr-gcc-1.png|Zusammenspiel zwischen avr-gcc und binutils]]
+
 
+
==Schritte der Codegenerierung==
+
 
+
Ohne die Angabe spezieller Optionen werden die Zwischenformate nur als temporäre Dateien angelegt und nach Beenden des gcc-Laufs wieder gelöscht. Dadurch fällt die Aufgliederung in Unterschritte nicht auf. In diesem Falle müssen Assembler und Linker/Locator auch nicht extra aufgerufen werden, sondern die Aufrufe erfolgen durch gcc. Ausnahme ist <tt>avr-objcopy</tt>, welches immer aufgerufen werden muss, wenn man z.B. eine HEX-Datei haben möchte.
+
 
+
===Precompileren===
+
 
+
Alle Preprozessor-Direktiven werden aufgelöst. Dazu gehören Direktiven wie
+
<pre>
+
#include <avr/io.h>
+
#include "meinzeug.h"
+
 
+
#define MAKRONAME ERSATZTEXT
+
 
+
#if !defined(__AVR__)
+
#error einen Fehler ausgeben und abbrechen
+
#else
+
/* Alles klar, wir koennen loslegen mit C-Code fuer AVR */
+
#endif
+
 
+
MAKRONAME
+
</pre>
+
Precompilieren besteht also nur aus reinem Textersatz: Auflösen von Makros, kopieren von anderen Dateien in die Quelle, etc.
+
 
+
===Compilieren===
+
 
+
In diesem Schritt geschieht der eigentliche Compilier-Vorgang: <tt>avr-gcc</tt> übersetzt die reine, precompilierte C-Quelle (*.i): Die Quelle wird auf Syntax-Fehler geprüft, es werden Optimierungen gemacht, und das übersetzte C-Programm als Assembler-Datei in (*.s) gespeichert.
+
 
+
===Assemblieren===
+
 
+
Der Assembler (<tt>avr-as</tt>) übersetzt den Assembler-Code (*.s) in das  AVR-eigene Objektformat elf32-avr (*.o). Das Objekt enthält schon Maschinen-Code. Zusätzlich gibt es aber noch Lücken, die erst später gefüllt werden und Debug-Informationen und ganz viel anderes Zeug.
+
  
===Linken und Lokatieren===
+
= Unterstützte AVR-Derivate=
  
Der Linker (<tt>avr-ld</tt>) bindet die angegebenen Objekte (*.o) zusammen und löst externe Referenzen auf. Der Linker entscheidet anhand der Beschreibung im Linker-Script, in welchen Speicheradressen und Sektionen die Daten landen: er ''lokatiert'' (von location, locate (en)).
+
:''[http://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html GCC Manual: AVR Options]
Module aus Bibliotheken (*.a) werden hinzugebunden (z.B. <tt>printf</tt>) und die elf32-avr Ausgabedatei (üblicherweise *.elf) erzeugt.
+
  
===Umwandeln ins gewünschte Objekt-Format===
+
Diese Liste der unterstützten Devices kann man anzeigen lassen mit
 +
> avr-gcc --target-help
 +
bzw. ab Version 4.7 mit
 +
> avr-gcc --help=target
  
Linker und Assembler erzeugen ihre Ausgabe im Objektformat elf32-avr. Wird ein anderes Objektformat wie Intel-HEX (*.hex), binary (*.bin) oder srec (*.srec) benötigt, kann <tt>avr-objcopy</tt> dazu verwendet werden, um diese zu erstellen. Der Inhalt einzelner Sections kann gezielt umkopiert oder ausgeblendet werden, so daß Dateien erstellt werden können, die nur den Inhalt des Flashs (Section <tt>.text</tt>) oder des EEPROMs (Section <tt>.eeprom</tt>) repräsentieren. Durch das Umwandeln in ein anderes Objektformat gehen üblicherweise Informationen wie Debug-Informationen verloren.
+
Siehe auch "AVR Options" in der GCC [[#Dokumentation|Dokumentation]].
  
 
=Kommandozeilen-Optionen=
 
=Kommandozeilen-Optionen=
Die Codegenerierung bei <tt>avr-gcc</tt> wird über Kommandozeilen-Optionen gesteuert. Diese legen fest, für welchen Controller Code zu erzeugen ist, wie stark optimiert wird, ob Debug-Informationen erzeugt werden, etc. Die Optionen teilen sich in zwei Gruppen: Optionen, die für alle GCC-Ports verfürgbar sind und maschinenspezifische Optionen, die nur für AVR verfügbar sind.
+
Die Codegenerierung bei avr-gcc wird über Kommandozeilen-Optionen gesteuert. Diese legen fest, für welchen Controller Code zu erzeugen ist, wie stark optimiert wird, ob Debug-Informationen erzeugt werden, etc. Die Optionen teilen sich in zwei Gruppen: Optionen, die für alle GCC-Ports verfürgbar sind und maschinenspezifische Optionen, die nur für AVR verfügbar sind.
  
 
Aus der Masse an GCC-Optionen kann hier nur ein kleiner Auszug der wichtigsten und am häufigsten verwendeten Optionen vorgestellt werden. Eine Auflistung aller GCC-Optionen mit Kurzbeschreibung umfasst knapp 1000 Zeilen &#150; ohne undokumentierte Optionen, versteht sich.
 
Aus der Masse an GCC-Optionen kann hier nur ein kleiner Auszug der wichtigsten und am häufigsten verwendeten Optionen vorgestellt werden. Eine Auflistung aller GCC-Optionen mit Kurzbeschreibung umfasst knapp 1000 Zeilen &#150; ohne undokumentierte Optionen, versteht sich.
Zeile 127: Zeile 90:
 
; <tt>--help</tt>: Anzeige der wichtigsten Optionen
 
; <tt>--help</tt>: Anzeige der wichtigsten Optionen
 
; <tt>--help -v</tt>: Überschüttet einen mit Optionen
 
; <tt>--help -v</tt>: Überschüttet einen mit Optionen
; <tt>--target-help</tt>: Anzeige der wichtigsten maschinenspezifischen Optionen
+
; <tt>--target-help<br/>--help=target</tt>: Anzeige der wichtigsten maschinenspezifischen Optionen und der unterstützten AVR-Derivate
; <tt>-O0</tt>: keine Optimierung
+
; <tt>-O0</tt>: keine Optimierung - sinnvoll zum debuggen
 
; <tt>-O1</tt>: Optimierung
 
; <tt>-O1</tt>: Optimierung
; <tt>-Os</tt>: optimiert für Code-Größe
+
; <tt>-Os</tt>: optimiert für Code-Größe – meist beste Wahl für µCs
 
; <tt>-O2</tt>: stärkere Optimierung für bessere Laufzeit
 
; <tt>-O2</tt>: stärkere Optimierung für bessere Laufzeit
 
; <tt>-g</tt>: erzeugt Debug-Informationen
 
; <tt>-g</tt>: erzeugt Debug-Informationen
; <tt>-c</tt>: (pre)compilert und assembliert nur bis zum Objekt (*.o), kein [[Linker|link]]-Lauf
+
; <tt>-gdwarf-3 -gstrict-dwarf</tt>: erzeugt Debug-Informationen nachdem DWARF-3 Standard und ohne GNU-spezifische Erweiterungen.
; <tt>-S</tt>: (pre)compilert nur und erzeugt [[Assembler]]-Ausgabe (*.s)
+
; <tt>-c</tt>: (pre)compilert und assembliert nur bis zum Objekt (<tt>*.o</tt>), kein Link-Lauf
; <tt>-E</tt>: nur Precompilat (*.i) erzeugen, kein Compilieren, kein Assemblieren, kein Linken
+
; <tt>-S</tt>: (pre)compilert nur und erzeugt Assembler-Ausgabe (<tt>*.s</tt>)
; <tt>-o</tt> &lt;filename&gt;: legt den Name der Ausgabedatei fest
+
; <tt>-E</tt>: nur Precompilat (<tt>*.i</tt> bzw. <tt>*.ii</tt>) erzeugen, kein Compilieren, kein Assemblieren, kein Linken
 +
; <tt>-o <filename></tt>: legt den Name der Ausgabedatei fest
 
; <tt>-v</tt>: zeigt Versionsinformationen an und ist geschwätzig (verbose): Anzeige der aufgerufenen tools
 
; <tt>-v</tt>: zeigt Versionsinformationen an und ist geschwätzig (verbose): Anzeige der aufgerufenen tools
; <tt>-I</tt>&lt;path&gt;: Angabe eines weiteren Include-Pfads, in dem Dateien mit <tt>#include &lt;...&gt;</tt> gesucht werden
+
; <tt>-I<path></tt>: Angabe eines weiteren Include-Pfads, in dem Dateien mit <tt>#include <...></tt> gesucht werden
; <tt>-E -dM </tt> &lt;filename&gt;:Anzeige aller Defines
+
; <tt>-E -dM <filename></tt>: Anzeige aller Defines
; <tt>-D</tt>&lt;name&gt;:Definiert [[Makro]] <tt>&lt;name&gt;</tt>
+
; <tt>-MM</tt>: Für die angegebenen Eingabe-Dateien wird eine Ausgabe erzeugt, die als [[make|Makefile]]-Fragment dienen kann und die Anhängigkeiten (dependencies) der Objekte von den Quellen/Headern beschreibt.
; <tt>-D</tt>&lt;name&gt;=&lt;wert&gt;:Definiert Makro <tt>&lt;name&gt;</tt> zu <tt>&lt;wert&gt;</tt>
+
; <tt>-D<name></tt>: Definiert Makro <tt><name></tt>
; <tt>-U</tt>&lt;name&gt;:Undefiniert Makro <tt>&lt;name&gt;</tt>
+
; <tt>-D<name>=<wert></tt>: Definiert Makro <tt><name></tt> zu <tt><wert></tt>
; <tt>-save-temps</tt>:Temporäre Dateien (*.i, *.s) werden nicht gelöscht. Teilweise fehlerhaft zusammen mit <tt>-c</tt>
+
; <tt>-U<name></tt>: Undefiniert Makro <tt><name></tt>
 
+
; <tt>-save-temps</tt>: Temporäre Dateien (<tt>*.i</tt>, <tt>*.s</tt>) werden nicht gelöscht.
; <tt>-Wa,</tt>&lt;options&gt;: übergibt Komma-getrennte Liste <tt>&lt;options&gt;</tt> an den Assembler (<tt>avr-as</tt>)
+
; <tt>-Wa,<options></tt>: übergibt Komma-getrennte Liste <tt><options></tt> an den Assembler (<tt>avr-as</tt>)
; <tt>-Wp,</tt>&lt;options&gt;:  übergibt Komma-getrennte Liste <tt>&lt;options&gt;</tt> an den Preprozessor
+
:;<tt>-Wa,-a=<filename></tt>: Assembler erzeugt ein Listing mit Name <tt><filename></tt>
; <tt>-Wl,</tt>&lt;options&gt;:  übergibt Komma-getrennte Liste <tt>&lt;options&gt;</tt> an den Linker (<tt>avr-ld</tt>)
+
; <tt>-Wp,<options></tt>:  übergibt Komma-getrennte Liste <tt><options></tt> an den Preprozessor
 +
; <tt>-Wl,<options></tt>:  übergibt Komma-getrennte Liste <tt><options></tt> an den Linker (<tt>avr-ld</tt>)
 +
:;<tt>-Wl,-Map=<filename></tt>: Linker erzeugt ein Map-File mit Name <tt><filename></tt>
 +
:;<tt>-Wl,--section-start=<section>=<address></tt>: Linker legt die [[avr-gcc/Interna#Sections|Section]] <tt><section></tt> ab Adresse <tt><address></tt>, z.B: <tt>.eeprom=0x810001</tt>
 
; <tt>-Wall</tt>: gibt mehr Warnungen, aber immer noch nicht alle
 
; <tt>-Wall</tt>: gibt mehr Warnungen, aber immer noch nicht alle
; <tt>-pedantic</tt>: geht bedonders pedantisch mit Code um
+
; <tt>-std=gnu99</tt>: Sagt dem Compiler, dass er C99 mit GNU-C Erweiterungen akzeptieren soll.  Das ist zum Beispiel der Fall, wenn man Embedded-C Code mit <tt>__flash</tt> verwenden will.
; <tt>-ansi</tt>: bricht mit einer Fehlermeldung ab, wenn kein ANSI-C verwendet wurde
+
; <tt>-std=c89<br/>-ansi</tt>: bricht mit einer Fehlermeldung ab, wenn kein ANSI-C (ISO C89) verwendet wurde
; <tt>-ffreestanding</tt>: Das erzeugte Programm läuft nicht in einer Umgebung wie einer Shell. Der Prototyp von <tt>main</tt> ist
+
; <tt>-std=c99</tt>: C99 mit einigen Erweiterungen, die nicht dem C99-Standard widersprechen
:<tt>
+
; <tt>-std=c99 -pedantic</tt>: Bricht mit einer Fehlermeldung ab, wenn kein ISO C99 verwendet wird
void main (void);
+
</tt>
+
  
 
==Maschinenspezifische Optionen für avr-gcc==
 
==Maschinenspezifische Optionen für avr-gcc==
 +
 
Maschinenabhängige Optionen beginnen immer mit '''-m'''
 
Maschinenabhängige Optionen beginnen immer mit '''-m'''
;<tt>-mmcu=xxx</tt>: Festlegen des Targets, für das Code generiert werden soll. Je nach Target werden unterschiedliche Instruktionen verwendet und andere Startup-Dateien (<tt>crtxxx.o</tt>) eingebunden. Spezielle Defines werden gesetzt, um in der Quelle zwischen den Targets unterscheiden zu können:  
+
;-mmcu=xxx: Festlegen des Targets (Zielsystem/Controller), für das Code generiert werden soll. Je nach Target muss avr-gcc unterschiedliche Instruktionen verwenden und andere Startup-Dateien (<tt>crtxxx.o</tt>) einbinden. avr-gcc setzt spezielle Defines, um auch in der Quelle zwischen den Targets unterscheiden zu können, falls das notwendig sein sollte:  
:<tt>
+
:{| border="0" cellpadding="0" cellspacing="0"
#ifdef __AVR_AT90S2313__
+
|
/* Code fuer AT90S2313 */
+
<pre>
#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega32__)
+
#ifdef __AVR_AT90S2313__
/* Code fuer Mega8 und Mega32 */  
+
/* Code fuer AT90S2313 */
#else
+
#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega32__)
#error Das ist noch nicht implementiert für diesen Controller!
+
/* Code fuer Mega8 und Mega32 */  
#endif
+
#else
</tt>
+
#error Das ist noch nicht implementiert für diesen Controller!
 
+
#endif
Zwar gibt es für alle AVR-Derivate die <tt>avr/io.h</tt>, aber die AVR-Familien unterscheiden sich in ihrer Hardware; z.B. darin, wie Register heissen oder wie Hardware zu initialisieren ist. Diese Abhängigkeit kann man in unterschiedlichen Codestücken aufteilen und wie oben gezeigt bedingt übersetzen. Dadurch hat man Funktionalitäten wie <tt>uart_init</tt> auf unterschiedlichen Controllern und wahrt den Überblick, weil nicht für jede Controller-Familie eine extra Datei notwendig ist.
+
</pre>
{|
+
|- valign="top"
+
|
+
:{| {{Blauetabelle}}
+
|+ '''AVR classic, &lt;= 8 kByte'''
+
|- bgcolor="#ccccff"
+
!|mcu || Builtin define
+
|-
+
|avr2 ||<tt>__AVR_ARCH__=2</tt>
+
|-
+
|[[AT90S2313|at90s2313]]  ||<tt>__AVR_AT90S2313__</tt>
+
|-
+
|at90s2323 ||<tt>__AVR_AT90S2323__</tt>
+
|-
+
|at90s2333 ||<tt>__AVR_AT90S2333__</tt>
+
|-
+
|at90s2343 ||<tt>__AVR_AT90S2343__</tt>
+
|-
+
|attiny22 ||<tt>__AVR_ATtiny22__</tt>
+
|-
+
|attiny26 ||<tt>__AVR_ATtiny26__</tt>
+
|-
+
|at90s4414 ||<tt>__AVR_AT90S4414__</tt>
+
|-
+
|at90s4433 ||<tt>__AVR_AT90S4433__</tt>
+
|-
+
|at90s4434 ||<tt>__AVR_AT90S4434__</tt>
+
|-
+
|at90s8515 ||<tt>__AVR_AT90S8515__</tt>
+
|-
+
|at90c8534 ||<tt>__AVR_AT90C8534__</tt>
+
|-
+
|at90s8535 ||<tt>__AVR_AT90S8535__</tt>
+
|-
+
|at86rf401 ||<tt>__AVR_AT86RF401__</tt>
+
|}
+
|  <!----------------------------------------------------------->
+
:{| {{Blauetabelle}}
+
|+ '''AVR classic, &gt; 8 kByte'''
+
|- bgcolor="#ccccff"
+
! |mcu ||Builtin define
+
|-
+
|avr3 ||<tt>__AVR_ARCH__=3</tt>
+
|-
+
|atmega103 ||<tt>__AVR_ATmega103__</tt>
+
|-
+
|atmega603 ||<tt>__AVR_ATmega603__</tt>
+
|-
+
|at43usb320 ||<tt>__AVR_AT43USB320__</tt>
+
|-
+
|at43usb355 ||<tt>__AVR_AT43USB355__</tt>
+
|-
+
|at76c711 ||<tt>__AVR_AT76C711__</tt>
+
|}
+
 
|}
 
|}
  
----<!----------------------------------------------------------->
+
: Zwar gibt es für alle AVR-Derivate die <tt>avr/io.h</tt>, aber die AVR-Familien unterscheiden sich in ihrer Hardware; z.B. darin, wie I/O-Register heissen oder wie Hardware zu initialisieren ist. Diese Abhängigkeit kann man in unterschiedlichen Codestücken aufteilen und wie oben gezeigt bedingt übersetzen. Dadurch hat man Funktionalitäten wie <tt>uart_init</tt> auf unterschiedlichen Controllern und wahrt den Überblick, weil nicht für jede Controller-Familie eine extra Datei notwendig ist.
  
{|
+
: Built-in Makros wie <tt>__AVR_ATmega8__</tt> und <tt>__AVR_ARCH__</tt> sind ab Version 4.7 im Kapitel "AVR Options" in der GCC [[#Dokumentation|Dokumentation]] erklärt, siehe z.B. [http://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html "AVR Built-in Macros"].
|- valign="top"
+
|
+
:{| {{Blauetabelle}}
+
|+ '''AVR enhanced, &lt;= 8 kByte'''
+
|- bgcolor="#ccccff"
+
!|mcu || Builtin define
+
|-
+
|avr4 ||<tt>__AVR_ARCH__=4</tt>
+
|-
+
|[[Atmel Controller Mega8|atmega8]] ||<tt>__AVR_ATmega8__</tt>
+
|-
+
|atmega8515 ||<tt>__AVR_ATmega8515__</tt>
+
|-
+
|atmega8535 ||<tt>__AVR_ATmega8535__</tt>
+
|}
+
|<!----------------------------------------------------------->
+
:{| {{Blauetabelle}}
+
|+ '''AVR enhanced, &gt; 8 kByte'''
+
|- bgcolor="#ccccff"
+
!|mcu ||Builtin define
+
|-
+
|avr5 ||<tt>__AVR_ARCH__=5</tt>
+
|-
+
|[[Atmel Controller Mega16 und Mega32|atmega16]] ||<tt>__AVR_ATmega16__</tt>
+
|-
+
|atmega161 ||<tt>__AVR_ATmega161__</tt>
+
|-
+
|atmega162 ||<tt>__AVR_ATmega162__</tt>
+
|-
+
|atmega163 ||<tt>__AVR_ATmega163__</tt>
+
|-
+
|atmega169 ||<tt>__AVR_ATmega169__</tt>
+
|-
+
|[[Atmel Controller Mega16 und Mega32|atmega32]] ||<tt>__AVR_ATmega32__</tt>
+
|-
+
|atmega323 ||<tt>__AVR_ATmega323__</tt>
+
|-
+
|[[ATMega64|atmega64]] ||<tt>__AVR_ATmega64__</tt>
+
|-
+
|[[Atmel Controller Mega128|atmega128]] ||<tt>__AVR_ATmega128__</tt>
+
|-
+
|at94k ||<tt>__AVR_AT94K__</tt>
+
|}
+
|}
+
  
----<!----------------------------------------------------------->
+
; -mint8: Datentyp <tt>int</tt> ist nur 8 Bit breit anstatt 16 Bit. Datentypen mit 64 Bit sind nicht verfügbar. 8-Bit <tt>int</tt> ist nicht C-Standard konform und wird nicht von der AVR Libc unterstützt (ausser in <tt>stdint.h</tt>).
 
+
 
+
:{| {{Blauetabelle}}
+
|+ '''AVR, nur Assembler'''
+
|- bgcolor="#ccccff"
+
! |mcu ||Builtin define
+
|-
+
|avr1 ||<tt>__AVR_ARCH__=1</tt>
+
|-
+
|at90s1200 ||<tt>__AVR_AT90S1200__</tt>
+
|-
+
|attiny11 ||<tt>__AVR_ATtiny11__</tt>
+
|-
+
|attiny12 ||<tt>__AVR_ATtiny12__</tt>
+
|-
+
|attiny15 ||<tt>__AVR_ATtiny15__</tt>
+
|-
+
|attiny28 ||<tt>__AVR_ATtiny28__</tt>
+
|}
+
 
+
----
+
 
+
; -minit-stack=xxx: Festlegen der Stack-Adresse
+
; -mint8: Datentyp <tt>int</tt> ist nur 8 Bit breit, anstatt 16 Bit. Datentypen mit 32 Bit wie <tt>long</tt> sind nicht verfügbar
+
 
; -mno-interrupts: Ändert den Stackpointer ohne Interrupts zu deaktivieren
 
; -mno-interrupts: Ändert den Stackpointer ohne Interrupts zu deaktivieren
 
; -mcall-prologues: Funktions-Prolog und -Epilog werden als Unterroutinen umgesetzt, um die Codegröße zu verkleinern
 
; -mcall-prologues: Funktions-Prolog und -Epilog werden als Unterroutinen umgesetzt, um die Codegröße zu verkleinern
 
; -mtiny-stack: Nur die unteren 8 Bit des Stackpointers werden verändert
 
; -mtiny-stack: Nur die unteren 8 Bit des Stackpointers werden verändert
; -mno-tablejump: Für ein <tt>switch</tt>-Statement werden keine Sprungtabellen angelegt
 
; -mshort-calls: Verwendet <tt>rjmp</tt>/<tt>rcall</tt> (begrenzte Sprungweite) auf Devices mit mehr als 8 kByte Flash
 
; -msize: Ausgabe der Instruktonslängen im asm-File
 
; -mdeb: (undokumentiert) Ausgabe von Debug-Informationen für GCC-Entwickler
 
; -morder1: (undokumentiert) andere Register-Allokierung
 
; -morder2: (undokumentiert) andere Register-Allokierung
 
  
=Builtin Defines=
+
=C++=
  
Zur bedingten Codeerzeugung und zur Erkennung, welcher Compiler sich an der Quelle zu schaffen macht, sind folgende Defines hilfreich
+
:''"C++ is a complex language and an evolving one, and its standard definition (the ISO C++ standard) was only recently completed. As a result, your C++ compiler may occasionally surprise you, even when its behavior is correct."''
  
==GCC==
+
Zudem sollte der Einsatz von C++ aus Effizienzgründen sehr kritisch betrachtet werden:
;<tt>__GNUC__</tt>: X wenn GCC-Version X.Y.Z
+
:''"When programming C++ in space- and runtime-sensitive environments like microcontrollers, extra care should be taken to avoid unwanted side effects of the C++ calling conventions like implied copy constructors that could be called upon function invocation etc. These things could easily add up into a considerable amount of time and program memory wasted. Thus, casual inspection of the generated assembler code (using the <tt>-S</tt> compiler option) seems to be warranted."''
;<tt>__GNUC_MINOR__</tt>: Y wenn GCC-Version X.Y.Z
+
;<tt>__GNUC_PATCHLEVEL__</tt>: Z wenn GCC-Version X.Y.Z
+
;<tt>__VERSION__</tt>: "X.Y.Z" wenn GCC-Version X.Y.Z
+
;<tt>__GXX_ABI_VERSION</tt>: Version der ABI (Application Binary Interface)
+
;<tt>__OPTIMIZE__</tt>: Optimierung ist aktiviert
+
;<tt>__NO_INLINE__</tt>: Ohne Schalter <tt>-finline</tt> resp. <tt>-finline-all-functions</tt> etc.
+
;<tt>__ASSEMBLER__</tt>: GCC betrachtet die Eingabe als Assembler-Code und compiliert nicht. Weiterleitung an den Assembler.
+
  
==avr-gcc==
+
Weiterhin unterliegt der Einsatz von C++ je nach Compiler/Lib-Version bestimmten Einschränkungen:
;<tt>__AVR</tt>: Definiert für Target avr, d.h. <tt>avr-gcc</tt> ist am Werk
+
*Einer kompletten C++ Implementierung fehlt die Unterstützung durch die <tt>libstdc++</tt>, dadurch fehlen Standardfunktionen, -Klassen und -Templates
;<tt>__AVR__</tt>: dito
+
* Die Operatoren <tt>new</tt> und <tt>delete</tt> sind nicht implementiert, ihre Verwendung führt zu unauflösbaren externen Referenzen (Linker-Fehler)
;<tt>__AVR_ARCH__</tt>: codiert den AVR-Kern, für den Code erzeugt wird (Classic, Mega, ...).  
+
*Nicht alle Header sind C++-sicher und müssen in <tt>extern "C" {...}</tt> eingeschlossen werden.
;<tt>__AVR_XXXX__</tt>: Gesetzt, wenn <tt>-mmcu=xxxx</tt>.  
+
*Exceptions werden nicht unterstützt und müssen via <tt>-fno-exceptions</tt> abgeschaltet werden, oder der Linker beschwert sich über eine unauflösbare externe Referenz zu <tt>__gxx_personality_sj0</tt>.
  
Siehe auch: [[Avr-gcc#Maschinenspezifische_Optionen_f.C3.BCr_avr-gcc|Maschinenspezifische Optionen]]
+
Als Treiber verwendet man wie immer avr-gcc. Standard-Endungen für C++ sind <tt>.c++</tt> und <tt>.cpp</tt>. Bei anderen Endungen teilt man mit <tt>-x c++</tt> mit, daß es sich um C++ Dateien handelt, oder ruft <tt>avr-c++</tt> direkt auf.
  
=Sections=
+
Interrupt-Service-Routinen (ISRs) sind C-Funktionen und werden definiert wie gehabt. Siehe auch [[#Interrupts|Interrupts]].
 +
#include <avr/io.h>
 +
#include <avr/interrupt.h>
 +
 +
#if defined (__cplusplus)
 +
extern "C" {
 +
#endif {{comment|__cplusplus}}
 +
 +
SIGNAL (SIG_NAME)
 +
{
 +
    {{comment|machwas}}
 +
}
 +
 +
INTERRUPT (SIG_NAME)
 +
{
 +
    {{comment|mach was}}
 +
}
 +
 +
#if defined (__cplusplus)
 +
}
 +
#endif {{comment|__cplusplus}}
 +
<tt>__cplusplus</tt> ist ein Standard [[avr-gcc/Interna#Builtin Defines|GCC-Builtin-Define]].
  
Sections sind mit Fächern vergleichbar, in die Daten, Code, Debug-Informationen usw. einsortiert werden. Zur Section <tt>.text</tt> gehört z.B. der ausführbare Code, welcher letztendlich im Flash landet. Wo genau das ist, braucht man nicht zu wissen und es spielt auch keine Rolle, wo eine bestimmte Funktion landet.
+
Globale Konstruktoren werden in [[avr-gcc/Interna#Sections|Section]] <tt>.init6</tt> ausgeführt, die Destruktoren in <tt>.fini6</tt>.
 
+
Für 'normalen' Code und 'normale' Daten braucht man sich nicht um die Sections zu kümmern, sie werden von <tt>avr-gcc</tt> automatisch richtig zugeordnet. Für spezielle Anwendungen kann es aber notwendig sein, die Ablage in eine andere Section zu machen; etwa wenn man Daten im EEPROM lesen/schreiben will. Wie das genau gemacht wird, steht im Abschnitt "[[avr-gcc#Attribute|Attribute]]" und es gibt ein Beispiele in den Abschnitten
+
"[[avr-gcc#SRAM, Flash, EEPROM: Datenablage am Beispiel Strings|Datenablage am Beispiel Strings]]" und "[[avr-gcc#Zufall|Zufall]]".
+
 
+
{| {{Blauetabelle}}
+
|- bgcolor="#ccccff"
+
! |Section
+
! |Ablage
+
! |Betrifft
+
! |Beschreibung
+
|-
+
|<tt>.text</tt>
+
|Flash
+
|Code
+
|normaler Programm-Code
+
|-
+
|<tt>.data</tt>
+
|SRAM
+
|Daten
+
|wird vom Startup-Code initialisiert, u.a. aus <tt>.progmem</tt>
+
|-
+
|<tt>.bss</tt>
+
|SRAM
+
|Daten
+
|wird vom Startup-Code zu 0 initialisiert
+
|-
+
|colspan="4" bgcolor="#bbbbff"|
+
|-
+
|<tt>.progmem</tt>
+
|Flash
+
|Daten
+
|wird vom Startup-Code nach <tt>.data</tt> kopiert
+
|-
+
|<tt>.eeprom</tt>
+
|EEPROM
+
|Daten
+
|Daten im EEPROM
+
|-
+
|<tt>.noinit</tt>
+
|SRAM
+
|Daten
+
|wird nicht initialisiert
+
|-
+
|colspan="4" bgcolor="#bbbbff"|
+
|-
+
|<tt>.init''n''</tt>
+
|Flash
+
|Code
+
|wird vor <tt>main</tt> ausgeführt, ''n'' = 0...9
+
|-
+
|<tt>.fini''n''</tt>
+
|Flash
+
|Code
+
|wird nach <tt>main</tt> ausgeführt, ''n'' = 9...0
+
|-
+
|<tt>.vectors</tt>
+
|Flash
+
|Code
+
|Vektor-Tabelle: Tabelle mit Sprüngen zur jeweiligen ISR
+
|-
+
|<tt>.bootloader</tt>
+
|Flash
+
|Code
+
|für den Bootloader
+
|}
+
 
+
Der Anfang einer Section kann auch dem Linker mitgegeben werden, etwa wenn wie üblich <tt>avr-gcc</tt> als Treiber für den Linker verwendet wird:
+
avr-gcc ...  -Wl,--section-start=.eeprom=0x810001
+
Damit beginnt Section <tt>.eeprom</tt> nicht an der (virtuellen) Adresse <tt>0x81000</tt>,
+
sondern ein Byte später.
+
Manche AVRs haben einen Silicon-Bug, der bei Verwendung der EEPROM-Adresse 0 zu Fehlern führt.
+
Mit der obigen Linker-Option wird diese Adresse nicht mehr verwendet.
+
 
+
=Attribute=
+
 
+
Mit Attributen kann man die Codeerzeugung beeinflussen. Es gibt verschiedene Attribute, die auf Daten, Typen, und/oder Funktionen anwendbar sind.
+
 
+
==Syntax==
+
<pre>
+
__attribute__ ((&lt;name&gt;))
+
__attribute__ ((&lt;name1&gt;, &lt;name2&gt;, ...))
+
__attribute__ ((&lt;name&gt; (&quot;&lt;wert&gt;&quot;)))
+
</pre>
+
 
+
==Nützliche Attribute von GCC==
+
 
+
{| {{Blauetabelle}}
+
|+ '''Tabelle: Attribue von GCC''' (Auszug)
+
|- bgcolor="#ccccff"
+
! |Attribut ||Funktionen ||Daten ||Typen ||Beschreibung
+
|-
+
| <tt>section ("&lt;name&gt;")</tt>
+
!| (x) || (x) || (x)
+
| Lokatiert nach Section <tt>&lt;name&gt;</tt>
+
|-
+
| <tt>noreturn</tt>
+
!| x ||  ||
+
| Die Funktion wird nie zurückkehren
+
|-
+
|<tt>inline</tt>
+
!| x  || ||
+
|Funktion wird geinlinet falls möglich
+
|-
+
|<tt>noinline</tt>
+
!|x  ||  ||
+
|Funktion wird keinesfalls geinlinet
+
|-
+
|<tt>packed</tt>
+
!| || x || x
+
|Datenablage in Strukturen erfolgt dicht, also ohne eventuelle Füllbytes
+
|}
+
 
+
==Attribute von avr-gcc==
+
 
+
{| {{Blauetabelle}}
+
|+ '''Tabelle: Attribute von <tt>avr-gcc</tt>'''
+
|- bgcolor="#ccccff"
+
! |Attribut ||Funktionen ||Daten ||Typen ||Beschreibung
+
|-
+
| <tt>progmem</tt>
+
!| x || x || x
+
| Lokatiert ins Flash
+
|-
+
| <tt>naked</tt>
+
!| x || ||
+
|Funktion wird ohne Prolog/Epilog erzeugt
+
|-
+
|<tt>interrupt</tt>
+
!| x || ||
+
|Hier nur wegen der Vollständigkeit erwähnt
+
|-
+
|<tt>signal</tt>
+
!| x ||  ||
+
|Hier nur wegen der Vollständigkeit erwähnt
+
|}
+
 
+
'''Beispiele:'''
+
 
+
<pre>
+
#define EEPROM __attribute__ ((section (".eeprom")))
+
 
+
const char EE_HALLO_WELT[] EEPROM = "Hallo Welt";
+
const int EE_wert EEPROM = 0x1234;
+
 
+
void __attribute__ ((noinline))
+
foo()
+
{
+
  /* Code */
+
}
+
</pre>
+
  
 
=Code-Beispiele=
 
=Code-Beispiele=
Dieser Abschnitt enthält Code-Schnippsel für <tt>avr-gcc</tt>. Es werden Besonderheiten besprochen, die für <tt>avr-gcc</tt> zu beachten sind.  
+
Dieser Abschnitt enthält Code-Schnippsel für avr-gcc. Es werden Besonderheiten besprochen, die für avr-gcc zu beachten sind.  
  
Dieser Abschnitt ist ''kein'' Tutorial zur C-Programmierung und ''keine'' Einführung in die Programmiersprache C im allgemeinen. Dafür sei auf einschlägige Tutorials/Bücher verwiesen.
+
Dieser Abschnitt ist ''kein'' [[C-Tutorial|Tutorial zur C-Programmierung]] und ''keine'' Einführung in die Programmiersprache C im allgemeinen. Dafür sei auf einschlägige Tutorials/Bücher verwiesen.
  
 
==Zugriff auf Special Function Registers (SFRs)==
 
==Zugriff auf Special Function Registers (SFRs)==
  
 
===Zugiff auf Bytes und Worte===
 
===Zugiff auf Bytes und Worte===
Auf SFRs wird generell über deren Adresse zugegriffen:
+
Für den Zugriff auf die SFRs gibt es Defines über den Include
<pre>
+
#include <avr/io.h>
  // Liest den Inhalt von SREG an Adresse 0x5f
+
Abhängig vom eingestellten Controller werden dann Defines eingebunden, über die auf SFRs wie auf normale Variablen zugegriffen werden kann. Die Namen der Defines sind i.d.R. die gleichen wie im AVR-Manual, also z.b. <tt>SREG</tt> für das Prozessorstatus-Register SREG:
  unsigned char sreg = *((unsigned char volatile*) 0x5f);
+
#include <avr/io.h>
</pre>
+
Das bedeutet in etwa: "Lies ein flüchtiges (<tt>volatile</tt>) Byte (<tt>unsigned char</tt>) von Adresse <tt>0x5f</tt>". Der Speicherinhalt von SFRs ist flüchtig, denn er kann sich ändern, ohne daß <tt>avr-gcc</tt> dies mitbekommt. Daher muss bei jedem C-Zugriff auf ein SFR dieses wirklich gelesen/geschrieben werden, was der Qualifier <tt>volatile</tt> sicherstellt. Ansonst geht der Compiler u.U. davon aus, daß der Inhalt bekannt ist und verwendet einen alten, in einem GPR befindlichen Wert.
+
...
 
+
   {{ccomment|SREG lesen}}
Um lesbaren, weniger fehleranfälligen und unter AVRs halbwegs portierbaren Code zu erhalten, gibt es Makrodefinitionen im Conroller-spezifischen Header <tt>ioxxxx.h</tt>, der neben anderen Dingen mit <tt>avr/io.h</tt> includet wird:
+
<pre>
+
#include <avr/io.h>
+
 
+
...
+
   // SREG lesen
+
 
   uint8_t sreg = SREG;
 
   uint8_t sreg = SREG;
 
   ...
 
   ...
   /// SREG schreiben
+
   {{ccomment|SREG schreiben}}
 
   SREG = sreg;
 
   SREG = sreg;
</pre>
+
<!-- 
Die Bezeichner der SFRs sind die gleichen wie im Manual. Evtl verschafft ein Blick in den Header Klarheit. Dieser befinden sich in
+
Auf SFRs wird generell über deren Adresse zugegriffen:
 +
{{ccomment|Liest den Inhalt von SREG an Adresse 0x5f}}
 +
unsigned char sreg = *((unsigned char volatile*) 0x5f);
 +
Das bedeutet in etwa: "Lies ein flüchtiges (<tt>volatile</tt>) Byte (<tt>unsigned char</tt>) von Adresse <tt>0x5f</tt>". Der Speicherinhalt von SFRs ist flüchtig, denn er kann sich ändern, ohne daß avr-gcc dies mitbekommt. Daher muss bei jedem C-Zugriff auf ein SFR dieses wirklich gelesen/geschrieben werden, was der Qualifier <tt>volatile</tt> sicherstellt. Ansonst geht der Compiler u.U. davon aus, daß der Inhalt bekannt ist und verwendet einen alten, in einem GPR befindlichen Wert.
  
<tt> &lt;AVR_INSTALL_DIR&gt;/avr/include/avr/io****.h</tt>
+
Um lesbaren, weniger fehleranfälligen und unter AVRs halbwegs portierbaren Code zu erhalten, gibt es Makrodefinitionen im Controller-spezifischen Header <tt>ioxxxx.h</tt>, der neben anderen Dingen mit <tt>avr/io.h</tt> includet wird:
 +
Die Bezeichner der SFRs sind die gleichen wie im Manual.
 +
-->
 +
Für einen Überblick über die eingebundenen Defines kann ein Blick in den Controller-spezifischen Header hilfreich sein. Dieser befindet sich in
 +
:<tt> &lt;GCC_HOME&gt;/avr/include/avr/io****.h</tt>
 +
z.B. <tt>iom32.h</tt> für einen [[ATmega32]].
  
Dieser Zugriff geht auch für 16-Bit Register wie <tt>TCNT1</tt>, für die eine bestimmte Reihenfolge für den Zugriff auf Low- und High-Teil eingehalten werden muss: <tt>avr-gcc</tt> generiert die Zugriffe in der richtigen Reihenfolge.
+
Dieser Zugriff geht auch für 16-Bit Register wie <tt>TCNT1</tt> oder <tt>ADC</tt>, für die eine bestimmte Reihenfolge für den Zugriff auf Low- und High-Teil eingehalten werden muss: avr-gcc generiert die Zugriffe in der richtigen Reihenfolge.
 
   uint16_t tcnt1 = TCNT1;
 
   uint16_t tcnt1 = TCNT1;
Zu beachten ist, daß dieser Zugriff nicht atomar erfolgt. Das Lesen/Schreiben mehrbytiger Werte muss in mehrere Byte-Zugriffe zerlegt werden. Zwischen diesen Zugriffen kann ein Interrupt auftreten, was zu fehlerhaften Resultaten führen kann. Entsprechende Codestücke müssen daher atomar gehalten werden, was über <tt>cli</tt> und <tt>sei</tt> realisierbar ist.
+
 
 +
{{FarbigerRahmen|
 +
Zu beachten ist, daß dieser Zugriff nicht atomar erfolgt. Das Lesen/Schreiben mehrbytiger Werte muss vom Compiler in mehrere Byte-Zugriffe zerlegt werden. Zwischen diesen Zugriffen kann ein [[Interrupt]] auftreten, wenn Interrupts aktiviert sind. Je nach Programm und welche Aufgaben eine [[ISR]] erledigt, kann dies zu Fehlfunktion führen. In dem Fall müssen diese Code-Stücke atomar gemacht werden, damit sie nicht durch einen [[IRQ]] unterbrochen werden können!
 +
}}
  
 
===Zugriff auf einzelne Bits===
 
===Zugriff auf einzelne Bits===
Zeile 536: Zeile 243:
  
  
Masken ergeben sich durch schieben von <tt>1</tt> an die richtige Position.
+
Masken ergeben sich durch Schieben von <tt>1</tt> an die richtige Position:
 
<pre>
 
<pre>
 
// Ports B_0 und B_1 als Ausgang
 
// Ports B_0 und B_1 als Ausgang
Zeile 549: Zeile 256:
  
 
Etwas anders sieht der Code aus, wenn die Bits einzeln gesetzt werden und das Register im bitadressierbaren Bereich liegt (SRAM <tt>0x20</tt> bis <tt>0x3f</tt> resp. I/O <tt>0x0</tt> bis <tt>0x1f</tt>):
 
Etwas anders sieht der Code aus, wenn die Bits einzeln gesetzt werden und das Register im bitadressierbaren Bereich liegt (SRAM <tt>0x20</tt> bis <tt>0x3f</tt> resp. I/O <tt>0x0</tt> bis <tt>0x1f</tt>):
 
 
<pre>
 
<pre>
 
// Ports B_0 und B_1 als Ausgang
 
// Ports B_0 und B_1 als Ausgang
Zeile 561: Zeile 267:
 
</pre>
 
</pre>
  
{{FarbigerRahmen|
+
Um Bits zu löschen, erzeugt man eine Maske, die an der betreffenden Stelle eine &nbsp;0 hat:
 +
<pre>
 +
// Ports B_2 als Eingang
 +
DDRB &= ~(1&lt;&lt;PB2);
 +
</pre>
 +
 
 +
{{FarbigerRahmen|  
 
Auch hier ist zu beachten, daß es Probleme geben kann, wenn nicht atomarer Code erzeugt wird, weil der AVR-Befehlssatz nicht mehr hergibt:
 
Auch hier ist zu beachten, daß es Probleme geben kann, wenn nicht atomarer Code erzeugt wird, weil der AVR-Befehlssatz nicht mehr hergibt:
 
<pre>
 
<pre>
Zeile 580: Zeile 292:
 
}} <!-- /FarbigerRahmen -->
 
}} <!-- /FarbigerRahmen -->
  
==Interrupts==
 
  
Um zu kennzeichnen, daß es sich bei einer Funktion um eine Interrupt Sevice Routine (ISR) handelt, gibt es spezielle Attribute. Diese brauchen nicht explizit hingeschrieben zu werden, ebensowenig wie die genaue Nummer des Interrupt Requests (IRQ). Dafür gibt es die Includes und die Makros:
+
Auch das Lesen einzelner Port-Pins geht über das Maskieren von SFRs:
 
<pre>
 
<pre>
#include <avr/io.h>
+
DDRB &= ~(1 << PB2);    // PortB.2 als INPUT
#include <avr/signal.h>
+
  
SIGNAL (SIG_OUTPUT_COMPARE1A)
+
if (PINB & (1 << PB2))
{
+
   // PortB.2 ist HIGH
   /* ISR-Code */
+
else
}
+
  // PortB.2 ist LOW
  
INTERRUPT (SIG_OUTPUT_COMPARE1B)
+
 
{
+
if (!(PINB & (1 << PB2)))
   /* ISR-Code */
+
   // PortB.2 ist LOW
}
+
else
 +
  // PortB.2 ist HIGH
 
</pre>
 
</pre>
  
Dadurch wird die Funktion mit dem richtigen Prolog/Epilog erzeugt, und es wird ein Eintrag in die Interrupt-Vektortabelle gemacht &#150; bei obigem Beispiel also zwei Einträge.
+
==Interrupts==
  
{{FarbigerRahmen|
+
: → ''[http://nongnu.org/avr-libc/user-manual/group__avr__interrupts.html AVR-Libc: Dokumentation zu <tt><avr/interrupt.h></tt>].
Die Schreibweise des Signal-Names muss genau die sein wie im Header, das schliesst auch Leerzeichen ein! Nicht alle GCC-Versionen bringen Fehler/Warnung, wenn die Schreibweise nicht stimmt.
+
SIGNAL (SIG_OUTPUT_COMPARE1A )  // !!! Macht NICHT das, was man will (Blank am Ende)!!!
+
}}
+
  
;<tt>SIGNAL</tt>: Mit Ausführung einer ISR deaktiviert die AVR-Hardware die Interrupts, so daß die ISR nicht durch andere Interrupt-Anforderungen unterbrochen wird. Beim Verlassen der ISR werden Interrupts wieder aktiviert.
+
Um zu kennzeichnen, daß es sich bei einer Funktion um eine Interrupt Sevice Routine (ISR) handelt, gibt es spezielle Attribute. Diese brauchen nicht explizit hingeschrieben zu werden, ebensowenig wie die genaue Nummer des Interrupt Requests (IRQ). Dafür gibt es Includes aus der AVR Libc und die folgenden Makros.
;<tt>INTERRUPT</tt>: Früh im ISR-Prolog werden mit <tt>sei</tt> die von der AVR-Hardware temporär deaktivierten Interrupts reaktiviert. Dadurch kann die ISR von einer IRQ unterbrochen werden. Das bietet die Möglichkeit, so etwas wie Interrupt-Priorisierung nachzubilden, was AVRs selbst nicht können.
+
  
{{FarbigerRahmen|Dauert die ISR zu lange und wird sie nochmals von ihrem eigenen IRQ unterbrochen, stürzt man ab.}}
+
#include <avr/io.h>
 +
#include <avr/interrupt.h>
 +
 +
{{ccomment|Eine nichtunterbrechbare Interrupt-Service-Routine}}
 +
ISR (TIMER1_COMPA_vect)
 +
{
 +
    {{ccomment|ISR-Code}}
 +
}
 +
 +
{{ccomment|Eine unterbrechbare Interrupt-Service-Routine}}
 +
ISR (TIMER0_OVF_vect, ISR_NOBLOCK)
 +
{
 +
    {{ccomment|ISR-Code}}
 +
}
 +
 
 +
Dadurch wird die Funktion mit dem richtigen Prolog/Epilog erzeugt, und es wird ein Eintrag in die Interrupt-Vektortabelle gemacht – bei obigem Beispiel also zwei Einträge.
 +
 
 +
Mit Ausführung einer ISR deaktiviert die AVR-Hardware die Interrupts, so daß die ISR nicht durch andere Interrupt-Anforderungen unterbrochen wird. Beim Verlassen der ISR werden Interrupts wieder automatisch durch die AVR-Hardware aktiviert. Tritt während der ISR ein IRQ auf, wird diese erst nach Beenden des ISR-Codes ausgeführt. Der Interrupt geht also nicht verloren.  Dies gilt allerding nicht für Level-getriggerte IRQs wie für manche externen Interrupts oder TWI-Interrupts. 
 +
 
 +
Zwischen zwei ISRs wird zusätzlich mindestens ein Befehl des normalen Programm-Codes abgearbeitet.
  
Nachschlagen kann man den Name in
+
Nachschlagen kann man die ISR-Namen im Device-spezifischen Header, die im Installationsverzeichnis liegen:
 
:<tt>&lt;GCC_HOME&gt;/avr/include/avr/ioxxxx.h</tt>
 
:<tt>&lt;GCC_HOME&gt;/avr/include/avr/ioxxxx.h</tt>
  
Zeile 619: Zeile 345:
 
* Der entsprechen IRQ muss aktiviert worden sein
 
* Der entsprechen IRQ muss aktiviert worden sein
 
* Das zum IRQ gehörende Ereignis muss eintreten
 
* Das zum IRQ gehörende Ereignis muss eintreten
 
+
#include <avr/io.h>
<pre>
+
#include <avr/interrupt.h>
#include <avr/io.h>
+
#include <avr/interrupt.h>
+
    ...
 
+
    {{ccomment|enable OutputCompareA Interrupt für Timer1}}
  ...
+
    TIMSK |= (1 << OCIE1A);
  // enable OutputCompareA Interrupt für Timer1
+
  TIMSK |= (1 << OCIE1A);
+
    {{ccomment|disable OutputCompareA Interrupt für Timer1}}
 
+
    TIMSK &= ~(1 << OCIE1A);
  // disable OutputCompareA Interrupt für Timer1
+
  TIMSK &= ~(1 << OCIE1A);
+
    {{ccomment|Interrupts aktivieren}}
 
+
    sei();
  // Interrupts aktivieren
+
  sei();
+
    {{ccomment|Interrupts abschalten}}
 
+
    cli();
  // Interrupts abschalten
+
Sperrt man eine Code-Sequenz durch Einschachteln in ein <tt>cli</tt>/<tt>sei</tt> Paar (man macht das Codestück "atomar", also ununterbrechbar), gehen währenddessen keine Interrupt-Anforderungen verloren. Die entsprechenden IRQ-Flags bleiben gesetzt, und nach dem <tt>sei</tt> werden die IRQs in der Reihenfolge ihrer Prioritäten abgearbeitet. Ausnahme ist, wenn in einem atomaren Block der selbe IRQ mehrfach auftritt. Der ISR-Code wird dann trotzdem nur einmal ausgeführt.
  cli();
+
</pre>
+
  
 
===default Interrupt===
 
===default Interrupt===
  
Für nicht implementierte Interrupts macht <tt>avr-gcc</tt> in die Vektortabelle einen Eintrag,
+
Für nicht implementierte Interrupts macht avr-gcc in die Vektortabelle einen Eintrag,
 
der zu <tt>__bad_interrupt</tt> (definiert im Startup-Code <tt>crt*.o</tt>) springt
 
der zu <tt>__bad_interrupt</tt> (definiert im Startup-Code <tt>crt*.o</tt>) springt
 
und von dort aus weiter zu Adresse&nbsp;0.  
 
und von dort aus weiter zu Adresse&nbsp;0.  
 
Dadurch läuft der AVR wieder von neuem los, wenn ein Interrupt auftritt,  
 
Dadurch läuft der AVR wieder von neuem los, wenn ein Interrupt auftritt,  
 
zu dem man keine ISR definiert hat  
 
zu dem man keine ISR definiert hat  
&#150; allerdings ohne die Hardware zurück zu setzen wie bei einem echten Reset.
+
&#150; allerdings ohne die Hardware zurückzusetzen wie bei einem echten Reset.
  
 
Möchte man diesen Fall abfangen, dann geht das über eine globale Funktion  
 
Möchte man diesen Fall abfangen, dann geht das über eine globale Funktion  
 
namens <tt>__vector_default</tt>:
 
namens <tt>__vector_default</tt>:
 
<pre>
 
<pre>
#include <avr/signal.h>
+
#include <avr/interrupt.h>
  
SIGNAL (__vector_default)
+
ISR (__vector_default)
 
   ...
 
   ...
 
</pre>
 
</pre>
 
Damit wird von <tt>__bad_interrupt</tt> aus nicht nach Adresse&nbsp;0 gesprungen,
 
Damit wird von <tt>__bad_interrupt</tt> aus nicht nach Adresse&nbsp;0 gesprungen,
sondern weiter zu <tt>__vector_default</tt>, welches durch <tt>SIGNAL</tt> oder
+
sondern weiter zu <tt>__vector_default</tt>, welches durch <tt>ISR()</tt> den üblichen ISR-Prolog/Epilog bekommt.
<tt>INTERRUPT</tt> den üblichen ISR-Prolog/Epilog bekommt.
+
  
Wenn man komplett eigenes Zeug machen will, dann macht man eine nackte Funktion,
+
So kann man z.B. eine Meldung ausgeben, eine Warnlampe blinken, in einer Endlosschleife landen, oder über den [[Watchdog]] einen richtigen Hardware-Reset auslösen, siehe auch Abschnitt "[[#Reset auslösen|Reset auslösen]]".
die z.B. eine Meldung ausgibt, in einer Endlosschleife landet, oder über den [[Watchdog]]
+
einen richtigen Reset auslöst. Falls <tt>__vector_default</tt> zurückkehren soll,
+
dann ist darauf zu achten, daß man dies mit <tt>reti</tt> (return from interrupt) tut
+
und evtl. verwendete Register und den Status (<tt>SREG</tt>) sichert:
+
<pre>
+
void __attribute__ ((naked))
+
__vector_default (void)
+
{
+
  __asm__ __volatile (
+
      "; mach was" "\n\t"
+
      "; mach was"  "\n\t"
+
      "reti"
+
      );
+
}
+
</pre>
+
  
 
===ISR mit eigenem Prolog/Epilog===
 
===ISR mit eigenem Prolog/Epilog===
Genauso implementiert man auch eine kleine ISR mit minimalem Overhead.
 
Mit <tt>naked</tt> befreit man die Routine vom Standard-Prolog/Epilog und
 
kümmert sich selbst darum:
 
<pre>
 
#include <avr/io.h>
 
  
void __attribute__ ((naked))  
+
Wenn man in einer ISR komplett eigenes Zeug machen will,
SIG_OVERFLOW0 (void)
+
dann definiert man eine naked Funktion.
{
+
Mit <tt>naked</tt> befreit man die Routine vom Standard-Prolog/Epilog.
  /* Port B.6 = 0 */
+
{{FarbigerRahmen|
  __asm__ __volatile (
+
Dabei ist darauf zu achten, daß die ISR mit <tt>reti</tt> (return from interrupt)
      "cbi %0, %1" "\n\t"
+
zurückkehrt und evtl. verwendete Register und den Status (<tt>SREG</tt>) sichert.
      "reti"
+
 
        :  
+
Komplexer oder nicht optimierter C-Code, der einen Framepointer braucht, funktioniert nicht mehr weil ohne Prolog der Framepointer nicht initialisiert wird. Die geschieht wenn nicht alle Werte in Registern gehalten werden können und vom Compiler auf dem Stack zwischengespeichert werden.
        : "M" (_SFR_IO_ADDR (PORTB)), "i" (6)
+
}}
  );
+
#include <avr/io.h>
}
+
#include <avr/interrupt.h>
</pre>
+
 +
ISR (TIMER0_OVF_vect, ISR_NAKED)
 +
{
 +
    {{ccomment|Port B.6 &#61; 0}}
 +
    {{ccomment|Diese Instruktion verändert nicht das SREG und kein anderes Register}}
 +
    {{ccomment|so daß der eigentliche Code nur 1 Befehl lang ist}}
 +
    __asm__ __volatile (
 +
      "cbi %0, %1" "\n\t"
 +
      "reti"
 +
          :  
 +
          : "M" (_SFR_IO_ADDR (PORTB)), "i" (6)
 +
    );
 +
}
 +
 
 +
Siehe auch [[Inline-Assembler in avr-gcc]].
 
Die ISR sieht dann so aus:
 
Die ISR sieht dann so aus:
 
<pre>
 
<pre>
 
__vector_9:
 
__vector_9:
   c6 98       cbi  0x18, 6
+
   c6 98       cbi  0x18, 6
   18 95       reti
+
   18 95       reti
 +
</pre>
 +
 
 +
Wiederum kann man als Funktionsname <tt>__vector_default</tt> nehmen,
 +
um nicht-implementierte IRQs abzufangen:
 +
<pre>
 +
void __attribute__ ((naked, used))
 +
__vector_default (void)
 +
...
 
</pre>
 
</pre>
  
Zeile 709: Zeile 434:
 
===Bestimmen der Stringlänge===
 
===Bestimmen der Stringlänge===
 
<pre>
 
<pre>
  /* Bestimmt die Laenge des Strings ohne die abschliessende '\0' zu zaehlen
+
  /* Bestimmt die Laenge des Strings ohne die abschliessende '\0' zu zaehlen */
 
  unsigned int strlength (const char *str)
 
  unsigned int strlength (const char *str)
 
  {
 
  {
Zeile 733: Zeile 458:
 
  char str2[] = "Hallo Welt!";
 
  char str2[] = "Hallo Welt!";
 
</pre>
 
</pre>
dann werden die Strings im SRAM abgelegt. Im Startup-Code werden die Strings vom Flash ins SRAM kopiert und belegen daher sowohl Platz im SRAM als auch im Flash. Wird ein String nicht verändert, braucht er nicht ins SRAM kopiert zu werden. Das spart Platz im knapp bemessenen SRAM. Allerdings muss anders auf den String zugegriffen werden, denn wegen der Harvard-Architektur des AVR-Kerns kann <tt>avr-gcc</tt> anhand der Adresse nicht unterscheiden, ob diese ins SRAM, ins Flash oder ins EEPROM zeigt.
+
dann werden die Strings im SRAM abgelegt. Im Startup-Code werden die Strings vom Flash ins SRAM kopiert und belegen daher sowohl Platz im SRAM als auch im Flash. Wird ein String nicht verändert, braucht er nicht ins SRAM kopiert zu werden. Das spart Platz im knapp bemessenen SRAM. Allerdings muss anders auf den String zugegriffen werden, denn wegen der Harvard-Architektur des AVR-Kerns kann avr-gcc anhand der Adresse nicht unterscheiden, ob diese ins SRAM, ins Flash oder ins EEPROM zeigt.
 
<pre>
 
<pre>
 
  #include <avr/pgmspace.h>
 
  #include <avr/pgmspace.h>
 
   
 
   
  const prog_char str3[] = "Hallo Welt!";
+
  const char str3[] PROGMEM = "Hallo Welt!";
 
   
 
   
  unsigned int strlen_P (const prog_char *str)
+
  size_t strlen_P (const char *str)
 
  {
 
  {
     unsigned int len = 0;
+
     size_t len = 0;
 
   
 
   
 
     while (1)
 
     while (1)
Zeile 753: Zeile 478:
 
  }
 
  }
 
   
 
   
  void foo()
+
  void foo (void)
 
  {
 
  {
     unsigned int len;
+
     size_t len;
 
     len = strlen_P (str3);
 
     len = strlen_P (str3);
     len = strlen_P (PSTR("String im Flash"));
+
     len = strlen_P (PSTR ("String im Flash"));
 
  }
 
  }
 
</pre>
 
</pre>
Zeile 766: Zeile 491:
 
  #include <avr/eeprom.h>
 
  #include <avr/eeprom.h>
 
   
 
   
  const char str4[] __attribute__ ((section(".eeprom"))) = "Hallo Welt!";
+
  const char str4[] EEMEM = "Hallo Welt!";
 
   
 
   
  unsigned int strlen_EE (const char *str)
+
  size_t strlen_EE (const char *str)
 
  {
 
  {
     unsigned int len = 0;
+
     size_t len = 0;
 
   
 
   
 
     while (1)
 
     while (1)
Zeile 783: Zeile 508:
 
</pre>
 
</pre>
  
==Zufall==
+
==Reset auslösen==
"Echte" Zufallszahlen zu generieren ist leider nicht möglich, hierzu muss man externe Hardware wie einen [[Rauschgenerator]] verwenden. Funktionen  wie <tt>rand</tt> und <tt>random</tt> basieren auf algebraischen Verfahren, die eine gute Verteilung der gelieferten Werte haben. Werden diese Funktionen mit dem selben Startwert (seed) initialisiert, liefern sie auch immer die gleiche Folge. In diesem Sinne sind die Werte nicht zufällig sondern nur scheinbar zufällig und "wüst umherhüpfend".
+
  
Um einen zufälligen Startwert zu erhalten, kann man den uninitialisierten Inhalt des SRAM verwenden, das nach dem power-up keinen definierten Zustand hat.
+
Falls ein Reset per Software ausgelöst werden soll, dann geht das am besten über den [[Watchdog]].
 +
Einfach nur an den Reset-Punkt an Adresse&nbsp;0 zu springen
 +
initialisiert zwar den Controller von neuem, aber es macht keinen wirkliches RESET mit Zurücksetzen der Hardware und allen I/O-Registern.  
  
===Startwert (seed) besorgen===
+
Durch den Watchdog kann man ein 'richtiges' RESET-Signal erzeugen lassen, so daß die AVR-Hardware genau so initialisiert ist, wie nach einem externen RESET. So kann man z.B. via [[UART]] ein RESET-Kommando schicken. Allerdings lässt sich der Watchdog nur minimal auf 15ms einstellen:
Am einfachsten geht dies, indem man eine Variable in die Sektion <tt>.noinit</tt> lokatiert und den Wert liest:
+
<tt>
+
unsigned long seed __attribute__ ((section (".noinit")));
+
</tt>
+
  
Etwas bessere Resultate erhält man, wenn man den ganzen Inhalt des nicht verwendeten SRAMs zur Bildung der seed heranzieht. Das Symbol <tt>__heap_start</tt> wird übrigens im standard Linker-Script definiert, <tt>RAMEND</tt> ist ein Makro aus <tt>ioxxxx.h</tt>:
+
#include <avr/wdt.h>
 +
#include <avr/interrupt.h>
 +
... 
 +
    cli();                    {{ccomment|Interrupts global abschalten}}
 +
    wdt_enable (WDTO_15MS);    {{ccomment|Watchdog aufziehen auf 15ms}}
 +
    while (1);                {{ccomment|warten, bis er zubeisst...}}
  
Das Beispiel interpretiert den SRAM-Inhalt als <tt>unsigned short</tt> Werte und berechnet die seed, indem die einzelnen Werte mit exor &quot;überlagert&quot; werden.
+
Welches Ereignis einen RESET ausgelöst hat, kann man im Register '''MCUCSR''' (''MCU Control and Status Register'') erfahren. Es gibt 4 mögliche RESET-Quellen:
 +
* Power-On Reset
 +
* External Reset
 +
* Brown-Out Reset
 +
* Watchdog Reset
  
<pre>
+
Soll der Inhalt von Variablen einen Reset überleben &ndash; eine Variable also nicht initialisiert werden &ndash; dann geht das so:
 
  #include <avr/io.h>
 
  #include <avr/io.h>
 
   
 
   
  unsigned short get_seed()
+
  {{ccomment| status informiert z.B. darüber, ob wir selber den Watchdog ausgelöst haben }}
 +
{{ccomment| oder nicht, oder andere Informationen }}
 +
uint8_t status __attribute__ ((__section__ (".noinit")));
 +
 +
int main (void)
 
  {
 
  {
    unsigned short seed = 0;
+
    {{ccomment|Wert von MCUSCR merken, möglichst früh im Programm }}
    unsigned short *p = (unsigned short*) (RAMEND+1);
+
    uint8_t mcucsr = MCUCSR;
    extern unsigned short __heap_start;
+
   
+
    while (p >= &__heap_start + 1)
+
      seed ^= * (--p);
+
   
+
    return seed;
+
}
+
</pre>
+
 
+
=== Pseudozufall in der avr-libc===
+
In der <tt>avr-libc</tt> finden sich Funktionen, um Pseudo-Zufallszahlen zu erhalten bzw. um Startwerte für die Algorithmen zu setzen:
+
 
+
#include <stdlib.h>
+
 
+
Prototypen und Defines:
+
<pre>
+
#define RAND MAX 0x7FFF
+
 
   
 
   
int rand (void);
+
    {{ccomment|MCUCSR zurücksetzen }}
void srand (unsigned int seed);
+
    MCUCSR = 0;
 
   
 
   
long random (void);
+
    {{ccomment|Watchdog-Reset }}
  void srandom (unsigned long seed);
+
    if (mcuscr & (1 << WDRF))
</pre>
+
    {
 +
        {{ccomment|status auswerten }}
 +
    }
 +
   
 +
    {{ccomment|Power-On Reset: status auf definierten Wert setzen }}
 +
    if (mcuscr & (1 << PORF))
 +
    {
 +
        status = 0;
 +
    }
 +
 +
    {{ccomment|status auswerten }}
 +
    ...
 +
}
  
== Frühe Codeausführung vor main()==
+
; An Adresse 0 springen
  
Mitunter ist es notwendig, Code unmittelbar nach dem Reset auszuführen, noch bevor man in <tt>main()</tt> mit der eigentlichen Programmausführung beginnt. Das kann zB zur Bedienung eines [[Watchdog]]-Timers erforderlich sein.
+
Falls wirklich zu Adresse 0 gesprungen werden soll – was in einem Bootloader erforderlich sein kann – dann geschieht das mittels einer Funktion <tt>reset</tt> wie folgt:
 
+
  extern void reset (void) __attribute__((noreturn));
Nach einen Reset und vor Aufruf von <tt>main</tt> werden Initialisierungen ausgeführt wie
+
  reset();
* setzen des Stackpointers
+
<tt>reset</tt> wird bein Linken mittels <tt>-Wl,--defsym=reset=0</tt> auf&nbsp;0&nbsp;gesetzt.
* Vorbelegung globaler Datenobjekte: Daten ohne Initializer werden zu 0 initialisert (Section <tt>.bss</tt>). Für Daten mit Initializer (Section <tt>.data</tt>) werden die Werte aus dem Flash ins SRAM kopiert.
+
Weitere Möglichkeit ist, im erzeugten Assembler&nbsp;<tt>0</tt> als Funktionsnamen zu verwenden:
* Initialisierung von Registern wie R1, in dem bei <tt>avr-gcc</tt> immer die Konstante 0 gehalten wird.
+
extern void reset (void) __asm__("0") __attribute__((__noreturn__));
 
+
reset();
Im Linker-Script werden Sections von <tt>.init0</tt> bis <tt>.init9</tt> definiert, die nacheinander abgearbeitet werden. Erst danach wird <tt>main</tt> betreten. Um Code früh auszuführen, legt man die Funktion in eine dieser Sections:
+
<tt>
+
/* !!! never call this function !!!
+
  void __attribute__ ((naked, section (".init3")))
+
  code_init3 (void)
+
{
+
    /* Code */
+
}
+
</tt>
+
Zu beachten ist dabei
+
* Eine so definierte Funktion darf keinesfalls aufgerufen werden!
+
* Zuweisungen wie <tt>i=0;</tt> ergeben vor <tt>.init3</tt> inkorrekten Code, da vor Ende von <tt>.init2</tt> Register R1 noch nicht mit 0 besetzt ist, <tt>avr-gcc</tt> aber davon ausgeht, daß es eben diesen Wert enthält.
+
* Lokale Variablen müssen in Registern liegen, denn vor Ende von <tt>.init2</tt> ist der Stackpointer noch nicht initialisiert. Zudem ist die Funktion <tt>naked</tt>, hat also insbesondere keinen Prolog, der den Framepointer (Y-Register) setzen könnte, falls er benötigt wird.
+
* Gegebenenfalls ist daher die Verwendung von inline-Assembler angezeigt oder die Implementierung in einem eigenen Assembler-Modul, das dazu gelinkt wird. Der erzeugte Code ist im List-File zu überfrüfen.
+
* Werden mehrere Funktionen in die gleiche init-Section gelegt, ist die Reihenfolge ihrer Ausführung nicht spezifiziert und i.a. nicht die gleiche wie in der Quelle.
+
Unbenutzte init-Sections haben die Nummern 0, 1, 3 und 5 bis 8. Die verbleibenden werden vom Startup-Code verwendet:
+
;<tt>.init2</tt>: Initialisieren von R1 mit 0 und setzen des Stackpointers
+
;<tt>.init4</tt>: Kopieren der Daten vom Flash ins SRAM (<tt>.data</tt>) und löschen von <tt>.bss</tt>
+
;<tt>.init6</tt>: C++ Konstruktoren
+
;<tt>.init9</tt>: Sprung zu <tt>main</tt>
+
  
 
=Includes=
 
=Includes=
Zeile 864: Zeile 574:
 
Die mit
 
Die mit
 
  #include <...>
 
  #include <...>
angegebenen Includes werden von <tt>avr-gcc</tt> in den  
+
angegebenen Includes werden von avr-gcc in den  
 
mit der Option '<tt>-I</tt>' anegegenen Pfaden gesucht.  
 
mit der Option '<tt>-I</tt>' anegegenen Pfaden gesucht.  
Dem Compiler bekannt ist der Pfad <tt><GCC_HOME>/avr/include</tt>.
+
Dem Compiler bekannt sind die Pfade
 +
<GCC_HOME>/avr/include                          Standard              (stdio.h, ...)
 +
<GCC_HOME>/avr/include/avr                      AVR-spezifisch        (avr/io.h, ...)
 +
<GCC_HOME>/lib/gcc/avr/<GCC_VERSION>/include    Standard, compilerabh. (limits.h, ...)
 +
 
 
Gibt man z.B. an  
 
Gibt man z.B. an  
 
  #include <stdio.h>
 
  #include <stdio.h>
 
dann wird automatisch in diesem Verzeichnis nach <tt>stdio.h</tt> gesucht.
 
dann wird automatisch in diesem Verzeichnis nach <tt>stdio.h</tt> gesucht.
In dem Verzeichnis stehen Standard-Includes die benötigt werden, wenn man libc-Funktionen  
+
In den Verzeichnissen stehen Standard-Includes, die benötigt werden, wenn man libc-Funktionen  
oder mathematische Funktionen verwendet.  
+
oder mathematische Funktionen etc. verwendet.  
 
AVR-spezifische Dinge stehen im Unterverzeichnis <tt>avr</tt>, etwa:
 
AVR-spezifische Dinge stehen im Unterverzeichnis <tt>avr</tt>, etwa:
 
  #include <avr/io.h>
 
  #include <avr/io.h>
 +
{{FarbigerRahmen|
 +
Als Pfad-Separator wird immer ein '''<tt>/</tt>''' verwendet, auch auf Windows-Betriebssystemen! Also kein '''<tt>\</tt>'''&nbsp;!
 +
}}
  
 
==Standard==
 
==Standard==
  
  ctype.h                  character conversion macros and ctype macros
+
  ctype.h                  Zeichen-Umwandlungs-Makros und ctype Makros
  errno.h                  provides symbolic names for various error codes
+
  errno.h                  Symbolische Namen für Fehlercodes
  inttypes.h               Use [u]intN_t if you need exactly N bits.
+
  stdint, inttypes.h       C99 definiert [u]intN_t wenn man genau N [un]signed
                           These typedefs are mandated by the C99 standard.
+
                           Bits braucht
  math.h                    mathematical functions
+
  math.h                    Mathematische Funktionen: sin, cos, log, gamma, bessel, ...
  setjmp.h                  The C library provides the setjmp() and longjmp() functions
+
  setjmp.h                  libc unterstützt setjmp() und longjmp(), um direkt in eine
                           to jump directly to another (non-local) function.  
+
                           andere (nicht-lokale) Funktion zu springen.  
  stdio.h                  Standard IO facilities
+
  stdio.h                  Standard I/O-Funktionen (printf, fscanf, ...)
  stdlib.h                  Declares some basic C macros and functions as defined by
+
  stdlib.h                  Deklariert grundlegende ISO C Makros und Funktionen
                           the ISO standard, plus some AVR-specific extensions
+
                           sowie einige AVR-spezifische Erweiterungen
  string.h                  perform string operations on NULL terminated strings
+
  string.h                  Stringoperationen auf NULL-terminierten Strings. (strlen, ...)
 +
stdarg.h                  Funktionen mit variabler Argumenanzahl
 +
limits.h                  Min- und Max-Werte von Skalaren (UCHAR_MAX, LONG_MIN, ...)
  
 
==AVR-spezifisch==
 
==AVR-spezifisch==
Zeile 895: Zeile 614:
 
sondern durch Angabe von
 
sondern durch Angabe von
 
  #include <avr/io.h>
 
  #include <avr/io.h>
Dadurch werden z.B. genau der I/O-Header eingebunden, der zum AVR-Modell passt, also
+
Dadurch werden genau die I/O-Header eingebunden, die zum AVR-Modell passen, also z.B. <tt>avr/iom8.h</tt> für [[ATmega8]] etc. Verantwortlich für die Auswahl des richtigen Sub-Headers ist der Schalter '<tt>-mmcu=xxx</tt>'.
<tt>avr/iom8.h</tt> für ATMega8,
+
<tt>avr/iotn2313</tt> für ATTiny2313,
+
<tt>avr/io2313.h</tt> für AT90S2313, etc.  
+
 
+
Verantwortlich dafür ist der Schalter '<tt>-mmcu=xxx</tt>'.
+
  
Obwohl diese Header nicht explizit angegeben werden müssen,  
+
Obwohl diese Sub-Header nicht explizit angegeben werden müssen,  
 
kann ein Blick dorthin hilfreich sein, um die Namen von [[SFR|SFRs]]  
 
kann ein Blick dorthin hilfreich sein, um die Namen von [[SFR|SFRs]]  
 
oder Signals nachzuschlagen.  
 
oder Signals nachzuschlagen.  
 
Diese Header werden im folgenden nicht alle einzeln aufgelistet.  
 
Diese Header werden im folgenden nicht alle einzeln aufgelistet.  
Ihre Namen sind immer '<tt>avr/io*.h</tt>', für ATMega <tt>avr/iom*.h</tt>'
+
Ihre Namen sind immer <tt>avr/io*.h</tt>.
und für ATTiny '<tt>avr/iotn*.h</tt>'.
+
* für ATmega: <tt>avr/iom*.h</tt>  
 +
* für ATtiny: <tt>avr/iotn*.h</tt>
 
<pre>
 
<pre>
 
avr/boot.h            Bootloader Support
 
avr/boot.h            Bootloader Support
avr/crc16.h          Prüfsumme CRC16
 
avr/delay.h          Verzögerungsschleife - loops for small accurate delays
 
 
avr/eeprom.h          EEPROM-Routinen
 
avr/eeprom.h          EEPROM-Routinen
avr/ina90.h          Kompatibilität mit IAR-AVR-Compiler
+
avr/interrupt.h      sei(), cli(), ISR(), ...
avr/interrupt.h      sei(), cli(), ...
+
avr/io.h              RAMEND, ***_vect, SFRs: PORTB, DDRB, PINB, SREG, ...,
avr/io.h              --> inttypes.h, io*.h
+
avr/pgmspace.h        Zugriff aufs Flash: Byte lesen, PROGMEM, pgm_read_***, ...
avr/io*.h            SFRs, SIG_****, SPM_PAGESIZE, RAMEND, XRAMEND, E2END, FLASHEND
+
avr/sleep.h          Power-Safe und Sleep-Modes
avr/parity.h          Parität
+
avr/pgmspace.h        Zugriff aufs Flash: Byte lesen, PROGMEM, prog_char, prog_uint8_t, ...
+
avr/portpins.h        Makros für Port-Pins
+
avr/signal.h          Makros SIGNAL() und INTERRUPT(), ...
+
avr/sleep.h          Power-Safe
+
avr/twi.h            I2C
+
 
avr/wdt.h            Watchdog
 
avr/wdt.h            Watchdog
</pre>
 
  
=Dateien (WinAVR)=
+
util/crc16.h          Prüfsumme CRC16
 
+
util/delay.h          Verzögerungsschleifen für kurze, exakte Verzögerungen
WinAVR bringt wesentlich mehr mit als nur <tt>avr-gcc</tt>:
+
util/parity.h        Parität
* Binutils (Assembler (<tt>avr-as</tt>), Linker (<tt>avr-ld</tt>), Tools (<tt>avr-size</tt>, <tt>avr-objdump</tt>, <tt>avr-objcopy</tt>, <tt>avr-nm</tt>, ...))
+
util/twi.h            I2C
* Debugger (<tt>avr-gdb</tt> mit Oberfläche <tt>insight</tt>)
+
* Bibliothek <tt>avr-libc</tt> inclusive Dokumentation
+
* Progger <tt>avrdude</tt>
+
* Progger <tt>uisp</tt>
+
* AVR-Simlator (simulavr),
+
* Programmers Notepad (kleiner Editor)
+
* Demo-Projekte
+
* viele Linux-Tools wie <tt>make</tt>, <tt>grep</tt>, <tt>sed</tt>, <tt>tar</tt>, etc.
+
* ...
+
 
+
'''Verzeichnisbaum''' (Auszug):
+
 
+
<pre>
+
.                              avr-gcc Installations-Verzeichnis
+
./avr/include                  Standard Includes
+
./avr/include/avr              Includes AVR-spezifisch
+
./avr/lib                      Startup-Code, Libs (avr2)
+
./avr/lib/avr3                            "        (avr3)
+
./avr/lib/avr4                            "        (avr4)
+
./avr/lib/avr5                            "        (avr5)
+
./avr/lib/ldscripts            Linker-Skripte
+
./bin                          Programme (avr-gcc.exe, giveio.sys, ...
+
./doc                          Doku (HTML + pdf) 
+
./doc/avr-libc                    avr-libc
+
./doc/avrdude-xxx                  avrdude
+
./doc/simulavr-xxx                simulavr     
+
./doc/uisp-xxx                    uisp
+
./examples                      Beispiel-Projekte
+
./examples/demo                    PWM mit AT90S2313
+
./examples/twitest                I2C mit ATMega
+
./info                          info pages
+
./man                          man pages
+
./mfile                        mfile
+
./pn                            Programmers Notepad
+
./utils/bin                    bzip2, diff, gawk, grep, make, sed, tar, ...
+
 
</pre>
 
</pre>
  
=Fallstricke und häufige Fehler=
+
==Anwendungs-spezifisch==
 +
Eigene Header, die nur innerhalb eigener Projekte gebraucht werden, includet man mit
 +
#include "..."
 +
Auch hier darf man Unterverzeichnisse angeben oder ins übergeordnete Verzeichnis:
 +
#include "../../mein-zeug.h"
 +
Mit der Option <tt>-I<path></tt> kann ein Pfad zu den bekannten Include-Pfaden hinzugefügt werden; im obigen Beispiel etwa <tt>-I../..</tt> und im Programm dann:
 +
#include "mein-zeug.h"
  
==Tippfehler==
+
= Optimierungen, Tipps & Tricks =
 +
:→ ''Hauptartikel: [[avr-gcc/Optimierungen|avr-gcc Optimierungen]]''
  
Tippfehler können immer passieren. Besonders fies ist es, wenn der Tippfehler nicht zu einer Warnung oder zu einer Fehlermeldung führt, weil der entstandene Code korrekter C-Code ist.
+
=Abkürzungen und Bezeichnungen=
 +
; [[GCC]]: GNU Compiler Collection
 +
; gcc: GNU C-Compiler
 +
; GPR: '''G'''eneral '''P'''urpose '''R'''egister
 +
; [[ISR]]: [[Interrupt|'''I'''nterrupt]] '''S'''ervice '''R'''outine
 +
; [[IRQ]]: '''I'''nterrupt '''R'''e'''q'''uest
 +
; Prolog/Epilog: Code am Anfang/Ende jeder Funktionen/ISR, der dazu dient, verwendete Register zu sichern, den Stack-Frame für lokale [[Variable|Variablen]] anzulegen (falls benötigt), Stackpointer zu setzen, zurück zu springen (<tt>ret</tt>, <tt>reti</tt>), etc.
 +
; SFR: '''S'''pecial '''F'''unction '''R'''egister
 +
; Target: Zielsystem, in unserem Falle avr
  
===Ein <tt>;</tt> zu viel===
+
=Siehe auch=
Ein reflexartig eingetippter oder nach Ändeungen stehen gebliebener <tt>;</tt> hat schon so manches
+
* [[C-Tutorial]]
Programm ausgeknockt:
+
'''Code-Beispiele'''
if (a == 0);
+
* [[Hallo Welt für AVR (LED blinken)]] - ein erstes Beispiel für avr-gcc
{
+
*[[:Kategorie:Quellcode_C|C-Codebeispiele]]
    /* mach was */
+
* [[Fallstricke bei der C-Programmierung]]
}
+
Wenn <tt>a == 0</tt> ist, dann wird <tt>;</tt> ausgeführt (also im Endeffekt garnichts). Danach kommt der Block, der im <tt>if</tt> stehen sollte. Der wird immer ausgeführt, denn er gehört nicht mehr zum <tt>if</tt>.
+
  
===Zuweisung statt Vergleich===
+
'''Details'''
if (a = 0)
+
* [[Inline-Assembler in avr-gcc|Inline-Assembler]]
{
+
* [[avr-gcc/Interna|Interna von avr-gcc]]
    /* mach was */
+
}
+
Zuerst wird <tt>a = 0</tt> gesetzt und dann überprüft, ob die <tt>if</tt>-Bedingung erfullt ist. Der Wert ist aber immer <tt>0</tt>, was ''nicht erfüllt'' bedeutet. Der nachfolgende Block wird nie betreten.
+
  
Abhilfe schafft, sich angewohnen zu schreiben
+
'''Installation (Linux)'''
if (0 == a)
+
* [[Linuxdistribution_Avr-live-cd]]
Wenn man dann eine Zuweisung eintippt, gibt's einen Fehler.
+
* [[avr-gcc und avrdude installieren]]
 +
'''Sonstiges'''
 +
* [[WinAVR]]
 +
* [[Speicherverbrauch bestimmen mit avr-gcc]]
 +
* [[Compiler]]
 +
* [[Dev-Cpp IDE]]
 +
* [[AVR]]
 +
----
 +
* [[Sourcevergleich]]
 +
* [[Codevergleich AVR-Compiler]]
  
 +
=Weblinks=
 +
==Dokumentation==
  
===Signal/Interrupt-Name vertippt oder Leerzeichen zu viel===
+
;Offline:
  
SIGNAL (SIG_OVEFRLOW0)
+
Je nach Distribution wird diese mit offline-Dokumentation als pdf, HTML, etc. ausgeliefert, die dann z.B. in Ordern wie den folgenden befindet:
{
+
    /* mach was */
+
}
+
Nicht alle Compiler-Versionen meckern da. Der ISR-Code wird nicht in die Interrupt-Tabelle eingetragen. Kommt es zum Interrupt, dann landet man in RESET.
+
  
===Nicht-atomarer Code===
+
: <GCC_HOME>/doc/gcc/
Verwendet man in einem Programm IRQs (Interrupts) und ändert in der ISR (Service Routine) ein Datum (Variable, SFR, ...), dann wird diese Änderung möglicherweise überschrieben, wenn die IRQ zu einem ungünstigen Zeitpunkt auftritt.
+
: <GCC_HOME>/doc/avr-libc/
Ein Beispiel, das zeigt, was passieren kann, findet sich im Abschnitt "[[avr-gcc#Zugrff auf einzelne Bits|Zugrff auf einzelne Bits]]". Ein weiteres Beispiel ist das Lesen, Schreiben oder Testen einer mehrbytigen Variable:
+
: etc.
<pre>
+
int volatile i;
+
  ...
+
  if (0 == i)
+
</prte>
+
Weil <tt>i</tt> länger als 1 Byte ist, kann es nicht in einem Befehl gelesen werden;
+
daher kann während des Lesens eine IRQ auftreten, in deren ISR der Wert von <tt>i</tt>
+
verändert wird. Der obige Code könnte etwa so assembliert werden:
+
<pre>
+
  ; i wird vom SRAM in das Registerpaar r24:r25 geladen
+
  lds r24,i
+
  ; wenn hier eine IRQ zuschlägt, in der i verändert wird, ist der Registerinhalt korrupt
+
  lds r25,(i)+1
+
  or r24,r25
+
  brne ...
+
</pre>
+
Natürlich können auch komplexere Datenstrukturen von diesem Phänomen betroffen sein.
+
Besonders unangenehm an dieser Klasse von Fehlern ist, daß sie nur sporadisch auftauchen
+
und man sie daher selbst mit einem guten Debugger sehr schlecht orten kann, da die zugehörige
+
Codestelle fast immer korrekt abgearbeitet wird.
+
  
Entweder man macht den ganzen betroffenen Block ununterbrechbar (atomar):
+
; Online
<pre>
+
#include <avr/io.h>
+
#include <avr/interrupt.h>
+
  ...
+
  unsigned char sreg = SREG;
+
  
  cli();
+
* [http://gcc.gnu.org/onlinedocs/cpp.pdf cpp.pdf (470 kByte)] - Dokumentation des C-Präprozessors (en)
  if (0 == i)
+
* [http://www.nongnu.org/avr-libc/user-manual/pages.html AVR Libc: User Manual] - Dokumentation zur [http://www.nongnu.org/avr-libc AVR Libc].
  {
+
* [http://sourceware.org/binutils/docs/ Binutils: Documentation] – Dokumentation der Binutils: Assembler, Linker, ...
      i = 1;
+
* [http://gcc.gnu.org/wiki/avr-gcc avr-gcc im GCC Wiki] – Dokumentation des Application Binary Interface (ABI): Registerverwendung, Calling Conventions, ...
      SREG = sreg;
+
{| {{Blauetabelle}}
  }
+
! [http://gcc.gnu.org GCC] Version ||colspan="2"| [http://gcc.gnu.org/onlinedocs Dokumentation] || AVR Options || Release Notes
  else
+
|-
  {
+
! Aktuelle Entwicklung
      SREG = sreg;
+
| [http://gcc.gnu.org/onlinedocs/gcc/ HTML]
      return;
+
| [http://gcc.gnu.org/onlinedocs/gcc.pdf pdf]
  }
+
| [http://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html online]
  ...
+
| [http://gcc.gnu.org/gcc-5/changes.html GCC 5]
</pre>
+
|-
oder je nach Programmstruktur sichert man den Wert, z.B. in eine lokale Variable.
+
! 4.9.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.9.1/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.9.1/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.9.1/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.9/changes.html GCC 4.9]
 +
|-
 +
! 4.8.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.8.3/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.8.3/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.8.3/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.8/changes.html GCC 4.8]
 +
|-
 +
! 4.7.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.7/changes.html GCC 4.7]
 +
|-
 +
! 4.6.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.6/changes.html GCC 4.6]
 +
|-
 +
! 4.5.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.5.4/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.5/changes.html GCC 4.5]
 +
|-
 +
! 4.4.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.4/changes.html GCC 4.4]
 +
|-
 +
! 4.3.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-4.3/changes.html GCC 4.3]
 +
|-
 +
! 3.4.x
 +
| [http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/ HTML]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc.pdf pdf]
 +
| [http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/AVR-Options.html online]
 +
| [http://gcc.gnu.org/gcc-3.4/changes.html GCC 3.4]
 +
|}
  
==Warnungen==
+
== Downloads==
 +
* [http://sourceforge.net/projects/winavr/ WinAVR-Projekt bei sourceforge.net (en)]
 +
* [http://cdk4avr.sourceforge.net/ avr-gcc und toolchain als Linux-Paket bei sourceforge.net (en)]
 +
* [[Linuxdistribution_Avr-live-cd]]
 +
== Tipps, Installation ==
 +
*[http://www.nongnu.org/avr-libc/user-manual/install_tools.html ''"Installing the GNU Tool Chain"''] Hilfe zum Build und Installation von GCC, binutils, etc unter Linux
 +
* Im GCC-Handbuch, siehe [[#Dokumentation|Dokumentation]].
 +
* [http://www.linuxfocus.org/Deutsch/November2004/article352.shtml www.linuxfocus.org (Artikel)] - Tipps zu Build und Installation von avr-gcc, binutils und avr-libc unter Linux
 +
* [http://users.rcn.com/rneswold/avr/ Rich Neswold: ''A GNU Development Environment for the AVR Microcontroller'']
 +
* [http://www.mikrocontroller.net/topic/24166 www.mikrocontroller.net (Foren-Beitrag)] - Installation von GCC und Toolchain unter Mac OS X
 +
* [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=125603#125603 www.roboternetz.de (Foren-Beitrag)] ''avrgcc + avrdude installieren''
 +
* [http://roboternetz.de/download/c_tutorial.zip AVR-Studio & C-Tutorial mit Installationsanleitung]
  
"''Wieso soll das Probleme machen? Das ist doch nur eine Warnung.''" liest man mitunter in Foren.
+
== Sonstiges ==
 
+
Warnungen zur Compile-Zeit werden gerne zu Fehlern zur Laufzeit. Letztere sind deutlich schwerer zu finden, als angewarnten Code zu korrigieren.
+
<pre>
+
void foo (long*);
+
 
+
void bar (int *p)
+
{
+
    foo (p+1);
+
    // Was soll das sein?!
+
    // ((long*) p) + 1      oder 
+
    // (long*) (p + 1)
+
}
+
</pre>
+
 
+
<pre>
+
> avr-gcc -c -o prog.o prog.c
+
 
+
prog.c: In function `bar':
+
prog.c:5: warning: passing arg 1 of `foo' from incompatible pointer type
+
</pre>
+
 
+
==Array-Index==
+
 
+
Informatiker am Bahnhof:
+
:''"0, 1, 2, ... Wo ist mein dritter Koffer?!"''
+
Gleiches gilt für Arrays:
+
#define NUM 10;
+
int a[NUM]; // für die N Werte a[0] ... a[N-1]
+
Ein Zugriff auf <tt>a[N]</tt> greift igrdendwo hin.
+
Wahlweise liest man Schrott oder überschreibt andere Daten, die in der Nähe liegen, und
+
plötzlich seltsame Werte enthalten.
+
 
+
==Bitweise vs. Logische Operatoren==
+
 
+
Die Operatoren AND, OR und NOT gibt es in C in zwei Ausprägungen
+
;bitweise: Die Operatoren <tt>^</tt>, <tt>|</tt>, <tt>&</tt>, <tt>~</tt>, <tt>|=</tt>,  <tt>^=</tt>, <tt>&=</tt>, <tt>|=</tt> operieren bitweise. Der entsprechende Operator wird also auf alle Bits des Wertes parallel angewandt und das Ergebnis für ein Bit ist unabhängig vom Inhalt der anderen Bits.
+
;logisch:  Die Operatoren <tt>||</tt>, <tt>&&</tt>, <tt>!</tt>, <tt>&&=</tt>, <tt>||=</tt> operieren auf dem ganzen (int) Wert und berücksichtigen nur, ob der Wert 0 (false) oder ungleich 0 (true) ist.
+
Dementsprechend liefert <tt>&&</tt> i.d.R. ein anderes Ergebnis als <tt>&</tt>:
+
if (a && b) // erfüllt, wenn a!=0 und b!=0
+
if (a & b)  // erfüllt, wenn in a und b an der gleichen Stelle ein Bit gesetzt ist
+
Ein Verwechseln bzw. unkorrektes Einsetzen der Operatoren gibt also ein falsches Programm.
+
 
+
==Ein , anstatt . in Konstante==
+
 
+
In C sowie im angloamerikanischen Sprachraum werden Dezimalbrüche mit einem <tt>.</tt> (Punkt) geschrieben und nicht wie im Deutschen mit einem <tt>,</tt> (Komma):
+
float pi;
+
pi = 3,14;        // *AUTSCH* soll wohl heissen 3.14
+
Die zweite Zeile besteht aus zwei durch ein Komme getrennten Anweisungen, so das das ganze
+
etwa gleichbedeutend ist mit
+
float pi;
+
pi = 3;
+
14;
+
Jedenfalls ist es korrekter C-Code.
+
Die <tt>14;</tt> ist ein Ausdruck, der nicht weiter gebraucht wird und daher wegfällt, da
+
er im Gegensatz zu einer void-Funktion keine Wirkung hat.
+
Man rechnet also mit dem Wert&nbsp;3 für <math>\pi</math>.
+
 
+
Das falsche Komma hat auch schon so manchen ebay-Freak aufs Kreuz gelegt...
+
 
+
==Führende 0 in Konstanten==
+
 
+
In C kennzeichnet eine führende 0 bei einer Zahlenkonstante,
+
daß die Zahl oktal dargestellt ist. Somit ist 010 nicht gleich 10.
+
if (a == 030) // ist a gleich 24?
+
 
+
==Warteschleife==
+
 
+
Oft sieht man den Versuch, Warteschleifen zurch Zählschleifen zu realisieren:
+
<pre>
+
void wait ()
+
{
+
  int i;
+
       
+
  for (i=0; i<50; i++);
+
}
+
</pre>
+
 
+
<tt>avr-gcc</tt> mit Optimierung erzeugt daraus
+
<pre>
+
wait:
+
  ret
+
</pre>
+
und das ist auch völlig in Ordnung, wenn man ein Blick in die C-Spezifikation wagt.
+
Die Schleife hat ''keine'' Wirkung auf die Welt! Sie sagt: "Führe 50 mal <tt>;</tt> aus". Und 50 mal Nichtstun ist eben nichts Tun...
+
 
+
Falls man wirklich auf diese Art warten möchte, hilft folgendes: Man gaukelt dem Compiler vor, es gäbe etwas unheimlich wichtiges in der Schleife zu tun, von dem er nichts mitbekommt.
+
<pre>
+
void wait ()
+
{
+
  int i;
+
 
+
  for (i=0; i<50; i++)
+
      __asm__ __volatile ("; nur ein asm-Kommentar");
+
}
+
</pre>
+
daraus entsteht
+
<pre>
+
wait:
+
  ldi r24,lo8(49)
+
  ldi r25,hi8(49)
+
.L5:
+
/* #APP */
+
  ; nur ein asm-Kommentar
+
/* #NOAPP */
+
  sbiw r24,1
+
  sbrs r25,7
+
  rjmp .L5
+
  ret
+
</pre>
+
Die Schleife wird nun 50 mal durchlaufen.
+
 
+
Wir bemerken, daß das inline Assembler nicht in Code resultiert
+
und daß die Schleifenvariable nicht hochzählt, sondern hinunter.
+
Auch diese Optimierung ist ok, denn <tt>i</tt> wird nirgends verwendet.
+
 
+
Daß bei dem Beispiel der gewünschte Code erzeugt wird ist mehr oder weniger Glückssache.
+
Ein Compiler transformiert seine Eingabe in ein Assembler- oder Maschinenprogramm, das die gleiche Wirkung hat. ''Wie'' dies genau geschieht, ist in aller Regel nicht festgelegt,
+
sondern nur, ''daß'' es passiert
+
&#150; ansonsten ist der Compiler selbst fehlerhaft oder implementiert kein&nbsp;C.
+
 
+
In dem lezten Beispiel der Warteschleife ist die Bedeutung des C-Codes etwa
+
''"führe 50 mal das Inline-Assembler-Muster <tt>"; nur ein asm-Kommentar"</tt> in den Code ein"''.
+
Dies wäre auch möglich komplett ohne Schleife(nvariable), indem der Compiler diese wegoptimiert und nur das asm 50 mal hintereinander ausgibt.
+
Das Resultat wäre im Maschinencode dann wiederum absolut ohne Effekt.
+
Bei gcc geschieht dies aber bestenfalls mit der Optimierungsstufe <tt>-O3</tt>, die für
+
AVR absolut nicht zu emfehlen ist, oder indem man von Hand die Optionen setzt, die diese
+
Optimierungsstufe nach sich zieht.
+
 
+
Eine Möglichkeit, dem auf C-Ebene zu begegnen, ist die Schleifenvariable&nbsp;<tt>i</tt>
+
als volatile zu deklarieren, so daß sie angelegt werden und genau so wie codiert verwendet werden muss:
+
<pre>
+
void wait ()
+
{
+
  int volatile i;
+
 
+
  for (i=0; i<50; i++)
+
      ;
+
}
+
</pre>
+
Das hat allerdings noch andere Konsequenzen, nämlich das Erzwingen des Framepointers
+
(bei avr-gcc im Y-Register r28:r29),
+
denn&nbsp;<tt>i</tt> lebt jetzt nicht mehr in einem Register, sondern im Frame und wird wirklich jedesmal von dort gelesen und geschrieben, was recht breiten Code ergibt:
+
<pre>
+
wait:
+
/* prologue: frame size=2 */
+
push r28
+
push r29
+
in r28,__SP_L__
+
in r29,__SP_H__
+
sbiw r28,2
+
in __tmp_reg__,__SREG__
+
cli
+
out __SP_H__,r29
+
out __SREG__,__tmp_reg__
+
out __SP_L__,r28
+
/* prologue end (size=10) */
+
std Y+1,__zero_reg__
+
std Y+2,__zero_reg__
+
ldd r24,Y+1
+
ldd r25,Y+2
+
sbiw r24,50
+
brge .L7
+
.L5:
+
ldd r24,Y+1
+
ldd r25,Y+2
+
adiw r24,1
+
std Y+1,r24
+
std Y+2,r25
+
ldd r24,Y+1
+
ldd r25,Y+2
+
sbiw r24,50
+
brlt .L5
+
.L7:
+
/* epilogue: frame size=2 */
+
adiw r28,2
+
in __tmp_reg__,__SREG__
+
cli
+
out __SP_H__,r29
+
out __SREG__,__tmp_reg__
+
out __SP_L__,r28
+
pop r29
+
pop r28
+
ret
+
</pre>
+
Das ist der totale Overkill.
+
Inakzeptabel breiter Code und ein einzelner Schleifendurchlauf dauert viele Instruktionen,
+
so dass die verstrichene Zeit nur recht grobkörnig eingestellt werden kann, denn ein Durchlauf dauert 18 Zyklen.
+
 
+
Näher am Ziel liegt ein Ausbau der asm-Variante, indem man dort erzwingt, daß <tt>i</tt>
+
wirklich vom Compiler angelegt wird. Das kann nur dadurch sichergestellt werden,
+
daß es verwendet wird. Dazu wird erzwungen, daß <tt>i</tt> in ein Register geladen wird.
+
Wir steuern also die Reload-Phase (die Phase, die Variablen in Register verteilt) von gcc.
+
Da diese Ausgabe möglicherweise öfter zu bewältigen ist, wurde der erzwungene Reload
+
als Makro definiert, der noch eine asm-Ausgabe macht, was reloadet wird:
+
<pre>
+
#define RELOAD(reg,var) \
+
  __asm__ __volatile (";RELOAD " reg " with " #var : "=" reg (var) : "0" (var) : "memory")
+
 
+
void wait ()
+
{
+
  int i;
+
 
+
  for (i=0; i<50; i++)
+
      RELOAD ("r", i);
+
}
+
</pre>
+
Das Makro im Beispiel löst auf zu
+
__asm__ __volatile (";RELOAD r with i" : "=r" (i) : "0" (i) : "memory")
+
Das bedeutet, daß <tt>i</tt> in ein Register der Registerklasse <tt>r</tt> geladen wird.
+
In allen GCC-Versionen steht dieses <tt>r</tt> für ein Standard-Register (GPR).
+
Der erzeugte Code sieht nun besser aus, ist aber immer noch abhängig vom Optimierungsgrad
+
etc.
+
<pre>
+
wait:
+
  ldi r24,lo8(0)
+
  ldi r25,hi8(0)
+
.L5:
+
/* #APP */
+
  ;RELOAD r with i
+
/* #NOAPP */
+
  adiw r24,1
+
  cpi r24,50
+
  cpc r25,__zero_reg__
+
  brlt .L5
+
  ret
+
</pre>
+
 
+
Eine weitere reine C-Variante könnte so aussehen:
+
<pre>
+
static int volatile dummy;
+
 
+
void wait ()
+
{
+
  int i;
+
 
+
  for (i=0; i<50; i++)
+
      dummy = i;
+
}
+
</pre>
+
Was zu diesem Code führt:
+
<pre>
+
wait:
+
ldi r24,lo8(0)
+
ldi r25,hi8(0)
+
.L5:
+
sts (dummy)+1,r25
+
sts dummy,r24
+
adiw r24,1
+
cpi r24,50
+
cpc r25,__zero_reg__
+
brlt .L5
+
 
+
ret
+
</pre>
+
oder so was
+
<pre>
+
void wait ()
+
{
+
  int i;
+
 
+
  for (i=0; i<50; i++)
+
      (void) (int * volatile) &i;
+
}
+
</pre>
+
was wieder den minimalen Code ergibt wie beim erzwungenen Reload:
+
<pre>
+
wait:
+
  ldi r24,lo8(0)
+
  ldi r25,hi8(0)
+
.L5:
+
  adiw r24,1
+
  cpi r24,50
+
  cpc r25,__zero_reg__
+
  brlt .L5
+
  ret
+
</pre>
+
 
+
===Fazit===
+
 
+
{{FarbigerRahmen|
+
Die einzig sichere Lösung, einen bestimmten Code generieren zu lassen, ist und bleibt das Gewünschte komplett in (Inline) Assembler auszudrücken.
+
}}
+
 
+
=Abkürzungen und Bezeichnungen=
+
; GCC: GNU Compiler Collection
+
; gcc: GNU C-Compiler
+
; GPR: '''G'''eneral '''P'''urpose '''R'''egister
+
; ISR: '''I'''nterrupt '''S'''ervice '''R'''outine
+
; IRQ: '''I'''nterrupt '''R'''e'''q'''uest
+
; Prolog/Epilog: Code am Anfang/Ende jeder Funktionen/ISR, der dazu dient, verwendete Register zu sichern, den Stack-Frame für lokale [[Variable|Variablen]] anzulegen (falls benötigt), Stackpointer zu setzen, zurück zu springen (<tt>ret</tt>, <tt>reti</tt>), etc.
+
; SFR: '''S'''pecial '''F'''unction '''R'''egister
+
; Target: Zielsystem, in unserem Falle avr
+
 
+
=Siehe auch=
+
*[[Avr]]
+
*[[Atmel]]
+
*[[Compiler]]
+
*[[Sourcevergleich]]
+
* [[WinAVR]]
+
*[[Hallo Welt für AVR (Blinky)]] - ein erstes Beispiel für avr-gcc
+
*[[:Kategorie:Quellcode_C|C-Codebeispiele]]
+
 
+
=Weblinks=
+
 
* [http://gcc.gnu.org/ Offizielle Homepage von GCC (en)]
 
* [http://gcc.gnu.org/ Offizielle Homepage von GCC (en)]
 
* [http://de.wikipedia.org/wiki/GNU_Compiler_Collection GCC in der deutschen Wikipedia]
 
* [http://de.wikipedia.org/wiki/GNU_Compiler_Collection GCC in der deutschen Wikipedia]
* [http://sourceforge.net/projects/winavr/ WinAVR-Projekt bei sourceforge.net (en)]
 
* [http://cdk4avr.sourceforge.net/ avr-gcc und toolchain als Linux-Paket bei sourceforge.net (en)]
 
 
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial avr-gcc-Tutorial auf mikrocontroller.net]
 
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial avr-gcc-Tutorial auf mikrocontroller.net]
* [http://www.linuxfocus.org/Deutsch/November2004/article352.shtml Tipps zu Build und Installation von avr-gcc, binutils und avr-libc unter Linux bei linuxfocus.org]
 
* [http://www.avrfreaks.net/AVRGCC/ avr-gcc bei avrfreaks.net (en)]
 
 
* [http://savannah.nongnu.org/projects/avr-libc/ Nützliche GCC Runtime-Libary]
 
* [http://savannah.nongnu.org/projects/avr-libc/ Nützliche GCC Runtime-Libary]
 
+
* [http://www.stromflo.de/dokuwiki/doku.php Atxmega-C-Tutorial]
=ToDo=
+
* sh interface
+
* atomic
+
* inline asm
+
* __init0
+
* optimizing
+
* hacks
+
  
 
=Autor=
 
=Autor=
Zeile 1.378: Zeile 785:
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Praxis]]
 
[[Kategorie:Praxis]]
 +
[[Kategorie:Quellcode C|!]]
 
[[Kategorie:Software]]
 
[[Kategorie:Software]]

Aktuelle Version vom 14. September 2014, 08:48 Uhr

avr-gcc ist ein freier C-Compiler, mit dem C-Programme zu ausführbaren Programmen übersetzen werden können, die auf Microcontrollern der AVR-Familie lauffähig sind. An Sprachen versteht avr-gcc sowohl C als auch C++. Neben Standard-C bzw. ANSI-C versteht avr-gcc auch GNU-C, das etwas mehr Möglichkeiten und kleinere Spracherweiterungen bietet.

avr-gcc kann auch dazu verwendet werden, um C/C++ Programme nach Assembler zu übersetzen oder um Bibliotheken zu erstellen, die später in unterschiedlichen Projekten verwendet werden können.

Wie bei allen aus der UNIX-Welt kommenden Programmen ist das Kommando-Interface von avr-gcc die Shell bzw. die Kommandozeile, über die Optionen, Parameter, Einstellungen und die Namen der zu übersetzenden Dateien angegeben werden.

How to Read

Dieser Artikel bespricht avr-gcc Version 3.x. Er ist kein C-Tutorial und kein AVR-Handbuch – das würde den Umfang des Artikels bei weitem sprengen.

Der Artikel ist ein Handbuch zu avr-gcc. Er bespricht zum Beispiel, wie avr-gcc angenwendet wird und Besonderheiten von avr-gcc-C, die nicht zum Sprachumfang von C gehören. Dazu zählen die Definition von Interrupt Service Routinen (ISRs) oder wie man Daten ins EEPROM legt.

Es wird also besprochen, wie eine ISR zu definieren ist, aber nicht, warum das gegebenenfalls notwendig oder nicht notwendig ist. Warum etwas gemacht wird, ist abhängig von der gestellten Aufgabe, etwa "Initialisiere den UART zur Benutzung mit 9600 Baud". Dafür enthält dieser Artikel zusammen mit dem AVR-Handbuch das Rüstzeug, bietet aber keine Lösungen für konkrete Aufgaben.

Neben diesem Artikel gibt es den Unterartikel Interna von avr-gcc wo Dinge wie die Registerverwendung, Attribute, Builtins und Sections von avr-gcc dargestellt werden. Zudem findet sich dort ein Überblick über die Arbeitsweise von gcc mit den Schritten

  • Precompilieren
  • Compilieren
  • Assemblieren
  • Linken

Ein weiterer Unterartikel widmet sich dem Thema Inline-Assembler in avr-gcc.

In den C-Codebeispielen befindet sich das ausführlichere Beispiel "Hallo Welt für AVR (LED blinken)", das nur eine LED blinkt und zeigt, wie ein kleines Projekt mit avr-gcc compiliert werden kann.

Es gibt ein C-Tutorial, das jedoch noch unvollständig und teilweise feherhaft ist (Stand 02/2006). Darüber hinaus gibt es ein C-Tutorial bei www.mikrocontroller.net.

Benutzer-Schnittstelle

Die Benutzer-Schnittstelle von avr-gcc ist – wie für alle Programme, die aus der UNIX-Welt kommen – die Kommandozeile einer Shell, Console bzw. Eingabeaufforderung.

Im einfachsten Fall sieht ein Aufruf von avr-gcc also so aus:

> avr-gcc

Dabei das '>' nicht mittippen, und ein ENTER am Ende der Zeile drücken. Die Antwort bei korrekter Installation ist dann

avr-gcc: no input files

Was bedeutet: das Programm avr-gcc wurde vom Betriebssystem gefunden und konnte/durfte gestartet werden. Dann gibt avr-gcc eine Fehlermeldung aus und beendet die Ausführung, weil er keine Eingabedatei(en) bekommen hat – was ja auch stimmt. Soweit ist also alles in Butter.

Um eine C-Datei foo.c mir avr-gcc optimiert zu einem lauffähigen elf-Programm foo.elf für einen ATmega32 zu compileren, würde man angeben

> avr-gcc -Os -mmcu=atmega32 foo.c -o foo.elf

Hat man seine Quellen auf zwei oder mehre Dateien verteilt, geht es analog:

> avr-gcc -Os -mmcu=atmega32 foo.c foo2.c -o foo.elf

Will man nur eine Objekt-Datei erstellen (nur compilieren, nicht linken), dann geht das wie folgt. Das kann günstig sein bei grösseren Projekten, wenn man das Projekt neu erzeugen will, aber nur in einer Quelldatei was geändert hat. Oder wenn das Objekt in einer Bibliothek landen soll.

> avr-gcc -Os -c -mmcu=atmega32 foo.c -o foo.o

Die ausführbare Gesamtdatei foo_all.elf erhält man dann, indem alle Objekte zusammenlinkt:

> avr-gcc -mmcu=atmega32 foo.o foo2.o foo3.o -o foo_all.elf

Um die ausführbare Datei in das oft verwendete Intex-HEX-Format umzuwandeln (einmal fürs Programm, einmal für ein Abbild des EEPROMs) gibt man an:

> avr-objcopy -O ihex -j .text -j .data                         foo_all.elf  foo_all.hex
> avr-objcopy -O ihex -j .eeprom --change-section-lma .eeprom=1 foo_all.elf  foo_all_eeprom.hex

GCC war immer Kommandozeilen-orientiert und wird es auch immer bleiben, denn das hat gute Gründe:

  • ein Compiler ist ein Compiler (und keine grafische Bedienschnittstelle)
  • die Plattformabhängigkeit wird auf ein Minimum reduziert
  • es gibt die Möglichkeit, avr-gcc per Skript oder make zu starten
  • avr-gcc kann durchaus in eine Umgebung integriert werden: in einen Editor oder in eine GUI wie neuere Versionen von AVR-Studio erfolgreich beweisen, etc. Der avr-gcc-Aufruf kann sogar von einem Server-Socket oder einer Web-Application heraus erfolgen, welche ein C-Programm empfängt, es von avr-gcc übersetzen lässt, und das Resultat zurückschickt oder sonst was damit anstellt.
  • Lizenzgründe: eine Umgebung, die avr-gcc integriert, kann durchaus proprietär oder nicht quelloffen sein und muss nicht der GPL unterliegen. Wieder ist AVR-Studio ein Beispiel.

Unterstützte AVR-Derivate

GCC Manual: AVR Options

Diese Liste der unterstützten Devices kann man anzeigen lassen mit

> avr-gcc --target-help

bzw. ab Version 4.7 mit

> avr-gcc --help=target

Siehe auch "AVR Options" in der GCC Dokumentation.

Kommandozeilen-Optionen

Die Codegenerierung bei avr-gcc wird über Kommandozeilen-Optionen gesteuert. Diese legen fest, für welchen Controller Code zu erzeugen ist, wie stark optimiert wird, ob Debug-Informationen erzeugt werden, etc. Die Optionen teilen sich in zwei Gruppen: Optionen, die für alle GCC-Ports verfürgbar sind und maschinenspezifische Optionen, die nur für AVR verfügbar sind.

Aus der Masse an GCC-Optionen kann hier nur ein kleiner Auszug der wichtigsten und am häufigsten verwendeten Optionen vorgestellt werden. Eine Auflistung aller GCC-Optionen mit Kurzbeschreibung umfasst knapp 1000 Zeilen – ohne undokumentierte Optionen, versteht sich.

Allgemeine Optionen für GCC

--help
Anzeige der wichtigsten Optionen
--help -v
Überschüttet einen mit Optionen
--target-help
--help=target
Anzeige der wichtigsten maschinenspezifischen Optionen und der unterstützten AVR-Derivate
-O0
keine Optimierung - sinnvoll zum debuggen
-O1
Optimierung
-Os
optimiert für Code-Größe – meist beste Wahl für µCs
-O2
stärkere Optimierung für bessere Laufzeit
-g
erzeugt Debug-Informationen
-gdwarf-3 -gstrict-dwarf
erzeugt Debug-Informationen nachdem DWARF-3 Standard und ohne GNU-spezifische Erweiterungen.
-c
(pre)compilert und assembliert nur bis zum Objekt (*.o), kein Link-Lauf
-S
(pre)compilert nur und erzeugt Assembler-Ausgabe (*.s)
-E
nur Precompilat (*.i bzw. *.ii) erzeugen, kein Compilieren, kein Assemblieren, kein Linken
-o <filename>
legt den Name der Ausgabedatei fest
-v
zeigt Versionsinformationen an und ist geschwätzig (verbose): Anzeige der aufgerufenen tools
-I<path>
Angabe eines weiteren Include-Pfads, in dem Dateien mit #include <...> gesucht werden
-E -dM <filename>
Anzeige aller Defines
-MM
Für die angegebenen Eingabe-Dateien wird eine Ausgabe erzeugt, die als Makefile-Fragment dienen kann und die Anhängigkeiten (dependencies) der Objekte von den Quellen/Headern beschreibt.
-D<name>
Definiert Makro <name>
-D<name>=<wert>
Definiert Makro <name> zu <wert>
-U<name>
Undefiniert Makro <name>
-save-temps
Temporäre Dateien (*.i, *.s) werden nicht gelöscht.
-Wa,<options>
übergibt Komma-getrennte Liste <options> an den Assembler (avr-as)
-Wa,-a=<filename>
Assembler erzeugt ein Listing mit Name <filename>
-Wp,<options>
übergibt Komma-getrennte Liste <options> an den Preprozessor
-Wl,<options>
übergibt Komma-getrennte Liste <options> an den Linker (avr-ld)
-Wl,-Map=<filename>
Linker erzeugt ein Map-File mit Name <filename>
-Wl,--section-start=<section>=<address>
Linker legt die Section <section> ab Adresse <address>, z.B: .eeprom=0x810001
-Wall
gibt mehr Warnungen, aber immer noch nicht alle
-std=gnu99
Sagt dem Compiler, dass er C99 mit GNU-C Erweiterungen akzeptieren soll. Das ist zum Beispiel der Fall, wenn man Embedded-C Code mit __flash verwenden will.
-std=c89
-ansi
bricht mit einer Fehlermeldung ab, wenn kein ANSI-C (ISO C89) verwendet wurde
-std=c99
C99 mit einigen Erweiterungen, die nicht dem C99-Standard widersprechen
-std=c99 -pedantic
Bricht mit einer Fehlermeldung ab, wenn kein ISO C99 verwendet wird

Maschinenspezifische Optionen für avr-gcc

Maschinenabhängige Optionen beginnen immer mit -m

-mmcu=xxx
Festlegen des Targets (Zielsystem/Controller), für das Code generiert werden soll. Je nach Target muss avr-gcc unterschiedliche Instruktionen verwenden und andere Startup-Dateien (crtxxx.o) einbinden. avr-gcc setzt spezielle Defines, um auch in der Quelle zwischen den Targets unterscheiden zu können, falls das notwendig sein sollte:
#ifdef __AVR_AT90S2313__
/* Code fuer AT90S2313 */
#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega32__)
/* Code fuer Mega8 und Mega32 */ 
#else
#error Das ist noch nicht implementiert für diesen Controller!
#endif
Zwar gibt es für alle AVR-Derivate die avr/io.h, aber die AVR-Familien unterscheiden sich in ihrer Hardware; z.B. darin, wie I/O-Register heissen oder wie Hardware zu initialisieren ist. Diese Abhängigkeit kann man in unterschiedlichen Codestücken aufteilen und wie oben gezeigt bedingt übersetzen. Dadurch hat man Funktionalitäten wie uart_init auf unterschiedlichen Controllern und wahrt den Überblick, weil nicht für jede Controller-Familie eine extra Datei notwendig ist.
Built-in Makros wie __AVR_ATmega8__ und __AVR_ARCH__ sind ab Version 4.7 im Kapitel "AVR Options" in der GCC Dokumentation erklärt, siehe z.B. "AVR Built-in Macros".
-mint8
Datentyp int ist nur 8 Bit breit anstatt 16 Bit. Datentypen mit 64 Bit sind nicht verfügbar. 8-Bit int ist nicht C-Standard konform und wird nicht von der AVR Libc unterstützt (ausser in stdint.h).
-mno-interrupts
Ändert den Stackpointer ohne Interrupts zu deaktivieren
-mcall-prologues
Funktions-Prolog und -Epilog werden als Unterroutinen umgesetzt, um die Codegröße zu verkleinern
-mtiny-stack
Nur die unteren 8 Bit des Stackpointers werden verändert

C++

"C++ is a complex language and an evolving one, and its standard definition (the ISO C++ standard) was only recently completed. As a result, your C++ compiler may occasionally surprise you, even when its behavior is correct."

Zudem sollte der Einsatz von C++ aus Effizienzgründen sehr kritisch betrachtet werden:

"When programming C++ in space- and runtime-sensitive environments like microcontrollers, extra care should be taken to avoid unwanted side effects of the C++ calling conventions like implied copy constructors that could be called upon function invocation etc. These things could easily add up into a considerable amount of time and program memory wasted. Thus, casual inspection of the generated assembler code (using the -S compiler option) seems to be warranted."

Weiterhin unterliegt der Einsatz von C++ je nach Compiler/Lib-Version bestimmten Einschränkungen:

  • Einer kompletten C++ Implementierung fehlt die Unterstützung durch die libstdc++, dadurch fehlen Standardfunktionen, -Klassen und -Templates
  • Die Operatoren new und delete sind nicht implementiert, ihre Verwendung führt zu unauflösbaren externen Referenzen (Linker-Fehler)
  • Nicht alle Header sind C++-sicher und müssen in extern "C" {...} eingeschlossen werden.
  • Exceptions werden nicht unterstützt und müssen via -fno-exceptions abgeschaltet werden, oder der Linker beschwert sich über eine unauflösbare externe Referenz zu __gxx_personality_sj0.

Als Treiber verwendet man wie immer avr-gcc. Standard-Endungen für C++ sind .c++ und .cpp. Bei anderen Endungen teilt man mit -x c++ mit, daß es sich um C++ Dateien handelt, oder ruft avr-c++ direkt auf.

Interrupt-Service-Routinen (ISRs) sind C-Funktionen und werden definiert wie gehabt. Siehe auch Interrupts.

#include <avr/io.h>
#include <avr/interrupt.h>

#if defined (__cplusplus)
extern "C" {
#endif /* __cplusplus */

SIGNAL (SIG_NAME)
{
   /* machwas */
}

INTERRUPT (SIG_NAME)
{
   /* mach was */
}

#if defined (__cplusplus)
}
#endif /* __cplusplus */

__cplusplus ist ein Standard GCC-Builtin-Define.

Globale Konstruktoren werden in Section .init6 ausgeführt, die Destruktoren in .fini6.

Code-Beispiele

Dieser Abschnitt enthält Code-Schnippsel für avr-gcc. Es werden Besonderheiten besprochen, die für avr-gcc zu beachten sind.

Dieser Abschnitt ist kein Tutorial zur C-Programmierung und keine Einführung in die Programmiersprache C im allgemeinen. Dafür sei auf einschlägige Tutorials/Bücher verwiesen.

Zugriff auf Special Function Registers (SFRs)

Zugiff auf Bytes und Worte

Für den Zugriff auf die SFRs gibt es Defines über den Include

#include <avr/io.h>

Abhängig vom eingestellten Controller werden dann Defines eingebunden, über die auf SFRs wie auf normale Variablen zugegriffen werden kann. Die Namen der Defines sind i.d.R. die gleichen wie im AVR-Manual, also z.b. SREG für das Prozessorstatus-Register SREG:

#include <avr/io.h>

...
  // SREG lesen
  uint8_t sreg = SREG;
  ...
  // SREG schreiben
  SREG = sreg;

Für einen Überblick über die eingebundenen Defines kann ein Blick in den Controller-spezifischen Header hilfreich sein. Dieser befindet sich in

<GCC_HOME>/avr/include/avr/io****.h

z.B. iom32.h für einen ATmega32.

Dieser Zugriff geht auch für 16-Bit Register wie TCNT1 oder ADC, für die eine bestimmte Reihenfolge für den Zugriff auf Low- und High-Teil eingehalten werden muss: avr-gcc generiert die Zugriffe in der richtigen Reihenfolge.

 uint16_t tcnt1 = TCNT1;

Zu beachten ist, daß dieser Zugriff nicht atomar erfolgt. Das Lesen/Schreiben mehrbytiger Werte muss vom Compiler in mehrere Byte-Zugriffe zerlegt werden. Zwischen diesen Zugriffen kann ein Interrupt auftreten, wenn Interrupts aktiviert sind. Je nach Programm und welche Aufgaben eine ISR erledigt, kann dies zu Fehlfunktion führen. In dem Fall müssen diese Code-Stücke atomar gemacht werden, damit sie nicht durch einen IRQ unterbrochen werden können!

Zugriff auf einzelne Bits

Zugriff auf Bits geht wie gewohnt mit den Bitoperationen & (and), | (or), ^ (xor) und ~ (not)

Wieder gibt es Defines in den AVR-Headern, mit denen man Masken für den Zugriff erhalten kann, etwa:

/* GIMSK / GICR */
#define INT1    7
#define INT0    6
#define IVSEL   1
#define IVCE    0


Masken ergeben sich durch Schieben von 1 an die richtige Position:

// Ports B_0 und B_1 als Ausgang
DDRB |= (1<<PB0) | (1<<PB1);

erzeugt

87 b3           in      r24, 0x17
83 60           ori     r24, 0x03
87 bb           out     0x17, r24

Etwas anders sieht der Code aus, wenn die Bits einzeln gesetzt werden und das Register im bitadressierbaren Bereich liegt (SRAM 0x20 bis 0x3f resp. I/O 0x0 bis 0x1f):

// Ports B_0 und B_1 als Ausgang
DDRB |= (1<<PB0);
DDRB |= (1<<PB1);

erzeugt

b8 9a           sbi     0x17, 0
b9 9a           sbi     0x17, 1

Um Bits zu löschen, erzeugt man eine Maske, die an der betreffenden Stelle eine  0 hat:

// Ports B_2 als Eingang
DDRB &= ~(1<<PB2);

Auch hier ist zu beachten, daß es Probleme geben kann, wenn nicht atomarer Code erzeugt wird, weil der AVR-Befehlssatz nicht mehr hergibt:

// toggle PORT B_0: wechseln 0 <--> 1 
PORTB ^= (1<<PB0);

ergibt

88 b3           in      r24, 0x18
; Wenn hier ein Interrupt auftritt, in dessen ISR PORTB verändert wird,
; dann wird die Änderung durch die letzte Instruktion wieder überschrieben!
91 e0           ldi     r25, 0x01
; dito
89 27           eor     r24, r25
; dito
88 bb           out     0x18, r24


Auch das Lesen einzelner Port-Pins geht über das Maskieren von SFRs:

DDRB &= ~(1 << PB2);    // PortB.2 als INPUT 

if (PINB & (1 << PB2))
   // PortB.2 ist HIGH
else
   // PortB.2 ist LOW


if (!(PINB & (1 << PB2)))
   // PortB.2 ist LOW
else
   // PortB.2 ist HIGH

Interrupts

AVR-Libc: Dokumentation zu <avr/interrupt.h>.

Um zu kennzeichnen, daß es sich bei einer Funktion um eine Interrupt Sevice Routine (ISR) handelt, gibt es spezielle Attribute. Diese brauchen nicht explizit hingeschrieben zu werden, ebensowenig wie die genaue Nummer des Interrupt Requests (IRQ). Dafür gibt es Includes aus der AVR Libc und die folgenden Makros.

#include <avr/io.h>
#include <avr/interrupt.h>

// Eine nichtunterbrechbare Interrupt-Service-Routine
ISR (TIMER1_COMPA_vect)
{
   // ISR-Code
}

// Eine unterbrechbare Interrupt-Service-Routine
ISR (TIMER0_OVF_vect, ISR_NOBLOCK)
{
   // ISR-Code
}

Dadurch wird die Funktion mit dem richtigen Prolog/Epilog erzeugt, und es wird ein Eintrag in die Interrupt-Vektortabelle gemacht – bei obigem Beispiel also zwei Einträge.

Mit Ausführung einer ISR deaktiviert die AVR-Hardware die Interrupts, so daß die ISR nicht durch andere Interrupt-Anforderungen unterbrochen wird. Beim Verlassen der ISR werden Interrupts wieder automatisch durch die AVR-Hardware aktiviert. Tritt während der ISR ein IRQ auf, wird diese erst nach Beenden des ISR-Codes ausgeführt. Der Interrupt geht also nicht verloren. Dies gilt allerding nicht für Level-getriggerte IRQs wie für manche externen Interrupts oder TWI-Interrupts.

Zwischen zwei ISRs wird zusätzlich mindestens ein Befehl des normalen Programm-Codes abgearbeitet.

Nachschlagen kann man die ISR-Namen im Device-spezifischen Header, die im Installationsverzeichnis liegen:

<GCC_HOME>/avr/include/avr/ioxxxx.h

Interrupts aktivieren

Damit eine ISR überhaupt zur Ausführung kommt, müssen drei Bedingungen erfüllt sein

  • Interrupts müssen global aktiviert sein
  • Der entsprechen IRQ muss aktiviert worden sein
  • Das zum IRQ gehörende Ereignis muss eintreten
#include <avr/io.h>
#include <avr/interrupt.h>

   ...
   // enable OutputCompareA Interrupt für Timer1
   TIMSK |= (1 << OCIE1A);

   // disable OutputCompareA Interrupt für Timer1
   TIMSK &= ~(1 << OCIE1A);

   // Interrupts aktivieren
   sei();

   // Interrupts abschalten
   cli();

Sperrt man eine Code-Sequenz durch Einschachteln in ein cli/sei Paar (man macht das Codestück "atomar", also ununterbrechbar), gehen währenddessen keine Interrupt-Anforderungen verloren. Die entsprechenden IRQ-Flags bleiben gesetzt, und nach dem sei werden die IRQs in der Reihenfolge ihrer Prioritäten abgearbeitet. Ausnahme ist, wenn in einem atomaren Block der selbe IRQ mehrfach auftritt. Der ISR-Code wird dann trotzdem nur einmal ausgeführt.

default Interrupt

Für nicht implementierte Interrupts macht avr-gcc in die Vektortabelle einen Eintrag, der zu __bad_interrupt (definiert im Startup-Code crt*.o) springt und von dort aus weiter zu Adresse 0. Dadurch läuft der AVR wieder von neuem los, wenn ein Interrupt auftritt, zu dem man keine ISR definiert hat – allerdings ohne die Hardware zurückzusetzen wie bei einem echten Reset.

Möchte man diesen Fall abfangen, dann geht das über eine globale Funktion namens __vector_default:

#include <avr/interrupt.h>

ISR (__vector_default)
  ...

Damit wird von __bad_interrupt aus nicht nach Adresse 0 gesprungen, sondern weiter zu __vector_default, welches durch ISR() den üblichen ISR-Prolog/Epilog bekommt.

So kann man z.B. eine Meldung ausgeben, eine Warnlampe blinken, in einer Endlosschleife landen, oder über den Watchdog einen richtigen Hardware-Reset auslösen, siehe auch Abschnitt "Reset auslösen".

ISR mit eigenem Prolog/Epilog

Wenn man in einer ISR komplett eigenes Zeug machen will, dann definiert man eine naked Funktion. Mit naked befreit man die Routine vom Standard-Prolog/Epilog.

Dabei ist darauf zu achten, daß die ISR mit reti (return from interrupt) zurückkehrt und evtl. verwendete Register und den Status (SREG) sichert.

Komplexer oder nicht optimierter C-Code, der einen Framepointer braucht, funktioniert nicht mehr weil ohne Prolog der Framepointer nicht initialisiert wird. Die geschieht wenn nicht alle Werte in Registern gehalten werden können und vom Compiler auf dem Stack zwischengespeichert werden.

#include <avr/io.h>
#include <avr/interrupt.h>

ISR (TIMER0_OVF_vect, ISR_NAKED)
{
   // Port B.6 = 0
   // Diese Instruktion verändert nicht das SREG und kein anderes Register
   // so daß der eigentliche Code nur 1 Befehl lang ist
   __asm__ __volatile (
      "cbi %0, %1" "\n\t"
      "reti"
         : 
         : "M" (_SFR_IO_ADDR (PORTB)), "i" (6)
   );
}

Siehe auch Inline-Assembler in avr-gcc. Die ISR sieht dann so aus:

__vector_9:
   c6 98        cbi   0x18, 6
   18 95        reti

Wiederum kann man als Funktionsname __vector_default nehmen, um nicht-implementierte IRQs abzufangen:

void __attribute__ ((naked, used))
__vector_default (void)
 ...

SRAM, Flash, EEPROM: Datenablage am Beispiel Strings

Die Programmiersprache C kennt selber keine Strings; das einzige, was C bekannt ist, ist der Datentyp char, der ein einzelnes Zeichen repräsentiert.

Darstellung in C

Ein String im Sinne von C ist ein Array von Charactern bzw. ein Zeiger auf den Anfang des Arrays. Die einzelnen Zeichen folgen im Speicher direkt aufeinander und werden in aufsteigenden Adressen gespeichert. Am String-Ende folgt als Abschluss der Character '\0', um das Ende zu kennzeichnen. Dies ist besonders bei der Berechnung des Speicherplatzes für Strings zu berücksichtigen, denn für die 0 muss auch Platz reserviert werden.

Bestimmen der Stringlänge

 /* Bestimmt die Laenge des Strings ohne die abschliessende '\0' zu zaehlen */
 unsigned int strlength (const char *str)
 {
   unsigned int len = 0;
   
   while (*str++)
      len++;
   
   return len;
 }

Die Stringlänge kann auch mit der Standard-Funktion strlen bestimmt werden, deren Prototyp sich in string.h befindet:

 #include <string.h>
 size_t strlen (const char*);

String im Flash belassen

Oftmals werden Strings nur zu Ausgabezwecken verwendet und nicht verändert. Verwendet man Sequenzen der Gestalt

 char *str1 = "Hallo Welt!";
 char str2[] = "Hallo Welt!";

dann werden die Strings im SRAM abgelegt. Im Startup-Code werden die Strings vom Flash ins SRAM kopiert und belegen daher sowohl Platz im SRAM als auch im Flash. Wird ein String nicht verändert, braucht er nicht ins SRAM kopiert zu werden. Das spart Platz im knapp bemessenen SRAM. Allerdings muss anders auf den String zugegriffen werden, denn wegen der Harvard-Architektur des AVR-Kerns kann avr-gcc anhand der Adresse nicht unterscheiden, ob diese ins SRAM, ins Flash oder ins EEPROM zeigt.

 #include <avr/pgmspace.h>
 
 const char str3[] PROGMEM = "Hallo Welt!";
 
 size_t strlen_P (const char *str)
 {
    size_t len = 0;
 
    while (1)
    {
       char c = (char) pgm_read_byte (str);
       if ('\0' == c)
          return len;
       len++;
       str++; 
    }
 }
 
 void foo (void)
 {
    size_t len;
    len = strlen_P (str3);
    len = strlen_P (PSTR ("String im Flash"));
 }

String ins EEPROM legen

Dies geht nach dem gleichen Muster, nach dem Strings ins Flash gelegt werden. Der Zugriff wird vergleichsweise langsam, denn der EEPROM ist langsamer als SRAM bzw. Flash.

 #include <avr/eeprom.h>
 
 const char str4[] EEMEM = "Hallo Welt!";
 
 size_t strlen_EE (const char *str)
 {
    size_t len = 0;
 
    while (1)
    {
       char c = (char) eeprom_read_byte (str);
       if ('\0' == c)
          return len;
       len++;
       str++; 
    }
 }

Reset auslösen

Falls ein Reset per Software ausgelöst werden soll, dann geht das am besten über den Watchdog. Einfach nur an den Reset-Punkt an Adresse 0 zu springen initialisiert zwar den Controller von neuem, aber es macht keinen wirkliches RESET mit Zurücksetzen der Hardware und allen I/O-Registern.

Durch den Watchdog kann man ein 'richtiges' RESET-Signal erzeugen lassen, so daß die AVR-Hardware genau so initialisiert ist, wie nach einem externen RESET. So kann man z.B. via UART ein RESET-Kommando schicken. Allerdings lässt sich der Watchdog nur minimal auf 15ms einstellen:

#include <avr/wdt.h>
#include <avr/interrupt.h>
...   
   cli();                     // Interrupts global abschalten
   wdt_enable (WDTO_15MS);    // Watchdog aufziehen auf 15ms
   while (1);                 // warten, bis er zubeisst...

Welches Ereignis einen RESET ausgelöst hat, kann man im Register MCUCSR (MCU Control and Status Register) erfahren. Es gibt 4 mögliche RESET-Quellen:

  • Power-On Reset
  • External Reset
  • Brown-Out Reset
  • Watchdog Reset

Soll der Inhalt von Variablen einen Reset überleben – eine Variable also nicht initialisiert werden – dann geht das so:

#include <avr/io.h>

//  status informiert z.B. darüber, ob wir selber den Watchdog ausgelöst haben 
//  oder nicht, oder andere Informationen 
uint8_t status __attribute__ ((__section__ (".noinit")));

int main (void)
{
    // Wert von MCUSCR merken, möglichst früh im Programm 
    uint8_t mcucsr = MCUCSR;

    // MCUCSR zurücksetzen 
    MCUCSR = 0;

    // Watchdog-Reset 
    if (mcuscr & (1 << WDRF))
    {
        // status auswerten 
    }

    // Power-On Reset: status auf definierten Wert setzen 
    if (mcuscr & (1 << PORF))
    {
        status = 0;
    }

    // status auswerten 
    ...
}
An Adresse 0 springen

Falls wirklich zu Adresse 0 gesprungen werden soll – was in einem Bootloader erforderlich sein kann – dann geschieht das mittels einer Funktion reset wie folgt:

extern void reset (void) __attribute__((noreturn));
reset();

reset wird bein Linken mittels -Wl,--defsym=reset=0 auf 0 gesetzt. Weitere Möglichkeit ist, im erzeugten Assembler 0 als Funktionsnamen zu verwenden:

extern void reset (void) __asm__("0") __attribute__((__noreturn__));
reset();

Includes

Die mit

#include <...>

angegebenen Includes werden von avr-gcc in den mit der Option '-I' anegegenen Pfaden gesucht. Dem Compiler bekannt sind die Pfade

<GCC_HOME>/avr/include                           Standard               (stdio.h, ...)
<GCC_HOME>/avr/include/avr                       AVR-spezifisch         (avr/io.h, ...)
<GCC_HOME>/lib/gcc/avr/<GCC_VERSION>/include     Standard, compilerabh. (limits.h, ...)

Gibt man z.B. an

#include <stdio.h>

dann wird automatisch in diesem Verzeichnis nach stdio.h gesucht. In den Verzeichnissen stehen Standard-Includes, die benötigt werden, wenn man libc-Funktionen oder mathematische Funktionen etc. verwendet. AVR-spezifische Dinge stehen im Unterverzeichnis avr, etwa:

#include <avr/io.h>

Als Pfad-Separator wird immer ein / verwendet, auch auf Windows-Betriebssystemen! Also kein \ !

Standard

ctype.h                   Zeichen-Umwandlungs-Makros und ctype Makros
errno.h                   Symbolische Namen für Fehlercodes
stdint, inttypes.h        C99 definiert [u]intN_t wenn man genau N [un]signed
                          Bits braucht
math.h                    Mathematische Funktionen: sin, cos, log, gamma, bessel, ...
setjmp.h                  libc unterstützt setjmp() und longjmp(), um direkt in eine
                          andere (nicht-lokale) Funktion zu springen. 
stdio.h                   Standard I/O-Funktionen (printf, fscanf, ...)
stdlib.h                  Deklariert grundlegende ISO C Makros und Funktionen 
                          sowie einige AVR-spezifische Erweiterungen
string.h                  Stringoperationen auf NULL-terminierten Strings. (strlen, ...)
stdarg.h                  Funktionen mit variabler Argumenanzahl
limits.h                  Min- und Max-Werte von Skalaren (UCHAR_MAX, LONG_MIN, ...)

AVR-spezifisch

Die AVR-spezifischen Includes finden sich wie gesagt im Unterverzeichnis avr. Die meisten dort befindlichen Header wird man nie direkt durch Angabe im C-File erhalten, sondern durch Angabe von

#include <avr/io.h>

Dadurch werden genau die I/O-Header eingebunden, die zum AVR-Modell passen, also z.B. avr/iom8.h für ATmega8 etc. Verantwortlich für die Auswahl des richtigen Sub-Headers ist der Schalter '-mmcu=xxx'.

Obwohl diese Sub-Header nicht explizit angegeben werden müssen, kann ein Blick dorthin hilfreich sein, um die Namen von SFRs oder Signals nachzuschlagen. Diese Header werden im folgenden nicht alle einzeln aufgelistet. Ihre Namen sind immer avr/io*.h.

  • für ATmega: avr/iom*.h
  • für ATtiny: avr/iotn*.h
avr/boot.h            Bootloader Support
avr/eeprom.h          EEPROM-Routinen
avr/interrupt.h       sei(), cli(), ISR(), ...
avr/io.h              RAMEND, ***_vect, SFRs: PORTB, DDRB, PINB, SREG, ..., 
avr/pgmspace.h        Zugriff aufs Flash: Byte lesen, PROGMEM, pgm_read_***, ...
avr/sleep.h           Power-Safe und Sleep-Modes
avr/wdt.h             Watchdog

util/crc16.h          Prüfsumme CRC16
util/delay.h          Verzögerungsschleifen für kurze, exakte Verzögerungen 
util/parity.h         Parität
util/twi.h            I2C

Anwendungs-spezifisch

Eigene Header, die nur innerhalb eigener Projekte gebraucht werden, includet man mit

#include "..."

Auch hier darf man Unterverzeichnisse angeben oder ins übergeordnete Verzeichnis:

#include "../../mein-zeug.h"

Mit der Option -I<path> kann ein Pfad zu den bekannten Include-Pfaden hinzugefügt werden; im obigen Beispiel etwa -I../.. und im Programm dann:

#include "mein-zeug.h"

Optimierungen, Tipps & Tricks

Hauptartikel: avr-gcc Optimierungen

Abkürzungen und Bezeichnungen

GCC
GNU Compiler Collection
gcc
GNU C-Compiler
GPR
General Purpose Register
ISR
Interrupt Service Routine
IRQ
Interrupt Request
Prolog/Epilog
Code am Anfang/Ende jeder Funktionen/ISR, der dazu dient, verwendete Register zu sichern, den Stack-Frame für lokale Variablen anzulegen (falls benötigt), Stackpointer zu setzen, zurück zu springen (ret, reti), etc.
SFR
Special Function Register
Target
Zielsystem, in unserem Falle avr

Siehe auch

Code-Beispiele

Details

Installation (Linux)

Sonstiges


Weblinks

Dokumentation

Offline

Je nach Distribution wird diese mit offline-Dokumentation als pdf, HTML, etc. ausgeliefert, die dann z.B. in Ordern wie den folgenden befindet:

<GCC_HOME>/doc/gcc/
<GCC_HOME>/doc/avr-libc/
etc.
Online
GCC Version Dokumentation AVR Options Release Notes
Aktuelle Entwicklung HTML pdf online GCC 5
4.9.x HTML pdf online GCC 4.9
4.8.x HTML pdf online GCC 4.8
4.7.x HTML pdf online GCC 4.7
4.6.x HTML pdf online GCC 4.6
4.5.x HTML pdf online GCC 4.5
4.4.x HTML pdf online GCC 4.4
4.3.x HTML pdf online GCC 4.3
3.4.x HTML pdf online GCC 3.4

Downloads

Tipps, Installation

Sonstiges

Autor

--SprinterSB 11:27, 7. Dez 2005 (CET)


LiFePO4 Speicher Test