Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Balkonkraftwerk Speicher und Wechselrichter Tests und Tutorials

(Division durch Multiplikation)
(ISR mit eigenem Prolog/Epilog: Framepointer, used)
 
(35 dazwischenliegende Versionen von 4 Benutzern werden nicht angezeigt)
Zeile 11: Zeile 11:
 
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.  
 
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 agenwendet wird und Besonderheiten von avr-gcc-C,
+
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.
die nicht zum Sprachumfang von C gehören.
+
 
Dazu zählen die Definition von [[#Interrupts|Interrupt]] Service Routinen ([[ISR|ISRs]])  
 
Dazu zählen die Definition von [[#Interrupts|Interrupt]] Service Routinen ([[ISR|ISRs]])  
 
oder wie man Daten ins [[EEPROM]] legt.
 
oder wie man Daten ins [[EEPROM]] legt.
Zeile 49: Zeile 48:
  
 
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
 
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
  > avr-gcc -O2 -mmcu=atmega32 foo.c -o foo.elf
+
  > avr-gcc -Os -mmcu=atmega32 foo.c -o foo.elf
 
Hat man seine Quellen auf zwei oder mehre Dateien verteilt, geht es analog:
 
Hat man seine Quellen auf zwei oder mehre Dateien verteilt, geht es analog:
  > avr-gcc -O2 -mmcu=atmega32 foo.c foo2.c -o foo.elf
+
  > 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.
 
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 -O2 -c -mmcu=atmega32 foo.c -o foo.o
+
  > avr-gcc -Os -c -mmcu=atmega32 foo.c -o foo.o
  
 
Die ausführbare Gesamtdatei <tt>foo_all.elf</tt> erhält man dann, indem alle Objekte zusammenlinkt:
 
Die ausführbare Gesamtdatei <tt>foo_all.elf</tt> erhält man dann, indem alle Objekte zusammenlinkt:
Zeile 61: Zeile 60:
 
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:
 
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:
 
  > avr-objcopy -O ihex -j .text -j .data                        foo_all.elf  foo_all.hex
 
  > avr-objcopy -O ihex -j .text -j .data                        foo_all.elf  foo_all.hex
  > avr-objcopy -O ihex -j .eeprom --change-section-lma .eeprom=0 foo_all.elf  foo_all_eeprom.hex
+
  > avr-objcopy -O ihex -j .eeprom --change-section-lma .eeprom=1 foo_all.elf  foo_all_eeprom.hex
  
 
----
 
----
Zeile 73: Zeile 72:
  
 
= Unterstützte AVR-Derivate=
 
= Unterstützte AVR-Derivate=
;Classic AVR&#58;: [[AT90S1200]], [[AT90S2313]], [[AT90S8515]], [[AT90S8535]], [[AT90S4433]], [[AT90S4414]], [[AT90S4434]], [[AT90S2323]], [[AT90S2333]], [[AT90S2343]],
 
  
;tinyAVR&#58;: [[ATtiny10]], [[ATtiny11]], [[ATtiny12]], [[ATtiny15]], [[ATtiny28]], [[ATtiny22]], [[ATtiny26]], [[AT90C8534]], [[AT86rf401]], [[ATtiny13]], [[ATtiny2313]], [[ATtiny261]], [[ATtiny461]], [[ATtiny861]], [[ATtiny24]], [[ATtiny44]], [[ATtiny84]], [[ATtiny25]], [[ATtiny45]], [[ATtiny85]]
+
:→ ''[http://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html GCC Manual: AVR Options]
  
;megaAVR&#58;: [[ATmega603]], [[ATmega103]], [[AT76C711]], [[ATmega48]], [[ATmega8]], [[ATmega83]], [[ATmega85]], [[ATmega88]], [[ATmega8515]], [[ATmega8535]], [[ATmega16]], [[ATmega161]], [[ATmega162]], [[ATmega163]], [[ATmega164p]], [[ATmega165]], [[ATmega165P]], [[ATmega168]], [[ATmega32]], [[ATmega323]], [[ATmega324P]], [[ATmega325]], [[ATmega3250]], [[ATmega64]], [[ATmega640]], [[ATmega644]], [[ATmega644P]], [[ATmega128]], [[ATmega1280]], [[ATmega1281]], [[ATmega645]], [[ATmega6450]], [[AT94K]]
+
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
  
; [[LCD]] AVR&#58;: [[ATmega169]], [[ATmega169P]], [[ATmega329]], [[ATmega3290]], [[ATmega649]], [[ATmega6490]],
+
Siehe auch "AVR Options" in der GCC [[#Dokumentation|Dokumentation]].
; Lightning AVR&#58;: [[AT90PWM2]], [[AT90PWM3]]
+
; [[CAN]] AVR&#58;: [[AT90CAN32]], [[AT90CAN64]], [[AT90CAN128]]
+
; Smart Battery&#58;: [[ATmega406]]
+
; [[USB]] AVR&#58;: [[AT43USB320]], [[AT43USB355]], [[AT90USB646]], [[AT90USB647]], [[AT90USB1286]], [[AT90USB1287]]
+
;
+
 
+
Diese Liste kann man avr-gcc anzeigen lassen mit
+
> avr-gcc --target-help
+
Bei Controllern ohne SRAM, ist man aber prakisch auf Assembler beschränkt.
+
  
 
=Kommandozeilen-Optionen=
 
=Kommandozeilen-Optionen=
Zeile 98: 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 und der unterstützten AVR-Derivate
+
; <tt>--target-help<br/>--help=target</tt>: Anzeige der wichtigsten maschinenspezifischen Optionen und der unterstützten AVR-Derivate
 
; <tt>-O0</tt>: keine Optimierung - sinnvoll zum debuggen
 
; <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 - meist beste Wahl für µCs
+
; <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>-gdwarf-3 -gstrict-dwarf</tt>: erzeugt Debug-Informationen nachdem DWARF-3 Standard und ohne GNU-spezifische Erweiterungen.
 
; <tt>-c</tt>: (pre)compilert und assembliert nur bis zum Objekt (<tt>*.o</tt>), kein Link-Lauf
 
; <tt>-c</tt>: (pre)compilert und assembliert nur bis zum Objekt (<tt>*.o</tt>), kein Link-Lauf
; <tt>-S</tt>: (pre)compilert nur und erzeugt Assembler-Ausgabe (*.s)
+
; <tt>-S</tt>: (pre)compilert nur und erzeugt Assembler-Ausgabe (<tt>*.s</tt>)
; <tt>-E</tt>: nur Precompilat (<tt>*.i</tt>) erzeugen, kein Compilieren, kein Assemblieren, kein Linken
+
; <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>-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
Zeile 115: Zeile 108:
 
; <tt>-D<name>=<wert></tt>: Definiert Makro <tt><name></tt> zu <tt><wert></tt>
 
; <tt>-D<name>=<wert></tt>: Definiert Makro <tt><name></tt> zu <tt><wert></tt>
 
; <tt>-U<name></tt>: Undefiniert Makro <tt><name></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. Teilweise fehlerhaft zusammen mit <tt>-c</tt>
+
; <tt>-save-temps</tt>: Temporäre Dateien (<tt>*.i</tt>, <tt>*.s</tt>) werden nicht gelöscht.
 
; <tt>-Wa,<options></tt>: übergibt Komma-getrennte Liste <tt><options></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>-Wa,-a=<filename></tt>: Assembler erzeugt ein Listing mit Name <tt><filename></tt>
 
:;<tt>-Wa,-a=<filename></tt>: Assembler erzeugt ein Listing mit Name <tt><filename></tt>
Zeile 121: Zeile 114:
 
; <tt>-Wl,<options></tt>:  übergibt Komma-getrennte Liste <tt><options></tt> an den Linker (<tt>avr-ld</tt>)
 
; <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,-Map=<filename></tt>: Linker erzeugt ein Map-File mit Name <tt><filename></tt>
:;<tt>-Wl,--oformat=<format></tt>: Linker erzeugt Ausgabe im Format <tt><format></tt>, z.b. <tt>ihex</tt> für Intel-HEX-File
 
 
:;<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>-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 besonders 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>-std=gnu99</tt>: Sagt dem Compiler, dass er die gnu99 erweiterung benutzen soll
+
 
; <tt>-std=c89<br/>-ansi</tt>: bricht mit einer Fehlermeldung ab, wenn kein ANSI-C (ISO C89) verwendet wurde
 
; <tt>-std=c89<br/>-ansi</tt>: bricht mit einer Fehlermeldung ab, wenn kein ANSI-C (ISO C89) verwendet wurde
; <tt>-std=c99</tt>: bricht mit einer Fehlermeldung ab, wenn kein ISO C99 verwendet wurde
+
; <tt>-std=c99</tt>: C99 mit einigen Erweiterungen, die nicht dem C99-Standard widersprechen
 +
; <tt>-std=c99 -pedantic</tt>: Bricht mit einer Fehlermeldung ab, wenn kein ISO C99 verwendet wird
  
 
==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'''
 
;-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:  
 
;-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:  
Zeile 145: Zeile 138:
 
|}
 
|}
  
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.
+
: 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.
{|
+
|- valign="top"
+
|
+
:{| {{Blauetabelle}}
+
|+ '''AVR classic, &lt;= 8 kByte'''
+
|- {{Hintergrund1}}
+
!|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>
+
|}
+
|}
+
  
----<!----------------------------------------------------------->
+
: 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"].
  
{|
+
; -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>).
|- 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>
+
|}
+
|}
+
 
+
----<!----------------------------------------------------------->
+
 
+
 
+
:{| {{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
 
 
 
  
 
=C++=
 
=C++=
Zeile 451: Zeile 311:
 
==Interrupts==
 
==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 Includes und die folgenden Makros.
+
: → ''[http://nongnu.org/avr-libc/user-manual/group__avr__interrupts.html AVR-Libc: Dokumentation zu <tt><avr/interrupt.h></tt>].
 +
 
 +
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.
  
Bitte beachte auch die Hinweise zu den [[#Inkompatibilität|Inkompatibilität]]en von avr-gcc!
 
 
  #include <avr/io.h>
 
  #include <avr/io.h>
 
  #include <avr/interrupt.h>
 
  #include <avr/interrupt.h>
 
   
 
   
 
  {{ccomment|Eine nichtunterbrechbare Interrupt-Service-Routine}}
 
  {{ccomment|Eine nichtunterbrechbare Interrupt-Service-Routine}}
  SIGNAL (SIG_OUTPUT_COMPARE1A)
+
  ISR (TIMER1_COMPA_vect)
 
  {
 
  {
 
     {{ccomment|ISR-Code}}
 
     {{ccomment|ISR-Code}}
Zeile 464: Zeile 325:
 
   
 
   
 
  {{ccomment|Eine unterbrechbare Interrupt-Service-Routine}}
 
  {{ccomment|Eine unterbrechbare Interrupt-Service-Routine}}
  INTERRUPT (SIG_OUTPUT_COMPARE1B)
+
  ISR (TIMER0_OVF_vect, ISR_NOBLOCK)
 
  {
 
  {
 
     {{ccomment|ISR-Code}}
 
     {{ccomment|ISR-Code}}
 
  }
 
  }
  
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.
+
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.
 
+
{{FarbigerRahmen|
+
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 automatisch 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. Zwischen zwei ISRs wird zusätzlich mindestens ein Befehl des normalen Programm-Codes abgearbeitet.
+
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.
;<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. Weiterhin kann man schneller auf bestimmte Ereignisse reagieren. Tritt während der ISR ein anderer IRQ auf, der schnell bedient werden muss, kann sofort der dringende ISR-Code ausgeführt werden. Ansonsten (Verwendung von <tt>SIGNAL</tt>) würde der Code erst ausgeführt werden, nachdem die aktuelle ISR beendet ist.
+
  
{{FarbigerRahmen|Dauert die ISR zu lange und wird sie nochmals von ihrem eigenen IRQ unterbrochen, stürzt man ab.}}
+
Zwischen zwei ISRs wird zusätzlich mindestens ein Befehl des normalen Programm-Codes abgearbeitet.
  
Nachschlagen kann man den Namen 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 521: Zeile 376:
 
#include <avr/interrupt.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.
+
  
 
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]]".
 
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]]".
Zeile 533: Zeile 387:
  
 
Wenn man in einer ISR komplett eigenes Zeug machen will,  
 
Wenn man in einer ISR komplett eigenes Zeug machen will,  
dann definiert man eine nackte Funktion.
+
dann definiert man eine naked Funktion.
 
Mit <tt>naked</tt> befreit man die Routine vom Standard-Prolog/Epilog.
 
Mit <tt>naked</tt> befreit man die Routine vom Standard-Prolog/Epilog.
 
{{FarbigerRahmen|
 
{{FarbigerRahmen|
 
Dabei ist darauf zu achten, daß die ISR mit <tt>reti</tt> (return from interrupt)  
 
Dabei ist darauf zu achten, daß die ISR mit <tt>reti</tt> (return from interrupt)  
 
zurückkehrt und evtl. verwendete Register und den Status (<tt>SREG</tt>) sichert.
 
zurückkehrt und evtl. verwendete Register und den Status (<tt>SREG</tt>) 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/io.h>
 +
#include <avr/interrupt.h>
 
   
 
   
  void __attribute__ ((naked))
+
  ISR (TIMER0_OVF_vect, ISR_NAKED)
SIG_OVERFLOW0 (void)
+
 
  {
 
  {
 
     {{ccomment|Port B.6 &#61; 0}}
 
     {{ccomment|Port B.6 &#61; 0}}
Zeile 566: Zeile 422:
 
um nicht-implementierte IRQs abzufangen:
 
um nicht-implementierte IRQs abzufangen:
 
<pre>
 
<pre>
void __attribute__ ((naked))  
+
void __attribute__ ((naked, used))
 
__vector_default (void)
 
__vector_default (void)
 
  ...
 
  ...
Zeile 606: Zeile 462:
 
  #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 622: 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 635: 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 653: Zeile 509:
  
 
==Reset auslösen==
 
==Reset auslösen==
 +
 
Falls ein Reset per Software ausgelöst werden soll, dann geht das am besten über den [[Watchdog]].
 
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 mit
+
Einfach nur an den Reset-Punkt an Adresse&nbsp;0 zu springen
goto *((void**) 0);
+
 
initialisiert zwar den Controller von neuem, aber es macht keinen wirkliches RESET mit Zurücksetzen der Hardware und allen I/O-Registern.  
 
initialisiert zwar den Controller von neuem, aber es macht keinen wirkliches RESET mit Zurücksetzen der Hardware und allen I/O-Registern.  
  
Zeile 678: Zeile 534:
 
  {{ccomment| status informiert z.B. darüber, ob wir selber den Watchdog ausgelöst haben }}
 
  {{ccomment| status informiert z.B. darüber, ob wir selber den Watchdog ausgelöst haben }}
 
  {{ccomment| oder nicht, oder andere Informationen }}
 
  {{ccomment| oder nicht, oder andere Informationen }}
  unsigned char status __attribute__ ((section (".noinit")));
+
  uint8_t status __attribute__ ((__section__ (".noinit")));
 
   
 
   
  void main (void)
+
  int main (void)
 
  {
 
  {
 
     {{ccomment|Wert von MCUSCR merken, möglichst früh im Programm }}
 
     {{ccomment|Wert von MCUSCR merken, möglichst früh im Programm }}
     unsigned char mcucsr = MCUCSR;
+
     uint8_t mcucsr = MCUCSR;
 
   
 
   
 
     {{ccomment|MCUCSR zurücksetzen }}
 
     {{ccomment|MCUCSR zurücksetzen }}
Zeile 702: Zeile 558:
 
     {{ccomment|status auswerten }}
 
     {{ccomment|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 <tt>reset</tt> wie folgt:
 +
extern void reset (void) __attribute__((noreturn));
 +
reset();
 +
<tt>reset</tt> wird bein Linken mittels <tt>-Wl,--defsym=reset=0</tt> auf&nbsp;0&nbsp;gesetzt.
 +
Weitere Möglichkeit ist, im erzeugten Assembler&nbsp;<tt>0</tt> als Funktionsnamen zu verwenden:
 +
extern void reset (void) __asm__("0") __attribute__((__noreturn__));
 +
reset();
  
 
=Includes=
 
=Includes=
Zeile 732: Zeile 596:
 
  ctype.h                  Zeichen-Umwandlungs-Makros und ctype Makros
 
  ctype.h                  Zeichen-Umwandlungs-Makros und ctype Makros
 
  errno.h                  Symbolische Namen für Fehlercodes
 
  errno.h                  Symbolische Namen für Fehlercodes
  inttypes.h               Definiert [u]intN_t wenn man genau N [un]signed Bits
+
  stdint, inttypes.h       C99 definiert [u]intN_t wenn man genau N [un]signed
                           braucht, ISO C99.
+
                           Bits braucht
 
  math.h                    Mathematische Funktionen: sin, cos, log, gamma, bessel, ...
 
  math.h                    Mathematische Funktionen: sin, cos, log, gamma, bessel, ...
 
  setjmp.h                  libc unterstützt setjmp() und longjmp(), um direkt in eine
 
  setjmp.h                  libc unterstützt setjmp() und longjmp(), um direkt in eine
 
                           andere (nicht-lokale) Funktion zu springen.  
 
                           andere (nicht-lokale) Funktion zu springen.  
  stdio.h                  Standard I/O-Funktionen (printf, ...).
+
  stdio.h                  Standard I/O-Funktionen (printf, fscanf, ...)
  stdlib.h                  Deklariert grundlegende ISO C-Makros und -Funktionen  
+
  stdlib.h                  Deklariert grundlegende ISO C Makros und Funktionen  
 
                           sowie einige AVR-spezifische Erweiterungen
 
                           sowie einige AVR-spezifische Erweiterungen
 
  string.h                  Stringoperationen auf NULL-terminierten Strings. (strlen, ...)
 
  string.h                  Stringoperationen auf NULL-terminierten Strings. (strlen, ...)
Zeile 750: Zeile 614:
 
sondern durch Angabe von
 
sondern durch Angabe von
 
  #include <avr/io.h>
 
  #include <avr/io.h>
Dadurch werden z.B. genau die I/O-Header eingebunden, die zum AVR-Modell passen, 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.h</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.  
Zeile 766: Zeile 625:
 
<pre>
 
<pre>
 
avr/boot.h            Bootloader Support
 
avr/boot.h            Bootloader Support
avr/crc16.h          [*] Prüfsumme CRC16
 
avr/delay.h          [*] Verzögerungsschleifen für kurze, exakte Verzögerungen
 
 
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
+
 
 
util/crc16.h          Prüfsumme CRC16
 
util/crc16.h          Prüfsumme CRC16
 
util/delay.h          Verzögerungsschleifen für kurze, exakte Verzögerungen  
 
util/delay.h          Verzögerungsschleifen für kurze, exakte Verzögerungen  
 
util/parity.h        Parität
 
util/parity.h        Parität
 
util/twi.h            I2C
 
util/twi.h            I2C
 
[*]  bei neueren avr-gcc-Versionen in util
 
[**] entfällt bei neueren avr-gcc-Versionen. Stattdessen avr/interrupt.h verwenden
 
 
</pre>
 
</pre>
  
Zeile 795: Zeile 643:
 
Auch hier darf man Unterverzeichnisse angeben oder ins übergeordnete Verzeichnis:
 
Auch hier darf man Unterverzeichnisse angeben oder ins übergeordnete Verzeichnis:
 
  #include "../../mein-zeug.h"
 
  #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"
  
=Optimierungen, Tipps & Tricks=
+
= Optimierungen, Tipps & Tricks =
 
+
:''Hauptartikel: [[avr-gcc/Optimierungen|avr-gcc Optimierungen]]''
Beim Programmieren in C möchte man sich möglichst wenig mit der Codeerzeugung selbst auseinandersetzen. Man verwendet ja gerade deshalb einen Compiler und programmiert nicht in Assembler, weil man sich nicht um Register-Belegungen o.ä. kümmern will, sondern nur um die zu lösende Aufgabe.
+
 
+
GCC erzeugt zwar recht guten Code, aber er ist nicht perfekt. Gerade auf Systemen wie AVR mit nur sehr begrenzten Resourcen muss man daher dem Compiler hilfreich zur Seite stehen, wenn man noch dichteren/schnelleren Code erhalten möchte.
+
 
+
:''"Unlike most other C compilers, GCC allows you to use -g with -O. The shortcuts taken by optimized code may occasionally produce surprising results: some variables you declared may not exist at all; flow of control may briefly move where you did not expect it; some statements may not be executed because they compute constant results or their values were already at hand; some statements may execute in different places because they were moved out of loops.''
+
 
+
:''Nevertheless it proves possible to debug optimized output. This makes it reasonable to use the optimizer for programs that might have bugs."''
+
 
+
Um das Ergebnis zu beurteilen, hilft ein Blick ins Listfile.
+
Siehe dazu auch die Abschnitte
+
"[[Hallo Welt für AVR (LED blinken)#Listfile erstellen|Listfile erstellen]]"
+
und
+
"[[Hallo Welt für AVR (LED blinken)#Die Größe ermitteln|Die Größe ermitteln]]"
+
im [[Hallo Welt für AVR (LED blinken)|Hallo Welt für AVR]].
+
 
+
==Optimierungsgrad==
+
Als Optimierungsgrad erweist sich <tt>-Os</tt> (Optimize for Size) als der beste, evtl. noch <tt>-O2</tt>. Ohne Angabe eines Optimierungsgrades wird nicht optimiert, was gleichbedeutend mit der Option <tt>-O0</tt> ist. Abzuraten ist von der maximalen Optimierung <tt>-O3</tt>, die wegen function inlining und loop unrolling zu sehr breitem Code führt und für AVR absolut nicht angesagt ist.
+
 
+
==Vermeide printf, scanf, malloc==
+
Funktionen von diesem Kaliber sind die absoluten Platz- und Zeitfresser.
+
 
+
Alternativen findet man reichlich in der <tt>avr-libc</tt> wie <tt>itoa</tt> und <tt>atoi</tt>.
+
Und für <tt>malloc</tt> und Konsorten sind dynamische Arrays und das Compiler-Builtin <tt>__builtin_alloca</tt> effizientere Alternativen, siehe auch im Abschnitt "[[avr-gcc#Dynamische Speicherallokierung|Dynamische Speicherallokierung]]".
+
 
+
==Konstante Strings ins Flash==
+
Konstante Strings, wie sie zu Ausgabezwecken Verwendung finden, werden im Programm oft nicht verändert und brauchen nicht SRAM zu belegen (und damit auch Flash, von wo aus sie vom Startup-Code ins SRAM kopiert werden), sondern gehören ins Flash!
+
 
+
Entsprechende Routinen, um auf Strings im Flash zuzugreifen, tragen die Suffix <tt>_P</tt>, wie z.B. <tt>strcmp_P</tt> mit dem Prototyp
+
extern int *strcmp_P (char *, const prog_char *)
+
Die Implementierungen befinden sich in der <tt>avr-libc</tt>.
+
 
+
'''Anwendung:'''
+
#include <avr/pgmspace.h>
+
+
const prog_char str_p[]    = "Ein String im Flash";
+
const char str2_p[] PROGMEM = "Noch ein String im Flash";
+
...
+
  {{ccomment|String im SRAM mit String im Flash vergleichen}}
+
  if (!strcmp_P (str_sram, str_p))
+
  {
+
      {{ccomment|mach was bei Gleichheit}}
+
  }
+
+
  {{ccomment|"foo" wird im RAM angelegt. Ineffizient für konstante Strings!}} 
+
  {{ccomment|Beachte, daß damit strcmp (nicht strcmp_P) benutzt werden muss.}} 
+
  if (!strcmp (str_sram, "foo"))
+
  {
+
      {{ccomment|mach was bei Gleichheit}}
+
  }
+
+
  {{ccomment|PSTR bewirkt, daß die String-Konstante "foo"}}
+
  {{ccomment|im Flash angelegt wird}}
+
  if (!strcmp_P (str_sram, PSTR ("foo"))
+
  {
+
      {{ccomment|mach was bei Gleichheit}}
+
  }
+
...
+
}
+
 
+
===Sprungtabelle===
+
Genauso macht man auch eine Sprungtabelle, um anhand von Kommando-Strings dazugehörige Funktionen ausführen zu lassen:
+
#include <avr/pgmspace.h>
+
+
int func1 (int arg)
+
{
+
    ...
+
}
+
+
#define TEXT_LEN 15
+
+
{{ccomment|Die Kommandostruktur}}
+
typedef struct
+
{
+
    int (*func)(int);      {{ccomment|Zeiger auf die auszuführende Funktion}}
+
    int arg;              {{ccomment|das Argument, das mitübergeben wird}}
+
    char text[1+TEXT_LEN]; {{ccomment|Text, maximal TEXT_LEN Zeichen lang}}
+
} command_t;
+
+
{{ccomment|Das Array mit den Kommandos.}}
+
{{ccomment|Die funcx sind vom Prototyp (z.B. func1 oben)}}
+
{{ccomment|int funcx (int arg);}}
+
const command_t commands[] PROGMEM =
+
{
+
    { func1, 0, "Befehl 1" },
+
    { func2, 3, "Befehl für func2" }
+
};
+
+
{{ccomment|Sucht in commands[] nach text und führt gegebenenfalls}}
+
{{ccomment|die dazugehörige Funktion funcx mit Argument arg aus.}}
+
{{ccomment|Liefert den Rückgabewert von funcx}}
+
{{ccomment|oder -1, falls text nicht gefunden wurde.}}
+
int execute (const char *text)
+
{
+
    {{ccomment|Schleifenvariable}}
+
    unsigned char i;
+
+
    {{ccomment|Wandert durch das Array mit Kommando-Strukturen}}
+
    const command_t * cmd = commands;
+
+
    {{ccomment|sizeof wird von gcc ausgewertet und ist wie eine Konstante,}}
+
    {{ccomment|denn beide sizeofs sind zur Compilezeit bekannt}}
+
    for (i=0; i < sizeof(commands) / sizeof(command_t); i++)
+
    {
+
      {{ccomment|Ist das der gesuchte String?}}
+
      if (strcmp_P (text, cmd->text))
+
      {
+
        {{ccomment|Nein, dann weitersuchen}}
+
        cmd++;
+
        continue;
+
      }
+
+
      {{ccomment|Ja}}
+
      int (*func)(int), arg;
+
+
      {{ccomment|Dann Funktionszeiger und Argument besorgen,}}
+
      func = (int(*)(int)) pgm_read_word (& cmd->func);
+
      arg  = (int)        pgm_read_word (& cmd->arg);
+
+
      {{ccomment|Funktion ausführen und deren Wert zurückliefern}}
+
      return func (arg);
+
    }
+
+
    {{ccomment|text ist nicht in commands}}
+
    return -1;
+
}
+
 
+
Nachteil dabei ist, daß jeder String den maximalen Platz von <tt>TEXT_LEN+1</tt> Zeichen belegt.
+
Falls man da noch weiter sparen will, dann kann man die Strings wieder ins Flash legen und ihre Adresse in der Struktur merken. Dadurch belegt ein String nur noch Länge+3 Zeichen (+3 wegen 1 Endezeichen und 2 Bytes für seine in der Struktur gemerkte Adresse). Die Definition der Tabelle wird aber umständlicher, weil jeder String einzeln angegeben werden muss:
+
#include <avr/pgmspace.h>
+
+
{{ccomment|Die Kommandostruktur}}
+
typedef struct
+
{
+
    ...
+
    char * text;  {{ccomment|Zeiger auf Text}}
+
} command_t;
+
+
const prog_char str_1[] = "Befehl 1";
+
const prog_char str_2[] = "Befehl für func2";
+
+
const command_t commands[] PROGMEM =
+
{
+
    { func1, 0, str_1 },
+
    { func2, 3, str_2 }
+
};
+
+
{{ccomment|Sucht in commands[] nach text und führt gegebenenfalls}}
+
{{ccomment|die dazugehörige Funktion funcx mit Argument arg aus.}}
+
{{ccomment|Liefert den Rückgabewert von funcx}}
+
{{ccomment|oder -1, falls text nicht gefunden wurde.}}
+
int execute (const char *text)
+
{
+
    {{ccomment|Schleifenvariable}}
+
    unsigned char i;
+
+
    {{ccomment|Wandert durch das Array mit Kommando-Strukturen}}
+
    const command_t * cmd = commands;
+
+
    {{ccomment|sizeof wird von gcc ausgewertet und ist wie eine Konstante,}}
+
    {{ccomment|denn beide sizeofs sind zur Compilezeit bekannt}}
+
    for (i=0; i < sizeof(commands) / sizeof (command_t); i++)
+
    {
+
      const prog_char * text_P;
+
+
      {{ccomment|Liest die Startadresse von str_x}}
+
      text_P = (const prog_char *) pgm_read_word (& cmd->text);
+
+
      {{ccomment|Ist das der gesuchte String?}}       
+
      if (strcmp_P (text, text_P))
+
      {
+
          ...
+
 
+
==Lokale Variablen verwenden==
+
 
+
Beim Manipulieren globaler Variablen kann es günstig sein, diese in eine lokale Variable zu kopieren, dort zu verändern, und sie danach wieder zu schreiben
+
<pre>
+
char var;
+
 
+
void foo1()
+
{
+
  var++;
+
  if (var > 10)
+
      var = 1;
+
}
+
</pre>
+
Dadurch wird einmal unnötig gespeichert (der dritte Befehl kann vermieden werden).
+
<pre>
+
foo1:
+
  lds r24,var        ; *movqi/4 [length = 2]
+
  subi r24,lo8(-(1)) ; addqi3/2 [length = 1]
+
  sts var,r24        ; *movqi/3 [length = 2]
+
  cpi r24,lo8(11)    ; cmpqi/2  [length = 1]
+
  brlt .L3          ; branch  [length = 1]
+
  ldi r24,lo8(1)    ; *movqi/2 [length = 1]
+
  sts var,r24        ; *movqi/3 [length = 2]
+
.L3:
+
  ret 
+
</pre>
+
Indem man eine lokale Variable (<tt>var2</tt>) verwendet für die Änderung von <tt>var</tt> vermeidet man dies:
+
<pre>
+
char var;
+
 
+
void foo2()
+
{
+
  char var2 = var;
+
 
+
  var2++;
+
  if (var2 > 10)
+
      var2 = 1;
+
     
+
  var = var2;
+
}
+
</pre>
+
Dadurch wird erst am Ende gespeichert. <tt>var2</tt> lebt in Register <tt>r24</tt>.
+
<pre>
+
foo2:
+
  lds r24, var      ; *movqi/4  [length = 2]
+
  subi r24,lo8(-(1)) ; addqi3/2  [length = 1]
+
  cpi r24,lo8(11)    ; cmpqi/2    [length = 1]
+
  brlt .L2          ; branch    [length = 1]
+
  ldi r24,lo8(1)    ; *movqi/2  [length = 1]
+
.L2:
+
  sts var, r24      ; *movqi/3  [length = 2]
+
  ret
+
</pre>
+
 
+
Bei diesem einfachen Beispiel spart man lediglich eine Instruktion. Bei komplexeren Rechnungen oder längeren Datentypen kann es aber durchaus lohnender sein, in lokale Register zu kopieren.
+
 
+
==Arithmetik==
+
 
+
=== Daten zerlegen/zusammensetzen ===
+
 
+
In systemnahen Programmen hat man oft was Problem, auf die einzelnen Bytes oder Bitfelder einer grösseren Datenstruktur zuzugreifen. Indem man sich ein Komposit baut, das die gewünschten Strukturen überlagert, kann man effizient z.B. auf Bytes zugreifen. Ausnahme sind Bitfelder, deren Verwendung etwas breiten Code ergibt. Bitfelder "von Hand" zu manipulieren, ist da manchmal effizienter, führt jedoch zu schlecht lesbarem Code.
+
 
+
Oft benötigt wird der Zugriff auf die einzelnen Bytes eines <tt>int</tt>, also der Zugriff auf die Bytes eines 16-Bit-Wertes:
+
 
+
typedef union
+
{
+
    unsigned char  asByte[2];
+
    unsigned short asWord;
+
    int            asInt;
+
} data16_t;
+
+
data16_t data;
+
...
+
    int foo;
+
    uint8_t wert;
+
+
    data.asInt = foo;
+
    wert = data.asByte[1]; {{ccomment|die oberen 8 Bits von foo}}
+
 
+
Ein komplexeres Beispiel, das noch mehr Datentypen überlagert:
+
typedef ... foo_t;
+
+
typedef union
+
{
+
    unsigned char byte[4];      {{ccomment| Zugriff als Bytes (8 Bit) }}
+
    unsigned short word[2];    {{ccomment| Zugriff als Words (16 Bit) }}
+
    signed long slong;          {{ccomment| Zugriff als signed long (32 Bit) }}
+
+
    struct {{ccomment| Zugriff auf einzelne Bitgruppen }}
+
    {
+
        unsigned bit_0_3 : 4;  {{ccomment| 4 Bits (0..3) }}
+
        unsigned bit_4_8 : 5;  {{ccomment| 5 Bits (4..8) }}
+
        unsigned bit_9_21 : 13; {{ccomment| 13 Bits (9..21) }}
+
        unsigned bit_22_31: 10; {{ccomment| 10 Bits (22..31) }}
+
    };
+
+
    foo_t foo; {{ccomment| Zugriff als foo-Struktur }}
+
} data_t;
+
+
...
+
{
+
    data_t data;
+
+
    data.byte[2] = 12;          {{ccomment| setzt byte 2 auf 12 }}
+
    data.bit_4_8 = 0x1f;        {{ccomment| setzt bits 4..8 (5 Stück) alle auf 1 }}
+
+
    int anInt = data.foo.anInt; {{ccomment| liest ein Feld von foo (hier ein int) }}
+
    ...
+
        }
+
 
+
===libgcc2 verwenden===
+
 
+
In der libgcc2 sind einige Arithmetik-Routinen in Assembler implementiert. Dazu gehören ein paar Algorithmen zu Division (mit Rest) und Multiplikation.
+
 
+
Von diesen Algorithmen werden durch die avr-libc jedoch nur zwei Strukturen und Funktionen veröffentlicht: <tt>div_t</tt> und <tt>ldiv_t</tt> resp. die Funktionen <tt>div()</tt> und <tt>ldiv()</tt>. Siehe dazu deine Dokumentation zur avr-libc. Damit kann man Quotient und zusätzlich den Rest bei einer Division 16/16 bzw. 32/32 berechnen lassen; den Rest bekommt man quasi kostenlos als Nebenprodukt. Das ist praktisch, wenn man z.b. eine Zahl in Dezimaldarstellung umwandeln möchte oder von/nach [[BCD]].
+
 
+
Zusätzlich zu den via avr-libc veröffentlichten Funktionen gibt es aber noch Routinen, die z.B. auf 8-Bit-Werten operieren oder mit <tt>unsigned</tt> Typen und dementsprechend effizienter sind.
+
 
+
'''Beispiel: Umwandeln nach Dezimalstring'''
+
 
+
Hier ein Beispiel, das Division mit Rest für <tt>unsigned short</tt> verwendet, um eine 16-Bit-Zahl in Dezimaldarstellung zu wandeln:
+
 
+
{{ccomment| Struktur definieren und Funktion bekannt machen }}
+
typedef struct
+
{
+
    unsigned short quot;
+
    unsigned short rem;
+
} udiv_t;
+
+
extern udiv_t udiv (unsigned short, unsigned short) __asm__("__udivmodhi4");
+
+
{{ccomment| 5 Ziffern (0...65535) und evtl. noch eine führende 0 }}
+
#define DIGITS 6
+
+
{{ccomment| +1 wegen String-Ende (wird im Startup auf 0 gesetzt) }}
+
char string[DIGITS+1];
+
+
{{ccomment| Wandelt zahl in Dezimaldarstellung um. }}
+
{{ccomment| Der return-Wert zeigt irgendwo ins string[]-Array. }}
+
{{ccomment| string[] wird verändert. }}
+
char* toString (unsigned short zahl)
+
{
+
    {{ccomment| s zeigt auf das Ende von string }}
+
    {{ccomment| string wird von hinten nach vorne gefüllt }}
+
    char *s = string + DIGITS;
+
 
+
    {{ccomment| qrem enthält Quotient (quot) und Rest (rem) der Divisionen }}
+
    udiv_t qrem = {.quot = zahl};
+
+
    do
+
    {
+
        {{ccomment| Division mit Rest durch 10 }}
+
        {{ccomment| quot: Ergebnis für den nächsten Durchlauf }}
+
        {{ccomment| rem: Rest ist die Ziffer im 10er-System }}
+
        qrem = udiv (qrem.quot, 10);
+
 
+
        {{ccomment| Ziffer in Zeichen wandeln und speichern }}
+
        *(--s) = '0' + qrem.rem;
+
    }
+
    while (0 != qrem.quot);
+
 
+
    {{ccomment| Falls eine führende '0' gespeichert wurde: weg damit }}
+
    {{ccomment| ausser zahl war selbst schon 0 }}
+
    if (*s == '0' && *(s+1) != '\0')
+
        s++;
+
+
    return s;
+
}
+
 
+
Falls man eine Division und/oder Rest für 8-Bit braucht, dann geht für <tt>unsigned</tt> analog.
+
 
+
'''Beispiel: BCD-Umrechnung'''
+
 
+
Wandeln einer 8-Bit-Zahl <tt>0 &lt;= num &lt; 100</tt> nach [[BCD]]
+
 
+
typedef struct
+
{
+
    unsigned char quot; {{ccomment| Quotient }}
+
    unsigned char rem;  {{ccomment| Rest (remainder) }}
+
} udiv8_t;
+
+
extern udiv8_t udiv8 (unsigned char, unsigned char) __asm__ ("__udivmodqi4");
+
+
{{ccomment| Wandelt num nach BCD um, 0 <&#61; num <&#61; 99 }}
+
{{ccomment| return-Wert ist dann 0x0 <&#61; return <&#61; 0x99 }}
+
unsigned char to_bcd (unsigned char num)
+
{
+
    udiv8_t qrem = udiv8 (num, 10);
+
+
    return (unsigned char) (qrem.quot << 4) | qrem.rem;
+
}
+
 
+
===Division durch Multiplikation===
+
Bei den AVRs (Mega...) sind Multiplikationen deulich schneller als Divisionen. Besonders bei Fleißkommazahlen lohnt es daher eine Division durch die Multiplikation mit dem Kehrwert zu ersetzen. Bei Integer Zahlen sind dem durch die Rundung Grenzen gesetzt.
+
 
+
===Multiplikation statt shifts===
+
Bei den AVRs (Mega...) sind Multiplikationen (Int oder Char) durch die Hardwareunterstützung zum Teil schneller shifts. Es ist also kontraproduktiv eine Multiplication mit 16 durch <<4 zu ersetzen.
+
 
+
===Vermeiden von float und double===
+
GCC kennt für die AVRs zur Zeit nur einen Fleißkommatyp. Da keine Hardwareunterstützung dafür vorhanden ist, dauern Fleißkommarechnungen relativ lange. Gerade Additionen sind deutlich langsamer als bei Integer.  Auch die Codelänge nimmt erheblich zu, selbst wenn nur wenige Fleißkommazahlen genutzt werden.
+
 
+
Ein Alternative zu Fleißkommazahlen ist die Benutzung von Festkommazahlen, auch wenn die nicht direkt von GCC unterstützt werden. Die Werte werden einfach alle mit einem konstanten Faktor (z.B. 256,1024 oder 1000) skaliert.
+
Es wird dann mit Integer oder Long Integer gerechnet und bei Multiplicationen / Divisionen der zusätzliche Skalenfaktor berücksichtig.
+
 
+
=Inkompatibilität=
+
 
+
[[GCC]] &ndash; und somit auch avr-gcc &ndash; werden ständig weiter entwickelt. Dies betrifft das Beheben von Fehlern, die Unterstützung neuer Architekturen/Sprachen/Betriebssysteme, Implementierung neuer Optimierungsalgorithmen, Vereinheitlichungen, etc.
+
 
+
Leider führt dies auch zu Inkompatibilitäten verschiedener avr-gcc-Versionen untereinander, und eine C-Quelle, die mit einer Version von avr-gcc fehler- und warnungsfrei übersetzt werden kann, ist mit einer anderen Version möglicherweise nicht compilierbar.
+
 
+
Die avr-gcc Version kann man anzeigen lassen, indem man in einer Shell/Eingabeaufforderung eintippt
+
avr-gcc -v
+
 
+
;<tt>#include <avr/signal.h></tt>: In Versionen bis 3.4.4 werden in dieser Header-Datei u.a. die Makros <tt>SIGNAL()</tt> und <tt>INTERRUPT()</tt> definiert, die man braucht, wenn man eine C-Funktion als Interrupt-Routine ([[ISR]]) kennzeichnen will. In neueren Versionen ab 3.4.5 sind diese Definitionen in den Header <tt>avr/interrupt.h</tt> gewandert, wo auch Makros wie <tt>sei()</tt> und <tt>cli()</tt> definiert werden. Die Inkludierung von <tt>avr/signal.h</tt> in den neueren avg-gcc Versionen führt zu einer Warnung, irgendwann vielleicht sogar zu einem Fehler, weil die Datei nicht mehr bei avr-gcc dabei ist und daher nicht mehr gefunden wird.
+
: '''Verwendet man bei einer älteren avr-gcc-Version <tt>SIGNAL</tt> bzw. <tt>INTERRUPT</tt> ohne <tt>avr/signal.h</tt> zu includen, wird u.U. stillschweigend falscher Code erzeugt.'''
+
 
+
:Unabhängig von der avr-gcc Version kann folgender Code verwendet werden:
+
:{|
+
<pre>
+
#include <avr/interrupt.h>
+
 
+
#ifndef SIGNAL
+
#include <avr/signal.h>
+
#endif /* SIGNAL */
+
</pre>
+
|}
+
 
+
;<tt>#include <util/...h></tt>: In <tt>util</tt> stehen jetzt Header wie <tt>util/parity.h</tt> oder das vielverwendete <tt>util/delay.h</tt>, die vormals im Include-Unterverzeichnis <tt>avr</tt> zu finden waren.
+
 
+
;Interrupt Service Routinen: Auch die API zur Definition von [[ISR]]s hat sich von avr-gcc Version 3.x zur Version 4.x geändert:
+
:'''avr-gcc 3.x'''
+
::{|
+
<pre>
+
SIGNAL (SIG_INTERRUPT0)
+
{
+
    /* Interrupt Code */
+
}
+
 
+
INTERRUPT (SIG_OVERFLOW0)
+
{
+
    /* Interrupt Code */
+
}
+
</pre>
+
|}
+
 
+
:'''avr-gcc 4.x'''
+
::{|
+
<pre>
+
ISR (INT0_vect)
+
{
+
    /* Interrupt Code */
+
}
+
 
+
void TIMER0_OVF_vect (void) __attribute__((interrupt));
+
void TIMER0_OVF_vect (void)
+
{
+
    /* Interrupt Code */
+
}
+
</pre>
+
|}
+
 
+
=Bugs=
+
 
+
==== Bit 7 bei SFR-Zugriff ====
+
 
+
Bei Sequenzen wie
+
PORTB &= ~ (1 << 7);
+
PORTD &= ~ (1 << 7);
+
 
+
macht avr-gcc eine CSE-Optimierung (common subexpression elimination). Er legt die Konstante 127 (<tt>~(1 << 7)</tt>) in ein Register und verwendet den Registerinhalt, anstatt die Konstante direkt zu verwenden.
+
 
+
Das führt dazu, daß statt der gewünschten Befehlsfolge
+
; Sequenz 1
+
cbi 37-0x20, 7
+
cbi 43-0x20, 7
+
Sequenzen folgender Gestalt erzeugt werden:
+
; Sequenz 2
+
ldi r18,    lo8(127)  ; 127 nach R18
+
+
in  r19,    37-0x20    ; PORTB nach R19 lesen
+
; Wird hier eine ISR ausgeführt, die PORTB verändert, dann wird die
+
; Änderung mit dem folgenden OUT überschrieben
+
and r19,    r18 ; oberstes Bit löschen
+
; dito
+
out 37-0x20, r19 ; PORTB schreiben
+
+
in r19,      43-0x20 ; analog
+
and r19,    r18
+
out 43-0x20, r19
+
 
+
Abgesehen davon, daß Sequenz&nbsp;2 deutlich mehr Programmspeicher und Laufzeit benötigt als Sequenz&nbsp;1, kann es zu folgendem Fehler kommen:
+
 
+
Bei der zweiten Codesequenz erfolgt der Zugriff auf die SFRs nicht atomar. Wird der C-Code in einem Programm ausgeführt, das in einer [[ISR]] zB Port B3 verändert und wird diese ISR zwischen dem IN und dem OUT Befehl ausgeführt, dann überschreibt der OUT-Befehl den Wert von PORTB und die Aktion für Port B3 in der ISR wird wieder rückgängig gemacht. Das ist dann ein Programmfehler.
+
 
+
Damit das Problem zum Tragen kommt, müssen mehrere Bedingungen erfüllt sein:
+
* Bei mindestens zwei C-Befehlen wird der Wert 127 als 8-Bit-Wert benötigt. Das ist zB auch der Fall, wenn dieser Wert nur gespeichert wird oder modulo 0x80 gerechnet wird.
+
* Mindestens einer dieser Befehle löscht Bit 7 eines SFR aus dem bitadressierbaren I/O-Bereich. (Ausser PORTx oder DDRx sind in diesem Bereich noch weitere SFR untergebracht.)
+
* Im Programm wird auf unterschiedlichen [[Interrupt]]-Ebenen auf dieses SFR zugegriffen und die oben gezeigt Stelle kann von der ISR unterbrochen werden.
+
* Die beiden C-Befehle müssen nicht unmittelbar aufeinander folgen. Es können auch andere C-Befehle dazwischen stehen.
+
 
+
Bei den meisten Programmen wird das angesprochene Problem nicht zu einem Fehler führen. Falls doch, kann man dem begegnen mit folgenden
+
 
+
;Workarounds:
+
* Die C-Stelle wird in CLI/SEI eingeschlossen und kann damit nicht mehr von einer ISR unterbrochen werden.
+
* Alternativ schreibt man vor und nach jedem problematischen SFR-Zugriff folgendes Kommando:
+
:{|
+
|-
+
|
+
__asm volatile (""::);
+
|}
+
:Dieses Kommando erzeugt keine Assembler-Instruktionen, vermeidet aber die unerwünschte "Optimierung" im gcc, und es entsteht Code gemäß Sequenz&nbsp;1.
+
* Alternativ definiert man sich ein Makro, mit dem man die Portzugriffe tätigt:
+
:{|
+
|-
+
|
+
#define CBI(A,B)    __asm volatile ("cbi\t%0, %1" :: "M" (_SFR_IO_ADDR(A)), "M" (B))
+
|}
+
:Und löscht das Bit mit
+
:{|
+
|-
+
|
+
CBI (PORTB, 7);
+
|}
+
 
+
==== <tt>__builtin_return_address(0)</tt> ====
+
ist entgegen der Spezifikation nicht implementiert und liefert in der Regel ein falsches Ergebnis.
+
 
+
;Workaround:
+
 
+
(hier für eine ISR):
+
#include <stdarg.h>
+
#include <avr/interrupt.h>
+
+
{{ccomment|The return address}}
+
unsigned short volatile return_addr;
+
+
{{ccomment|Define our special SIGNAL (INTERRUPT analoguous)}}
+
#define SIGNAL_BRA(signame,var)                                        \
+
      void signame (unsigned short var, ...) __attribute__ ((signal));  \
+
      void signame (unsigned short var, ...)
+
+
#define SWAP_BYTES(x) (((x) >> 8) | ((x) << 8))
+
+
SIGNAL_BRA (SIG_OUTPUT_COMPARE1A, dummy)
+
{
+
    {
+
      va_list vlist;
+
      va_start (vlist, dummy);
+
 
+
      {{ccomment|Get the return address from the stack}}
+
      dummy = va_arg (vlist, unsigned short);
+
+
      {{ccomment|Convert stack address to code location}}
+
      addr = SWAP_BYTES(dummy) << 1;
+
      va_end (vlist);
+
    }
+
    {{ccomment|ISR Code}}
+
}
+
 
+
 
+
==== gcc 4.x ====
+
 
+
In der 4er-Version gab es tiefgreifende interne Änderungen im Compiler; er ist noch instabil und kann momentan nicht für den Produktiv-Einsatz empfohlen werden (Stand 02/2006).
+
 
+
Die Auswirkungen der Optimierungen auf das Zielsystem <tt>avr</tt> bleibt abzuwarten, dürfte aber nicht wesentlich sein. Die neue 4-er Version wurde u.a. deshalb aufgelegt, um mit dem Intel-Compiler gleichzuziehen &ndash; also im Hinblick auf das Zielsystem <tt>i386</tt>) gemacht.
+
 
+
Auch nach dem Release der 4-er Version wird die 3-er Version weiterentwickelt, da erst nach 1-2 Jahren nach Release einer neuer Major-Version ein Umstieg anzuraten ist und auch erst dann getätigt wird (zumindest im professionellen Bereich).
+
  
 
=Abkürzungen und Bezeichnungen=
 
=Abkürzungen und Bezeichnungen=
Zeile 1.374: Zeile 685:
 
=Weblinks=
 
=Weblinks=
 
==Dokumentation==
 
==Dokumentation==
* [http://gcc.gnu.org/onlinedocs/ GCC online documentation (en)] Offline findest du die Doku für gcc, cpp (Präprozessor) und avr-libc bei [[WinAVR]] in
+
 
:<pre><GCC_HOME>/doc/gcc/</pre>
+
;Offline:
: bzw.
+
 
:<pre><GCC_HOME>/doc/avr-libc/</pre>
+
Je nach Distribution wird diese mit offline-Dokumentation als pdf, HTML, etc. ausgeliefert, die dann z.B. in Ordern wie den folgenden befindet:
:Online finden sich die Dokumente in
+
 
* [http://gcc.gnu.org/onlinedocs/gcc.pdf gcc.pdf (1900 kByte)] - Dokumentation des C/C++/Java-Compilers GCC (en)
+
: <GCC_HOME>/doc/gcc/
 +
: <GCC_HOME>/doc/avr-libc/
 +
: etc.
 +
 
 +
; Online
 +
 
 
* [http://gcc.gnu.org/onlinedocs/cpp.pdf cpp.pdf (470 kByte)] - Dokumentation des C-Präprozessors (en)
 
* [http://gcc.gnu.org/onlinedocs/cpp.pdf cpp.pdf (470 kByte)] - Dokumentation des C-Präprozessors (en)
* [http://www.nongnu.org/avr-libc/user-manual/modules.html http://www.nongnu.org/avr-libc/user-manual] - Dokumentation zur avr-libc.
+
* [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, ...
 +
* [http://gcc.gnu.org/wiki/avr-gcc avr-gcc im GCC Wiki] – Dokumentation des Application Binary Interface (ABI): Registerverwendung, Calling Conventions, ...
 +
{| {{Blauetabelle}}
 +
! [http://gcc.gnu.org GCC] Version ||colspan="2"| [http://gcc.gnu.org/onlinedocs Dokumentation] || AVR Options || Release Notes
 +
|-
 +
! Aktuelle Entwicklung
 +
| [http://gcc.gnu.org/onlinedocs/gcc/ HTML]
 +
| [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]
 +
|-
 +
! 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]
 +
|}
  
 
== Downloads==
 
== Downloads==
Zeile 1.394: Zeile 769:
 
* [http://www.mikrocontroller.net/topic/24166 www.mikrocontroller.net (Foren-Beitrag)] - Installation von GCC und Toolchain unter Mac OS X
 
* [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://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]
  
 
== Sonstiges ==
 
== Sonstiges ==
Zeile 1.399: Zeile 775:
 
* [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://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.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]
  
 
=Autor=
 
=Autor=

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