<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kbuchegg</id>
	<title>Mikrocontroller.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kbuchegg"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Kbuchegg"/>
	<updated>2026-04-08T17:30:53Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90659</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90659"/>
		<updated>2015-12-11T14:38:26Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: Ergänzung eines Codebeispiels&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[avr-gcc]] aus der [http://gcc.gnu.org/ GNU Compiler Collection] (GCC) erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Programmiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong] ([[C|Liste von C-Tutorials]]). Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern.&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR-Controllern finden. Beim Paket [[WinAVR]] gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden ständig weiterentwickelt. Einige Unterschiede, die sich im Verlauf der Entwicklung ergeben haben, werden hier und im Artikel [[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen]] zwar angesprochen, Anfängern und Umsteigern sei jedoch empfohlen, eine aktuelle Versionen zu nutzen.&lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in [[Media:AVR-GCC-Tutorial.pdf|PDF-Form]] erhältlich (nicht immer auf aktuellem Stand).&lt;br /&gt;
&lt;br /&gt;
== Weiterführende Kapitel ==&lt;br /&gt;
&lt;br /&gt;
Um dieses riesige Tutorial etwas überschaubarer zu gestalten, wurden einige Kapitel ausgelagert, die nicht unmittelbar mit den Grundlagen von avr-gcc in Verbindung stehen. All diese Seiten gehören zur [[:Kategorie:avr-gcc Tutorial]].&lt;br /&gt;
&lt;br /&gt;
;UART: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der UART|Der UART]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;ADC: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Analoge Ein- und Ausgabe|Analoge Ein- und Ausgabe (ADC)]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Timer: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Die Timer und Zähler des AVR|Die Timer und Zähler des AVR]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;LCD: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Ansteuerung]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Watchdog: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der Watchdog|Der Watchdog]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Assembler: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Assembler und Inline-Assembler|Assembler und Inline-Assembler]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;alte Quellen anpassen: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen anpassen]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Makefiles: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]]&#039;&#039; sowie als Alternative für sehr kleine Projekte → Hauptartikel: &#039;&#039;[[C ohne Makefile]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels einer AVR-Toolchain zu erstellen wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Eine AVR-Toolchain bestehend aus avr-gcc, den avr-Binutils (Assembler, Linker, etc) und einer Standard-C Bibliothek.  Üblich ist die AVR-LibC, die auch quasi in allen avr-gcc Distributionen enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Hardware wird keine benötigt – bis auf einen PC natürlich, auf dem der Compiler ablaufen kann.  Selbst ohne AVR-Hardware kann man also bereits C-Programme für AVRs schreiben, compiliern und sich das Look-and-Feel von avr-gcc sowie von IDEs wie [[Atmel Studio]], Eclipse oder leichtgewichtigeren Entwicklungsumbgebungen anschauen. Selbst das Debuggen und Simulieren ist mithilfe entsprechender Tools wie Debugger und Simulator in gewissen Grenzen möglich.&lt;br /&gt;
&lt;br /&gt;
Um Programme für AVRs mittels einer AVR-Toolchain zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR-Controllers, der vom avr-gcc Compiler unterstützt wird.&amp;lt;ref&amp;gt;Für eine Liste der unterstützten COntroller siehe die Dokumentation des Compilers oder [http://www.nongnu.org/avr-libc/user-manual/index.html#supported_devices AVR-Libc: Supported Devices].&amp;lt;/ref&amp;gt; Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. &lt;br /&gt;
&lt;br /&gt;
:Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man in den Artikeln [[AVR#Starterkits|AVR Starterkits]] und [[AVR-Tutorial: Equipment]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]].&lt;br /&gt;
&lt;br /&gt;
* Wer unter Windows und Linux gleichermassen entwickeln will, der sollte sich die [http://www.eclipse.org/ IDE Eclipse for C/C++ Developers] und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] ansehen. Beide sind unter Windows und Linux einfach zu installieren, siehe auch [[AVR Eclipse]]. Ebenfalls unter Linux und Windows verfügbar ist die Entwicklungsumgebung [http://www.codeblocks.org/ Code::Blocks]&amp;lt;ref&amp;gt;Aktuelle, stabile Versionen sind als Nightly Builds regelmäßig im [http://forums.codeblocks.org/ Forum] verfügbar.&amp;lt;/ref&amp;gt;. Innerhalb dieser Entwicklungsumgebung können ohne die Installation zusätzlicher Plugins &amp;quot;AVR-Projekte&amp;quot; angelegt werden. Für Linux gibt es auch noch das [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=25220 KontrollerLab].&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht klappt? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/gcc GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net AVRfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRfreaks] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispielprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.tty1.net/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu drei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs (ohne Anspruch auf Vollständigkeit): [http://www.eclipse.org/ Eclipse for C/C++ Developers] (d.h. inkl. CDT) und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] (für diverse Plattformen, u.a. Linux und MS Windows, IDE und Plugin kostenlos), [http://sourceforge.net/projects/kontrollerlab KontrollerLab] (Linux/KDE, kostenlos). [http://www.atmanecl.com/EnglishSite/SoftwareEnglish.htm AtmanAvr] (MS Windows, relativ günstig), KamAVR (MS-Windows, kostenlos, wird augenscheinlich nicht mehr weiterentwickelt), [http://www.amctools.com/vmlab.htm VMLab] (MS Windows, ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand von &amp;quot;make&amp;quot; und den &amp;quot;Makefiles&amp;quot; näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
* Das Generieren des Programms ohne IDE und ohne Makefile. In diesem Fall muss die Quellcodedatei durch eine vorgefertigte Kommandofolge an den Compiler übergeben werden. Der Artikel [[C ohne Makefile]] zeigt, wie das funktioniert. Diese Vorgehensweise empfiehlt sich jedoch nur für kleine Programme, die nicht auf verschiedene Quellcodedateien verteilt sind.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|AVR-GCC-Tutorial/Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. (Hinweis: Bei Version 4.16 wird beides bereits gesetzt). Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Ein kurzes Wort zur Hardware: Bei diesem Programm werden alle Pins von PORTB auf Ausgang gesetzt, und einige davon werden auf HIGH andere auf LOW gesetzt. Das kann je nach angeschlossener Hardware an diesen Pins kritisch sein. Am ungefährlichsten ist es, wenn nichts an den Pins angeschlossen ist und man die Funktion des Programmes durch eine Spannungsmessung mit einem Multimeter kontrolliert. Die Spannung wird dabei zwischen GND-Pin und den einzelnen Pins von PORTB gemessen.&lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xFF;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/   // (6)&lt;br /&gt;
   }                         // (7)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (8)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# In dieser Zeile wird eine sogenannte Header-Datei eingebunden. In &amp;lt;code&amp;gt;avr/io.h&amp;lt;/code&amp;gt; sind die Registernamen definiert, die im späteren Verlauf genutzt werden. Auch unter Windows wird ein&amp;amp;nbsp;&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; zur Kennzeichnung von Unterverzeichnissen in Include-Dateinamen verwendet und kein&amp;amp;nbsp;&amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Hier beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Die Anschlüsse eines AVR (Pins) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Datenrichtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B als Ausgänge eingestellt.&lt;br /&gt;
# Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential). Es ist sinnvoll, sich möglichst frühzeitig eine alternative Schreibweise beizubringen, die wegen der leichteren Überprüfbarkeit und Portierbarkeit oft im weiteren Tutorial und in Forenbeiträgen benutzt wird. Die Zuordnung sieht in diesem Fall so aus, Näheres dazu im Artikel [[Bitmanipulation]]:&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;PORTB = (1&amp;lt;&amp;lt;PB1) | (1&amp;lt;&amp;lt;PB0);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# ist der Beginn der sogenannte &#039;&#039;Hauptschleife&#039;&#039; (main-loop). Dies ist eine Endlosschleife, welche kontinuierlich wiederkehrende Befehle enthält.&lt;br /&gt;
# In diesem Beispiel ist die Hauptschleife leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert. Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife kehrt das Programm aus &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; zurück, alle Interrupts werden deaktiviert und eine Endlosschleife betreten.&lt;br /&gt;
# Ende der Hauptschleife und Sprung zur passenden, öffnenden Klammer, also zu 5.&lt;br /&gt;
# ist das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int main(void)&amp;lt;/syntaxhighlight&amp;gt; besagt, dass die Funktion einen int-Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; (ohne Endung) im selben Verzeichnis, in dem auch die Datei &amp;lt;code&amp;gt;main.c&amp;lt;/code&amp;gt; mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\beispiel&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei &amp;lt;code&amp;gt;main.hex&amp;lt;/code&amp;gt;, in welcher der Code für den AVR enthalten ist. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming ([[ISP]]) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann&amp;lt;ref&amp;gt;z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio&amp;lt;/ref&amp;gt;, kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine [[LED]] leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige Datentypen (Integer) =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; definiert, die folgendermaßen eingebunden werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;stdint.h&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|+ &#039;&#039;&#039;int-Typen aus &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; (C99)&#039;&#039;&#039;&amp;lt;br/&amp;gt;&amp;amp;nbsp;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenbehaftete int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || −128 ⋯ 127 || −2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || −32768 ⋯ 32767 || −2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;signed int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || −2147483648 ⋯ 2147483647 || −2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || −9223372036854775808 ⋯ 9223372036854775807 || −2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenlose int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || 0 ⋯ 255 || 0 ⋯ 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || 0 ⋯ 65535 || 0 ⋯ 2&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || 0 ⋯ 4294967295 || 0 ⋯ 2&amp;lt;sup&amp;gt;32&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || 0 ⋯ 18446744073709551615 || 0 ⋯ 2&amp;lt;sup&amp;gt;64&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Neben den Typen gibt es auch Makros für die Bereichsgrenzen wie &amp;lt;code&amp;gt;INT8_MIN&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;UINT16_MAX&amp;lt;/code&amp;gt;. Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html Dokumentation der avr-libc: Standard Integer Types].&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines µC-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif|left]]&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat. Es wird hier nach dem sogenannten EVA-Prinzip gehandelt. EVA steht für &amp;quot;Eingabe, Verarbeitung, Ausgabe&amp;quot;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif|left]]&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher Dinge erledigt werden können, welche nicht zeitkritisch sind. Wenn ein Interrupt ausgelöst wird, so wird automatisch die zugeordnete Interruptfunktion ausgeführt.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Register haben einen besonderen Stellenwert bei den AVR Controllern. Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers. Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind, selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.&amp;amp;nbsp;B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.&amp;amp;nbsp;B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039; wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Beispiele in den folgenden Abschnitten demonstrieren den Zugriff auf Register anhand der Register für I/O-Ports (PORTx, DDRx, PINx), die Vorgehensweise ist jedoch für alle Register (z.&amp;amp;nbsp;B. die des UART, ADC, SPI) analog.&lt;br /&gt;
&lt;br /&gt;
== Schreiben in Register ==&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man Register einfach wie eine Variable setzen.&amp;lt;ref&amp;gt;In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt, outp() ist nicht mehr erforderlich.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang, vgl. Abschnitt Zugriff auf Ports): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
&lt;br /&gt;
    // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
    // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
    DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
    /* Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
       aber übersichtlicher und selbsterklärend: */&lt;br /&gt;
    DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4);&lt;br /&gt;
&lt;br /&gt;
    while (1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ausführliche Schreibweise sollte bevorzugt verwendet werden, da dadurch die Zuweisungen selbsterklärend sind und somit der Code leichter nachvollzogen werden kann. Atmel verwendet sie auch bei Beispielen in Datenblätten und in den allermeisten Quellcodes zu Application-Notes. Mehr zu der Schreibweise mit &amp;quot;|&amp;quot; und &amp;quot;&amp;lt;&amp;lt;&amp;quot; findet man unter [[Bitmanipulation]].&lt;br /&gt;
&lt;br /&gt;
Der gcc C-Compiler unterstützt ab Version 4.3.0 Konstanten im Binärformat, z.&amp;amp;nbsp;B. DDRB&amp;amp;nbsp;=&amp;amp;nbsp;0b00011111. Diese Schreibweise ist jedoch nur in GNU-C verfügbar und nicht in ISO-C definiert. Man sollte sie daher nicht verwenden, wenn Code mit anderen ausgetauscht oder mit anderen Compilern bzw. älteren Versionen des gcc genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
== Verändern von Registerinhalten ==&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt und löscht man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // Hiermit wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // Hiermit wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird jeweils nur der Zustand des angegebenen Bits geändert, der vorherige Zustand der anderen Bits bleibt erhalten. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= ( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten AVR Registern mit Bits, die durch Beschreiben mit einer logischen 1 gelöscht werden, muss eine absolute Zuweisung benutzt werden. Ein ODER löscht in diesen Registern ALLE gesetzten Bits!&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TIFR2 = (1&amp;lt;&amp;lt;OCF2A); // Nur Bit OCF2A löschen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
== Lesen aus Registern ==&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände von Bits erfolgt durch Einlesen des gesamten Registerinhalts und ausblenden der Bits deren Zustand nicht von Interesse ist. Einige Beispiele zum Prüfen ob Bits gesetzt oder gelöscht sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define MEINBIT0 0 &lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
extern test1();&lt;br /&gt;
&lt;br /&gt;
// Funkion test1 aufrufen, wenn Bit 0 in Register PINA gesetzt (1) ist&lt;br /&gt;
i = PINA;         // Inhalt in Arbeitsvariable&lt;br /&gt;
i = i &amp;amp; 0x01;     // alle Bits bis auf Bit 0 ausblenden (bitweise und)&lt;br /&gt;
                  // falls das Bit gesetzt war, hat i den Inhalt 1&lt;br /&gt;
if ( i != 0 ) {   // Ergebnis ungleich 0 (wahr)? &lt;br /&gt;
  test1();         // dann muss Bit 0 in i gesetzt sein -&amp;gt; Funktion aufrufen&lt;br /&gt;
}&lt;br /&gt;
// verkürzt:&lt;br /&gt;
if ( ( PINA &amp;amp; 0x01 ) != 0 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( PINA &amp;amp; 0x01 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// mit definierter Bitnummer:&lt;br /&gt;
if ( PINA &amp;amp; ( 1 &amp;lt;&amp;lt; MEINBIT0 ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und/oder Bit 2 gesetzt ist. (Bit 0 und 2 also Wert 5) &lt;br /&gt;
// (Bedenke: Bit 0 hat Wert 1, Bit 1 hat Wert 2 und Bit 2 hat Wert 4)&lt;br /&gt;
if ( PINA &amp;amp; 0x05 ) {&lt;br /&gt;
  test1();  // Vergleich &amp;lt;&amp;gt; 0 (wahr), also mindestens eines der Bits gesetzt&lt;br /&gt;
}&lt;br /&gt;
// mit definierten Bitnummern:&lt;br /&gt;
if ( PINA &amp;amp; ( ( 1 &amp;lt;&amp;lt; MEINBIT0 ) | ( 1 &amp;lt;&amp;lt; MEINBIT2 ) ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und Bit 2 gesetzt sind&lt;br /&gt;
if ( ( PINA &amp;amp; 0x05 ) == 0x05 ) {  // nur wahr, wenn beide Bits gesetzt&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion test2() aufrufen, wenn Bit 0 gelöscht (0) ist&lt;br /&gt;
i = PINA;        // einlesen in temporäre Variable&lt;br /&gt;
i = i &amp;amp; 0x01;    // maskieren von Bit 0&lt;br /&gt;
if ( i == 0 ) {  // Vergleich ist wahr, wenn Bit 0 nicht gesetzt ist&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// analog mit not-Operator&lt;br /&gt;
if ( !i ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( !( PINA &amp;amp; 0x01 ) ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Warten auf einen bestimmten Zustand ==&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek avr-libc Funktionen, die warten, bis ein bestimmter Zustand eines Bits erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend&amp;quot; gewartet wird. Der Programmablauf bleibt also an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den [[Watchdog]] ein, muss man darauf achten, dass dieser auch noch getriggert wird (Zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Register (ADC, ICR1, OCR1x, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (Low-Byte) und &amp;quot;H&amp;quot; (High-Byte) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese Register kann dann direkt zugegriffen werden. Dies ist zum Beispiel der Fall für Register wie ADC oder TCNT1.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
    /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
    foo = ADC; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei anderen Registern, wie zum Beispiel Baudraten-Register, liegen High- und Low-Teil nicht direkt nebeneinander im SFR-Bereich, so dass ein 16-Bit Zugriff nicht möglich ist und der Zugriff zusammengebastelt werden muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
   uint16_t baud = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
&lt;br /&gt;
   UBRRH = (uint8_t) (baud &amp;gt;&amp;gt; 8);&lt;br /&gt;
   UBRRL = (uint8_t) baud;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen wie ATmega8 oder ATmega16 teilen sich UBRRH und UCSRC die gleiche Speicher-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL), welches Register tatsächlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und übergibt den Wert &#039;&#039;3&#039;&#039;. Und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und übergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Speziell bei den 16-Bit-Timern und auch beim ADC ist es bei allen Zugriffen auf Datenregister erforderlich, dass diese Daten synchronisiert sind. Wenn z.&amp;amp;nbsp;B. bei einem 16-Bit-Timer das High-Byte des Zählregisters gelesen wurde und vor dem Lesezugriff auf das Low-Byte ein Überlauf des Low-Bytes stattfindet, erhält man einen völlig unsinnigen Wert. Auch die Compare-Register müssen synchron geschrieben werden, da es ansonsten zu unerwünschten Compare-Ereignissen kommen kann. &lt;br /&gt;
&lt;br /&gt;
Beim ADC besteht das Problem darin, dass zwischen den Zugriffen auf die beiden Teilregister eine Wandlung beendet werden kann und der ADC ein neues Ergebnis in ADCL und ADCH schreiben will, wodurch High- und Low-Byte nicht zusammenpassen.&lt;br /&gt;
&lt;br /&gt;
Um diese Datenmüllproduktion zu verhindern, gibt es in beiden Fällen eine Synchronisation, die jeweils durch den Zugriff auf das Low-Byte ausgelöst wird:&lt;br /&gt;
* Bei den Timer-Registern (das gilt für alle TCNT-, OCR- und ICR-Register bei den 16-Bit-Timern) wird bei einem &#039;&#039;Lesezugriff&#039;&#039; auf das Low-Byte automatisch das High-Byte in ein temporäres Register, das ansonsten nach außen nicht sichtbar ist, geschoben. Greift man nun &#039;&#039;anschließend&#039;&#039; auf das High-Byte zu, dann wird eben dieses temporäre Register gelesen.&lt;br /&gt;
* Bei einem &#039;&#039;Schreibzugriff&#039;&#039; auf eines der genannten Register wird das High-Byte in besagtem temporären Register zwischengespeichert und erst beim Schreiben des Low-Bytes werden &#039;&#039;beide&#039;&#039; gleichzeitig in das eigentliche Register übernommen.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet für die Reihenfolge:&lt;br /&gt;
* Lesezugriff: Erst Low-Byte, dann High-Byte&lt;br /&gt;
* Schreibzugriff: Erst High-Byte, dann Low-Byte&lt;br /&gt;
&lt;br /&gt;
Des weiteren ist zu beachten, dass es für all diese 16-Bit-Register nur ein einziges temporäres Register gibt, so dass das Auftreten eines Interrupts, in dessen Handler ein solches Register manipuliert wird, bei einem durch ihn unterbrochenen Zugriff i.d.R. zu Datenmüll führt. 16-Bit-Zugriffe sind generell nicht atomar! Wenn mit Interrupts gearbeitet wird, kann es erforderlich sein, vor einem solchen Zugriff auf ein 16-Bit-Register die Interrupt-Bearbeitung zu deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Beim ADC-Datenregister ADCH/ADCL ist die Synchronisierung anders gelöst. Hier wird beim Lesezugriff (ADCH/ADCL sind logischerweise read-only) auf das Low-Byte ADCL beide Teilregister für Zugriffe seitens des ADC so lange gesperrt, bis das High-Byte ADCH ausgelesen wurde. Dadurch kann der ADC nach einem Zugriff auf ADCL keinen neuen Wert in ADCH/ADCL ablegen, bis ADCH gelesen wurde. Ergebnisse von Wandlungen, die zwischen einem Zugriff auf ADCL und ADCH beendet werden, gehen verloren!&lt;br /&gt;
&lt;br /&gt;
Nach einem Zugriff auf ADCL muss grundsätzlich ADCH gelesen werden!&lt;br /&gt;
&lt;br /&gt;
In beiden Fällen – also sowohl bei den Timern als auch beim ADC – werden vom C-Compiler 16-Bit Pseudo-Register zur Verfügung gestellt (z.&amp;amp;nbsp;B. TCNT1H/TCNT1L → TCNT1, ADCH/ADCL → ADC bzw. ADCW), bei deren Verwendung der Compiler automatisch die richtige Zugriffsreihenfolge regelt. In C-Programmen sollten grundsätzlich diese 16-Bit-Register verwendet werden! Sollte trotzdem ein Zugriff auf ein Teilregister erforderlich sein, sind obige Angaben zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass auch ein Zugriff auf die 16-Bit-Register vom Compiler in zwei 8-Bit-Zugriffe aufgeteilt wird und dementsprechend genauso nicht-atomar ist wie die Einzelzugriffe. Auch hier gilt, dass u.U. die Interrupt-Bearbeitung gesperrt werden muss, um Datenmüll zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Beim ADC gibt es für den Fall, dass eine Auflösung von 8 Bit ausreicht, die Möglichkeit, das Ergebnis &amp;quot;linksbündig&amp;quot; in ADCH/ADCL auszurichten, so dass die relevanten 8 MSB in ADCH stehen. In diesem Fall muss bzw. sollte nur ADCH ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
ADC und ADCW sind unterschiedliche Bezeichner für das selbe Registerpaar. Üblicherweise kann man in C-Programmen ADC verwenden, was analog zu den anderen 16-Bit-Registern benannt ist. ADCW (ADC Word) existiert nur deshalb, weil die Headerdateien auch für Assembler vorgesehen sind und es bereits einen Assembler-Befehl namens &#039;&#039;adc&#039;&#039; gibt. &lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t key_pressed (volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if (last_state == (*inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit)))&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t i = key_pressed (&amp;amp;PINB, PB1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass avr-libc FAQ: &amp;quot;How do I pass an IO port as a parameter to a function?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf IO-Ports =&lt;br /&gt;
&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output). Diese Register dienen dazu:&lt;br /&gt;
* einzustellen welche der Anschlüsse (&amp;quot;Beinchen&amp;quot;) des Controllers als Ein- oder Ausgänge dienen&lt;br /&gt;
* bei Ausgängen deren Zustand festzulegen&lt;br /&gt;
* bei Eingängen deren Zustand zu erfassen&lt;br /&gt;
&lt;br /&gt;
Mittels GPIO werden digitale Zustände gesetzt und erfasst, d.h. die Spannung an einem Ausgang wird ein- oder ausgeschaltet und an einem Eingang wird erfasst, ob die anliegende Spannung über oder unter einem bestimmten Schwellwert liegt. Im Datenblatt Abschnitt Electrical Characteristics/DC Characteristics finden sich die Spannungswerte (V_OL, V_OH für Ausgänge, V_IL, V_IH für Eingänge).&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
Die physischen Ein- und Ausgänge werden bei AVR-Controllern zu logischen Ports gruppiert.&lt;br /&gt;
&lt;br /&gt;
Alle Ports werden über Register gesteuert. Dazu sind jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! DDRx&lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
! PINx&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
! PORTx&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt. Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
  // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
  // direkte Zuweisung - standardkonform */&lt;br /&gt;
  DDRB = 0x1F;    /* &lt;br /&gt;
&lt;br /&gt;
  // übersichtliche Alternative - Binärschreibweise, aber kein ISO-C&lt;br /&gt;
  DDRB = 0b00011111;&lt;br /&gt;
&lt;br /&gt;
  // Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
  // aber übersichtlicher und selbsterklärend:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet. Weitere Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
  DDRB = 0xff; &lt;br /&gt;
  // Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~(1 &amp;lt;&amp;lt; DDB0);&lt;br /&gt;
  // Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4));&lt;br /&gt;
  // Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB3);&lt;br /&gt;
  // Alle Pins auf Eingang:&lt;br /&gt;
  DDRB = 0x00;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.&amp;amp;nbsp;B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&lt;br /&gt;
    // übersichtliche Alternative - Binärschreibweise&lt;br /&gt;
    PORTB = 0b00000100;    /* direkte Zuweisung - übersichtlich */&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&lt;br /&gt;
* zuerst die Bits im PORTx-Register setzen&lt;br /&gt;
* anschließend die Datenrichtung auf Ausgang stellen&lt;br /&gt;
&lt;br /&gt;
Daraus ergibt sich die Abfolge für einen Pin, der bisher als Eingang mit abgeschaltetem Pull-Up konfiguriert war:&lt;br /&gt;
* setze PORTx: interner Pull-Up aktiv&lt;br /&gt;
* setze DDRx: Ausgang (&amp;quot;high&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Bei der Reihenfolge erst DDRx und dann PORTx kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.&amp;amp;nbsp;B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Spannungstabelle&#039;&#039;&#039; &amp;lt;br /&amp;gt; &amp;lt;small&amp;gt;(ca. Grenzwerte)&amp;lt;/small&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
! Low || High&lt;br /&gt;
|-&lt;br /&gt;
! bei 5 V&lt;br /&gt;
| 1 V || 3,5 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 3,3 V&lt;br /&gt;
| 0,66 V || 2,31 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 1,8 V&lt;br /&gt;
| 0,36 V || 1,26 V&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
{{Warnung|Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;nicht&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.}}&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation#Bits_prüfen]]&lt;br /&gt;
&lt;br /&gt;
=== Interne Pull-Up Widerstände ===&lt;br /&gt;
&lt;br /&gt;
Portpins für Ein- und Ausgänge (GPIO) eines AVR verfügen über zuschaltbare interne Pull-Up Widerstände (nominal mehrere 10kOhm, z.&amp;amp;nbsp;B. ATmega16 20-50kOhm). Diese können in vielen Fällen statt externer Widerstände genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;PC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Taster und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller, ist zwischen zwei unterschiedliche Methoden zu unterscheiden: &#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300&amp;quot; heights=&amp;quot;300&amp;quot; caption=&amp;quot;Anschluss mechanischer Kontakte an einen µC&amp;quot;&amp;gt;&lt;br /&gt;
Image:Active Low.gif|&#039;&#039;&#039;Active Low:&#039;&#039;&#039; Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet. Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt, wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter &#039;&#039;&#039;Pull-Up&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
Image:Active High.gif|&#039;&#039;&#039;Active High:&#039;&#039;&#039; Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet. Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein &#039;&#039;&#039;Pull-Down&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten. &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert von Pull-Up- und Pull-Down-Widerständen ist an sich nicht kritisch. Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert. Die AVRs verfügen an den meisten Pins über zuschaltbare interne Pull-Up Widerstände (vgl. Abschnitt [[AVR-GCC-Tutorial#Interne Pull-Up Widerstände|Interne Pull-Up Widerstände]]), welche insbesondere wie hier bei Tastern und ähnlichen Bauteilen (z.&amp;amp;nbsp;B. Drehgebern) statt externer Bauteile verwendet werden können. Interne Pull-Down-Widerstand sind nicht verfügbar und müssen daher in Form zusätzlicher Bauteile in die Schaltung eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
==== Taster entprellen ====&lt;br /&gt;
&lt;br /&gt;
Siehe: &#039;&#039;[[Entprellung#Warteschleifen-Verfahren|Entprellung: Warteschleifen-Verfahren]]&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Außerdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr (abgesehen von möglicherweise auftretenden Interrupts, falls welche aktiviert sind). Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.&amp;amp;nbsp;B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.&amp;amp;nbsp;B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt, und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Einschränkung liegt darin, daß sie möglicherweise länger warten, als erwartet, nämlich in dem Fall, daß Interrupts auftreten und die _delay...()-Funktion unterbrechen. Genau genommen warten diese nämlich nicht eine bestimmte Zeit, sondern verbrauchen eine bestimmte Anzahl von Prozessortakten. Die wiederum ist so bemessen, daß ohne Unterbrechung durch Interrupts die gewünschte Wartezeit erreicht wird.&lt;br /&gt;
Wird das Warten aber durch eine oder mehrere ISR unterbrochen, die zusammen 1% Prozessorzeit verbrauchen, dann dauert das Warten etwa 1% länger. Bei 50% Last durch die ISR dauert das Warten doppelt solange wie gewünscht, bei 90% zehnmal solange...&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen bis 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4µs warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.&amp;amp;nbsp;B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms)&lt;br /&gt;
{&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 )                  // Endlosschleife&lt;br /&gt;
    {                &lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.&amp;amp;nbsp;B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.7 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Es ist nicht möglich, eine Variable als Argument zu übergeben. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Trick zur Übergabe einer Variablen an _delay_ms():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void sleep ( uint8_t ms )&lt;br /&gt;
{&lt;br /&gt;
    for(; ms &amp;gt; 0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    int x = 0;                  // Variable als Wartezeit erstellen&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        sleep(x); &lt;br /&gt;
        x++;       &lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die _delay_ms() und die _delay_us aus &#039;&#039;&#039;avr-libc 1.7.0&#039;&#039;&#039; sind fehlerhaft. _delay_ms () läuft 4x schneller als erwartet. Abhilfe ist eine korrigierte Includedatei: [http://www.mikrocontroller.net/topic/196738#1943039]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:2em;&amp;quot;&amp;gt;&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Tritt ein Interrupt auf, unterbricht (engl. interrupts) der Controller die Verarbeitung des Hauptprogramms und verzweigt zu einer Interruptroutine. Das Hauptprogramm wird also beim Eintreffen eines Interrupts unterbrochen, die Interruptroutine ausgeführt und danach erst wieder das Hauptprogramm an der Unterbrechungsstelle fortgesetzt (vgl. die Abbildung).&lt;br /&gt;
&lt;br /&gt;
Um Interrupts verarbeiten zu können, ist folgendes zu beachten:&lt;br /&gt;
&lt;br /&gt;
* Für jede aktivierte Interruptquelle ist eine Funktion zu programmieren, in der die beim Auftreten des jeweiligen Interrupts erforderlichen Verarbeitungsschritte enthalten sind. Für diese Funktion existieren verschiedene Bezeichnungen. Üblich sind die englischen Begriffe Interrupt-Handler oder Interrupt-Service-Routinen (ISR), man findet aber auch die Bezeichnungen Interruptverarbeitungs- oder -behandlungsroutine oder auch kurz Interruptroutine. Zum Beispiel wird üblicherweise in der ISR zur Verarbeitung des Empfangsinterrupts eines UARTs (UART-RX Interrupt) das empfangene Zeichen in einen Zwischenspeicher (FIFO-Buffer) kopiert, dessen Inhalt später von anderen Programmteilen geleert wird. Sofern der Zwischenspeicher ausreichend groß ist, geht also kein Zeichen verloren, auch wenn im Hauptprogramm zeitintensive Operationen durchgeführt werden.&lt;br /&gt;
* Die benötigten Interrupts sind in den jeweiligen Funktionsbausteinen einzuschalten. Dies erfolgt über das jeweilige Aktivierungsbit (Interrupt Enable) in einem der Hardwareregister (z.B. RX(Complete)Interrupt Enable eines UARTs)&lt;br /&gt;
* Sämtliche Interrupts werden über einen weiteren globalen Schalter aktiviert und deaktiviert. Zur Verarbeitung der Interrupts ist dieser Schalter zu aktivieren (sei(), siehe unten).&lt;br /&gt;
 &lt;br /&gt;
Alle Punkte sind zu beachten. Fehlt z.B. die globale Aktivierung, werden Interruptroutinen auch dann nicht aufgerufen, wenn sie im Funktionsbaustein eingeschaltet sind und eine Behandlungsroutine verhanden ist.&lt;br /&gt;
&lt;br /&gt;
Siehe auch&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
* Artikel [[Interrupt]]&lt;br /&gt;
* Artikel [[Multitasking]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen sollten möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Microcontroller-Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammenhängen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039; Register.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit &lt;br /&gt;
| 7 || 6|| 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039; || &#039;&#039;&#039;INT0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039; || &#039;&#039;&#039;INTF0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;SE&#039;&#039;&#039;|| &#039;&#039;&#039;SM&#039;&#039;&#039;|| &#039;&#039;&#039;ISC11&#039;&#039;&#039;|| &#039;&#039;&#039;ISC10&#039;&#039;&#039;|| &#039;&#039;&#039;ISC01&#039;&#039;&#039;|| &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R || R || R/W || R/W || R/W || R/W || R/W || R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt über den Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC11 || ISC10 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC01 || ISC00 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Dieses wird automatisch wieder gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.  Es ist möglich das GIE-Bit in der ISR zu setzen und so schon wieder weitere Interrupts zuzulassen - allerdings sollte man damit vorsichtig sein und genau wissen was man damit macht. Kritisch wird es vor allem wenn der gleiche Interrupt noch einmal kommt, bevor die ISR abgearbeitet ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit avr-gcc ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;Anmerkung eines Nutzers: Ich habe mir das Thema hier angearbeitet und hatte am Anfang starke Probleme: Jeder Interrupt muss nochmals einzeln aktiviert werden. Es reicht nicht nur per &#039;&#039;sei()&#039;&#039; die Interrupts global zu aktiveren.&#039;&#039; - mthomas: Hoffentlich duch die modifizerte Einleitung etwas offensichtlicher erläutert. Ansonsten bitte per Eintrag auf die Diskussionseite nochmals melden) --&amp;gt; &lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrupts aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Zu den aktivierten Interrupts ist eine Funktion zu programmieren, deren Code aufgerufen wird, wenn der betreffende Interrupt auftritt (Interrupt-Handler, Interrupt-Service-Routine). Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe den [[AVR-GCC-Tutorial#Anhang|Anhang]].)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h  - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR(XXX,ISR_NOBLOCK) /* veraltet: INTERRUPT(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt-Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.&amp;amp;nbsp;B. &#039;&#039;TIMER0_OVF_vect&#039;&#039;). Der Unterschied im Vergleich zu einer herkömmlichen ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit durch Einfügen einer SEI-Anweisung direkt wieder gesetzt und somit alle Interrupts zugelassen werden &amp;amp;ndash; auch XXX-Interrupts. &lt;br /&gt;
&lt;br /&gt;
Bei unsachgemässer Handhabung kann dies zu erheblichen Problemen durch Rekursion wie einem Stack-Overflow oder anderen unerwarteten Effekten führen und sollte wirklich nur dann eingesetzt werden, wenn man sich sicher ist, das Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
Insbesondere sollte möglichst am ISR-Anfang die auslösende IRQ-Quelle deaktiviert und erst am Ende der ISR wieder aktiviert werden. Robuster als die Verwendung einer NOBLOCK-ISR ist daher folgender ISR-Aufbau:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR (XXX) &lt;br /&gt;
{&lt;br /&gt;
    // Implementiere die ISR ohne zunaechst weitere IRQs zuzulassen&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Deaktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Erlaube alle Interrupts (ausser XXX)&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    //... Code ...&lt;br /&gt;
&lt;br /&gt;
    // IRQs global deaktivieren um die XXX-IRQ wieder gefahrlos &lt;br /&gt;
    // aktivieren zu koennen&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Aktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Weise kann sich die XXX-IRQ nicht selbst unterbrechen, was zu einer Art Endlosschleife führen würde.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen, die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)) als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.&amp;amp;nbsp;B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird innerhalb einer ISR mehrfach auf eine mit volatile deklarierte Variable zugegriffen, wirkt sich dies ungünstig auf die Verarbeitungsgeschwindigkeit aus, da bei jedem Zugriff mit dem Speicherinhalt abgeglichen wird. Da bei AVR-Controllern &#039;&#039;innerhalb&#039;&#039; einer ISR keine Unterbrechungen zu erwarten sind, bietet es sich an, einen Zwischenspeicher in Form einer lokalen Variable zu verwenden, deren Inhalt zu Beginn und am Ende mit dem der volatile Variable synchronisiert wird. Lokale Variable werden bei eingeschalteter Optimierung mit hoher Wahrscheinlichkeit in Prozessorregistern verwaltet und der Zugriff darauf ist daher nur mit wenigen internen Operationen verbunden. Die ISR aus dem vorherigen Beispiel lässt sich so optimieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
&lt;br /&gt;
   tmp_kc = gKeyCounter; // Uebernahme in lokale Arbeitsvariable&lt;br /&gt;
&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   gKeyCounter = tmp_kc; // Zurueckschreiben&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich die Disassemblies (Ausschnitte der &amp;quot;lss-Dateien&amp;quot;, compiliert für ATmega162) im Anschluss. Man erkennt den viermaligen Zugriff auf die Speicheraddresse von &#039;&#039;gKeyCounter&#039;&#039; (hier 0x032A) in der ISR ohne &amp;quot;Cache&amp;quot;-Variable und den zweimaligen Zugriff in der Variante mit Zwischenspeicher. Im Beispiel ist der Vorteil gering, bei komplexeren Routinen kann die Zwischenspeicherung in lokalen Variablen jedoch zu deutlicheren Verbesserungen führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
    if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     876:	ca 99       	sbic	0x19, 2	; 25&lt;br /&gt;
     878:	0a c0       	rjmp	.+20     	; 0x88e &amp;lt;__vector_13+0x24&amp;gt;&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
     87a:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     87e:	88 3c       	cpi	r24, 0xC8	; 200 &lt;br /&gt;
     880:	40 f4       	brcc	.+16     	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
     882:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	02 c0       	rjmp	.+4      	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
     88e:	10 92 2a 03 	sts	0x032A, r1&lt;br /&gt;
     892:	8f 91       	pop	r24&lt;br /&gt;
     894:	0f 90       	pop	r0&lt;br /&gt;
     896:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     898:	0f 90       	pop	r0&lt;br /&gt;
     89a:	1f 90       	pop	r1&lt;br /&gt;
     89c:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
 &lt;br /&gt;
   tmp_kc = gKeyCounter;&lt;br /&gt;
     876:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
 &lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     87a:	ca 9b       	sbis	0x19, 2	; 25&lt;br /&gt;
     87c:	02 c0       	rjmp	.+4      	; 0x882 &amp;lt;__vector_13+0x18&amp;gt;&lt;br /&gt;
     87e:	80 e0       	ldi	r24, 0x00	; 0&lt;br /&gt;
     880:	03 c0       	rjmp	.+6      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
     882:	88 3c       	cpi	r24, 0xC8	; 200&lt;br /&gt;
     884:	08 f4       	brcc	.+2      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   gKeyCounter = tmp_kc;&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	8f 91       	pop	r24&lt;br /&gt;
     88e:	0f 90       	pop	r0&lt;br /&gt;
     890:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     892:	0f 90       	pop	r0&lt;br /&gt;
     894:	1f 90       	pop	r1&lt;br /&gt;
     896:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== volatile und Pointer ===&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;volatile&#039;&#039;&#039; in Verbindung mit Pointern ist zu beachten, ob der Pointer selbst oder die Variable, auf die der Pointer zeigt, &#039;&#039;&#039;volatile&#039;&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t *a;   // das Ziel von a ist volatile&lt;br /&gt;
&lt;br /&gt;
uint8_t *volatile a;   // a selbst ist volatile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls der Pointer volatile ist (zweiter Fall im Beispiel), ist zu beachten, dass der Wert des Pointers, also eine Speicheradresse, intern in mehr als einem Byte verwaltet wird. Lese- und Schreibzugriffe im Hauptprogramm (außerhalb von Interrupt-Routinen) sind daher so zu implementieren, dass alle Teilbytes der Adresse konsistent bleiben, vgl. dazu den folgenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interrupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
&lt;br /&gt;
   // oder: mehrfach lesen, bis man konsistente Daten hat&lt;br /&gt;
   uint16_t count1 = gMyCounter16Bit;&lt;br /&gt;
   uint16_t count2 = gMyCounter16Bit;&lt;br /&gt;
   while (count1 != count2) {&lt;br /&gt;
       count1 = count2;&lt;br /&gt;
       count2 = gMyCounter16Bit;&lt;br /&gt;
   }&lt;br /&gt;
   tmpCnt = count1;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die avr-libc bietet ab Version 1.6.0(?) einige Hilfsfunktionen/Makros, mit der im Beispiel oben gezeigten Funktionalität, die zusätzlich auch sogenannte [http://en.wikipedia.org/wiki/Memory_barrier memory barriers] beinhalten. Diese stehen nach #include &amp;lt;util/atomic.h&amp;gt; zur Verfügung.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // analog zu cli, Zugriff, sei:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_FORCEON) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
// oder:&lt;br /&gt;
&lt;br /&gt;
    // analog zu Sicherung des SREG, cli, Zugriff und Zurückschreiben des SREG:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch [http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html Dokumentation der avr-libc zu atomic.h]&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.&amp;amp;nbsp;B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0 auf &#039;&#039;&#039;1&#039;&#039;&#039;, PORTA ist danach 0b0000000&#039;&#039;&#039;1&#039;&#039;&#039;. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-&#039;&#039;&#039;ODER&#039;&#039;&#039;-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8. (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Interruptflags löschen ==&lt;br /&gt;
&lt;br /&gt;
Beim Löschen von Interruptflags haben AVRs eine Besonderheit, die auch im Datenblatt beschrieben ist: Es wird zum Löschen eine 1 in das betreffende Bit geschrieben. &lt;br /&gt;
&lt;br /&gt;
Hinweis:&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei Registern mit mehreren Interrupt-Flag-Bits (wie die Timer Interrupt Flag Register)  &#039;&#039;&#039;nicht&#039;&#039;&#039;  die übliche bitweise VerODERung nehmen, sondern eine direkte Zuweisung machen. Da sonst weitere Flags, als nur das gewünschte, ebenfalls gelöscht werden könnten.&amp;lt;br /&amp;gt;&lt;br /&gt;
([http://www.mikrocontroller.net/topic/171148#1640133 Erklärung]).&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten [[Sleep Mode |&#039;&#039;Sleep-Modes&#039;&#039;]] (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
;set_sleep_mode (uint8_t mode): Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.&amp;amp;nbsp;B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
;sleep_enable(): Aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
;sleep_cpu(): Versetzt den Controller in den Schlafmodus .sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt.&lt;br /&gt;
;sleep_disable(): Deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
;sleep_mode(): Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts (besser nicht benutzen).&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sleep Modi ==&lt;br /&gt;
Zu beachten ist, dass unterschiedliche Prozessoren aus der AVR Familie unterschiedliche Sleep-Modi unterstützen oder nicht unterstützen. Auskunft über die tatsächlichen Gegebenheiten gibt, wie immer, das zum Prozessor gehörende Datenblatt. Die unterschiedlichen Modi unterscheiden sich dadurch, welche Bereiche des Prozessors abgeschaltet werden. Damit korrespondiert unmittelbar welche Möglichkeiten es gibt, den Prozessor aus den jeweiligen Sleep Modus wieder aufzuwecken.&lt;br /&gt;
&lt;br /&gt;
;Idle Mode (SLEEP_MODE_IDLE): Die CPU kann durch SPI, USART, Analog Comperator, ADC, TWI, Timer, Watchdog und irgendeinen anderen Interrupt wieder aufgeweckt werden.&lt;br /&gt;
&lt;br /&gt;
;ADC Noise Reduction Mode (SLEEP_MODE_ADC): In diesem Modus liegt das Hauptaugenmerk darauf, die CPU soweit stillzulegen, dass der ADC möglichst keine Störungen aus dem inneren der CPU auffangen kann. Aufwachen aus diesem Modus kann ausgelöst werden durch den ADC, externe Interrupts, TWI, Timer und Watchdog.&lt;br /&gt;
&lt;br /&gt;
;Power-Down Mode (SLEEP_MODE_PWR_DOWN): In diesem Modus wird ein externer Oszillator (Quarz, Quarzoszillator) gestoppt. Geweckt werden kann die CPU durch einen externen Level Interrupt, TWI, Watchdog, Brown-Out-Reset&lt;br /&gt;
&lt;br /&gt;
;Power-Save-Mode (SLEEP_MODE_PWR_SAVE): Power-Save ist identisch zu Power-Down mit einer Ausnahme: Ist der Timer 2 auf die Verwendung eines externen Taktes konfiguriert, so läuft dieser Timer auch im Power-Save weiter und kann die CPU mit einem Interrupt aufwecken.&lt;br /&gt;
&lt;br /&gt;
;Standby-Mode (SLEEP_MODE_STANDBY, SLEEP_MODE_EXT_STANDBY): Voraussetzung für den Standby-Modus ist die Verwendung eines Quarzes oder eines Quarzoszillators (also einer externen Taktquelle). Ansonsten ist dieser Modus identisch zum Power-Down Modus. Vorteil dieses Modus ist eine kürzere Aufwachzeit.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Zeiger =&lt;br /&gt;
Zeiger (engl. &#039;&#039;Pointer&#039;&#039;) sind Variablen, die die Adresse von Daten oder Funktionen enthalten und belegen 16 Bits. Die Größe hängt mit dem adressierbaren Speicherbereich zusammen und der GCC reserviert dann den entsprechenden Platz.&lt;br /&gt;
Ggf. ist es also günstiger, Indizes auf Arrays (Listen) zu verwenden, so dass der GCC für die Zeigerarithmetik den erforderlichen RAM nur temporär benötigt.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[Zeiger]]&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.&amp;amp;nbsp;B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Binäre Daten zum Programm hinzufügen]]&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;alloziert&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Flash mit PROGMEM und pgm_read ==&lt;br /&gt;
&lt;br /&gt;
→ [http://nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html avr-libc: Doku zu avr/pgmspace.h]&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc erst ab Version 4.7 &amp;quot;transparent&amp;quot; möglich. Um Daten aus dem Flash zu lesen, muss die AVR-Instruktion LPM (&#039;&#039;Load from Program Memory&#039;&#039;) erzeugt werden, bei Controllern mit mehr als 64kiB Flash auch ELMP.&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es das AVR-spezifische GCC-Attribut &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt;, mit dem eine Variablendeklaration im &#039;&#039;static storage&#039;&#039;&amp;lt;ref&amp;gt;Variablen der Speicherklasse &#039;&#039;static storage&#039;&#039; haben eine unbegrenzte Lebensdauer.  Beispiel für solche Variablen sind globale Variablen, aber auch static-Variablen innerhalb einer Funktion gehören dazu.  Beispiele für Variablen, die nicht &#039;&#039;static storage&#039;&#039; sind: auto-Variablen (&amp;quot;normale&amp;quot; lokale Variablen), register-Variablen, durch malloc geschaffene Objekte, etc.&amp;lt;/ref&amp;gt; markiert werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const int value __attribute__((progmem)) = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effekt ist, dass die so markierte Variable nicht im RAM sondern im Flash angelegt wird.  Wird durch &amp;quot;normalen&amp;quot; C-Code auf solch eine Variable zugegriffen, wird jedoch aus dem RAM gelesen und nicht aus dem Flash!&lt;br /&gt;
&lt;br /&gt;
Zum Lesen aus dem Flash stellt die avr-libc daher zahlreiche Makros zur Verfügung.  Zudem wird das Makro &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; definiert, das etwas Tipparbeit spart:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const int value PROGMEM = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; funktioniert im Wesentlichen wie ein Section-Attribut, das die Daten in der Section &amp;lt;tt&amp;gt;.progmem.data&amp;lt;/tt&amp;gt; ablegt.  Im Gegensatz zum Section-Attribut werden jedoch noch weitere Prüfungen unternommen, ab avr-gcc 4.6 etwa muss die entsprechende Variable &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; sein.&lt;br /&gt;
&lt;br /&gt;
=== Integer und float ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen von Skalaren stellt die avr-libc folgende Makros zu Verfügung, die jeweils ein Argument erhalten: Die 16-Bit Adresse des zu lesenden Wertes&amp;lt;ref&amp;gt;Damit ist der mögliche Speicherbereich für Flash-Konstanten auf 64kiB begrenzt. Einige pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher, intern via Assembler-Anweisung ELPM. Die Initialisierungswerte des Speicherinhalts jenseits der 64kiB-Marke müssen dann jedoch auf anderem Weg angelegt werden, d.h. nicht per PROGMEM. Evtl. eigene Section und Linker-Optionen. Alt und nicht ganz korrekt: Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kiB Flash bei Controllern mit mehr als 64kiB.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| {{Tabelle}}&lt;br /&gt;
|+ Übersicht der &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt; Funktionen aus&amp;lt;br/&amp;gt;dem Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; der avr-libc&lt;br /&gt;
|-&lt;br /&gt;
! Gelesener Wert || &amp;lt;tt&amp;gt;pgm_read_xxx&amp;lt;/tt&amp;gt; || Anzahl Bytes&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint8_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_byte&amp;lt;/tt&amp;gt; || 1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint16_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; || 2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint32_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_dword&amp;lt;/tt&amp;gt; || 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;float&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_float&amp;lt;/tt&amp;gt;&amp;lt;ref&amp;gt;ab avr-libc 1.7.0&amp;lt;/ref&amp;gt; || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Soll ein Zeiger gelesen werden, so verwendet man &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; und castet das Ergebnis zum gewünschten Zeiger-Typ.&lt;br /&gt;
&lt;br /&gt;
;Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t aByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* int-Array */&lt;br /&gt;
const int anArray[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
  /* Zeiger */&lt;br /&gt;
  static const uint8_t* const aPointer PROGMEM = &amp;amp;aByte;&lt;br /&gt;
&lt;br /&gt;
  uint8_t a        = pgm_read_byte (&amp;amp;aByte);&lt;br /&gt;
  int a2           = (int) pgm_read_word (&amp;amp;anArray[2]);&lt;br /&gt;
  const uint8_t* p = (const uint8_t*) pgm_read_word (&amp;amp;aPointer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
In den Flash-Funktionen der avr-libc sind keine der pgm_read_xxxx Nomenklatur folgenden Funktionen, die Speicherblöcke auslesen oder vergleichen. Die enstprechende Funktionen sind Varianten von &amp;lt;tt&amp;gt;memcpy&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp&amp;lt;/tt&amp;gt; und heißt &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, usw.  Für weitere Funktionen und deren Prototypen siehe die Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Strings sind in C nichts anderes als eine Abfolge von Zeichen und einem &amp;lt;tt&amp;gt;&#039;\0&#039;&amp;lt;/tt&amp;gt; als Stringende. Der prinzipielle Weg ist daher identisch zum  Lesen von Bytes, wobei auf die [[FAQ#Wie funktioniert String-Verarbeitung in C?|Besonderheiten von Strings]] wie 0-Terminierung geachtet werden muss.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
size_t my_string_length (const char* addr)&lt;br /&gt;
{&lt;br /&gt;
    size_t length = 0;&lt;br /&gt;
&lt;br /&gt;
    while (pgm_read_byte (addr++))&lt;br /&gt;
    {&lt;br /&gt;
        len++;&lt;br /&gt;
    }&lt;br /&gt;
    return length;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Unterstützung des Programmierers steht das Repertoire der str-Funktionen auch in jeweils eine Variante zur Verfügung, die mit dem Flash-Speicher arbeiten kann. Die Funktionsnamen tragen den Suffix &amp;lt;tt&amp;gt;_P&amp;lt;/tt&amp;gt;. Darüber hinaus gibt es das Makro &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt;, das ein String-Literal im Flash-Speicher ablegt und die Adresse des Strings liefert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Liefert true zurück, wenn string_im_ram gleich &amp;quot;Hallo Welt&amp;quot; ist. */&lt;br /&gt;
int foo (const char* string_im_ram)&lt;br /&gt;
{&lt;br /&gt;
    return strcmp_P (string_im_ram, PSTR (&amp;quot;Hallo Welt&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; nur innerhalb von Funktionen verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
; Array aus Strings:&lt;br /&gt;
&lt;br /&gt;
Arrays aus Strings im Flash-Speicher werden in zwei Schritten angelegt:&lt;br /&gt;
&lt;br /&gt;
# Zuerst die einzelnen Elemente des Arrays und&lt;br /&gt;
# im Anschluss ein Array, in dem die Startaddressen der Strings abgelegt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen wird zuerst die Adresse des gewünschten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, um auf das Element (den String) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const char str1[] PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char str2[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char str3[] PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const array[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   str1, str2, str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Lese die Adresse des i-ten Strings aus array[]&lt;br /&gt;
    const char *parray = (const char*) pgm_read_word (&amp;amp;array[i]);&lt;br /&gt;
&lt;br /&gt;
    // Kopiere den Inhalt der Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, parray);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist, die Strings in einem 2-dimensionalen char-Array abzulegen anstatt deren Adresse in einem 1-dimensionalen Adress-Array zu speichern.&lt;br /&gt;
&lt;br /&gt;
Vorteil ist, dass der Code einfacher wird.  Nachteil ist, dass bei unterschiedlich langen Strings Speicherplatz verschwendet wird, weil sich die Array-Dimension and der Länge des längsten Strings orientieret.  Bei in etwa gleich langen Strings kann es aber sogar Speicherplatz sparen, denn es die Adressen der einzelnen Strings müssen nicht abgespeichert werden.&amp;lt;ref&amp;gt;In unserem Hund-Katze-Maus Beispiel belegt die erste Variante 22 Bytes Daten und 18 Bytes Code, die zweite Variante mit 2-dimensionalem Array belegt 18 Bytes Daten und 20 Bytes Code. Gemessen wurde mit avr-gcc 4.8 -Os für ATmega8.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Die &amp;quot;6&amp;quot; ist 1 plus die Länge des längsten Strings (&amp;quot;Katze&amp;quot;)&lt;br /&gt;
const char array[][6] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere den Inhalt der i-ten Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, array[i]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_array How do I put an array of strings completely in ROM?]&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so kompliziert ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm (Flash) und Datenspeicher (RAM) auf. Der C-Standard sieht keine unterschiedlichen Adressräume vor.&lt;br /&gt;
&lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart (const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette, dann weiß die Funktion nicht, ob die Adresse in den Flash-Speicher oder das RAM zeigt. Weder aus dem Pointer-Wert, also dem Zahlenwert, noch aus dem &amp;quot;const&amp;quot; kann auf den Ort der Ablage geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler bilden die Harvard-Architektur ab, indem sie in einen Pointer nicht nur die Adresse speichern, sondern auch den Ablageort wie &#039;&#039;Flash&#039;&#039; oder &#039;&#039;RAM&#039;&#039;. In einem Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben.&lt;br /&gt;
&lt;br /&gt;
Dies hat jedoch auch Nachteile, denn bei jedem Zugriff über einen Zeiger muss zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden, wie der Zugriff auszuführen ist und entsprechend länglicher und langsamer wird der erzeugte Code.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
=== Variablenzugriff &amp;gt;64kB ===&lt;br /&gt;
&lt;br /&gt;
Die Zeiger beim avr-gcc sind nur 16 Bit breit, können somit also nur 64kiB Datenspeicher adressieren. Als Funktionspointer können sie beim AVR bis zu 128 kiB Programmspeicher adressieren, weil Funktionsadressen immer 16-Bit Worte adressieren und nicht Bytes. Um Flashzugriff jenseits von 64KiB zu bewerkstelligen gibt es mehrere Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Address-Spaces wie &amp;lt;tt&amp;gt;__flash1&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;, siehe Abschnitt &amp;quot;[[#Jenseits von flash|Jenseits von __flash]]&amp;quot;.&lt;br /&gt;
* Die Funktionen bzw. Makros &amp;lt;tt&amp;gt;pgm_read_xxx_far&amp;lt;/tt&amp;gt; der AVR-Libc, wie im folgenden beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unverständlicherweise gibt es in der AVR-Libc keine Funktion, um 32-Bit Pointer zu erhalten. Hier schafft ein eigenes GNU-C99 Makro Abhilfe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Macro to access strings defined in PROGMEM above 64kB&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR(var)                     \&lt;br /&gt;
({ uint_farptr_t tmp;                \&lt;br /&gt;
   __asm__ (                         \&lt;br /&gt;
       &amp;quot;ldi    %A0, lo8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %B0, hi8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %C0, hh8(%1)&amp;quot;         \&lt;br /&gt;
       : &amp;quot;=d&amp;quot; (tmp)                  \&lt;br /&gt;
       : &amp;quot;i&amp;quot;  (&amp;amp;(var)));             \&lt;br /&gt;
   tmp;                              \&lt;br /&gt;
})&lt;br /&gt;
//-------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//===================================================================&lt;br /&gt;
// Define a section above 64kiB (FAR_SECTION)&lt;br /&gt;
// and add the required linker argument below&lt;br /&gt;
// -Wl,--section-start=.far_section=0x10000&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR_SECTION   __attribute__((__section__(&amp;quot;.far_section&amp;quot;)))&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Just a Sample&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
const char MyString[] FAR_SECTION = &amp;quot;Hier liegt mein FAR-Teststring!&amp;quot;;&lt;br /&gt;
const char MyBmp64[]  FAR_SECTION = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  char MyChar;&lt;br /&gt;
  DDRC = 0xFF;&lt;br /&gt;
  do&lt;br /&gt;
  {&lt;br /&gt;
    MyChar = pgm_read_byte_far(FAR(MyBmp64));&lt;br /&gt;
    PORTC  = MyChar;&lt;br /&gt;
  }&lt;br /&gt;
  while(MyChar);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. man muss&lt;br /&gt;
* Das Makro &amp;lt;tt&amp;gt;FAR&amp;lt;/tt&amp;gt; im Quellcode einfügen&lt;br /&gt;
* Die Definition der neuen Section &amp;lt;tt&amp;gt;FAR_SECTION&amp;lt;/tt&amp;gt; einfügen&lt;br /&gt;
* Die Variablen mit dieser Section kennzeichnen&lt;br /&gt;
* Dem Linker mittels Kommandozeilenoption die Startadrese dieser Section mitteilen&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf diese Variablen kann nur mittels direkter Pointerarithmetik erfolgen, eine Indizierung von Arrays mit variablem Index ist nicht möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int n=3;&lt;br /&gt;
MyChar = pgm_read_byte_far(FAR(MyBmp64)+n);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flash mit __flash und Embedded-C ==&lt;br /&gt;
&lt;br /&gt;
Ab Version 4.7 unterstützt avr-gcc &#039;&#039;Adress-Spaces&#039;&#039; gemäß dem Embedded-C Dokument ISO/IEC TR18037.  Der geläufigste Adress-Space ist &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;, der im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; kein GCC-Attribut ist, sondern ein Qualifier und damit syntaktisch ähnlich verwendet wird wie &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;volatile&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GCC kennt keine eigene Option zum Aktivieren von Embedded-C, es wird als GNU-C Erweiterung behandelt. Daher müssen C-Module, die Address-Spaces verwenden, mit &amp;lt;tt&amp;gt;-std=gnu99&amp;lt;/tt&amp;gt; o.ä. compiliert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash int value = 10;&lt;br /&gt;
&lt;br /&gt;
int get_value (void)&lt;br /&gt;
{&lt;br /&gt;
  return value;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; sind keine speziellen Bibliotheksfunktionen oder -makros für den Zugriff mehr notwendig: Der Code zum Lesen der Variable ist &amp;quot;normales&amp;quot; C.&lt;br /&gt;
# Die Variable wird im richtigen Speicherbereich (Flash) angelegt.&lt;br /&gt;
# &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; ist nur zusammen mit read-only Objekten oder Zeigern, d.h. nur zusammen mit &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt;, erlaubt.&lt;br /&gt;
# Zugriffe wie im obigen Beispiel können (weg)optimiert werden.  Das Beispiel entspricht einem &amp;quot;&amp;lt;tt&amp;gt;return 10&amp;lt;/tt&amp;gt;&amp;quot;.  Es besteht keine Notwendigkeit, für &amp;lt;tt&amp;gt;value&amp;lt;/tt&amp;gt; überhaupt Flash-Speicher zu reservieren.&lt;br /&gt;
&lt;br /&gt;
Auch Zeiger-Indirektionen sind problemlos möglich.  Zu beachten ist, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; auf der richtigen Seite des &amp;quot;&amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;&amp;quot; in der Zeigerdeklaration bzw. -definition steht:&lt;br /&gt;
* &#039;&#039;&#039;Rechts vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger selbst liegt im Flash&lt;br /&gt;
* &#039;&#039;&#039;Links vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger enthält eine Flash-Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// val ist eine Variable im Flash&lt;br /&gt;
const __flash int val = 42;&lt;br /&gt;
&lt;br /&gt;
// pval liegt auch im Flash und enthält die Adresse von val&lt;br /&gt;
const __flash int* const __flash pval = &amp;amp;val;&lt;br /&gt;
&lt;br /&gt;
char get_val (void)&lt;br /&gt;
{&lt;br /&gt;
  // liest den Wert von val über die in pval abgelegte Adresse&lt;br /&gt;
  return *pval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
Um Speicherbereiche vom Flash in den RAM zu kopieren, gibt es zwei Möglichkeiten: Zum einen können wie bei &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; beschreiben die Funktionen der avr-libc wie &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;movmem_P&amp;lt;/tt&amp;gt;, etc. verwendet werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // buf wird auf dem Stack angelegt&lt;br /&gt;
    data_t buf;&lt;br /&gt;
    &lt;br /&gt;
    // Kopiere Daten vom Flash nach buf ins RAM&lt;br /&gt;
    memcpy_P (&amp;amp;buf, pdata, sizeof (data_t));&lt;br /&gt;
 &lt;br /&gt;
    // Sende die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum anderen kann eine Struktur auch über direktes Kopieren ins RAM geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere Daten ins RAM.  buf wird auf dem Stack angelegt&lt;br /&gt;
    const data_t buf = *pdata;&lt;br /&gt;
    &lt;br /&gt;
    // Verwendet die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Natürlich können auch Strings im Flash abgelegt werden und auch mit Funktionen wie &amp;lt;tt&amp;gt;strcpy_P&amp;lt;/tt&amp;gt; aus der avr-libc verarbeitet werden.  Zudem ist es möglich, Flash-Zeiger mit der Adresse eines String-Literals zu initialisieren:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define FSTR(X) ((const __flash char[]) { X } )&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    FSTR (&amp;quot;Hund&amp;quot;), FSTR (&amp;quot;Katze&amp;quot;), FSTR (&amp;quot;Maus&amp;quot;)&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
size_t get_len (uint8_t tier)&lt;br /&gt;
{&lt;br /&gt;
    return strlen_P (array[tier]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider sieht der Embedded-C Draft nicht vor, String-Literale direkt in einem anderen Adress-Space als &#039;&#039;generic&#039;&#039; anzulegen, so dass hier der Umweg über &amp;lt;tt&amp;gt;FSTR&amp;lt;/tt&amp;gt; genommen werden muss.  Dieses Konstrukt ist nur ausserhalb von Funktionen möglich und kann daher nicht als Ersatz für &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; aus der avr-libc dienen.&lt;br /&gt;
&lt;br /&gt;
Soll &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; ein 2-dimensonales Array sein anstatt ein 1-dimensionales Array von Zeigern, dann geht das ohne große Verrenkungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Die 6 ergibt sich aus 1 plus der Länge des längsten Strings &amp;quot;Katze&amp;quot;&lt;br /&gt;
const __flash char array[][6] = &lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiters besteht die Möglichkeit, &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; analog anzulegen, wie man es mit &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; machen würde:  Jeder String wird explizit angelegt und seine Adresse bei der Initialisierung von &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; verwendet.  Dies entspricht dem ersten Beispiel eines 1-dimensionalen Zeigerarrays:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char strHund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char strKatze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char strMaus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    strHund, strKatze, strMaus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Casts ===&lt;br /&gt;
&lt;br /&gt;
Embedded C fordert, dass zwei Adress-Spaces entweder disjunkt sind – d.h. sie enthalten keine gemeinsamen Adressen – oder aber ein Space komplett im anderen enthalten ist, also eine Teilmengen-Beziehung besteht.  Die Adress-Spaces von avr-gcc sind so implementiert, dass jeder Space Teilmenge jedes anderes ist.  Zwar haben Spaces wie RAM und Flash physikalisch keinen Speicherbereich gemein, allerdings ermöglicht diese Implementierung das Casten von Zeigern zu unterschiedlichen Adress-Spaces&amp;lt;ref&amp;gt;Im Gegensatz zu einem Attribute wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist ein (Adress Space) Qualifier Teil des Zeiger-Typs.&amp;lt;/ref&amp;gt;:  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
char read_char (const char *address, bool data_in_flash)&lt;br /&gt;
{&lt;br /&gt;
    if (data_in_flash)&lt;br /&gt;
        return *(const __flash char*) address;&lt;br /&gt;
    else&lt;br /&gt;
        return *address;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Cast selbst erzeugt keinen zusätzlichen Code, da eine RAM-Adresse und eine Flash-Adresse die gleiche Binärdarstellung haben.  Allerdings wird über den nach &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gecasteten Zeiger anders zugegriffen, nämlich per LPM.&lt;br /&gt;
&lt;br /&gt;
=== Jenseits von __flash ===&lt;br /&gt;
&lt;br /&gt;
Ausser &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gibt es auch folgende Address-Spaces:&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;: Mit &#039;&#039;N&#039;&#039; = 1..5 sind fünf weitere Spaces, die analog zu &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; funktionieren und deren Zeiger ebenfalls 16 Bit breit sind.  avr-gcc erwartet, dass die zugehörigen Daten, welche in die Section &amp;lt;tt&amp;gt;.progmem&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;&amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; abgelegt werden, so lokatiert sind, dass das high-Byte der Adresse (Bits 16..23) gerade &#039;&#039;N&#039;&#039; ist.  Dies wird in Binutils noch nicht unterstützt&amp;lt;ref&amp;gt;[http://sourceware.org/PR14406 Binutils PR14406]: Support .progmem&amp;lt;N&amp;gt;.data sections to work with GCC&#039;s [http://gcc.gnu.org/PR49868 PR49868].&amp;lt;/ref&amp;gt; (Stand Binutils 2.24) Die Unterstützung kann durch ein eigenes Linker-Skript erreicht werden, welches diese Sections wie vom Compiler erwartet lokatiert.&lt;br /&gt;
; &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;: Dieser Address-Space implementiert 3-Byte Zeiger und unterstützt Lesen über 64KiB-Segmentgrenzen hinweg.  Das MSB (Bit 23) gibt dabei an, ob der &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger eine Flash-Adresse enthält (Bit23 = 0) oder eine RAM-Adresse (Bit23 = 1), was folgenden Code erlaubt:&lt;br /&gt;
&amp;lt;!-- Bitte die Tabelle belassen, damit der Code richtig eingerückt wird! --&amp;gt;&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const __memx int a_flash = 42;&lt;br /&gt;
const        int a_ram   = 100;&lt;br /&gt;
&lt;br /&gt;
static int get_a (const __memx int* pa)&lt;br /&gt;
{&lt;br /&gt;
    return *pa;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    return get_a (&amp;amp;a_flash) + get_a (&amp;amp;a_ram);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}Dies bedeutet, dass erst zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden kann, ob aus dem RAM oder aus dem Flash gelesen werden soll, was &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt; im Vergleich zu den anderen Address-Spaces langsamer macht. Ausserdem ist zu beachten, dass &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger zwar 24-Bit Zeiger sind, die zugrundeliegende Adress-Arithmetik jedoch gemäß dem C-Standard erfolgt, also als 16-Bit Arithmetik.&lt;br /&gt;
&lt;br /&gt;
=== __flash, progmem und Portierbarkeit ===&lt;br /&gt;
&lt;br /&gt;
Da ab er aktuellen Compilerversion 4.7 sowohl &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; als auch &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; und die &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen zur Verfügung stehen, ergibt sich die Frage, welche Variante &amp;quot;besser&amp;quot; ist und wie zwischen ihnen hin- und her zu portieren ist.&lt;br /&gt;
&lt;br /&gt;
Zunächst sei erwähnt, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; kein Ersatz für &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; ist, sondern lediglich eine Alternative dazu.  Das &amp;quot;alte&amp;quot; progmem wird weiterhin mir gleicher Semantik unterstützt, so dass alter Code ohne Änderungen mit den neueren Compilerversionen übersetzbar bleibt.&lt;br /&gt;
&lt;br /&gt;
Von der Codegüte her dürften sich keine großen Unterschiede ergeben.  Es ist nicht zu erwarten, dass die eine oder die andere Variante wesentlich besseren oder schlechteren Code erzeugt — von einer Ausnahme abgesehen:  Der Wert beim Zugriff ist zur Compilezeit bekannt und kann daher eliminiert werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char x[] = { &#039;A&#039;, &#039;V&#039;, &#039;R&#039; };&lt;br /&gt;
&lt;br /&gt;
char foo (void)&lt;br /&gt;
{&lt;br /&gt;
    return x[2];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies wird übersetzt wie &amp;quot;&amp;lt;tt&amp;gt;return &#039;R&#039;;&amp;lt;/tt&amp;gt;&amp;quot;, und das Array &amp;lt;tt&amp;gt;x[]&amp;lt;/tt&amp;gt; kann komplett wegoptimiert werden und entfallen.&lt;br /&gt;
&lt;br /&gt;
==== progmem → __flash ====&lt;br /&gt;
&lt;br /&gt;
Portierung in diese Richtung bedeutet, alten Code anzupassen.  Zwingend ist die Portierung nicht, da &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; weiterhin unterstützt wird.&lt;br /&gt;
Allerdings ist eine Quelle mit &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; besser lesbar, denn der Code wird von den &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen befreit, die vor allem bei Mehrfach-Indirektion den Code ziemlich verunstalten und unleserlich machen können.&lt;br /&gt;
Weiterer Vorteil von &amp;lt;tt&amp;gt;_flash&amp;lt;/tt&amp;gt; ist, daß eine striktere Typprüfung erfolgen kann.&lt;br /&gt;
&lt;br /&gt;
Eine Portierung wird man in zwei Schritten vornehmen:&lt;br /&gt;
&lt;br /&gt;
;1. Definitionen von Flash-Variablen werden angepasst:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
static const char hund[]  PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char katze[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char maus[]  PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const tier[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char hund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char katze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char maus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
const __flash char * const __flash tier[] = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; wird nicht mehr benötigt.  Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; müssen Qualifier immer links von der definierten Variablen stehen; bei Attributen wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist das mehr oder weniger egal.&lt;br /&gt;
&lt;br /&gt;
Nachdem diese Anpassung erfolgreich abgeschlossen ist, folgt Schritt&lt;br /&gt;
&lt;br /&gt;
; 2. Der Code wird von &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Aufrufen bereinigt:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const char *tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    const char* ptier = (const char*) pgm_read_word (&amp;amp;tier[i]);&lt;br /&gt;
    return (char) pgm_read_byte (&amp;amp;ptier[0]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const __flash char * const __flash tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    return tier[i][0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Dateien direkt im Flash einbinden ==&lt;br /&gt;
&lt;br /&gt;
Wenn man größere Dateien direkt im Programm einbinden will, ohne sie vorher in C Quelltext umzuwandeln, muss man das mit dem Linker machen. Wie das geht steht hier.&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/webdoc/avrlibcreferencemanual/FAQ_1faq_binarydata.html Atmel, avr gcc Dokumentation]&lt;br /&gt;
* [http://nongnu.org/avr-libc/user-manual/FAQ.html#faq_binarydata Nongnu avr gcc Dokumentation]&lt;br /&gt;
&lt;br /&gt;
Wie man das dann praktisch umsetzt, sieht man in diesem Beitrag.&lt;br /&gt;
&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/361429?goto=4056910#4056910 Forumsbeitrag]: Binärdateien mittels Linker einbinden&lt;br /&gt;
&lt;br /&gt;
== Flash in der Anwendung schreiben ==&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option – auch bekannt als [[Bootloader]]-Support – können Teile des Flash-Speichers vom Anwendungsprogramm beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktion in einem besonderen Speicherbereich, der Boot-Section des Programmspeichers/Flash, abgelegt ist.&lt;br /&gt;
&lt;br /&gt;
Bei einigen kleinen AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* Forumsbeitrag [http://www.mikrocontroller.net/topic/163632#1561622 Daten in Programmspeicher speichern]&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Möchte man Werte aus einem Programm heraus so speichern, dass sie auch nach dem Abschalten der Versorgungsspannung noch erhalten bleiben und nach dem Wiederherstellen der Versorgungsspannung bei erneutem Programmstart wieder zur Verfügung stehen, dann benutzt man das EEPROM.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h der avr-libc definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16 Bit), Fließkommawerte (32 Bit, single-precision, float) und Datenblöcke geschrieben und gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen kümmern sich auch um diverse Details, die bei der Benutzung des EEPROMs normalerweise notwendig sind:&lt;br /&gt;
* EEPROM-Operationen sind im Vergleich relativ langsam. Man muss daher darauf achten, dass eine vorhergehende Operation abgeschlossen ist, ehe die nächste Operation mit dem EEPROM gestartet wird. Die in der avr-libc implementierten Funktionen aus eeprom.h berücksichtigten dies. Soll beim Aufruf einer EEPROM-Funktion sichergestellt werden, dass diese nicht intern in einer Warteschleife auf den Abschluss der vorherigen Operation wartet, kann vorher per eeprom_is_ready testen, ob der Zugriff auf den EEPROM-Speicher sofort möglich ist.&lt;br /&gt;
* Es ist darauf zu achten, dass die EEPROM-Funktionen nicht durch einen Interrupt unterbrochen werden. Einige Phasen des Zugriffs sind zeitkritisch und müssen in einer definierten bzw. begrenzten Anzahl von Takten durchgeführt werden. Durch einen unterbrechenden Interrupt würde diese Restriktion nicht mehr eingehalten. Auch dieses Detail wird von den avr-libc Funktionen berücksichtigt, so dass man sich als C-Programmierer nicht darum kümmern muss. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. Dies gilt für jede einzelne Zelle. &lt;br /&gt;
&lt;br /&gt;
Bei geschickter Programmierung (z.&amp;amp;nbsp;B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den gesamten EEPROM-Speicher, erreichen. Auf jeden Fall sollte man aber eine Abschätzung über die zu erwartende Lebensdauer des EEPROM durchführen. Wird ein Wert im EEPROM im Durchschnitt nur einmal pro Woche verändert, wird die garantierte Anzahl der Schreibzyklen innerhalb der voraussichtlichen Verwendungszeit des Controllers sicherlich nicht erreicht werden. Welcher Controller ist schon 100000 / 52 = 1923 Jahre im Einsatz? In diesem Fall lohnt es sich daher nicht, erweiterte Programmfunktionen zu implementieren, mit denen die Anzahl der Schreibzugriffe minimiert wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit, Schreibzyklen einzusparen, besteht in der Vorabprüfung, ob der zu speichernde Wert im EEPROM bereits enthalten ist und nur veränderte Werte zu schreiben. In aktuelleren Versionen der avr-libc sind bereits Funktionen enthalten, die solche Prüfungen enthalten (eeprom_update_*).&lt;br /&gt;
&lt;br /&gt;
Lesezugriffe können beliebig oft durchgeführt werden. Sie unterliegen keinen Einschränkungen in Bezug auf deren Anzahl. &lt;br /&gt;
&lt;br /&gt;
=== EEMEM ===&lt;br /&gt;
Um eine Variable im EEPROM anzulegen, stellt die avr-libc das Makro EEMEM zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Array */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3, 70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7, 79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die grundsätzliche Vorgehensweise ist identisch zur Verwendung von PROGMEM. Auch hier erzeugt man sich spezielle attributierte Variablen (EEMEM erledigt das), die vom Compiler/Linker nicht wie normale Variablen behandelt werden. Compiler/Linker kümmern sich zwar darum, dass diesen Variablen eine Adresse zugewiesen wird, diese Adresse ist dann aber die Adresse der &#039;Variablen&#039; im EEPROM. Um die dort gespeicherten Werte zu lesen bzw. zu schreiben, übergibt man diese Adresse an spezielle Funktionen, die die entsprechenden Werte aus dem EEPROM holen bzw. das EEPROM neu beschreiben.&lt;br /&gt;
&lt;br /&gt;
Die mittels EEMEM erzeugten &#039;Variablen&#039; sind also mehr als Platzhalter zu verstehen, denn als echte Variablen. Es geht nur darum, im C-Programm symbolische Namen zur Verfügung zu haben, anstatt mit echten EEPROM-Adressen hantieren zu müssen, etwas, das grundsätzlich aber auch genauso gut möglich ist. Nur muss man sich in diesem Fall dann selbst darum kümmern, dass mehrere &#039;Variablen&#039; ohne Überschneidung im EEPROM angeordnet werden.&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EEPROM_DEF 0xFF&lt;br /&gt;
&lt;br /&gt;
void eeprom_example (void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    // myByte lesen (Wert = 123)&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByte);&lt;br /&gt;
&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // Variablen eeFooByte geschrieben&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
&lt;br /&gt;
    // Beispiel fuer eeprom_update_byte: die EEPROM-Zelle wird nur&lt;br /&gt;
    // dann beschrieben, wenn deren Inhalt sich vom Parameterwert&lt;br /&gt;
    // unterscheidet. In diesem Beispiel erfolgt also kein Schreib-&lt;br /&gt;
    // zugriff, da die Werte gleich sind.&lt;br /&gt;
    eeprom_update_byte(&amp;amp;eeFooByte, myByte);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z. B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ((myByte = eeprom_read_byte (&amp;amp;eeFooByte)) == EEPROM_DEF)&lt;br /&gt;
    {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // lesen&lt;br /&gt;
    uint16_t myWord = eeprom_read_word (&amp;amp;eeFooWord);&lt;br /&gt;
&lt;br /&gt;
    // schreiben&lt;br /&gt;
    eeprom_write_word (&amp;amp;eeFooWord, 2222);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &amp;lt;code&amp;gt;eeprom_read_block()&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;eeprom_write_block()&amp;lt;/code&amp;gt;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Adresse und die Länge des Datenblocks in Bytes als &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t  myByteBuffer[3];&lt;br /&gt;
uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
void eeprom_block_example (void)&lt;br /&gt;
{&lt;br /&gt;
    /* Datenblock aus EEPROM lesen  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, 3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit 16-Bit Array */&lt;br /&gt;
    eeprom_read_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock in EEPROM schreiben */&lt;br /&gt;
    eeprom_write_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fließkommawerte lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
In der avr-libc stehen auch EEPROM-Funktionen für Variablen des Typs float (Fließkommazahlen mit &amp;quot;einfacher&amp;quot; Genauigkeit) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
float eeFloat EEMEM = 12.34f;&lt;br /&gt;
&lt;br /&gt;
float void eeprom_float_example(float value)&lt;br /&gt;
{&lt;br /&gt;
   /* float in EEPROM schreiben */&lt;br /&gt;
   eeprom_write_float(&amp;amp;eeFloat, value);&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM lesen */&lt;br /&gt;
   return  eeprom_read_float(&amp;amp;eeFloat);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklarationen abgeleitete EEPROM-Inhalt in eine Datei geschrieben werden. Die übliche Dateiendung ist .eep, Daten im Intel Hex-Format. Damit können Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. &lt;br /&gt;
&lt;br /&gt;
Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen, siehe dazu die Erläuterungen im [[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]].&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden, wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse&amp;lt;ref&amp;gt;vgl. Datenblatt Abschnitt Fuse Bits&amp;lt;/ref&amp;gt; nicht die korrekten Werte:&lt;br /&gt;
; EESAVE = 0 (programmed): Die Daten im EEPROM bleiben erhalten. Werden sie nicht neu geschrieben, so enthält das EEPROM evtl. Daten, die nicht mehr zum Programm passen.&lt;br /&gt;
; EESAVE = 1 (unprogrammed): Beim Programmieren werden die Daten im EEPROM gelöscht, also auf 0xff gesetzt.&lt;br /&gt;
&lt;br /&gt;
Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenenfalls einen Standardwert nutzen. Das geht natürlich nur, wenn 0xFF selbst nicht als Datenwert vorkommen kann.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define DUTY_CYCLE_DEFAULT 0x80&lt;br /&gt;
&lt;br /&gt;
uint8_t eeDutyCycle EEMEM;   // Platzhalter für EEPROM&lt;br /&gt;
uint8_t DutyCycle;           // die echte Variable&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DutyCycle = eeprom_read_byte( &amp;amp;eeDutyCycle );&lt;br /&gt;
  if( DutyCycle == 0xFF )                     // das allererste mal. Im EEPROM steht noch kein gültiger Wert&lt;br /&gt;
  {&lt;br /&gt;
    DutyCycle = DUTY_CYCLE_DEFAULT;&lt;br /&gt;
    eeprom_writeByte( &amp;amp;eeDutyCycle, DutyCycle );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Direkter Zugriff auf EEPROM-Adressen ===&lt;br /&gt;
&lt;br /&gt;
Will man direkt auf bestimmte EEPROM Adressen zugreifen, dann sind folgende Funktionen hilfreich, um sich die Typecasts zu ersparen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Byte aus dem EEPROM lesen&lt;br /&gt;
uint8_t EEPReadByte(uint16_t addr)&lt;br /&gt;
{&lt;br /&gt;
  return eeprom_read_byte((uint8_t *)addr);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Byte in das EEPROM schreiben&lt;br /&gt;
void EEPWriteByte(uint16_t addr, uint8_t val)&lt;br /&gt;
{&lt;br /&gt;
  eeprom_write_byte((uint8_t *)addr, val);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder als Makro:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define   EEPReadByte(addr)         eeprom_read_byte((uint8_t *)addr)     &lt;br /&gt;
#define   EEPWriteByte(addr, val)   eeprom_write_byte((uint8_t *)addr, val)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EEPWriteByte(0x20, 128);   // Byte an die Adresse 0x20 schreiben&lt;br /&gt;
...&lt;br /&gt;
Val=EEPReadByte(0x20);     // EEPROM-Wert von Adresse 0x20 lesen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Was steckt dahinter? - EEPROM-Register ===&lt;br /&gt;
Auch wenn es normalerweise keinen Grund gibt, in C selbst an den Steuerregistern herumzuschrauben - die eeprom Funktionen erledigen das alles zuverlässig - der Vollständigkeit halber der registermässige technische Unterbau.&lt;br /&gt;
Um das EEPROM anzusteuern, sind drei Register von Bedeutung:&lt;br /&gt;
;EEAR: Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL, da in einem 8-Bit-Register keine 512 Adressen adressiert werden können.&lt;br /&gt;
;EEDR: Hier werden die Daten eingetragen, die geschrieben werden sollen, bzw. es enthält die gelesenen Daten.&lt;br /&gt;
;EECR: Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Aufbau des EECR-Registers&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
| - || - || - ||- || EERIE || EEMWE || EEWE || EERE&lt;br /&gt;
|-&lt;br /&gt;
! Read/Write&lt;br /&gt;
| R || R || R || R || R/W || R/W || R/W || R/W&lt;br /&gt;
|-&lt;br /&gt;
!Init Value&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bedeutung der Bits&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Bit 4-7: nicht belegt&lt;br /&gt;
&lt;br /&gt;
;Bit 3 (EERIE): &#039;&#039;EEPROM Ready Interrupt Enable&#039;&#039;: Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7), wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0, wird kein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
;Bit 2 EEMWE): &#039;&#039;EEPROM Master Write Enable&#039;&#039;: Dieses Bit bestimmt, dass, wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE = 0 ist und EEWE = 1 gesetzt wird, hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst. Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&lt;br /&gt;
&lt;br /&gt;
;Bit 1 (EEWE): &#039;&#039;EEPROM Write Enable&#039;&#039;: Dieses Bit löst den Schreibvorgang aus, wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist, wird dieses Bit automatisch wieder auf 0 gesetzt und, sofern EERIE gesetzt ist, ein Interrupt ausgelöst. Ein Schreibvorgang sieht typischerweise wie folgt aus:&lt;br /&gt;
:# EEPROM-Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Daten übergeben an EEDR&lt;br /&gt;
:# Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1&lt;br /&gt;
:# (Optional) Warten, bis Schreibvorgang abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
;Bit 0 EERE: &#039;&#039;EEPROM Read Enable&#039;&#039;: Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden, wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen, die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit, wenn das Bit EEWE=0 ist. Ist der Lesevorgang abgeschlossen, wird das Bit wieder auf 0 gesetzt, und das EEPROM ist für neue Lese- und Schreibbefehle wieder bereit. Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&lt;br /&gt;
:# Bereitschaft zum Lesen prüfen (EEWE=0)&lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Lesezyklus auslösen mit EERE = 1&lt;br /&gt;
:# Warten, bis Lesevorgang abgeschlossen EERE = 0&lt;br /&gt;
:# Daten abholen aus EEDR&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von sprintf und printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel, d.h. formatiert, Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bieten sich &#039;&#039;&#039;sprintf&#039;&#039;&#039; oder &#039;&#039;&#039;printf&#039;&#039;&#039; an. Alle *printf-Varianten sind jedoch ziemlich speicherintensiv und der Einsatz in einem Mikrocontroller mit knappem Speicher muss sorgsam abgewogen werden.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;sprintf&#039;&#039;&#039; wird die Ausgabe zunächst in einem Puffer vorbereitet und anschließend mit einfachen Funktionen zeichenweise ausgegeben. Es liegt in der Verantwortung des Programmierers, genügend Platz im Puffer für die erwarteten Zeichen bereitzuhalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
// Ausgabe eines unsigned Integerwertes&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t puffer[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( puffer, &amp;quot;Zählerstand: %u&amp;quot;, value );&lt;br /&gt;
    uart_puts( puffer );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, den STREAM stdout (Standardausgabe) auf eine eigene Ausgabefunktion umzuleiten. Dazu wird dem Ausgabemechanismus der C-Bibliothek eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben. Wohin die Ausgabe dann tatsächlich stattfindet, ist Sache der Ausgabefunktion. Im Beispiel unten wird auf UART ausgegeben. Alle anderen, höheren Funktionen wie z.&amp;amp;nbsp;B. &#039;&#039;&#039;printf&#039;&#039;&#039;, greifen letztendlich auf diese primitive Ausgabefunktion zurück. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
&lt;br /&gt;
// a. Deklaration der primitiven Ausgabefunktion&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
// b. Umleiten der Standardausgabe stdout (Teil 1)&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
// c. Definition der Ausgabefunktion&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t antwort = 42;&lt;br /&gt;
    uart_init();&lt;br /&gt;
&lt;br /&gt;
    // b. Umleiten der Standardausgabe stdout (Teil 2)&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
&lt;br /&gt;
    // Anwendung&lt;br /&gt;
    printf( &amp;quot;Die Antwort ist %d.\n&amp;quot;, antwort );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
= Anmerkungen =&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:avr-gcc Tutorial| ]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90658</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90658"/>
		<updated>2015-12-11T14:31:23Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Was steckt dahinter? - EEPROM-Register */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[avr-gcc]] aus der [http://gcc.gnu.org/ GNU Compiler Collection] (GCC) erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Programmiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong] ([[C|Liste von C-Tutorials]]). Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern.&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR-Controllern finden. Beim Paket [[WinAVR]] gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden ständig weiterentwickelt. Einige Unterschiede, die sich im Verlauf der Entwicklung ergeben haben, werden hier und im Artikel [[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen]] zwar angesprochen, Anfängern und Umsteigern sei jedoch empfohlen, eine aktuelle Versionen zu nutzen.&lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in [[Media:AVR-GCC-Tutorial.pdf|PDF-Form]] erhältlich (nicht immer auf aktuellem Stand).&lt;br /&gt;
&lt;br /&gt;
== Weiterführende Kapitel ==&lt;br /&gt;
&lt;br /&gt;
Um dieses riesige Tutorial etwas überschaubarer zu gestalten, wurden einige Kapitel ausgelagert, die nicht unmittelbar mit den Grundlagen von avr-gcc in Verbindung stehen. All diese Seiten gehören zur [[:Kategorie:avr-gcc Tutorial]].&lt;br /&gt;
&lt;br /&gt;
;UART: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der UART|Der UART]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;ADC: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Analoge Ein- und Ausgabe|Analoge Ein- und Ausgabe (ADC)]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Timer: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Die Timer und Zähler des AVR|Die Timer und Zähler des AVR]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;LCD: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Ansteuerung]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Watchdog: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der Watchdog|Der Watchdog]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Assembler: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Assembler und Inline-Assembler|Assembler und Inline-Assembler]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;alte Quellen anpassen: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen anpassen]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Makefiles: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]]&#039;&#039; sowie als Alternative für sehr kleine Projekte → Hauptartikel: &#039;&#039;[[C ohne Makefile]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels einer AVR-Toolchain zu erstellen wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Eine AVR-Toolchain bestehend aus avr-gcc, den avr-Binutils (Assembler, Linker, etc) und einer Standard-C Bibliothek.  Üblich ist die AVR-LibC, die auch quasi in allen avr-gcc Distributionen enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Hardware wird keine benötigt – bis auf einen PC natürlich, auf dem der Compiler ablaufen kann.  Selbst ohne AVR-Hardware kann man also bereits C-Programme für AVRs schreiben, compiliern und sich das Look-and-Feel von avr-gcc sowie von IDEs wie [[Atmel Studio]], Eclipse oder leichtgewichtigeren Entwicklungsumbgebungen anschauen. Selbst das Debuggen und Simulieren ist mithilfe entsprechender Tools wie Debugger und Simulator in gewissen Grenzen möglich.&lt;br /&gt;
&lt;br /&gt;
Um Programme für AVRs mittels einer AVR-Toolchain zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR-Controllers, der vom avr-gcc Compiler unterstützt wird.&amp;lt;ref&amp;gt;Für eine Liste der unterstützten COntroller siehe die Dokumentation des Compilers oder [http://www.nongnu.org/avr-libc/user-manual/index.html#supported_devices AVR-Libc: Supported Devices].&amp;lt;/ref&amp;gt; Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. &lt;br /&gt;
&lt;br /&gt;
:Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man in den Artikeln [[AVR#Starterkits|AVR Starterkits]] und [[AVR-Tutorial: Equipment]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]].&lt;br /&gt;
&lt;br /&gt;
* Wer unter Windows und Linux gleichermassen entwickeln will, der sollte sich die [http://www.eclipse.org/ IDE Eclipse for C/C++ Developers] und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] ansehen. Beide sind unter Windows und Linux einfach zu installieren, siehe auch [[AVR Eclipse]]. Ebenfalls unter Linux und Windows verfügbar ist die Entwicklungsumgebung [http://www.codeblocks.org/ Code::Blocks]&amp;lt;ref&amp;gt;Aktuelle, stabile Versionen sind als Nightly Builds regelmäßig im [http://forums.codeblocks.org/ Forum] verfügbar.&amp;lt;/ref&amp;gt;. Innerhalb dieser Entwicklungsumgebung können ohne die Installation zusätzlicher Plugins &amp;quot;AVR-Projekte&amp;quot; angelegt werden. Für Linux gibt es auch noch das [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=25220 KontrollerLab].&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht klappt? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/gcc GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net AVRfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRfreaks] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispielprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.tty1.net/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu drei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs (ohne Anspruch auf Vollständigkeit): [http://www.eclipse.org/ Eclipse for C/C++ Developers] (d.h. inkl. CDT) und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] (für diverse Plattformen, u.a. Linux und MS Windows, IDE und Plugin kostenlos), [http://sourceforge.net/projects/kontrollerlab KontrollerLab] (Linux/KDE, kostenlos). [http://www.atmanecl.com/EnglishSite/SoftwareEnglish.htm AtmanAvr] (MS Windows, relativ günstig), KamAVR (MS-Windows, kostenlos, wird augenscheinlich nicht mehr weiterentwickelt), [http://www.amctools.com/vmlab.htm VMLab] (MS Windows, ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand von &amp;quot;make&amp;quot; und den &amp;quot;Makefiles&amp;quot; näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
* Das Generieren des Programms ohne IDE und ohne Makefile. In diesem Fall muss die Quellcodedatei durch eine vorgefertigte Kommandofolge an den Compiler übergeben werden. Der Artikel [[C ohne Makefile]] zeigt, wie das funktioniert. Diese Vorgehensweise empfiehlt sich jedoch nur für kleine Programme, die nicht auf verschiedene Quellcodedateien verteilt sind.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|AVR-GCC-Tutorial/Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. (Hinweis: Bei Version 4.16 wird beides bereits gesetzt). Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Ein kurzes Wort zur Hardware: Bei diesem Programm werden alle Pins von PORTB auf Ausgang gesetzt, und einige davon werden auf HIGH andere auf LOW gesetzt. Das kann je nach angeschlossener Hardware an diesen Pins kritisch sein. Am ungefährlichsten ist es, wenn nichts an den Pins angeschlossen ist und man die Funktion des Programmes durch eine Spannungsmessung mit einem Multimeter kontrolliert. Die Spannung wird dabei zwischen GND-Pin und den einzelnen Pins von PORTB gemessen.&lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xFF;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/   // (6)&lt;br /&gt;
   }                         // (7)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (8)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# In dieser Zeile wird eine sogenannte Header-Datei eingebunden. In &amp;lt;code&amp;gt;avr/io.h&amp;lt;/code&amp;gt; sind die Registernamen definiert, die im späteren Verlauf genutzt werden. Auch unter Windows wird ein&amp;amp;nbsp;&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; zur Kennzeichnung von Unterverzeichnissen in Include-Dateinamen verwendet und kein&amp;amp;nbsp;&amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Hier beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Die Anschlüsse eines AVR (Pins) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Datenrichtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B als Ausgänge eingestellt.&lt;br /&gt;
# Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential). Es ist sinnvoll, sich möglichst frühzeitig eine alternative Schreibweise beizubringen, die wegen der leichteren Überprüfbarkeit und Portierbarkeit oft im weiteren Tutorial und in Forenbeiträgen benutzt wird. Die Zuordnung sieht in diesem Fall so aus, Näheres dazu im Artikel [[Bitmanipulation]]:&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;PORTB = (1&amp;lt;&amp;lt;PB1) | (1&amp;lt;&amp;lt;PB0);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# ist der Beginn der sogenannte &#039;&#039;Hauptschleife&#039;&#039; (main-loop). Dies ist eine Endlosschleife, welche kontinuierlich wiederkehrende Befehle enthält.&lt;br /&gt;
# In diesem Beispiel ist die Hauptschleife leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert. Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife kehrt das Programm aus &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; zurück, alle Interrupts werden deaktiviert und eine Endlosschleife betreten.&lt;br /&gt;
# Ende der Hauptschleife und Sprung zur passenden, öffnenden Klammer, also zu 5.&lt;br /&gt;
# ist das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int main(void)&amp;lt;/syntaxhighlight&amp;gt; besagt, dass die Funktion einen int-Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; (ohne Endung) im selben Verzeichnis, in dem auch die Datei &amp;lt;code&amp;gt;main.c&amp;lt;/code&amp;gt; mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\beispiel&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei &amp;lt;code&amp;gt;main.hex&amp;lt;/code&amp;gt;, in welcher der Code für den AVR enthalten ist. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming ([[ISP]]) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann&amp;lt;ref&amp;gt;z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio&amp;lt;/ref&amp;gt;, kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine [[LED]] leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige Datentypen (Integer) =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; definiert, die folgendermaßen eingebunden werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;stdint.h&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|+ &#039;&#039;&#039;int-Typen aus &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; (C99)&#039;&#039;&#039;&amp;lt;br/&amp;gt;&amp;amp;nbsp;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenbehaftete int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || −128 ⋯ 127 || −2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || −32768 ⋯ 32767 || −2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;signed int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || −2147483648 ⋯ 2147483647 || −2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || −9223372036854775808 ⋯ 9223372036854775807 || −2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenlose int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || 0 ⋯ 255 || 0 ⋯ 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || 0 ⋯ 65535 || 0 ⋯ 2&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || 0 ⋯ 4294967295 || 0 ⋯ 2&amp;lt;sup&amp;gt;32&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || 0 ⋯ 18446744073709551615 || 0 ⋯ 2&amp;lt;sup&amp;gt;64&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Neben den Typen gibt es auch Makros für die Bereichsgrenzen wie &amp;lt;code&amp;gt;INT8_MIN&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;UINT16_MAX&amp;lt;/code&amp;gt;. Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html Dokumentation der avr-libc: Standard Integer Types].&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines µC-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif|left]]&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat. Es wird hier nach dem sogenannten EVA-Prinzip gehandelt. EVA steht für &amp;quot;Eingabe, Verarbeitung, Ausgabe&amp;quot;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif|left]]&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher Dinge erledigt werden können, welche nicht zeitkritisch sind. Wenn ein Interrupt ausgelöst wird, so wird automatisch die zugeordnete Interruptfunktion ausgeführt.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Register haben einen besonderen Stellenwert bei den AVR Controllern. Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers. Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind, selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.&amp;amp;nbsp;B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.&amp;amp;nbsp;B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039; wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Beispiele in den folgenden Abschnitten demonstrieren den Zugriff auf Register anhand der Register für I/O-Ports (PORTx, DDRx, PINx), die Vorgehensweise ist jedoch für alle Register (z.&amp;amp;nbsp;B. die des UART, ADC, SPI) analog.&lt;br /&gt;
&lt;br /&gt;
== Schreiben in Register ==&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man Register einfach wie eine Variable setzen.&amp;lt;ref&amp;gt;In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt, outp() ist nicht mehr erforderlich.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang, vgl. Abschnitt Zugriff auf Ports): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
&lt;br /&gt;
    // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
    // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
    DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
    /* Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
       aber übersichtlicher und selbsterklärend: */&lt;br /&gt;
    DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4);&lt;br /&gt;
&lt;br /&gt;
    while (1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ausführliche Schreibweise sollte bevorzugt verwendet werden, da dadurch die Zuweisungen selbsterklärend sind und somit der Code leichter nachvollzogen werden kann. Atmel verwendet sie auch bei Beispielen in Datenblätten und in den allermeisten Quellcodes zu Application-Notes. Mehr zu der Schreibweise mit &amp;quot;|&amp;quot; und &amp;quot;&amp;lt;&amp;lt;&amp;quot; findet man unter [[Bitmanipulation]].&lt;br /&gt;
&lt;br /&gt;
Der gcc C-Compiler unterstützt ab Version 4.3.0 Konstanten im Binärformat, z.&amp;amp;nbsp;B. DDRB&amp;amp;nbsp;=&amp;amp;nbsp;0b00011111. Diese Schreibweise ist jedoch nur in GNU-C verfügbar und nicht in ISO-C definiert. Man sollte sie daher nicht verwenden, wenn Code mit anderen ausgetauscht oder mit anderen Compilern bzw. älteren Versionen des gcc genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
== Verändern von Registerinhalten ==&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt und löscht man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // Hiermit wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // Hiermit wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird jeweils nur der Zustand des angegebenen Bits geändert, der vorherige Zustand der anderen Bits bleibt erhalten. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= ( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten AVR Registern mit Bits, die durch Beschreiben mit einer logischen 1 gelöscht werden, muss eine absolute Zuweisung benutzt werden. Ein ODER löscht in diesen Registern ALLE gesetzten Bits!&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TIFR2 = (1&amp;lt;&amp;lt;OCF2A); // Nur Bit OCF2A löschen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
== Lesen aus Registern ==&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände von Bits erfolgt durch Einlesen des gesamten Registerinhalts und ausblenden der Bits deren Zustand nicht von Interesse ist. Einige Beispiele zum Prüfen ob Bits gesetzt oder gelöscht sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define MEINBIT0 0 &lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
extern test1();&lt;br /&gt;
&lt;br /&gt;
// Funkion test1 aufrufen, wenn Bit 0 in Register PINA gesetzt (1) ist&lt;br /&gt;
i = PINA;         // Inhalt in Arbeitsvariable&lt;br /&gt;
i = i &amp;amp; 0x01;     // alle Bits bis auf Bit 0 ausblenden (bitweise und)&lt;br /&gt;
                  // falls das Bit gesetzt war, hat i den Inhalt 1&lt;br /&gt;
if ( i != 0 ) {   // Ergebnis ungleich 0 (wahr)? &lt;br /&gt;
  test1();         // dann muss Bit 0 in i gesetzt sein -&amp;gt; Funktion aufrufen&lt;br /&gt;
}&lt;br /&gt;
// verkürzt:&lt;br /&gt;
if ( ( PINA &amp;amp; 0x01 ) != 0 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( PINA &amp;amp; 0x01 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// mit definierter Bitnummer:&lt;br /&gt;
if ( PINA &amp;amp; ( 1 &amp;lt;&amp;lt; MEINBIT0 ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und/oder Bit 2 gesetzt ist. (Bit 0 und 2 also Wert 5) &lt;br /&gt;
// (Bedenke: Bit 0 hat Wert 1, Bit 1 hat Wert 2 und Bit 2 hat Wert 4)&lt;br /&gt;
if ( PINA &amp;amp; 0x05 ) {&lt;br /&gt;
  test1();  // Vergleich &amp;lt;&amp;gt; 0 (wahr), also mindestens eines der Bits gesetzt&lt;br /&gt;
}&lt;br /&gt;
// mit definierten Bitnummern:&lt;br /&gt;
if ( PINA &amp;amp; ( ( 1 &amp;lt;&amp;lt; MEINBIT0 ) | ( 1 &amp;lt;&amp;lt; MEINBIT2 ) ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und Bit 2 gesetzt sind&lt;br /&gt;
if ( ( PINA &amp;amp; 0x05 ) == 0x05 ) {  // nur wahr, wenn beide Bits gesetzt&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion test2() aufrufen, wenn Bit 0 gelöscht (0) ist&lt;br /&gt;
i = PINA;        // einlesen in temporäre Variable&lt;br /&gt;
i = i &amp;amp; 0x01;    // maskieren von Bit 0&lt;br /&gt;
if ( i == 0 ) {  // Vergleich ist wahr, wenn Bit 0 nicht gesetzt ist&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// analog mit not-Operator&lt;br /&gt;
if ( !i ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( !( PINA &amp;amp; 0x01 ) ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Warten auf einen bestimmten Zustand ==&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek avr-libc Funktionen, die warten, bis ein bestimmter Zustand eines Bits erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend&amp;quot; gewartet wird. Der Programmablauf bleibt also an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den [[Watchdog]] ein, muss man darauf achten, dass dieser auch noch getriggert wird (Zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Register (ADC, ICR1, OCR1x, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (Low-Byte) und &amp;quot;H&amp;quot; (High-Byte) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese Register kann dann direkt zugegriffen werden. Dies ist zum Beispiel der Fall für Register wie ADC oder TCNT1.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
    /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
    foo = ADC; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei anderen Registern, wie zum Beispiel Baudraten-Register, liegen High- und Low-Teil nicht direkt nebeneinander im SFR-Bereich, so dass ein 16-Bit Zugriff nicht möglich ist und der Zugriff zusammengebastelt werden muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
   uint16_t baud = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
&lt;br /&gt;
   UBRRH = (uint8_t) (baud &amp;gt;&amp;gt; 8);&lt;br /&gt;
   UBRRL = (uint8_t) baud;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen wie ATmega8 oder ATmega16 teilen sich UBRRH und UCSRC die gleiche Speicher-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL), welches Register tatsächlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und übergibt den Wert &#039;&#039;3&#039;&#039;. Und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und übergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Speziell bei den 16-Bit-Timern und auch beim ADC ist es bei allen Zugriffen auf Datenregister erforderlich, dass diese Daten synchronisiert sind. Wenn z.&amp;amp;nbsp;B. bei einem 16-Bit-Timer das High-Byte des Zählregisters gelesen wurde und vor dem Lesezugriff auf das Low-Byte ein Überlauf des Low-Bytes stattfindet, erhält man einen völlig unsinnigen Wert. Auch die Compare-Register müssen synchron geschrieben werden, da es ansonsten zu unerwünschten Compare-Ereignissen kommen kann. &lt;br /&gt;
&lt;br /&gt;
Beim ADC besteht das Problem darin, dass zwischen den Zugriffen auf die beiden Teilregister eine Wandlung beendet werden kann und der ADC ein neues Ergebnis in ADCL und ADCH schreiben will, wodurch High- und Low-Byte nicht zusammenpassen.&lt;br /&gt;
&lt;br /&gt;
Um diese Datenmüllproduktion zu verhindern, gibt es in beiden Fällen eine Synchronisation, die jeweils durch den Zugriff auf das Low-Byte ausgelöst wird:&lt;br /&gt;
* Bei den Timer-Registern (das gilt für alle TCNT-, OCR- und ICR-Register bei den 16-Bit-Timern) wird bei einem &#039;&#039;Lesezugriff&#039;&#039; auf das Low-Byte automatisch das High-Byte in ein temporäres Register, das ansonsten nach außen nicht sichtbar ist, geschoben. Greift man nun &#039;&#039;anschließend&#039;&#039; auf das High-Byte zu, dann wird eben dieses temporäre Register gelesen.&lt;br /&gt;
* Bei einem &#039;&#039;Schreibzugriff&#039;&#039; auf eines der genannten Register wird das High-Byte in besagtem temporären Register zwischengespeichert und erst beim Schreiben des Low-Bytes werden &#039;&#039;beide&#039;&#039; gleichzeitig in das eigentliche Register übernommen.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet für die Reihenfolge:&lt;br /&gt;
* Lesezugriff: Erst Low-Byte, dann High-Byte&lt;br /&gt;
* Schreibzugriff: Erst High-Byte, dann Low-Byte&lt;br /&gt;
&lt;br /&gt;
Des weiteren ist zu beachten, dass es für all diese 16-Bit-Register nur ein einziges temporäres Register gibt, so dass das Auftreten eines Interrupts, in dessen Handler ein solches Register manipuliert wird, bei einem durch ihn unterbrochenen Zugriff i.d.R. zu Datenmüll führt. 16-Bit-Zugriffe sind generell nicht atomar! Wenn mit Interrupts gearbeitet wird, kann es erforderlich sein, vor einem solchen Zugriff auf ein 16-Bit-Register die Interrupt-Bearbeitung zu deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Beim ADC-Datenregister ADCH/ADCL ist die Synchronisierung anders gelöst. Hier wird beim Lesezugriff (ADCH/ADCL sind logischerweise read-only) auf das Low-Byte ADCL beide Teilregister für Zugriffe seitens des ADC so lange gesperrt, bis das High-Byte ADCH ausgelesen wurde. Dadurch kann der ADC nach einem Zugriff auf ADCL keinen neuen Wert in ADCH/ADCL ablegen, bis ADCH gelesen wurde. Ergebnisse von Wandlungen, die zwischen einem Zugriff auf ADCL und ADCH beendet werden, gehen verloren!&lt;br /&gt;
&lt;br /&gt;
Nach einem Zugriff auf ADCL muss grundsätzlich ADCH gelesen werden!&lt;br /&gt;
&lt;br /&gt;
In beiden Fällen – also sowohl bei den Timern als auch beim ADC – werden vom C-Compiler 16-Bit Pseudo-Register zur Verfügung gestellt (z.&amp;amp;nbsp;B. TCNT1H/TCNT1L → TCNT1, ADCH/ADCL → ADC bzw. ADCW), bei deren Verwendung der Compiler automatisch die richtige Zugriffsreihenfolge regelt. In C-Programmen sollten grundsätzlich diese 16-Bit-Register verwendet werden! Sollte trotzdem ein Zugriff auf ein Teilregister erforderlich sein, sind obige Angaben zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass auch ein Zugriff auf die 16-Bit-Register vom Compiler in zwei 8-Bit-Zugriffe aufgeteilt wird und dementsprechend genauso nicht-atomar ist wie die Einzelzugriffe. Auch hier gilt, dass u.U. die Interrupt-Bearbeitung gesperrt werden muss, um Datenmüll zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Beim ADC gibt es für den Fall, dass eine Auflösung von 8 Bit ausreicht, die Möglichkeit, das Ergebnis &amp;quot;linksbündig&amp;quot; in ADCH/ADCL auszurichten, so dass die relevanten 8 MSB in ADCH stehen. In diesem Fall muss bzw. sollte nur ADCH ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
ADC und ADCW sind unterschiedliche Bezeichner für das selbe Registerpaar. Üblicherweise kann man in C-Programmen ADC verwenden, was analog zu den anderen 16-Bit-Registern benannt ist. ADCW (ADC Word) existiert nur deshalb, weil die Headerdateien auch für Assembler vorgesehen sind und es bereits einen Assembler-Befehl namens &#039;&#039;adc&#039;&#039; gibt. &lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t key_pressed (volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if (last_state == (*inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit)))&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t i = key_pressed (&amp;amp;PINB, PB1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass avr-libc FAQ: &amp;quot;How do I pass an IO port as a parameter to a function?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf IO-Ports =&lt;br /&gt;
&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output). Diese Register dienen dazu:&lt;br /&gt;
* einzustellen welche der Anschlüsse (&amp;quot;Beinchen&amp;quot;) des Controllers als Ein- oder Ausgänge dienen&lt;br /&gt;
* bei Ausgängen deren Zustand festzulegen&lt;br /&gt;
* bei Eingängen deren Zustand zu erfassen&lt;br /&gt;
&lt;br /&gt;
Mittels GPIO werden digitale Zustände gesetzt und erfasst, d.h. die Spannung an einem Ausgang wird ein- oder ausgeschaltet und an einem Eingang wird erfasst, ob die anliegende Spannung über oder unter einem bestimmten Schwellwert liegt. Im Datenblatt Abschnitt Electrical Characteristics/DC Characteristics finden sich die Spannungswerte (V_OL, V_OH für Ausgänge, V_IL, V_IH für Eingänge).&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
Die physischen Ein- und Ausgänge werden bei AVR-Controllern zu logischen Ports gruppiert.&lt;br /&gt;
&lt;br /&gt;
Alle Ports werden über Register gesteuert. Dazu sind jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! DDRx&lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
! PINx&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
! PORTx&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt. Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
  // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
  // direkte Zuweisung - standardkonform */&lt;br /&gt;
  DDRB = 0x1F;    /* &lt;br /&gt;
&lt;br /&gt;
  // übersichtliche Alternative - Binärschreibweise, aber kein ISO-C&lt;br /&gt;
  DDRB = 0b00011111;&lt;br /&gt;
&lt;br /&gt;
  // Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
  // aber übersichtlicher und selbsterklärend:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet. Weitere Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
  DDRB = 0xff; &lt;br /&gt;
  // Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~(1 &amp;lt;&amp;lt; DDB0);&lt;br /&gt;
  // Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4));&lt;br /&gt;
  // Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB3);&lt;br /&gt;
  // Alle Pins auf Eingang:&lt;br /&gt;
  DDRB = 0x00;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.&amp;amp;nbsp;B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&lt;br /&gt;
    // übersichtliche Alternative - Binärschreibweise&lt;br /&gt;
    PORTB = 0b00000100;    /* direkte Zuweisung - übersichtlich */&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&lt;br /&gt;
* zuerst die Bits im PORTx-Register setzen&lt;br /&gt;
* anschließend die Datenrichtung auf Ausgang stellen&lt;br /&gt;
&lt;br /&gt;
Daraus ergibt sich die Abfolge für einen Pin, der bisher als Eingang mit abgeschaltetem Pull-Up konfiguriert war:&lt;br /&gt;
* setze PORTx: interner Pull-Up aktiv&lt;br /&gt;
* setze DDRx: Ausgang (&amp;quot;high&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Bei der Reihenfolge erst DDRx und dann PORTx kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.&amp;amp;nbsp;B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Spannungstabelle&#039;&#039;&#039; &amp;lt;br /&amp;gt; &amp;lt;small&amp;gt;(ca. Grenzwerte)&amp;lt;/small&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
! Low || High&lt;br /&gt;
|-&lt;br /&gt;
! bei 5 V&lt;br /&gt;
| 1 V || 3,5 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 3,3 V&lt;br /&gt;
| 0,66 V || 2,31 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 1,8 V&lt;br /&gt;
| 0,36 V || 1,26 V&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
{{Warnung|Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;nicht&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.}}&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation#Bits_prüfen]]&lt;br /&gt;
&lt;br /&gt;
=== Interne Pull-Up Widerstände ===&lt;br /&gt;
&lt;br /&gt;
Portpins für Ein- und Ausgänge (GPIO) eines AVR verfügen über zuschaltbare interne Pull-Up Widerstände (nominal mehrere 10kOhm, z.&amp;amp;nbsp;B. ATmega16 20-50kOhm). Diese können in vielen Fällen statt externer Widerstände genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;PC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Taster und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller, ist zwischen zwei unterschiedliche Methoden zu unterscheiden: &#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300&amp;quot; heights=&amp;quot;300&amp;quot; caption=&amp;quot;Anschluss mechanischer Kontakte an einen µC&amp;quot;&amp;gt;&lt;br /&gt;
Image:Active Low.gif|&#039;&#039;&#039;Active Low:&#039;&#039;&#039; Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet. Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt, wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter &#039;&#039;&#039;Pull-Up&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
Image:Active High.gif|&#039;&#039;&#039;Active High:&#039;&#039;&#039; Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet. Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein &#039;&#039;&#039;Pull-Down&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten. &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert von Pull-Up- und Pull-Down-Widerständen ist an sich nicht kritisch. Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert. Die AVRs verfügen an den meisten Pins über zuschaltbare interne Pull-Up Widerstände (vgl. Abschnitt [[AVR-GCC-Tutorial#Interne Pull-Up Widerstände|Interne Pull-Up Widerstände]]), welche insbesondere wie hier bei Tastern und ähnlichen Bauteilen (z.&amp;amp;nbsp;B. Drehgebern) statt externer Bauteile verwendet werden können. Interne Pull-Down-Widerstand sind nicht verfügbar und müssen daher in Form zusätzlicher Bauteile in die Schaltung eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
==== Taster entprellen ====&lt;br /&gt;
&lt;br /&gt;
Siehe: &#039;&#039;[[Entprellung#Warteschleifen-Verfahren|Entprellung: Warteschleifen-Verfahren]]&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Außerdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr (abgesehen von möglicherweise auftretenden Interrupts, falls welche aktiviert sind). Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.&amp;amp;nbsp;B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.&amp;amp;nbsp;B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt, und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Einschränkung liegt darin, daß sie möglicherweise länger warten, als erwartet, nämlich in dem Fall, daß Interrupts auftreten und die _delay...()-Funktion unterbrechen. Genau genommen warten diese nämlich nicht eine bestimmte Zeit, sondern verbrauchen eine bestimmte Anzahl von Prozessortakten. Die wiederum ist so bemessen, daß ohne Unterbrechung durch Interrupts die gewünschte Wartezeit erreicht wird.&lt;br /&gt;
Wird das Warten aber durch eine oder mehrere ISR unterbrochen, die zusammen 1% Prozessorzeit verbrauchen, dann dauert das Warten etwa 1% länger. Bei 50% Last durch die ISR dauert das Warten doppelt solange wie gewünscht, bei 90% zehnmal solange...&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen bis 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4µs warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.&amp;amp;nbsp;B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms)&lt;br /&gt;
{&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 )                  // Endlosschleife&lt;br /&gt;
    {                &lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.&amp;amp;nbsp;B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.7 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Es ist nicht möglich, eine Variable als Argument zu übergeben. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Trick zur Übergabe einer Variablen an _delay_ms():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void sleep ( uint8_t ms )&lt;br /&gt;
{&lt;br /&gt;
    for(; ms &amp;gt; 0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    int x = 0;                  // Variable als Wartezeit erstellen&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        sleep(x); &lt;br /&gt;
        x++;       &lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die _delay_ms() und die _delay_us aus &#039;&#039;&#039;avr-libc 1.7.0&#039;&#039;&#039; sind fehlerhaft. _delay_ms () läuft 4x schneller als erwartet. Abhilfe ist eine korrigierte Includedatei: [http://www.mikrocontroller.net/topic/196738#1943039]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:2em;&amp;quot;&amp;gt;&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Tritt ein Interrupt auf, unterbricht (engl. interrupts) der Controller die Verarbeitung des Hauptprogramms und verzweigt zu einer Interruptroutine. Das Hauptprogramm wird also beim Eintreffen eines Interrupts unterbrochen, die Interruptroutine ausgeführt und danach erst wieder das Hauptprogramm an der Unterbrechungsstelle fortgesetzt (vgl. die Abbildung).&lt;br /&gt;
&lt;br /&gt;
Um Interrupts verarbeiten zu können, ist folgendes zu beachten:&lt;br /&gt;
&lt;br /&gt;
* Für jede aktivierte Interruptquelle ist eine Funktion zu programmieren, in der die beim Auftreten des jeweiligen Interrupts erforderlichen Verarbeitungsschritte enthalten sind. Für diese Funktion existieren verschiedene Bezeichnungen. Üblich sind die englischen Begriffe Interrupt-Handler oder Interrupt-Service-Routinen (ISR), man findet aber auch die Bezeichnungen Interruptverarbeitungs- oder -behandlungsroutine oder auch kurz Interruptroutine. Zum Beispiel wird üblicherweise in der ISR zur Verarbeitung des Empfangsinterrupts eines UARTs (UART-RX Interrupt) das empfangene Zeichen in einen Zwischenspeicher (FIFO-Buffer) kopiert, dessen Inhalt später von anderen Programmteilen geleert wird. Sofern der Zwischenspeicher ausreichend groß ist, geht also kein Zeichen verloren, auch wenn im Hauptprogramm zeitintensive Operationen durchgeführt werden.&lt;br /&gt;
* Die benötigten Interrupts sind in den jeweiligen Funktionsbausteinen einzuschalten. Dies erfolgt über das jeweilige Aktivierungsbit (Interrupt Enable) in einem der Hardwareregister (z.B. RX(Complete)Interrupt Enable eines UARTs)&lt;br /&gt;
* Sämtliche Interrupts werden über einen weiteren globalen Schalter aktiviert und deaktiviert. Zur Verarbeitung der Interrupts ist dieser Schalter zu aktivieren (sei(), siehe unten).&lt;br /&gt;
 &lt;br /&gt;
Alle Punkte sind zu beachten. Fehlt z.B. die globale Aktivierung, werden Interruptroutinen auch dann nicht aufgerufen, wenn sie im Funktionsbaustein eingeschaltet sind und eine Behandlungsroutine verhanden ist.&lt;br /&gt;
&lt;br /&gt;
Siehe auch&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
* Artikel [[Interrupt]]&lt;br /&gt;
* Artikel [[Multitasking]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen sollten möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Microcontroller-Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammenhängen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039; Register.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit &lt;br /&gt;
| 7 || 6|| 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039; || &#039;&#039;&#039;INT0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039; || &#039;&#039;&#039;INTF0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;SE&#039;&#039;&#039;|| &#039;&#039;&#039;SM&#039;&#039;&#039;|| &#039;&#039;&#039;ISC11&#039;&#039;&#039;|| &#039;&#039;&#039;ISC10&#039;&#039;&#039;|| &#039;&#039;&#039;ISC01&#039;&#039;&#039;|| &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R || R || R/W || R/W || R/W || R/W || R/W || R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt über den Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC11 || ISC10 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC01 || ISC00 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Dieses wird automatisch wieder gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.  Es ist möglich das GIE-Bit in der ISR zu setzen und so schon wieder weitere Interrupts zuzulassen - allerdings sollte man damit vorsichtig sein und genau wissen was man damit macht. Kritisch wird es vor allem wenn der gleiche Interrupt noch einmal kommt, bevor die ISR abgearbeitet ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit avr-gcc ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;Anmerkung eines Nutzers: Ich habe mir das Thema hier angearbeitet und hatte am Anfang starke Probleme: Jeder Interrupt muss nochmals einzeln aktiviert werden. Es reicht nicht nur per &#039;&#039;sei()&#039;&#039; die Interrupts global zu aktiveren.&#039;&#039; - mthomas: Hoffentlich duch die modifizerte Einleitung etwas offensichtlicher erläutert. Ansonsten bitte per Eintrag auf die Diskussionseite nochmals melden) --&amp;gt; &lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrupts aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Zu den aktivierten Interrupts ist eine Funktion zu programmieren, deren Code aufgerufen wird, wenn der betreffende Interrupt auftritt (Interrupt-Handler, Interrupt-Service-Routine). Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe den [[AVR-GCC-Tutorial#Anhang|Anhang]].)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h  - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR(XXX,ISR_NOBLOCK) /* veraltet: INTERRUPT(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt-Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.&amp;amp;nbsp;B. &#039;&#039;TIMER0_OVF_vect&#039;&#039;). Der Unterschied im Vergleich zu einer herkömmlichen ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit durch Einfügen einer SEI-Anweisung direkt wieder gesetzt und somit alle Interrupts zugelassen werden &amp;amp;ndash; auch XXX-Interrupts. &lt;br /&gt;
&lt;br /&gt;
Bei unsachgemässer Handhabung kann dies zu erheblichen Problemen durch Rekursion wie einem Stack-Overflow oder anderen unerwarteten Effekten führen und sollte wirklich nur dann eingesetzt werden, wenn man sich sicher ist, das Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
Insbesondere sollte möglichst am ISR-Anfang die auslösende IRQ-Quelle deaktiviert und erst am Ende der ISR wieder aktiviert werden. Robuster als die Verwendung einer NOBLOCK-ISR ist daher folgender ISR-Aufbau:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR (XXX) &lt;br /&gt;
{&lt;br /&gt;
    // Implementiere die ISR ohne zunaechst weitere IRQs zuzulassen&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Deaktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Erlaube alle Interrupts (ausser XXX)&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    //... Code ...&lt;br /&gt;
&lt;br /&gt;
    // IRQs global deaktivieren um die XXX-IRQ wieder gefahrlos &lt;br /&gt;
    // aktivieren zu koennen&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Aktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Weise kann sich die XXX-IRQ nicht selbst unterbrechen, was zu einer Art Endlosschleife führen würde.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen, die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)) als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.&amp;amp;nbsp;B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird innerhalb einer ISR mehrfach auf eine mit volatile deklarierte Variable zugegriffen, wirkt sich dies ungünstig auf die Verarbeitungsgeschwindigkeit aus, da bei jedem Zugriff mit dem Speicherinhalt abgeglichen wird. Da bei AVR-Controllern &#039;&#039;innerhalb&#039;&#039; einer ISR keine Unterbrechungen zu erwarten sind, bietet es sich an, einen Zwischenspeicher in Form einer lokalen Variable zu verwenden, deren Inhalt zu Beginn und am Ende mit dem der volatile Variable synchronisiert wird. Lokale Variable werden bei eingeschalteter Optimierung mit hoher Wahrscheinlichkeit in Prozessorregistern verwaltet und der Zugriff darauf ist daher nur mit wenigen internen Operationen verbunden. Die ISR aus dem vorherigen Beispiel lässt sich so optimieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
&lt;br /&gt;
   tmp_kc = gKeyCounter; // Uebernahme in lokale Arbeitsvariable&lt;br /&gt;
&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   gKeyCounter = tmp_kc; // Zurueckschreiben&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich die Disassemblies (Ausschnitte der &amp;quot;lss-Dateien&amp;quot;, compiliert für ATmega162) im Anschluss. Man erkennt den viermaligen Zugriff auf die Speicheraddresse von &#039;&#039;gKeyCounter&#039;&#039; (hier 0x032A) in der ISR ohne &amp;quot;Cache&amp;quot;-Variable und den zweimaligen Zugriff in der Variante mit Zwischenspeicher. Im Beispiel ist der Vorteil gering, bei komplexeren Routinen kann die Zwischenspeicherung in lokalen Variablen jedoch zu deutlicheren Verbesserungen führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
    if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     876:	ca 99       	sbic	0x19, 2	; 25&lt;br /&gt;
     878:	0a c0       	rjmp	.+20     	; 0x88e &amp;lt;__vector_13+0x24&amp;gt;&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
     87a:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     87e:	88 3c       	cpi	r24, 0xC8	; 200 &lt;br /&gt;
     880:	40 f4       	brcc	.+16     	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
     882:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	02 c0       	rjmp	.+4      	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
     88e:	10 92 2a 03 	sts	0x032A, r1&lt;br /&gt;
     892:	8f 91       	pop	r24&lt;br /&gt;
     894:	0f 90       	pop	r0&lt;br /&gt;
     896:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     898:	0f 90       	pop	r0&lt;br /&gt;
     89a:	1f 90       	pop	r1&lt;br /&gt;
     89c:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
 &lt;br /&gt;
   tmp_kc = gKeyCounter;&lt;br /&gt;
     876:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
 &lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     87a:	ca 9b       	sbis	0x19, 2	; 25&lt;br /&gt;
     87c:	02 c0       	rjmp	.+4      	; 0x882 &amp;lt;__vector_13+0x18&amp;gt;&lt;br /&gt;
     87e:	80 e0       	ldi	r24, 0x00	; 0&lt;br /&gt;
     880:	03 c0       	rjmp	.+6      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
     882:	88 3c       	cpi	r24, 0xC8	; 200&lt;br /&gt;
     884:	08 f4       	brcc	.+2      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   gKeyCounter = tmp_kc;&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	8f 91       	pop	r24&lt;br /&gt;
     88e:	0f 90       	pop	r0&lt;br /&gt;
     890:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     892:	0f 90       	pop	r0&lt;br /&gt;
     894:	1f 90       	pop	r1&lt;br /&gt;
     896:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== volatile und Pointer ===&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;volatile&#039;&#039;&#039; in Verbindung mit Pointern ist zu beachten, ob der Pointer selbst oder die Variable, auf die der Pointer zeigt, &#039;&#039;&#039;volatile&#039;&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t *a;   // das Ziel von a ist volatile&lt;br /&gt;
&lt;br /&gt;
uint8_t *volatile a;   // a selbst ist volatile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls der Pointer volatile ist (zweiter Fall im Beispiel), ist zu beachten, dass der Wert des Pointers, also eine Speicheradresse, intern in mehr als einem Byte verwaltet wird. Lese- und Schreibzugriffe im Hauptprogramm (außerhalb von Interrupt-Routinen) sind daher so zu implementieren, dass alle Teilbytes der Adresse konsistent bleiben, vgl. dazu den folgenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interrupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
&lt;br /&gt;
   // oder: mehrfach lesen, bis man konsistente Daten hat&lt;br /&gt;
   uint16_t count1 = gMyCounter16Bit;&lt;br /&gt;
   uint16_t count2 = gMyCounter16Bit;&lt;br /&gt;
   while (count1 != count2) {&lt;br /&gt;
       count1 = count2;&lt;br /&gt;
       count2 = gMyCounter16Bit;&lt;br /&gt;
   }&lt;br /&gt;
   tmpCnt = count1;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die avr-libc bietet ab Version 1.6.0(?) einige Hilfsfunktionen/Makros, mit der im Beispiel oben gezeigten Funktionalität, die zusätzlich auch sogenannte [http://en.wikipedia.org/wiki/Memory_barrier memory barriers] beinhalten. Diese stehen nach #include &amp;lt;util/atomic.h&amp;gt; zur Verfügung.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // analog zu cli, Zugriff, sei:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_FORCEON) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
// oder:&lt;br /&gt;
&lt;br /&gt;
    // analog zu Sicherung des SREG, cli, Zugriff und Zurückschreiben des SREG:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch [http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html Dokumentation der avr-libc zu atomic.h]&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.&amp;amp;nbsp;B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0 auf &#039;&#039;&#039;1&#039;&#039;&#039;, PORTA ist danach 0b0000000&#039;&#039;&#039;1&#039;&#039;&#039;. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-&#039;&#039;&#039;ODER&#039;&#039;&#039;-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8. (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Interruptflags löschen ==&lt;br /&gt;
&lt;br /&gt;
Beim Löschen von Interruptflags haben AVRs eine Besonderheit, die auch im Datenblatt beschrieben ist: Es wird zum Löschen eine 1 in das betreffende Bit geschrieben. &lt;br /&gt;
&lt;br /&gt;
Hinweis:&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei Registern mit mehreren Interrupt-Flag-Bits (wie die Timer Interrupt Flag Register)  &#039;&#039;&#039;nicht&#039;&#039;&#039;  die übliche bitweise VerODERung nehmen, sondern eine direkte Zuweisung machen. Da sonst weitere Flags, als nur das gewünschte, ebenfalls gelöscht werden könnten.&amp;lt;br /&amp;gt;&lt;br /&gt;
([http://www.mikrocontroller.net/topic/171148#1640133 Erklärung]).&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten [[Sleep Mode |&#039;&#039;Sleep-Modes&#039;&#039;]] (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
;set_sleep_mode (uint8_t mode): Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.&amp;amp;nbsp;B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
;sleep_enable(): Aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
;sleep_cpu(): Versetzt den Controller in den Schlafmodus .sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt.&lt;br /&gt;
;sleep_disable(): Deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
;sleep_mode(): Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts (besser nicht benutzen).&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sleep Modi ==&lt;br /&gt;
Zu beachten ist, dass unterschiedliche Prozessoren aus der AVR Familie unterschiedliche Sleep-Modi unterstützen oder nicht unterstützen. Auskunft über die tatsächlichen Gegebenheiten gibt, wie immer, das zum Prozessor gehörende Datenblatt. Die unterschiedlichen Modi unterscheiden sich dadurch, welche Bereiche des Prozessors abgeschaltet werden. Damit korrespondiert unmittelbar welche Möglichkeiten es gibt, den Prozessor aus den jeweiligen Sleep Modus wieder aufzuwecken.&lt;br /&gt;
&lt;br /&gt;
;Idle Mode (SLEEP_MODE_IDLE): Die CPU kann durch SPI, USART, Analog Comperator, ADC, TWI, Timer, Watchdog und irgendeinen anderen Interrupt wieder aufgeweckt werden.&lt;br /&gt;
&lt;br /&gt;
;ADC Noise Reduction Mode (SLEEP_MODE_ADC): In diesem Modus liegt das Hauptaugenmerk darauf, die CPU soweit stillzulegen, dass der ADC möglichst keine Störungen aus dem inneren der CPU auffangen kann. Aufwachen aus diesem Modus kann ausgelöst werden durch den ADC, externe Interrupts, TWI, Timer und Watchdog.&lt;br /&gt;
&lt;br /&gt;
;Power-Down Mode (SLEEP_MODE_PWR_DOWN): In diesem Modus wird ein externer Oszillator (Quarz, Quarzoszillator) gestoppt. Geweckt werden kann die CPU durch einen externen Level Interrupt, TWI, Watchdog, Brown-Out-Reset&lt;br /&gt;
&lt;br /&gt;
;Power-Save-Mode (SLEEP_MODE_PWR_SAVE): Power-Save ist identisch zu Power-Down mit einer Ausnahme: Ist der Timer 2 auf die Verwendung eines externen Taktes konfiguriert, so läuft dieser Timer auch im Power-Save weiter und kann die CPU mit einem Interrupt aufwecken.&lt;br /&gt;
&lt;br /&gt;
;Standby-Mode (SLEEP_MODE_STANDBY, SLEEP_MODE_EXT_STANDBY): Voraussetzung für den Standby-Modus ist die Verwendung eines Quarzes oder eines Quarzoszillators (also einer externen Taktquelle). Ansonsten ist dieser Modus identisch zum Power-Down Modus. Vorteil dieses Modus ist eine kürzere Aufwachzeit.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Zeiger =&lt;br /&gt;
Zeiger (engl. &#039;&#039;Pointer&#039;&#039;) sind Variablen, die die Adresse von Daten oder Funktionen enthalten und belegen 16 Bits. Die Größe hängt mit dem adressierbaren Speicherbereich zusammen und der GCC reserviert dann den entsprechenden Platz.&lt;br /&gt;
Ggf. ist es also günstiger, Indizes auf Arrays (Listen) zu verwenden, so dass der GCC für die Zeigerarithmetik den erforderlichen RAM nur temporär benötigt.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[Zeiger]]&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.&amp;amp;nbsp;B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Binäre Daten zum Programm hinzufügen]]&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;alloziert&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Flash mit PROGMEM und pgm_read ==&lt;br /&gt;
&lt;br /&gt;
→ [http://nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html avr-libc: Doku zu avr/pgmspace.h]&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc erst ab Version 4.7 &amp;quot;transparent&amp;quot; möglich. Um Daten aus dem Flash zu lesen, muss die AVR-Instruktion LPM (&#039;&#039;Load from Program Memory&#039;&#039;) erzeugt werden, bei Controllern mit mehr als 64kiB Flash auch ELMP.&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es das AVR-spezifische GCC-Attribut &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt;, mit dem eine Variablendeklaration im &#039;&#039;static storage&#039;&#039;&amp;lt;ref&amp;gt;Variablen der Speicherklasse &#039;&#039;static storage&#039;&#039; haben eine unbegrenzte Lebensdauer.  Beispiel für solche Variablen sind globale Variablen, aber auch static-Variablen innerhalb einer Funktion gehören dazu.  Beispiele für Variablen, die nicht &#039;&#039;static storage&#039;&#039; sind: auto-Variablen (&amp;quot;normale&amp;quot; lokale Variablen), register-Variablen, durch malloc geschaffene Objekte, etc.&amp;lt;/ref&amp;gt; markiert werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const int value __attribute__((progmem)) = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effekt ist, dass die so markierte Variable nicht im RAM sondern im Flash angelegt wird.  Wird durch &amp;quot;normalen&amp;quot; C-Code auf solch eine Variable zugegriffen, wird jedoch aus dem RAM gelesen und nicht aus dem Flash!&lt;br /&gt;
&lt;br /&gt;
Zum Lesen aus dem Flash stellt die avr-libc daher zahlreiche Makros zur Verfügung.  Zudem wird das Makro &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; definiert, das etwas Tipparbeit spart:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const int value PROGMEM = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; funktioniert im Wesentlichen wie ein Section-Attribut, das die Daten in der Section &amp;lt;tt&amp;gt;.progmem.data&amp;lt;/tt&amp;gt; ablegt.  Im Gegensatz zum Section-Attribut werden jedoch noch weitere Prüfungen unternommen, ab avr-gcc 4.6 etwa muss die entsprechende Variable &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; sein.&lt;br /&gt;
&lt;br /&gt;
=== Integer und float ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen von Skalaren stellt die avr-libc folgende Makros zu Verfügung, die jeweils ein Argument erhalten: Die 16-Bit Adresse des zu lesenden Wertes&amp;lt;ref&amp;gt;Damit ist der mögliche Speicherbereich für Flash-Konstanten auf 64kiB begrenzt. Einige pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher, intern via Assembler-Anweisung ELPM. Die Initialisierungswerte des Speicherinhalts jenseits der 64kiB-Marke müssen dann jedoch auf anderem Weg angelegt werden, d.h. nicht per PROGMEM. Evtl. eigene Section und Linker-Optionen. Alt und nicht ganz korrekt: Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kiB Flash bei Controllern mit mehr als 64kiB.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| {{Tabelle}}&lt;br /&gt;
|+ Übersicht der &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt; Funktionen aus&amp;lt;br/&amp;gt;dem Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; der avr-libc&lt;br /&gt;
|-&lt;br /&gt;
! Gelesener Wert || &amp;lt;tt&amp;gt;pgm_read_xxx&amp;lt;/tt&amp;gt; || Anzahl Bytes&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint8_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_byte&amp;lt;/tt&amp;gt; || 1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint16_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; || 2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint32_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_dword&amp;lt;/tt&amp;gt; || 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;float&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_float&amp;lt;/tt&amp;gt;&amp;lt;ref&amp;gt;ab avr-libc 1.7.0&amp;lt;/ref&amp;gt; || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Soll ein Zeiger gelesen werden, so verwendet man &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; und castet das Ergebnis zum gewünschten Zeiger-Typ.&lt;br /&gt;
&lt;br /&gt;
;Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t aByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* int-Array */&lt;br /&gt;
const int anArray[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
  /* Zeiger */&lt;br /&gt;
  static const uint8_t* const aPointer PROGMEM = &amp;amp;aByte;&lt;br /&gt;
&lt;br /&gt;
  uint8_t a        = pgm_read_byte (&amp;amp;aByte);&lt;br /&gt;
  int a2           = (int) pgm_read_word (&amp;amp;anArray[2]);&lt;br /&gt;
  const uint8_t* p = (const uint8_t*) pgm_read_word (&amp;amp;aPointer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
In den Flash-Funktionen der avr-libc sind keine der pgm_read_xxxx Nomenklatur folgenden Funktionen, die Speicherblöcke auslesen oder vergleichen. Die enstprechende Funktionen sind Varianten von &amp;lt;tt&amp;gt;memcpy&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp&amp;lt;/tt&amp;gt; und heißt &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, usw.  Für weitere Funktionen und deren Prototypen siehe die Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Strings sind in C nichts anderes als eine Abfolge von Zeichen und einem &amp;lt;tt&amp;gt;&#039;\0&#039;&amp;lt;/tt&amp;gt; als Stringende. Der prinzipielle Weg ist daher identisch zum  Lesen von Bytes, wobei auf die [[FAQ#Wie funktioniert String-Verarbeitung in C?|Besonderheiten von Strings]] wie 0-Terminierung geachtet werden muss.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
size_t my_string_length (const char* addr)&lt;br /&gt;
{&lt;br /&gt;
    size_t length = 0;&lt;br /&gt;
&lt;br /&gt;
    while (pgm_read_byte (addr++))&lt;br /&gt;
    {&lt;br /&gt;
        len++;&lt;br /&gt;
    }&lt;br /&gt;
    return length;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Unterstützung des Programmierers steht das Repertoire der str-Funktionen auch in jeweils eine Variante zur Verfügung, die mit dem Flash-Speicher arbeiten kann. Die Funktionsnamen tragen den Suffix &amp;lt;tt&amp;gt;_P&amp;lt;/tt&amp;gt;. Darüber hinaus gibt es das Makro &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt;, das ein String-Literal im Flash-Speicher ablegt und die Adresse des Strings liefert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Liefert true zurück, wenn string_im_ram gleich &amp;quot;Hallo Welt&amp;quot; ist. */&lt;br /&gt;
int foo (const char* string_im_ram)&lt;br /&gt;
{&lt;br /&gt;
    return strcmp_P (string_im_ram, PSTR (&amp;quot;Hallo Welt&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; nur innerhalb von Funktionen verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
; Array aus Strings:&lt;br /&gt;
&lt;br /&gt;
Arrays aus Strings im Flash-Speicher werden in zwei Schritten angelegt:&lt;br /&gt;
&lt;br /&gt;
# Zuerst die einzelnen Elemente des Arrays und&lt;br /&gt;
# im Anschluss ein Array, in dem die Startaddressen der Strings abgelegt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen wird zuerst die Adresse des gewünschten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, um auf das Element (den String) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const char str1[] PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char str2[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char str3[] PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const array[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   str1, str2, str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Lese die Adresse des i-ten Strings aus array[]&lt;br /&gt;
    const char *parray = (const char*) pgm_read_word (&amp;amp;array[i]);&lt;br /&gt;
&lt;br /&gt;
    // Kopiere den Inhalt der Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, parray);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist, die Strings in einem 2-dimensionalen char-Array abzulegen anstatt deren Adresse in einem 1-dimensionalen Adress-Array zu speichern.&lt;br /&gt;
&lt;br /&gt;
Vorteil ist, dass der Code einfacher wird.  Nachteil ist, dass bei unterschiedlich langen Strings Speicherplatz verschwendet wird, weil sich die Array-Dimension and der Länge des längsten Strings orientieret.  Bei in etwa gleich langen Strings kann es aber sogar Speicherplatz sparen, denn es die Adressen der einzelnen Strings müssen nicht abgespeichert werden.&amp;lt;ref&amp;gt;In unserem Hund-Katze-Maus Beispiel belegt die erste Variante 22 Bytes Daten und 18 Bytes Code, die zweite Variante mit 2-dimensionalem Array belegt 18 Bytes Daten und 20 Bytes Code. Gemessen wurde mit avr-gcc 4.8 -Os für ATmega8.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Die &amp;quot;6&amp;quot; ist 1 plus die Länge des längsten Strings (&amp;quot;Katze&amp;quot;)&lt;br /&gt;
const char array[][6] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere den Inhalt der i-ten Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, array[i]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_array How do I put an array of strings completely in ROM?]&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so kompliziert ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm (Flash) und Datenspeicher (RAM) auf. Der C-Standard sieht keine unterschiedlichen Adressräume vor.&lt;br /&gt;
&lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart (const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette, dann weiß die Funktion nicht, ob die Adresse in den Flash-Speicher oder das RAM zeigt. Weder aus dem Pointer-Wert, also dem Zahlenwert, noch aus dem &amp;quot;const&amp;quot; kann auf den Ort der Ablage geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler bilden die Harvard-Architektur ab, indem sie in einen Pointer nicht nur die Adresse speichern, sondern auch den Ablageort wie &#039;&#039;Flash&#039;&#039; oder &#039;&#039;RAM&#039;&#039;. In einem Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben.&lt;br /&gt;
&lt;br /&gt;
Dies hat jedoch auch Nachteile, denn bei jedem Zugriff über einen Zeiger muss zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden, wie der Zugriff auszuführen ist und entsprechend länglicher und langsamer wird der erzeugte Code.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
=== Variablenzugriff &amp;gt;64kB ===&lt;br /&gt;
&lt;br /&gt;
Die Zeiger beim avr-gcc sind nur 16 Bit breit, können somit also nur 64kiB Datenspeicher adressieren. Als Funktionspointer können sie beim AVR bis zu 128 kiB Programmspeicher adressieren, weil Funktionsadressen immer 16-Bit Worte adressieren und nicht Bytes. Um Flashzugriff jenseits von 64KiB zu bewerkstelligen gibt es mehrere Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Address-Spaces wie &amp;lt;tt&amp;gt;__flash1&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;, siehe Abschnitt &amp;quot;[[#Jenseits von flash|Jenseits von __flash]]&amp;quot;.&lt;br /&gt;
* Die Funktionen bzw. Makros &amp;lt;tt&amp;gt;pgm_read_xxx_far&amp;lt;/tt&amp;gt; der AVR-Libc, wie im folgenden beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unverständlicherweise gibt es in der AVR-Libc keine Funktion, um 32-Bit Pointer zu erhalten. Hier schafft ein eigenes GNU-C99 Makro Abhilfe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Macro to access strings defined in PROGMEM above 64kB&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR(var)                     \&lt;br /&gt;
({ uint_farptr_t tmp;                \&lt;br /&gt;
   __asm__ (                         \&lt;br /&gt;
       &amp;quot;ldi    %A0, lo8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %B0, hi8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %C0, hh8(%1)&amp;quot;         \&lt;br /&gt;
       : &amp;quot;=d&amp;quot; (tmp)                  \&lt;br /&gt;
       : &amp;quot;i&amp;quot;  (&amp;amp;(var)));             \&lt;br /&gt;
   tmp;                              \&lt;br /&gt;
})&lt;br /&gt;
//-------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//===================================================================&lt;br /&gt;
// Define a section above 64kiB (FAR_SECTION)&lt;br /&gt;
// and add the required linker argument below&lt;br /&gt;
// -Wl,--section-start=.far_section=0x10000&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR_SECTION   __attribute__((__section__(&amp;quot;.far_section&amp;quot;)))&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Just a Sample&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
const char MyString[] FAR_SECTION = &amp;quot;Hier liegt mein FAR-Teststring!&amp;quot;;&lt;br /&gt;
const char MyBmp64[]  FAR_SECTION = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  char MyChar;&lt;br /&gt;
  DDRC = 0xFF;&lt;br /&gt;
  do&lt;br /&gt;
  {&lt;br /&gt;
    MyChar = pgm_read_byte_far(FAR(MyBmp64));&lt;br /&gt;
    PORTC  = MyChar;&lt;br /&gt;
  }&lt;br /&gt;
  while(MyChar);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. man muss&lt;br /&gt;
* Das Makro &amp;lt;tt&amp;gt;FAR&amp;lt;/tt&amp;gt; im Quellcode einfügen&lt;br /&gt;
* Die Definition der neuen Section &amp;lt;tt&amp;gt;FAR_SECTION&amp;lt;/tt&amp;gt; einfügen&lt;br /&gt;
* Die Variablen mit dieser Section kennzeichnen&lt;br /&gt;
* Dem Linker mittels Kommandozeilenoption die Startadrese dieser Section mitteilen&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf diese Variablen kann nur mittels direkter Pointerarithmetik erfolgen, eine Indizierung von Arrays mit variablem Index ist nicht möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int n=3;&lt;br /&gt;
MyChar = pgm_read_byte_far(FAR(MyBmp64)+n);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flash mit __flash und Embedded-C ==&lt;br /&gt;
&lt;br /&gt;
Ab Version 4.7 unterstützt avr-gcc &#039;&#039;Adress-Spaces&#039;&#039; gemäß dem Embedded-C Dokument ISO/IEC TR18037.  Der geläufigste Adress-Space ist &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;, der im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; kein GCC-Attribut ist, sondern ein Qualifier und damit syntaktisch ähnlich verwendet wird wie &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;volatile&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GCC kennt keine eigene Option zum Aktivieren von Embedded-C, es wird als GNU-C Erweiterung behandelt. Daher müssen C-Module, die Address-Spaces verwenden, mit &amp;lt;tt&amp;gt;-std=gnu99&amp;lt;/tt&amp;gt; o.ä. compiliert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash int value = 10;&lt;br /&gt;
&lt;br /&gt;
int get_value (void)&lt;br /&gt;
{&lt;br /&gt;
  return value;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; sind keine speziellen Bibliotheksfunktionen oder -makros für den Zugriff mehr notwendig: Der Code zum Lesen der Variable ist &amp;quot;normales&amp;quot; C.&lt;br /&gt;
# Die Variable wird im richtigen Speicherbereich (Flash) angelegt.&lt;br /&gt;
# &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; ist nur zusammen mit read-only Objekten oder Zeigern, d.h. nur zusammen mit &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt;, erlaubt.&lt;br /&gt;
# Zugriffe wie im obigen Beispiel können (weg)optimiert werden.  Das Beispiel entspricht einem &amp;quot;&amp;lt;tt&amp;gt;return 10&amp;lt;/tt&amp;gt;&amp;quot;.  Es besteht keine Notwendigkeit, für &amp;lt;tt&amp;gt;value&amp;lt;/tt&amp;gt; überhaupt Flash-Speicher zu reservieren.&lt;br /&gt;
&lt;br /&gt;
Auch Zeiger-Indirektionen sind problemlos möglich.  Zu beachten ist, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; auf der richtigen Seite des &amp;quot;&amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;&amp;quot; in der Zeigerdeklaration bzw. -definition steht:&lt;br /&gt;
* &#039;&#039;&#039;Rechts vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger selbst liegt im Flash&lt;br /&gt;
* &#039;&#039;&#039;Links vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger enthält eine Flash-Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// val ist eine Variable im Flash&lt;br /&gt;
const __flash int val = 42;&lt;br /&gt;
&lt;br /&gt;
// pval liegt auch im Flash und enthält die Adresse von val&lt;br /&gt;
const __flash int* const __flash pval = &amp;amp;val;&lt;br /&gt;
&lt;br /&gt;
char get_val (void)&lt;br /&gt;
{&lt;br /&gt;
  // liest den Wert von val über die in pval abgelegte Adresse&lt;br /&gt;
  return *pval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
Um Speicherbereiche vom Flash in den RAM zu kopieren, gibt es zwei Möglichkeiten: Zum einen können wie bei &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; beschreiben die Funktionen der avr-libc wie &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;movmem_P&amp;lt;/tt&amp;gt;, etc. verwendet werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // buf wird auf dem Stack angelegt&lt;br /&gt;
    data_t buf;&lt;br /&gt;
    &lt;br /&gt;
    // Kopiere Daten vom Flash nach buf ins RAM&lt;br /&gt;
    memcpy_P (&amp;amp;buf, pdata, sizeof (data_t));&lt;br /&gt;
 &lt;br /&gt;
    // Sende die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum anderen kann eine Struktur auch über direktes Kopieren ins RAM geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere Daten ins RAM.  buf wird auf dem Stack angelegt&lt;br /&gt;
    const data_t buf = *pdata;&lt;br /&gt;
    &lt;br /&gt;
    // Verwendet die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Natürlich können auch Strings im Flash abgelegt werden und auch mit Funktionen wie &amp;lt;tt&amp;gt;strcpy_P&amp;lt;/tt&amp;gt; aus der avr-libc verarbeitet werden.  Zudem ist es möglich, Flash-Zeiger mit der Adresse eines String-Literals zu initialisieren:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define FSTR(X) ((const __flash char[]) { X } )&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    FSTR (&amp;quot;Hund&amp;quot;), FSTR (&amp;quot;Katze&amp;quot;), FSTR (&amp;quot;Maus&amp;quot;)&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
size_t get_len (uint8_t tier)&lt;br /&gt;
{&lt;br /&gt;
    return strlen_P (array[tier]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider sieht der Embedded-C Draft nicht vor, String-Literale direkt in einem anderen Adress-Space als &#039;&#039;generic&#039;&#039; anzulegen, so dass hier der Umweg über &amp;lt;tt&amp;gt;FSTR&amp;lt;/tt&amp;gt; genommen werden muss.  Dieses Konstrukt ist nur ausserhalb von Funktionen möglich und kann daher nicht als Ersatz für &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; aus der avr-libc dienen.&lt;br /&gt;
&lt;br /&gt;
Soll &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; ein 2-dimensonales Array sein anstatt ein 1-dimensionales Array von Zeigern, dann geht das ohne große Verrenkungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Die 6 ergibt sich aus 1 plus der Länge des längsten Strings &amp;quot;Katze&amp;quot;&lt;br /&gt;
const __flash char array[][6] = &lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiters besteht die Möglichkeit, &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; analog anzulegen, wie man es mit &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; machen würde:  Jeder String wird explizit angelegt und seine Adresse bei der Initialisierung von &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; verwendet.  Dies entspricht dem ersten Beispiel eines 1-dimensionalen Zeigerarrays:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char strHund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char strKatze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char strMaus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    strHund, strKatze, strMaus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Casts ===&lt;br /&gt;
&lt;br /&gt;
Embedded C fordert, dass zwei Adress-Spaces entweder disjunkt sind – d.h. sie enthalten keine gemeinsamen Adressen – oder aber ein Space komplett im anderen enthalten ist, also eine Teilmengen-Beziehung besteht.  Die Adress-Spaces von avr-gcc sind so implementiert, dass jeder Space Teilmenge jedes anderes ist.  Zwar haben Spaces wie RAM und Flash physikalisch keinen Speicherbereich gemein, allerdings ermöglicht diese Implementierung das Casten von Zeigern zu unterschiedlichen Adress-Spaces&amp;lt;ref&amp;gt;Im Gegensatz zu einem Attribute wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist ein (Adress Space) Qualifier Teil des Zeiger-Typs.&amp;lt;/ref&amp;gt;:  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
char read_char (const char *address, bool data_in_flash)&lt;br /&gt;
{&lt;br /&gt;
    if (data_in_flash)&lt;br /&gt;
        return *(const __flash char*) address;&lt;br /&gt;
    else&lt;br /&gt;
        return *address;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Cast selbst erzeugt keinen zusätzlichen Code, da eine RAM-Adresse und eine Flash-Adresse die gleiche Binärdarstellung haben.  Allerdings wird über den nach &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gecasteten Zeiger anders zugegriffen, nämlich per LPM.&lt;br /&gt;
&lt;br /&gt;
=== Jenseits von __flash ===&lt;br /&gt;
&lt;br /&gt;
Ausser &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gibt es auch folgende Address-Spaces:&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;: Mit &#039;&#039;N&#039;&#039; = 1..5 sind fünf weitere Spaces, die analog zu &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; funktionieren und deren Zeiger ebenfalls 16 Bit breit sind.  avr-gcc erwartet, dass die zugehörigen Daten, welche in die Section &amp;lt;tt&amp;gt;.progmem&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;&amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; abgelegt werden, so lokatiert sind, dass das high-Byte der Adresse (Bits 16..23) gerade &#039;&#039;N&#039;&#039; ist.  Dies wird in Binutils noch nicht unterstützt&amp;lt;ref&amp;gt;[http://sourceware.org/PR14406 Binutils PR14406]: Support .progmem&amp;lt;N&amp;gt;.data sections to work with GCC&#039;s [http://gcc.gnu.org/PR49868 PR49868].&amp;lt;/ref&amp;gt; (Stand Binutils 2.24) Die Unterstützung kann durch ein eigenes Linker-Skript erreicht werden, welches diese Sections wie vom Compiler erwartet lokatiert.&lt;br /&gt;
; &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;: Dieser Address-Space implementiert 3-Byte Zeiger und unterstützt Lesen über 64KiB-Segmentgrenzen hinweg.  Das MSB (Bit 23) gibt dabei an, ob der &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger eine Flash-Adresse enthält (Bit23 = 0) oder eine RAM-Adresse (Bit23 = 1), was folgenden Code erlaubt:&lt;br /&gt;
&amp;lt;!-- Bitte die Tabelle belassen, damit der Code richtig eingerückt wird! --&amp;gt;&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const __memx int a_flash = 42;&lt;br /&gt;
const        int a_ram   = 100;&lt;br /&gt;
&lt;br /&gt;
static int get_a (const __memx int* pa)&lt;br /&gt;
{&lt;br /&gt;
    return *pa;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    return get_a (&amp;amp;a_flash) + get_a (&amp;amp;a_ram);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}Dies bedeutet, dass erst zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden kann, ob aus dem RAM oder aus dem Flash gelesen werden soll, was &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt; im Vergleich zu den anderen Address-Spaces langsamer macht. Ausserdem ist zu beachten, dass &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger zwar 24-Bit Zeiger sind, die zugrundeliegende Adress-Arithmetik jedoch gemäß dem C-Standard erfolgt, also als 16-Bit Arithmetik.&lt;br /&gt;
&lt;br /&gt;
=== __flash, progmem und Portierbarkeit ===&lt;br /&gt;
&lt;br /&gt;
Da ab er aktuellen Compilerversion 4.7 sowohl &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; als auch &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; und die &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen zur Verfügung stehen, ergibt sich die Frage, welche Variante &amp;quot;besser&amp;quot; ist und wie zwischen ihnen hin- und her zu portieren ist.&lt;br /&gt;
&lt;br /&gt;
Zunächst sei erwähnt, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; kein Ersatz für &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; ist, sondern lediglich eine Alternative dazu.  Das &amp;quot;alte&amp;quot; progmem wird weiterhin mir gleicher Semantik unterstützt, so dass alter Code ohne Änderungen mit den neueren Compilerversionen übersetzbar bleibt.&lt;br /&gt;
&lt;br /&gt;
Von der Codegüte her dürften sich keine großen Unterschiede ergeben.  Es ist nicht zu erwarten, dass die eine oder die andere Variante wesentlich besseren oder schlechteren Code erzeugt — von einer Ausnahme abgesehen:  Der Wert beim Zugriff ist zur Compilezeit bekannt und kann daher eliminiert werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char x[] = { &#039;A&#039;, &#039;V&#039;, &#039;R&#039; };&lt;br /&gt;
&lt;br /&gt;
char foo (void)&lt;br /&gt;
{&lt;br /&gt;
    return x[2];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies wird übersetzt wie &amp;quot;&amp;lt;tt&amp;gt;return &#039;R&#039;;&amp;lt;/tt&amp;gt;&amp;quot;, und das Array &amp;lt;tt&amp;gt;x[]&amp;lt;/tt&amp;gt; kann komplett wegoptimiert werden und entfallen.&lt;br /&gt;
&lt;br /&gt;
==== progmem → __flash ====&lt;br /&gt;
&lt;br /&gt;
Portierung in diese Richtung bedeutet, alten Code anzupassen.  Zwingend ist die Portierung nicht, da &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; weiterhin unterstützt wird.&lt;br /&gt;
Allerdings ist eine Quelle mit &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; besser lesbar, denn der Code wird von den &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen befreit, die vor allem bei Mehrfach-Indirektion den Code ziemlich verunstalten und unleserlich machen können.&lt;br /&gt;
Weiterer Vorteil von &amp;lt;tt&amp;gt;_flash&amp;lt;/tt&amp;gt; ist, daß eine striktere Typprüfung erfolgen kann.&lt;br /&gt;
&lt;br /&gt;
Eine Portierung wird man in zwei Schritten vornehmen:&lt;br /&gt;
&lt;br /&gt;
;1. Definitionen von Flash-Variablen werden angepasst:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
static const char hund[]  PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char katze[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char maus[]  PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const tier[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char hund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char katze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char maus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
const __flash char * const __flash tier[] = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; wird nicht mehr benötigt.  Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; müssen Qualifier immer links von der definierten Variablen stehen; bei Attributen wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist das mehr oder weniger egal.&lt;br /&gt;
&lt;br /&gt;
Nachdem diese Anpassung erfolgreich abgeschlossen ist, folgt Schritt&lt;br /&gt;
&lt;br /&gt;
; 2. Der Code wird von &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Aufrufen bereinigt:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const char *tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    const char* ptier = (const char*) pgm_read_word (&amp;amp;tier[i]);&lt;br /&gt;
    return (char) pgm_read_byte (&amp;amp;ptier[0]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const __flash char * const __flash tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    return tier[i][0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Dateien direkt im Flash einbinden ==&lt;br /&gt;
&lt;br /&gt;
Wenn man größere Dateien direkt im Programm einbinden will, ohne sie vorher in C Quelltext umzuwandeln, muss man das mit dem Linker machen. Wie das geht steht hier.&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/webdoc/avrlibcreferencemanual/FAQ_1faq_binarydata.html Atmel, avr gcc Dokumentation]&lt;br /&gt;
* [http://nongnu.org/avr-libc/user-manual/FAQ.html#faq_binarydata Nongnu avr gcc Dokumentation]&lt;br /&gt;
&lt;br /&gt;
Wie man das dann praktisch umsetzt, sieht man in diesem Beitrag.&lt;br /&gt;
&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/361429?goto=4056910#4056910 Forumsbeitrag]: Binärdateien mittels Linker einbinden&lt;br /&gt;
&lt;br /&gt;
== Flash in der Anwendung schreiben ==&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option – auch bekannt als [[Bootloader]]-Support – können Teile des Flash-Speichers vom Anwendungsprogramm beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktion in einem besonderen Speicherbereich, der Boot-Section des Programmspeichers/Flash, abgelegt ist.&lt;br /&gt;
&lt;br /&gt;
Bei einigen kleinen AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* Forumsbeitrag [http://www.mikrocontroller.net/topic/163632#1561622 Daten in Programmspeicher speichern]&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Möchte man Werte aus einem Programm heraus so speichern, dass sie auch nach dem Abschalten der Versorgungsspannung noch erhalten bleiben und nach dem Wiederherstellen der Versorgungsspannung bei erneutem Programmstart wieder zur Verfügung stehen, dann benutzt man das EEPROM.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h der avr-libc definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16 Bit), Fließkommawerte (32 Bit, single-precision, float) und Datenblöcke geschrieben und gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen kümmern sich auch um diverse Details, die bei der Benutzung des EEPROMs normalerweise notwendig sind:&lt;br /&gt;
* EEPROM-Operationen sind im Vergleich relativ langsam. Man muss daher darauf achten, dass eine vorhergehende Operation abgeschlossen ist, ehe die nächste Operation mit dem EEPROM gestartet wird. Die in der avr-libc implementierten Funktionen aus eeprom.h berücksichtigten dies. Soll beim Aufruf einer EEPROM-Funktion sichergestellt werden, dass diese nicht intern in einer Warteschleife auf den Abschluss der vorherigen Operation wartet, kann vorher per eeprom_is_ready testen, ob der Zugriff auf den EEPROM-Speicher sofort möglich ist.&lt;br /&gt;
* Es ist darauf zu achten, dass die EEPROM-Funktionen nicht durch einen Interrupt unterbrochen werden. Einige Phasen des Zugriffs sind zeitkritisch und müssen in einer definierten bzw. begrenzten Anzahl von Takten durchgeführt werden. Durch einen unterbrechenden Interrupt würde diese Restriktion nicht mehr eingehalten. Auch dieses Detail wird von den avr-libc Funktionen berücksichtigt, so dass man sich als C-Programmierer nicht darum kümmern muss. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. Dies gilt für jede einzelne Zelle. &lt;br /&gt;
&lt;br /&gt;
Bei geschickter Programmierung (z.&amp;amp;nbsp;B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den gesamten EEPROM-Speicher, erreichen. Auf jeden Fall sollte man aber eine Abschätzung über die zu erwartende Lebensdauer des EEPROM durchführen. Wird ein Wert im EEPROM im Durchschnitt nur einmal pro Woche verändert, wird die garantierte Anzahl der Schreibzyklen innerhalb der voraussichtlichen Verwendungszeit des Controllers sicherlich nicht erreicht werden. Welcher Controller ist schon 100000 / 52 = 1923 Jahre im Einsatz? In diesem Fall lohnt es sich daher nicht, erweiterte Programmfunktionen zu implementieren, mit denen die Anzahl der Schreibzugriffe minimiert wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit, Schreibzyklen einzusparen, besteht in der Vorabprüfung, ob der zu speichernde Wert im EEPROM bereits enthalten ist und nur veränderte Werte zu schreiben. In aktuelleren Versionen der avr-libc sind bereits Funktionen enthalten, die solche Prüfungen enthalten (eeprom_update_*).&lt;br /&gt;
&lt;br /&gt;
Lesezugriffe können beliebig oft durchgeführt werden. Sie unterliegen keinen Einschränkungen in Bezug auf deren Anzahl. &lt;br /&gt;
&lt;br /&gt;
=== EEMEM ===&lt;br /&gt;
Um eine Variable im EEPROM anzulegen, stellt die avr-libc das Makro EEMEM zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Array */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3, 70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7, 79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die grundsätzliche Vorgehensweise ist identisch zur Verwendung von PROGMEM. Auch hier erzeugt man sich spezielle attributierte Variablen (EEMEM erledigt das), die vom Compiler/Linker nicht wie normale Variablen behandelt werden. Compiler/Linker kümmern sich zwar darum, dass diesen Variablen eine Adresse zugewiesen wird, diese Adresse ist dann aber die Adresse der &#039;Variablen&#039; im EEPROM. Um die dort gespeicherten Werte zu lesen bzw. zu schreiben, übergibt man diese Adresse an spezielle Funktionen, die die entsprechenden Werte aus dem EEPROM holen bzw. das EEPROM neu beschreiben.&lt;br /&gt;
&lt;br /&gt;
Die mittels EEMEM erzeugten &#039;Variablen&#039; sind also mehr als Platzhalter zu verstehen, denn als echte Variablen. Es geht nur darum, im C-Programm symbolische Namen zur Verfügung zu haben, anstatt mit echten EEPROM-Adressen hantieren zu müssen, etwas, das grundsätzlich aber auch genauso gut möglich ist. Nur muss man sich in diesem Fall dann selbst darum kümmern, dass mehrere &#039;Variablen&#039; ohne Überschneidung im EEPROM angeordnet werden.&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EEPROM_DEF 0xFF&lt;br /&gt;
&lt;br /&gt;
void eeprom_example (void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    // myByte lesen (Wert = 123)&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByte);&lt;br /&gt;
&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // Variablen eeFooByte geschrieben&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
&lt;br /&gt;
    // Beispiel fuer eeprom_update_byte: die EEPROM-Zelle wird nur&lt;br /&gt;
    // dann beschrieben, wenn deren Inhalt sich vom Parameterwert&lt;br /&gt;
    // unterscheidet. In diesem Beispiel erfolgt also kein Schreib-&lt;br /&gt;
    // zugriff, da die Werte gleich sind.&lt;br /&gt;
    eeprom_update_byte(&amp;amp;eeFooByte, myByte);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z. B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ((myByte = eeprom_read_byte (&amp;amp;eeFooByte)) == EEPROM_DEF)&lt;br /&gt;
    {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // lesen&lt;br /&gt;
    uint16_t myWord = eeprom_read_word (&amp;amp;eeFooWord);&lt;br /&gt;
&lt;br /&gt;
    // schreiben&lt;br /&gt;
    eeprom_write_word (&amp;amp;eeFooWord, 2222);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &amp;lt;code&amp;gt;eeprom_read_block()&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;eeprom_write_block()&amp;lt;/code&amp;gt;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Adresse und die Länge des Datenblocks in Bytes als &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t  myByteBuffer[3];&lt;br /&gt;
uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
void eeprom_block_example (void)&lt;br /&gt;
{&lt;br /&gt;
    /* Datenblock aus EEPROM lesen  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, 3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit 16-Bit Array */&lt;br /&gt;
    eeprom_read_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock in EEPROM schreiben */&lt;br /&gt;
    eeprom_write_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fließkommawerte lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
In der avr-libc stehen auch EEPROM-Funktionen für Variablen des Typs float (Fließkommazahlen mit &amp;quot;einfacher&amp;quot; Genauigkeit) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
float eeFloat EEMEM = 12.34f;&lt;br /&gt;
&lt;br /&gt;
float void eeprom_float_example(float value)&lt;br /&gt;
{&lt;br /&gt;
   /* float in EEPROM schreiben */&lt;br /&gt;
   eeprom_write_float(&amp;amp;eeFloat, value);&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM lesen */&lt;br /&gt;
   return  eeprom_read_float(&amp;amp;eeFloat);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklarationen abgeleitete EEPROM-Inhalt in eine Datei geschrieben werden. Die übliche Dateiendung ist .eep, Daten im Intel Hex-Format. Damit können Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. &lt;br /&gt;
&lt;br /&gt;
Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen, siehe dazu die Erläuterungen im [[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]].&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden, wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse&amp;lt;ref&amp;gt;vgl. Datenblatt Abschnitt Fuse Bits&amp;lt;/ref&amp;gt; nicht die korrekten Werte:&lt;br /&gt;
; EESAVE = 0 (programmed): Die Daten im EEPROM bleiben erhalten. Werden sie nicht neu geschrieben, so enthält das EEPROM evtl. Daten, die nicht mehr zum Programm passen.&lt;br /&gt;
; EESAVE = 1 (unprogrammed): Beim Programmieren werden die Daten im EEPROM gelöscht, also auf 0xff gesetzt.&lt;br /&gt;
&lt;br /&gt;
Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
=== Direkter Zugriff auf EEPROM-Adressen ===&lt;br /&gt;
&lt;br /&gt;
Will man direkt auf bestimmte EEPROM Adressen zugreifen, dann sind folgende Funktionen hilfreich, um sich die Typecasts zu ersparen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Byte aus dem EEPROM lesen&lt;br /&gt;
uint8_t EEPReadByte(uint16_t addr)&lt;br /&gt;
{&lt;br /&gt;
  return eeprom_read_byte((uint8_t *)addr);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Byte in das EEPROM schreiben&lt;br /&gt;
void EEPWriteByte(uint16_t addr, uint8_t val)&lt;br /&gt;
{&lt;br /&gt;
  eeprom_write_byte((uint8_t *)addr, val);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder als Makro:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define   EEPReadByte(addr)         eeprom_read_byte((uint8_t *)addr)     &lt;br /&gt;
#define   EEPWriteByte(addr, val)   eeprom_write_byte((uint8_t *)addr, val)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EEPWriteByte(0x20, 128);   // Byte an die Adresse 0x20 schreiben&lt;br /&gt;
...&lt;br /&gt;
Val=EEPReadByte(0x20);     // EEPROM-Wert von Adresse 0x20 lesen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Was steckt dahinter? - EEPROM-Register ===&lt;br /&gt;
Auch wenn es normalerweise keinen Grund gibt, in C selbst an den Steuerregistern herumzuschrauben - die eeprom Funktionen erledigen das alles zuverlässig - der Vollständigkeit halber der registermässige technische Unterbau.&lt;br /&gt;
Um das EEPROM anzusteuern, sind drei Register von Bedeutung:&lt;br /&gt;
;EEAR: Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL, da in einem 8-Bit-Register keine 512 Adressen adressiert werden können.&lt;br /&gt;
;EEDR: Hier werden die Daten eingetragen, die geschrieben werden sollen, bzw. es enthält die gelesenen Daten.&lt;br /&gt;
;EECR: Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Aufbau des EECR-Registers&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
| - || - || - ||- || EERIE || EEMWE || EEWE || EERE&lt;br /&gt;
|-&lt;br /&gt;
! Read/Write&lt;br /&gt;
| R || R || R || R || R/W || R/W || R/W || R/W&lt;br /&gt;
|-&lt;br /&gt;
!Init Value&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bedeutung der Bits&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Bit 4-7: nicht belegt&lt;br /&gt;
&lt;br /&gt;
;Bit 3 (EERIE): &#039;&#039;EEPROM Ready Interrupt Enable&#039;&#039;: Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7), wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0, wird kein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
;Bit 2 EEMWE): &#039;&#039;EEPROM Master Write Enable&#039;&#039;: Dieses Bit bestimmt, dass, wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE = 0 ist und EEWE = 1 gesetzt wird, hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst. Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&lt;br /&gt;
&lt;br /&gt;
;Bit 1 (EEWE): &#039;&#039;EEPROM Write Enable&#039;&#039;: Dieses Bit löst den Schreibvorgang aus, wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist, wird dieses Bit automatisch wieder auf 0 gesetzt und, sofern EERIE gesetzt ist, ein Interrupt ausgelöst. Ein Schreibvorgang sieht typischerweise wie folgt aus:&lt;br /&gt;
:# EEPROM-Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Daten übergeben an EEDR&lt;br /&gt;
:# Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1&lt;br /&gt;
:# (Optional) Warten, bis Schreibvorgang abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
;Bit 0 EERE: &#039;&#039;EEPROM Read Enable&#039;&#039;: Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden, wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen, die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit, wenn das Bit EEWE=0 ist. Ist der Lesevorgang abgeschlossen, wird das Bit wieder auf 0 gesetzt, und das EEPROM ist für neue Lese- und Schreibbefehle wieder bereit. Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&lt;br /&gt;
:# Bereitschaft zum Lesen prüfen (EEWE=0)&lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Lesezyklus auslösen mit EERE = 1&lt;br /&gt;
:# Warten, bis Lesevorgang abgeschlossen EERE = 0&lt;br /&gt;
:# Daten abholen aus EEDR&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von sprintf und printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel, d.h. formatiert, Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bieten sich &#039;&#039;&#039;sprintf&#039;&#039;&#039; oder &#039;&#039;&#039;printf&#039;&#039;&#039; an. Alle *printf-Varianten sind jedoch ziemlich speicherintensiv und der Einsatz in einem Mikrocontroller mit knappem Speicher muss sorgsam abgewogen werden.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;sprintf&#039;&#039;&#039; wird die Ausgabe zunächst in einem Puffer vorbereitet und anschließend mit einfachen Funktionen zeichenweise ausgegeben. Es liegt in der Verantwortung des Programmierers, genügend Platz im Puffer für die erwarteten Zeichen bereitzuhalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
// Ausgabe eines unsigned Integerwertes&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t puffer[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( puffer, &amp;quot;Zählerstand: %u&amp;quot;, value );&lt;br /&gt;
    uart_puts( puffer );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, den STREAM stdout (Standardausgabe) auf eine eigene Ausgabefunktion umzuleiten. Dazu wird dem Ausgabemechanismus der C-Bibliothek eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben. Wohin die Ausgabe dann tatsächlich stattfindet, ist Sache der Ausgabefunktion. Im Beispiel unten wird auf UART ausgegeben. Alle anderen, höheren Funktionen wie z.&amp;amp;nbsp;B. &#039;&#039;&#039;printf&#039;&#039;&#039;, greifen letztendlich auf diese primitive Ausgabefunktion zurück. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
&lt;br /&gt;
// a. Deklaration der primitiven Ausgabefunktion&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
// b. Umleiten der Standardausgabe stdout (Teil 1)&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
// c. Definition der Ausgabefunktion&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t antwort = 42;&lt;br /&gt;
    uart_init();&lt;br /&gt;
&lt;br /&gt;
    // b. Umleiten der Standardausgabe stdout (Teil 2)&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
&lt;br /&gt;
    // Anwendung&lt;br /&gt;
    printf( &amp;quot;Die Antwort ist %d.\n&amp;quot;, antwort );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
= Anmerkungen =&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:avr-gcc Tutorial| ]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90657</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90657"/>
		<updated>2015-12-11T14:29:23Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* EEPROM-Register */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[avr-gcc]] aus der [http://gcc.gnu.org/ GNU Compiler Collection] (GCC) erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Programmiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong] ([[C|Liste von C-Tutorials]]). Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern.&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR-Controllern finden. Beim Paket [[WinAVR]] gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden ständig weiterentwickelt. Einige Unterschiede, die sich im Verlauf der Entwicklung ergeben haben, werden hier und im Artikel [[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen]] zwar angesprochen, Anfängern und Umsteigern sei jedoch empfohlen, eine aktuelle Versionen zu nutzen.&lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in [[Media:AVR-GCC-Tutorial.pdf|PDF-Form]] erhältlich (nicht immer auf aktuellem Stand).&lt;br /&gt;
&lt;br /&gt;
== Weiterführende Kapitel ==&lt;br /&gt;
&lt;br /&gt;
Um dieses riesige Tutorial etwas überschaubarer zu gestalten, wurden einige Kapitel ausgelagert, die nicht unmittelbar mit den Grundlagen von avr-gcc in Verbindung stehen. All diese Seiten gehören zur [[:Kategorie:avr-gcc Tutorial]].&lt;br /&gt;
&lt;br /&gt;
;UART: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der UART|Der UART]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;ADC: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Analoge Ein- und Ausgabe|Analoge Ein- und Ausgabe (ADC)]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Timer: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Die Timer und Zähler des AVR|Die Timer und Zähler des AVR]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;LCD: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Ansteuerung]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Watchdog: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der Watchdog|Der Watchdog]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Assembler: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Assembler und Inline-Assembler|Assembler und Inline-Assembler]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;alte Quellen anpassen: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen anpassen]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Makefiles: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]]&#039;&#039; sowie als Alternative für sehr kleine Projekte → Hauptartikel: &#039;&#039;[[C ohne Makefile]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels einer AVR-Toolchain zu erstellen wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Eine AVR-Toolchain bestehend aus avr-gcc, den avr-Binutils (Assembler, Linker, etc) und einer Standard-C Bibliothek.  Üblich ist die AVR-LibC, die auch quasi in allen avr-gcc Distributionen enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Hardware wird keine benötigt – bis auf einen PC natürlich, auf dem der Compiler ablaufen kann.  Selbst ohne AVR-Hardware kann man also bereits C-Programme für AVRs schreiben, compiliern und sich das Look-and-Feel von avr-gcc sowie von IDEs wie [[Atmel Studio]], Eclipse oder leichtgewichtigeren Entwicklungsumbgebungen anschauen. Selbst das Debuggen und Simulieren ist mithilfe entsprechender Tools wie Debugger und Simulator in gewissen Grenzen möglich.&lt;br /&gt;
&lt;br /&gt;
Um Programme für AVRs mittels einer AVR-Toolchain zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR-Controllers, der vom avr-gcc Compiler unterstützt wird.&amp;lt;ref&amp;gt;Für eine Liste der unterstützten COntroller siehe die Dokumentation des Compilers oder [http://www.nongnu.org/avr-libc/user-manual/index.html#supported_devices AVR-Libc: Supported Devices].&amp;lt;/ref&amp;gt; Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. &lt;br /&gt;
&lt;br /&gt;
:Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man in den Artikeln [[AVR#Starterkits|AVR Starterkits]] und [[AVR-Tutorial: Equipment]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]].&lt;br /&gt;
&lt;br /&gt;
* Wer unter Windows und Linux gleichermassen entwickeln will, der sollte sich die [http://www.eclipse.org/ IDE Eclipse for C/C++ Developers] und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] ansehen. Beide sind unter Windows und Linux einfach zu installieren, siehe auch [[AVR Eclipse]]. Ebenfalls unter Linux und Windows verfügbar ist die Entwicklungsumgebung [http://www.codeblocks.org/ Code::Blocks]&amp;lt;ref&amp;gt;Aktuelle, stabile Versionen sind als Nightly Builds regelmäßig im [http://forums.codeblocks.org/ Forum] verfügbar.&amp;lt;/ref&amp;gt;. Innerhalb dieser Entwicklungsumgebung können ohne die Installation zusätzlicher Plugins &amp;quot;AVR-Projekte&amp;quot; angelegt werden. Für Linux gibt es auch noch das [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=25220 KontrollerLab].&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht klappt? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/gcc GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net AVRfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRfreaks] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispielprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.tty1.net/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu drei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs (ohne Anspruch auf Vollständigkeit): [http://www.eclipse.org/ Eclipse for C/C++ Developers] (d.h. inkl. CDT) und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] (für diverse Plattformen, u.a. Linux und MS Windows, IDE und Plugin kostenlos), [http://sourceforge.net/projects/kontrollerlab KontrollerLab] (Linux/KDE, kostenlos). [http://www.atmanecl.com/EnglishSite/SoftwareEnglish.htm AtmanAvr] (MS Windows, relativ günstig), KamAVR (MS-Windows, kostenlos, wird augenscheinlich nicht mehr weiterentwickelt), [http://www.amctools.com/vmlab.htm VMLab] (MS Windows, ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand von &amp;quot;make&amp;quot; und den &amp;quot;Makefiles&amp;quot; näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
* Das Generieren des Programms ohne IDE und ohne Makefile. In diesem Fall muss die Quellcodedatei durch eine vorgefertigte Kommandofolge an den Compiler übergeben werden. Der Artikel [[C ohne Makefile]] zeigt, wie das funktioniert. Diese Vorgehensweise empfiehlt sich jedoch nur für kleine Programme, die nicht auf verschiedene Quellcodedateien verteilt sind.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|AVR-GCC-Tutorial/Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. (Hinweis: Bei Version 4.16 wird beides bereits gesetzt). Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Ein kurzes Wort zur Hardware: Bei diesem Programm werden alle Pins von PORTB auf Ausgang gesetzt, und einige davon werden auf HIGH andere auf LOW gesetzt. Das kann je nach angeschlossener Hardware an diesen Pins kritisch sein. Am ungefährlichsten ist es, wenn nichts an den Pins angeschlossen ist und man die Funktion des Programmes durch eine Spannungsmessung mit einem Multimeter kontrolliert. Die Spannung wird dabei zwischen GND-Pin und den einzelnen Pins von PORTB gemessen.&lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xFF;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/   // (6)&lt;br /&gt;
   }                         // (7)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (8)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# In dieser Zeile wird eine sogenannte Header-Datei eingebunden. In &amp;lt;code&amp;gt;avr/io.h&amp;lt;/code&amp;gt; sind die Registernamen definiert, die im späteren Verlauf genutzt werden. Auch unter Windows wird ein&amp;amp;nbsp;&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; zur Kennzeichnung von Unterverzeichnissen in Include-Dateinamen verwendet und kein&amp;amp;nbsp;&amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Hier beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Die Anschlüsse eines AVR (Pins) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Datenrichtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B als Ausgänge eingestellt.&lt;br /&gt;
# Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential). Es ist sinnvoll, sich möglichst frühzeitig eine alternative Schreibweise beizubringen, die wegen der leichteren Überprüfbarkeit und Portierbarkeit oft im weiteren Tutorial und in Forenbeiträgen benutzt wird. Die Zuordnung sieht in diesem Fall so aus, Näheres dazu im Artikel [[Bitmanipulation]]:&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;PORTB = (1&amp;lt;&amp;lt;PB1) | (1&amp;lt;&amp;lt;PB0);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# ist der Beginn der sogenannte &#039;&#039;Hauptschleife&#039;&#039; (main-loop). Dies ist eine Endlosschleife, welche kontinuierlich wiederkehrende Befehle enthält.&lt;br /&gt;
# In diesem Beispiel ist die Hauptschleife leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert. Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife kehrt das Programm aus &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; zurück, alle Interrupts werden deaktiviert und eine Endlosschleife betreten.&lt;br /&gt;
# Ende der Hauptschleife und Sprung zur passenden, öffnenden Klammer, also zu 5.&lt;br /&gt;
# ist das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int main(void)&amp;lt;/syntaxhighlight&amp;gt; besagt, dass die Funktion einen int-Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; (ohne Endung) im selben Verzeichnis, in dem auch die Datei &amp;lt;code&amp;gt;main.c&amp;lt;/code&amp;gt; mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\beispiel&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei &amp;lt;code&amp;gt;main.hex&amp;lt;/code&amp;gt;, in welcher der Code für den AVR enthalten ist. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming ([[ISP]]) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann&amp;lt;ref&amp;gt;z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio&amp;lt;/ref&amp;gt;, kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine [[LED]] leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige Datentypen (Integer) =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; definiert, die folgendermaßen eingebunden werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;stdint.h&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|+ &#039;&#039;&#039;int-Typen aus &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; (C99)&#039;&#039;&#039;&amp;lt;br/&amp;gt;&amp;amp;nbsp;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenbehaftete int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || −128 ⋯ 127 || −2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || −32768 ⋯ 32767 || −2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;signed int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || −2147483648 ⋯ 2147483647 || −2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || −9223372036854775808 ⋯ 9223372036854775807 || −2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenlose int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || 0 ⋯ 255 || 0 ⋯ 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || 0 ⋯ 65535 || 0 ⋯ 2&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || 0 ⋯ 4294967295 || 0 ⋯ 2&amp;lt;sup&amp;gt;32&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || 0 ⋯ 18446744073709551615 || 0 ⋯ 2&amp;lt;sup&amp;gt;64&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Neben den Typen gibt es auch Makros für die Bereichsgrenzen wie &amp;lt;code&amp;gt;INT8_MIN&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;UINT16_MAX&amp;lt;/code&amp;gt;. Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html Dokumentation der avr-libc: Standard Integer Types].&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines µC-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif|left]]&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat. Es wird hier nach dem sogenannten EVA-Prinzip gehandelt. EVA steht für &amp;quot;Eingabe, Verarbeitung, Ausgabe&amp;quot;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif|left]]&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher Dinge erledigt werden können, welche nicht zeitkritisch sind. Wenn ein Interrupt ausgelöst wird, so wird automatisch die zugeordnete Interruptfunktion ausgeführt.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Register haben einen besonderen Stellenwert bei den AVR Controllern. Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers. Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind, selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.&amp;amp;nbsp;B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.&amp;amp;nbsp;B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039; wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Beispiele in den folgenden Abschnitten demonstrieren den Zugriff auf Register anhand der Register für I/O-Ports (PORTx, DDRx, PINx), die Vorgehensweise ist jedoch für alle Register (z.&amp;amp;nbsp;B. die des UART, ADC, SPI) analog.&lt;br /&gt;
&lt;br /&gt;
== Schreiben in Register ==&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man Register einfach wie eine Variable setzen.&amp;lt;ref&amp;gt;In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt, outp() ist nicht mehr erforderlich.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang, vgl. Abschnitt Zugriff auf Ports): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
&lt;br /&gt;
    // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
    // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
    DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
    /* Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
       aber übersichtlicher und selbsterklärend: */&lt;br /&gt;
    DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4);&lt;br /&gt;
&lt;br /&gt;
    while (1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ausführliche Schreibweise sollte bevorzugt verwendet werden, da dadurch die Zuweisungen selbsterklärend sind und somit der Code leichter nachvollzogen werden kann. Atmel verwendet sie auch bei Beispielen in Datenblätten und in den allermeisten Quellcodes zu Application-Notes. Mehr zu der Schreibweise mit &amp;quot;|&amp;quot; und &amp;quot;&amp;lt;&amp;lt;&amp;quot; findet man unter [[Bitmanipulation]].&lt;br /&gt;
&lt;br /&gt;
Der gcc C-Compiler unterstützt ab Version 4.3.0 Konstanten im Binärformat, z.&amp;amp;nbsp;B. DDRB&amp;amp;nbsp;=&amp;amp;nbsp;0b00011111. Diese Schreibweise ist jedoch nur in GNU-C verfügbar und nicht in ISO-C definiert. Man sollte sie daher nicht verwenden, wenn Code mit anderen ausgetauscht oder mit anderen Compilern bzw. älteren Versionen des gcc genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
== Verändern von Registerinhalten ==&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt und löscht man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // Hiermit wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // Hiermit wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird jeweils nur der Zustand des angegebenen Bits geändert, der vorherige Zustand der anderen Bits bleibt erhalten. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= ( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten AVR Registern mit Bits, die durch Beschreiben mit einer logischen 1 gelöscht werden, muss eine absolute Zuweisung benutzt werden. Ein ODER löscht in diesen Registern ALLE gesetzten Bits!&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TIFR2 = (1&amp;lt;&amp;lt;OCF2A); // Nur Bit OCF2A löschen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
== Lesen aus Registern ==&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände von Bits erfolgt durch Einlesen des gesamten Registerinhalts und ausblenden der Bits deren Zustand nicht von Interesse ist. Einige Beispiele zum Prüfen ob Bits gesetzt oder gelöscht sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define MEINBIT0 0 &lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
extern test1();&lt;br /&gt;
&lt;br /&gt;
// Funkion test1 aufrufen, wenn Bit 0 in Register PINA gesetzt (1) ist&lt;br /&gt;
i = PINA;         // Inhalt in Arbeitsvariable&lt;br /&gt;
i = i &amp;amp; 0x01;     // alle Bits bis auf Bit 0 ausblenden (bitweise und)&lt;br /&gt;
                  // falls das Bit gesetzt war, hat i den Inhalt 1&lt;br /&gt;
if ( i != 0 ) {   // Ergebnis ungleich 0 (wahr)? &lt;br /&gt;
  test1();         // dann muss Bit 0 in i gesetzt sein -&amp;gt; Funktion aufrufen&lt;br /&gt;
}&lt;br /&gt;
// verkürzt:&lt;br /&gt;
if ( ( PINA &amp;amp; 0x01 ) != 0 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( PINA &amp;amp; 0x01 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// mit definierter Bitnummer:&lt;br /&gt;
if ( PINA &amp;amp; ( 1 &amp;lt;&amp;lt; MEINBIT0 ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und/oder Bit 2 gesetzt ist. (Bit 0 und 2 also Wert 5) &lt;br /&gt;
// (Bedenke: Bit 0 hat Wert 1, Bit 1 hat Wert 2 und Bit 2 hat Wert 4)&lt;br /&gt;
if ( PINA &amp;amp; 0x05 ) {&lt;br /&gt;
  test1();  // Vergleich &amp;lt;&amp;gt; 0 (wahr), also mindestens eines der Bits gesetzt&lt;br /&gt;
}&lt;br /&gt;
// mit definierten Bitnummern:&lt;br /&gt;
if ( PINA &amp;amp; ( ( 1 &amp;lt;&amp;lt; MEINBIT0 ) | ( 1 &amp;lt;&amp;lt; MEINBIT2 ) ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und Bit 2 gesetzt sind&lt;br /&gt;
if ( ( PINA &amp;amp; 0x05 ) == 0x05 ) {  // nur wahr, wenn beide Bits gesetzt&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion test2() aufrufen, wenn Bit 0 gelöscht (0) ist&lt;br /&gt;
i = PINA;        // einlesen in temporäre Variable&lt;br /&gt;
i = i &amp;amp; 0x01;    // maskieren von Bit 0&lt;br /&gt;
if ( i == 0 ) {  // Vergleich ist wahr, wenn Bit 0 nicht gesetzt ist&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// analog mit not-Operator&lt;br /&gt;
if ( !i ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( !( PINA &amp;amp; 0x01 ) ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Warten auf einen bestimmten Zustand ==&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek avr-libc Funktionen, die warten, bis ein bestimmter Zustand eines Bits erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend&amp;quot; gewartet wird. Der Programmablauf bleibt also an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den [[Watchdog]] ein, muss man darauf achten, dass dieser auch noch getriggert wird (Zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Register (ADC, ICR1, OCR1x, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (Low-Byte) und &amp;quot;H&amp;quot; (High-Byte) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese Register kann dann direkt zugegriffen werden. Dies ist zum Beispiel der Fall für Register wie ADC oder TCNT1.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
    /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
    foo = ADC; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei anderen Registern, wie zum Beispiel Baudraten-Register, liegen High- und Low-Teil nicht direkt nebeneinander im SFR-Bereich, so dass ein 16-Bit Zugriff nicht möglich ist und der Zugriff zusammengebastelt werden muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
   uint16_t baud = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
&lt;br /&gt;
   UBRRH = (uint8_t) (baud &amp;gt;&amp;gt; 8);&lt;br /&gt;
   UBRRL = (uint8_t) baud;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen wie ATmega8 oder ATmega16 teilen sich UBRRH und UCSRC die gleiche Speicher-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL), welches Register tatsächlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und übergibt den Wert &#039;&#039;3&#039;&#039;. Und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und übergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Speziell bei den 16-Bit-Timern und auch beim ADC ist es bei allen Zugriffen auf Datenregister erforderlich, dass diese Daten synchronisiert sind. Wenn z.&amp;amp;nbsp;B. bei einem 16-Bit-Timer das High-Byte des Zählregisters gelesen wurde und vor dem Lesezugriff auf das Low-Byte ein Überlauf des Low-Bytes stattfindet, erhält man einen völlig unsinnigen Wert. Auch die Compare-Register müssen synchron geschrieben werden, da es ansonsten zu unerwünschten Compare-Ereignissen kommen kann. &lt;br /&gt;
&lt;br /&gt;
Beim ADC besteht das Problem darin, dass zwischen den Zugriffen auf die beiden Teilregister eine Wandlung beendet werden kann und der ADC ein neues Ergebnis in ADCL und ADCH schreiben will, wodurch High- und Low-Byte nicht zusammenpassen.&lt;br /&gt;
&lt;br /&gt;
Um diese Datenmüllproduktion zu verhindern, gibt es in beiden Fällen eine Synchronisation, die jeweils durch den Zugriff auf das Low-Byte ausgelöst wird:&lt;br /&gt;
* Bei den Timer-Registern (das gilt für alle TCNT-, OCR- und ICR-Register bei den 16-Bit-Timern) wird bei einem &#039;&#039;Lesezugriff&#039;&#039; auf das Low-Byte automatisch das High-Byte in ein temporäres Register, das ansonsten nach außen nicht sichtbar ist, geschoben. Greift man nun &#039;&#039;anschließend&#039;&#039; auf das High-Byte zu, dann wird eben dieses temporäre Register gelesen.&lt;br /&gt;
* Bei einem &#039;&#039;Schreibzugriff&#039;&#039; auf eines der genannten Register wird das High-Byte in besagtem temporären Register zwischengespeichert und erst beim Schreiben des Low-Bytes werden &#039;&#039;beide&#039;&#039; gleichzeitig in das eigentliche Register übernommen.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet für die Reihenfolge:&lt;br /&gt;
* Lesezugriff: Erst Low-Byte, dann High-Byte&lt;br /&gt;
* Schreibzugriff: Erst High-Byte, dann Low-Byte&lt;br /&gt;
&lt;br /&gt;
Des weiteren ist zu beachten, dass es für all diese 16-Bit-Register nur ein einziges temporäres Register gibt, so dass das Auftreten eines Interrupts, in dessen Handler ein solches Register manipuliert wird, bei einem durch ihn unterbrochenen Zugriff i.d.R. zu Datenmüll führt. 16-Bit-Zugriffe sind generell nicht atomar! Wenn mit Interrupts gearbeitet wird, kann es erforderlich sein, vor einem solchen Zugriff auf ein 16-Bit-Register die Interrupt-Bearbeitung zu deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Beim ADC-Datenregister ADCH/ADCL ist die Synchronisierung anders gelöst. Hier wird beim Lesezugriff (ADCH/ADCL sind logischerweise read-only) auf das Low-Byte ADCL beide Teilregister für Zugriffe seitens des ADC so lange gesperrt, bis das High-Byte ADCH ausgelesen wurde. Dadurch kann der ADC nach einem Zugriff auf ADCL keinen neuen Wert in ADCH/ADCL ablegen, bis ADCH gelesen wurde. Ergebnisse von Wandlungen, die zwischen einem Zugriff auf ADCL und ADCH beendet werden, gehen verloren!&lt;br /&gt;
&lt;br /&gt;
Nach einem Zugriff auf ADCL muss grundsätzlich ADCH gelesen werden!&lt;br /&gt;
&lt;br /&gt;
In beiden Fällen – also sowohl bei den Timern als auch beim ADC – werden vom C-Compiler 16-Bit Pseudo-Register zur Verfügung gestellt (z.&amp;amp;nbsp;B. TCNT1H/TCNT1L → TCNT1, ADCH/ADCL → ADC bzw. ADCW), bei deren Verwendung der Compiler automatisch die richtige Zugriffsreihenfolge regelt. In C-Programmen sollten grundsätzlich diese 16-Bit-Register verwendet werden! Sollte trotzdem ein Zugriff auf ein Teilregister erforderlich sein, sind obige Angaben zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass auch ein Zugriff auf die 16-Bit-Register vom Compiler in zwei 8-Bit-Zugriffe aufgeteilt wird und dementsprechend genauso nicht-atomar ist wie die Einzelzugriffe. Auch hier gilt, dass u.U. die Interrupt-Bearbeitung gesperrt werden muss, um Datenmüll zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Beim ADC gibt es für den Fall, dass eine Auflösung von 8 Bit ausreicht, die Möglichkeit, das Ergebnis &amp;quot;linksbündig&amp;quot; in ADCH/ADCL auszurichten, so dass die relevanten 8 MSB in ADCH stehen. In diesem Fall muss bzw. sollte nur ADCH ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
ADC und ADCW sind unterschiedliche Bezeichner für das selbe Registerpaar. Üblicherweise kann man in C-Programmen ADC verwenden, was analog zu den anderen 16-Bit-Registern benannt ist. ADCW (ADC Word) existiert nur deshalb, weil die Headerdateien auch für Assembler vorgesehen sind und es bereits einen Assembler-Befehl namens &#039;&#039;adc&#039;&#039; gibt. &lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t key_pressed (volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if (last_state == (*inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit)))&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t i = key_pressed (&amp;amp;PINB, PB1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass avr-libc FAQ: &amp;quot;How do I pass an IO port as a parameter to a function?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf IO-Ports =&lt;br /&gt;
&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output). Diese Register dienen dazu:&lt;br /&gt;
* einzustellen welche der Anschlüsse (&amp;quot;Beinchen&amp;quot;) des Controllers als Ein- oder Ausgänge dienen&lt;br /&gt;
* bei Ausgängen deren Zustand festzulegen&lt;br /&gt;
* bei Eingängen deren Zustand zu erfassen&lt;br /&gt;
&lt;br /&gt;
Mittels GPIO werden digitale Zustände gesetzt und erfasst, d.h. die Spannung an einem Ausgang wird ein- oder ausgeschaltet und an einem Eingang wird erfasst, ob die anliegende Spannung über oder unter einem bestimmten Schwellwert liegt. Im Datenblatt Abschnitt Electrical Characteristics/DC Characteristics finden sich die Spannungswerte (V_OL, V_OH für Ausgänge, V_IL, V_IH für Eingänge).&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
Die physischen Ein- und Ausgänge werden bei AVR-Controllern zu logischen Ports gruppiert.&lt;br /&gt;
&lt;br /&gt;
Alle Ports werden über Register gesteuert. Dazu sind jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! DDRx&lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
! PINx&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
! PORTx&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt. Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
  // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
  // direkte Zuweisung - standardkonform */&lt;br /&gt;
  DDRB = 0x1F;    /* &lt;br /&gt;
&lt;br /&gt;
  // übersichtliche Alternative - Binärschreibweise, aber kein ISO-C&lt;br /&gt;
  DDRB = 0b00011111;&lt;br /&gt;
&lt;br /&gt;
  // Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
  // aber übersichtlicher und selbsterklärend:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet. Weitere Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
  DDRB = 0xff; &lt;br /&gt;
  // Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~(1 &amp;lt;&amp;lt; DDB0);&lt;br /&gt;
  // Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4));&lt;br /&gt;
  // Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB3);&lt;br /&gt;
  // Alle Pins auf Eingang:&lt;br /&gt;
  DDRB = 0x00;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.&amp;amp;nbsp;B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&lt;br /&gt;
    // übersichtliche Alternative - Binärschreibweise&lt;br /&gt;
    PORTB = 0b00000100;    /* direkte Zuweisung - übersichtlich */&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&lt;br /&gt;
* zuerst die Bits im PORTx-Register setzen&lt;br /&gt;
* anschließend die Datenrichtung auf Ausgang stellen&lt;br /&gt;
&lt;br /&gt;
Daraus ergibt sich die Abfolge für einen Pin, der bisher als Eingang mit abgeschaltetem Pull-Up konfiguriert war:&lt;br /&gt;
* setze PORTx: interner Pull-Up aktiv&lt;br /&gt;
* setze DDRx: Ausgang (&amp;quot;high&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Bei der Reihenfolge erst DDRx und dann PORTx kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.&amp;amp;nbsp;B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Spannungstabelle&#039;&#039;&#039; &amp;lt;br /&amp;gt; &amp;lt;small&amp;gt;(ca. Grenzwerte)&amp;lt;/small&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
! Low || High&lt;br /&gt;
|-&lt;br /&gt;
! bei 5 V&lt;br /&gt;
| 1 V || 3,5 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 3,3 V&lt;br /&gt;
| 0,66 V || 2,31 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 1,8 V&lt;br /&gt;
| 0,36 V || 1,26 V&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
{{Warnung|Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;nicht&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.}}&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation#Bits_prüfen]]&lt;br /&gt;
&lt;br /&gt;
=== Interne Pull-Up Widerstände ===&lt;br /&gt;
&lt;br /&gt;
Portpins für Ein- und Ausgänge (GPIO) eines AVR verfügen über zuschaltbare interne Pull-Up Widerstände (nominal mehrere 10kOhm, z.&amp;amp;nbsp;B. ATmega16 20-50kOhm). Diese können in vielen Fällen statt externer Widerstände genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;PC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Taster und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller, ist zwischen zwei unterschiedliche Methoden zu unterscheiden: &#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300&amp;quot; heights=&amp;quot;300&amp;quot; caption=&amp;quot;Anschluss mechanischer Kontakte an einen µC&amp;quot;&amp;gt;&lt;br /&gt;
Image:Active Low.gif|&#039;&#039;&#039;Active Low:&#039;&#039;&#039; Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet. Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt, wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter &#039;&#039;&#039;Pull-Up&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
Image:Active High.gif|&#039;&#039;&#039;Active High:&#039;&#039;&#039; Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet. Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein &#039;&#039;&#039;Pull-Down&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten. &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert von Pull-Up- und Pull-Down-Widerständen ist an sich nicht kritisch. Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert. Die AVRs verfügen an den meisten Pins über zuschaltbare interne Pull-Up Widerstände (vgl. Abschnitt [[AVR-GCC-Tutorial#Interne Pull-Up Widerstände|Interne Pull-Up Widerstände]]), welche insbesondere wie hier bei Tastern und ähnlichen Bauteilen (z.&amp;amp;nbsp;B. Drehgebern) statt externer Bauteile verwendet werden können. Interne Pull-Down-Widerstand sind nicht verfügbar und müssen daher in Form zusätzlicher Bauteile in die Schaltung eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
==== Taster entprellen ====&lt;br /&gt;
&lt;br /&gt;
Siehe: &#039;&#039;[[Entprellung#Warteschleifen-Verfahren|Entprellung: Warteschleifen-Verfahren]]&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Außerdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr (abgesehen von möglicherweise auftretenden Interrupts, falls welche aktiviert sind). Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.&amp;amp;nbsp;B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.&amp;amp;nbsp;B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt, und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Einschränkung liegt darin, daß sie möglicherweise länger warten, als erwartet, nämlich in dem Fall, daß Interrupts auftreten und die _delay...()-Funktion unterbrechen. Genau genommen warten diese nämlich nicht eine bestimmte Zeit, sondern verbrauchen eine bestimmte Anzahl von Prozessortakten. Die wiederum ist so bemessen, daß ohne Unterbrechung durch Interrupts die gewünschte Wartezeit erreicht wird.&lt;br /&gt;
Wird das Warten aber durch eine oder mehrere ISR unterbrochen, die zusammen 1% Prozessorzeit verbrauchen, dann dauert das Warten etwa 1% länger. Bei 50% Last durch die ISR dauert das Warten doppelt solange wie gewünscht, bei 90% zehnmal solange...&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen bis 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4µs warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.&amp;amp;nbsp;B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms)&lt;br /&gt;
{&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 )                  // Endlosschleife&lt;br /&gt;
    {                &lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.&amp;amp;nbsp;B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.7 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Es ist nicht möglich, eine Variable als Argument zu übergeben. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Trick zur Übergabe einer Variablen an _delay_ms():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void sleep ( uint8_t ms )&lt;br /&gt;
{&lt;br /&gt;
    for(; ms &amp;gt; 0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    int x = 0;                  // Variable als Wartezeit erstellen&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        sleep(x); &lt;br /&gt;
        x++;       &lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die _delay_ms() und die _delay_us aus &#039;&#039;&#039;avr-libc 1.7.0&#039;&#039;&#039; sind fehlerhaft. _delay_ms () läuft 4x schneller als erwartet. Abhilfe ist eine korrigierte Includedatei: [http://www.mikrocontroller.net/topic/196738#1943039]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:2em;&amp;quot;&amp;gt;&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Tritt ein Interrupt auf, unterbricht (engl. interrupts) der Controller die Verarbeitung des Hauptprogramms und verzweigt zu einer Interruptroutine. Das Hauptprogramm wird also beim Eintreffen eines Interrupts unterbrochen, die Interruptroutine ausgeführt und danach erst wieder das Hauptprogramm an der Unterbrechungsstelle fortgesetzt (vgl. die Abbildung).&lt;br /&gt;
&lt;br /&gt;
Um Interrupts verarbeiten zu können, ist folgendes zu beachten:&lt;br /&gt;
&lt;br /&gt;
* Für jede aktivierte Interruptquelle ist eine Funktion zu programmieren, in der die beim Auftreten des jeweiligen Interrupts erforderlichen Verarbeitungsschritte enthalten sind. Für diese Funktion existieren verschiedene Bezeichnungen. Üblich sind die englischen Begriffe Interrupt-Handler oder Interrupt-Service-Routinen (ISR), man findet aber auch die Bezeichnungen Interruptverarbeitungs- oder -behandlungsroutine oder auch kurz Interruptroutine. Zum Beispiel wird üblicherweise in der ISR zur Verarbeitung des Empfangsinterrupts eines UARTs (UART-RX Interrupt) das empfangene Zeichen in einen Zwischenspeicher (FIFO-Buffer) kopiert, dessen Inhalt später von anderen Programmteilen geleert wird. Sofern der Zwischenspeicher ausreichend groß ist, geht also kein Zeichen verloren, auch wenn im Hauptprogramm zeitintensive Operationen durchgeführt werden.&lt;br /&gt;
* Die benötigten Interrupts sind in den jeweiligen Funktionsbausteinen einzuschalten. Dies erfolgt über das jeweilige Aktivierungsbit (Interrupt Enable) in einem der Hardwareregister (z.B. RX(Complete)Interrupt Enable eines UARTs)&lt;br /&gt;
* Sämtliche Interrupts werden über einen weiteren globalen Schalter aktiviert und deaktiviert. Zur Verarbeitung der Interrupts ist dieser Schalter zu aktivieren (sei(), siehe unten).&lt;br /&gt;
 &lt;br /&gt;
Alle Punkte sind zu beachten. Fehlt z.B. die globale Aktivierung, werden Interruptroutinen auch dann nicht aufgerufen, wenn sie im Funktionsbaustein eingeschaltet sind und eine Behandlungsroutine verhanden ist.&lt;br /&gt;
&lt;br /&gt;
Siehe auch&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
* Artikel [[Interrupt]]&lt;br /&gt;
* Artikel [[Multitasking]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen sollten möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Microcontroller-Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammenhängen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039; Register.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit &lt;br /&gt;
| 7 || 6|| 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039; || &#039;&#039;&#039;INT0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039; || &#039;&#039;&#039;INTF0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;SE&#039;&#039;&#039;|| &#039;&#039;&#039;SM&#039;&#039;&#039;|| &#039;&#039;&#039;ISC11&#039;&#039;&#039;|| &#039;&#039;&#039;ISC10&#039;&#039;&#039;|| &#039;&#039;&#039;ISC01&#039;&#039;&#039;|| &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R || R || R/W || R/W || R/W || R/W || R/W || R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt über den Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC11 || ISC10 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC01 || ISC00 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Dieses wird automatisch wieder gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.  Es ist möglich das GIE-Bit in der ISR zu setzen und so schon wieder weitere Interrupts zuzulassen - allerdings sollte man damit vorsichtig sein und genau wissen was man damit macht. Kritisch wird es vor allem wenn der gleiche Interrupt noch einmal kommt, bevor die ISR abgearbeitet ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit avr-gcc ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;Anmerkung eines Nutzers: Ich habe mir das Thema hier angearbeitet und hatte am Anfang starke Probleme: Jeder Interrupt muss nochmals einzeln aktiviert werden. Es reicht nicht nur per &#039;&#039;sei()&#039;&#039; die Interrupts global zu aktiveren.&#039;&#039; - mthomas: Hoffentlich duch die modifizerte Einleitung etwas offensichtlicher erläutert. Ansonsten bitte per Eintrag auf die Diskussionseite nochmals melden) --&amp;gt; &lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrupts aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Zu den aktivierten Interrupts ist eine Funktion zu programmieren, deren Code aufgerufen wird, wenn der betreffende Interrupt auftritt (Interrupt-Handler, Interrupt-Service-Routine). Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe den [[AVR-GCC-Tutorial#Anhang|Anhang]].)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h  - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR(XXX,ISR_NOBLOCK) /* veraltet: INTERRUPT(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt-Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.&amp;amp;nbsp;B. &#039;&#039;TIMER0_OVF_vect&#039;&#039;). Der Unterschied im Vergleich zu einer herkömmlichen ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit durch Einfügen einer SEI-Anweisung direkt wieder gesetzt und somit alle Interrupts zugelassen werden &amp;amp;ndash; auch XXX-Interrupts. &lt;br /&gt;
&lt;br /&gt;
Bei unsachgemässer Handhabung kann dies zu erheblichen Problemen durch Rekursion wie einem Stack-Overflow oder anderen unerwarteten Effekten führen und sollte wirklich nur dann eingesetzt werden, wenn man sich sicher ist, das Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
Insbesondere sollte möglichst am ISR-Anfang die auslösende IRQ-Quelle deaktiviert und erst am Ende der ISR wieder aktiviert werden. Robuster als die Verwendung einer NOBLOCK-ISR ist daher folgender ISR-Aufbau:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR (XXX) &lt;br /&gt;
{&lt;br /&gt;
    // Implementiere die ISR ohne zunaechst weitere IRQs zuzulassen&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Deaktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Erlaube alle Interrupts (ausser XXX)&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    //... Code ...&lt;br /&gt;
&lt;br /&gt;
    // IRQs global deaktivieren um die XXX-IRQ wieder gefahrlos &lt;br /&gt;
    // aktivieren zu koennen&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Aktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Weise kann sich die XXX-IRQ nicht selbst unterbrechen, was zu einer Art Endlosschleife führen würde.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen, die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)) als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.&amp;amp;nbsp;B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird innerhalb einer ISR mehrfach auf eine mit volatile deklarierte Variable zugegriffen, wirkt sich dies ungünstig auf die Verarbeitungsgeschwindigkeit aus, da bei jedem Zugriff mit dem Speicherinhalt abgeglichen wird. Da bei AVR-Controllern &#039;&#039;innerhalb&#039;&#039; einer ISR keine Unterbrechungen zu erwarten sind, bietet es sich an, einen Zwischenspeicher in Form einer lokalen Variable zu verwenden, deren Inhalt zu Beginn und am Ende mit dem der volatile Variable synchronisiert wird. Lokale Variable werden bei eingeschalteter Optimierung mit hoher Wahrscheinlichkeit in Prozessorregistern verwaltet und der Zugriff darauf ist daher nur mit wenigen internen Operationen verbunden. Die ISR aus dem vorherigen Beispiel lässt sich so optimieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
&lt;br /&gt;
   tmp_kc = gKeyCounter; // Uebernahme in lokale Arbeitsvariable&lt;br /&gt;
&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   gKeyCounter = tmp_kc; // Zurueckschreiben&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich die Disassemblies (Ausschnitte der &amp;quot;lss-Dateien&amp;quot;, compiliert für ATmega162) im Anschluss. Man erkennt den viermaligen Zugriff auf die Speicheraddresse von &#039;&#039;gKeyCounter&#039;&#039; (hier 0x032A) in der ISR ohne &amp;quot;Cache&amp;quot;-Variable und den zweimaligen Zugriff in der Variante mit Zwischenspeicher. Im Beispiel ist der Vorteil gering, bei komplexeren Routinen kann die Zwischenspeicherung in lokalen Variablen jedoch zu deutlicheren Verbesserungen führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
    if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     876:	ca 99       	sbic	0x19, 2	; 25&lt;br /&gt;
     878:	0a c0       	rjmp	.+20     	; 0x88e &amp;lt;__vector_13+0x24&amp;gt;&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
     87a:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     87e:	88 3c       	cpi	r24, 0xC8	; 200 &lt;br /&gt;
     880:	40 f4       	brcc	.+16     	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
     882:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	02 c0       	rjmp	.+4      	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
     88e:	10 92 2a 03 	sts	0x032A, r1&lt;br /&gt;
     892:	8f 91       	pop	r24&lt;br /&gt;
     894:	0f 90       	pop	r0&lt;br /&gt;
     896:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     898:	0f 90       	pop	r0&lt;br /&gt;
     89a:	1f 90       	pop	r1&lt;br /&gt;
     89c:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
 &lt;br /&gt;
   tmp_kc = gKeyCounter;&lt;br /&gt;
     876:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
 &lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     87a:	ca 9b       	sbis	0x19, 2	; 25&lt;br /&gt;
     87c:	02 c0       	rjmp	.+4      	; 0x882 &amp;lt;__vector_13+0x18&amp;gt;&lt;br /&gt;
     87e:	80 e0       	ldi	r24, 0x00	; 0&lt;br /&gt;
     880:	03 c0       	rjmp	.+6      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
     882:	88 3c       	cpi	r24, 0xC8	; 200&lt;br /&gt;
     884:	08 f4       	brcc	.+2      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   gKeyCounter = tmp_kc;&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	8f 91       	pop	r24&lt;br /&gt;
     88e:	0f 90       	pop	r0&lt;br /&gt;
     890:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     892:	0f 90       	pop	r0&lt;br /&gt;
     894:	1f 90       	pop	r1&lt;br /&gt;
     896:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== volatile und Pointer ===&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;volatile&#039;&#039;&#039; in Verbindung mit Pointern ist zu beachten, ob der Pointer selbst oder die Variable, auf die der Pointer zeigt, &#039;&#039;&#039;volatile&#039;&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t *a;   // das Ziel von a ist volatile&lt;br /&gt;
&lt;br /&gt;
uint8_t *volatile a;   // a selbst ist volatile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls der Pointer volatile ist (zweiter Fall im Beispiel), ist zu beachten, dass der Wert des Pointers, also eine Speicheradresse, intern in mehr als einem Byte verwaltet wird. Lese- und Schreibzugriffe im Hauptprogramm (außerhalb von Interrupt-Routinen) sind daher so zu implementieren, dass alle Teilbytes der Adresse konsistent bleiben, vgl. dazu den folgenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interrupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
&lt;br /&gt;
   // oder: mehrfach lesen, bis man konsistente Daten hat&lt;br /&gt;
   uint16_t count1 = gMyCounter16Bit;&lt;br /&gt;
   uint16_t count2 = gMyCounter16Bit;&lt;br /&gt;
   while (count1 != count2) {&lt;br /&gt;
       count1 = count2;&lt;br /&gt;
       count2 = gMyCounter16Bit;&lt;br /&gt;
   }&lt;br /&gt;
   tmpCnt = count1;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die avr-libc bietet ab Version 1.6.0(?) einige Hilfsfunktionen/Makros, mit der im Beispiel oben gezeigten Funktionalität, die zusätzlich auch sogenannte [http://en.wikipedia.org/wiki/Memory_barrier memory barriers] beinhalten. Diese stehen nach #include &amp;lt;util/atomic.h&amp;gt; zur Verfügung.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // analog zu cli, Zugriff, sei:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_FORCEON) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
// oder:&lt;br /&gt;
&lt;br /&gt;
    // analog zu Sicherung des SREG, cli, Zugriff und Zurückschreiben des SREG:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch [http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html Dokumentation der avr-libc zu atomic.h]&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.&amp;amp;nbsp;B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0 auf &#039;&#039;&#039;1&#039;&#039;&#039;, PORTA ist danach 0b0000000&#039;&#039;&#039;1&#039;&#039;&#039;. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-&#039;&#039;&#039;ODER&#039;&#039;&#039;-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8. (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Interruptflags löschen ==&lt;br /&gt;
&lt;br /&gt;
Beim Löschen von Interruptflags haben AVRs eine Besonderheit, die auch im Datenblatt beschrieben ist: Es wird zum Löschen eine 1 in das betreffende Bit geschrieben. &lt;br /&gt;
&lt;br /&gt;
Hinweis:&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei Registern mit mehreren Interrupt-Flag-Bits (wie die Timer Interrupt Flag Register)  &#039;&#039;&#039;nicht&#039;&#039;&#039;  die übliche bitweise VerODERung nehmen, sondern eine direkte Zuweisung machen. Da sonst weitere Flags, als nur das gewünschte, ebenfalls gelöscht werden könnten.&amp;lt;br /&amp;gt;&lt;br /&gt;
([http://www.mikrocontroller.net/topic/171148#1640133 Erklärung]).&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten [[Sleep Mode |&#039;&#039;Sleep-Modes&#039;&#039;]] (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
;set_sleep_mode (uint8_t mode): Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.&amp;amp;nbsp;B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
;sleep_enable(): Aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
;sleep_cpu(): Versetzt den Controller in den Schlafmodus .sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt.&lt;br /&gt;
;sleep_disable(): Deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
;sleep_mode(): Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts (besser nicht benutzen).&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sleep Modi ==&lt;br /&gt;
Zu beachten ist, dass unterschiedliche Prozessoren aus der AVR Familie unterschiedliche Sleep-Modi unterstützen oder nicht unterstützen. Auskunft über die tatsächlichen Gegebenheiten gibt, wie immer, das zum Prozessor gehörende Datenblatt. Die unterschiedlichen Modi unterscheiden sich dadurch, welche Bereiche des Prozessors abgeschaltet werden. Damit korrespondiert unmittelbar welche Möglichkeiten es gibt, den Prozessor aus den jeweiligen Sleep Modus wieder aufzuwecken.&lt;br /&gt;
&lt;br /&gt;
;Idle Mode (SLEEP_MODE_IDLE): Die CPU kann durch SPI, USART, Analog Comperator, ADC, TWI, Timer, Watchdog und irgendeinen anderen Interrupt wieder aufgeweckt werden.&lt;br /&gt;
&lt;br /&gt;
;ADC Noise Reduction Mode (SLEEP_MODE_ADC): In diesem Modus liegt das Hauptaugenmerk darauf, die CPU soweit stillzulegen, dass der ADC möglichst keine Störungen aus dem inneren der CPU auffangen kann. Aufwachen aus diesem Modus kann ausgelöst werden durch den ADC, externe Interrupts, TWI, Timer und Watchdog.&lt;br /&gt;
&lt;br /&gt;
;Power-Down Mode (SLEEP_MODE_PWR_DOWN): In diesem Modus wird ein externer Oszillator (Quarz, Quarzoszillator) gestoppt. Geweckt werden kann die CPU durch einen externen Level Interrupt, TWI, Watchdog, Brown-Out-Reset&lt;br /&gt;
&lt;br /&gt;
;Power-Save-Mode (SLEEP_MODE_PWR_SAVE): Power-Save ist identisch zu Power-Down mit einer Ausnahme: Ist der Timer 2 auf die Verwendung eines externen Taktes konfiguriert, so läuft dieser Timer auch im Power-Save weiter und kann die CPU mit einem Interrupt aufwecken.&lt;br /&gt;
&lt;br /&gt;
;Standby-Mode (SLEEP_MODE_STANDBY, SLEEP_MODE_EXT_STANDBY): Voraussetzung für den Standby-Modus ist die Verwendung eines Quarzes oder eines Quarzoszillators (also einer externen Taktquelle). Ansonsten ist dieser Modus identisch zum Power-Down Modus. Vorteil dieses Modus ist eine kürzere Aufwachzeit.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Zeiger =&lt;br /&gt;
Zeiger (engl. &#039;&#039;Pointer&#039;&#039;) sind Variablen, die die Adresse von Daten oder Funktionen enthalten und belegen 16 Bits. Die Größe hängt mit dem adressierbaren Speicherbereich zusammen und der GCC reserviert dann den entsprechenden Platz.&lt;br /&gt;
Ggf. ist es also günstiger, Indizes auf Arrays (Listen) zu verwenden, so dass der GCC für die Zeigerarithmetik den erforderlichen RAM nur temporär benötigt.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[Zeiger]]&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.&amp;amp;nbsp;B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Binäre Daten zum Programm hinzufügen]]&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;alloziert&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Flash mit PROGMEM und pgm_read ==&lt;br /&gt;
&lt;br /&gt;
→ [http://nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html avr-libc: Doku zu avr/pgmspace.h]&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc erst ab Version 4.7 &amp;quot;transparent&amp;quot; möglich. Um Daten aus dem Flash zu lesen, muss die AVR-Instruktion LPM (&#039;&#039;Load from Program Memory&#039;&#039;) erzeugt werden, bei Controllern mit mehr als 64kiB Flash auch ELMP.&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es das AVR-spezifische GCC-Attribut &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt;, mit dem eine Variablendeklaration im &#039;&#039;static storage&#039;&#039;&amp;lt;ref&amp;gt;Variablen der Speicherklasse &#039;&#039;static storage&#039;&#039; haben eine unbegrenzte Lebensdauer.  Beispiel für solche Variablen sind globale Variablen, aber auch static-Variablen innerhalb einer Funktion gehören dazu.  Beispiele für Variablen, die nicht &#039;&#039;static storage&#039;&#039; sind: auto-Variablen (&amp;quot;normale&amp;quot; lokale Variablen), register-Variablen, durch malloc geschaffene Objekte, etc.&amp;lt;/ref&amp;gt; markiert werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const int value __attribute__((progmem)) = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effekt ist, dass die so markierte Variable nicht im RAM sondern im Flash angelegt wird.  Wird durch &amp;quot;normalen&amp;quot; C-Code auf solch eine Variable zugegriffen, wird jedoch aus dem RAM gelesen und nicht aus dem Flash!&lt;br /&gt;
&lt;br /&gt;
Zum Lesen aus dem Flash stellt die avr-libc daher zahlreiche Makros zur Verfügung.  Zudem wird das Makro &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; definiert, das etwas Tipparbeit spart:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const int value PROGMEM = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; funktioniert im Wesentlichen wie ein Section-Attribut, das die Daten in der Section &amp;lt;tt&amp;gt;.progmem.data&amp;lt;/tt&amp;gt; ablegt.  Im Gegensatz zum Section-Attribut werden jedoch noch weitere Prüfungen unternommen, ab avr-gcc 4.6 etwa muss die entsprechende Variable &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; sein.&lt;br /&gt;
&lt;br /&gt;
=== Integer und float ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen von Skalaren stellt die avr-libc folgende Makros zu Verfügung, die jeweils ein Argument erhalten: Die 16-Bit Adresse des zu lesenden Wertes&amp;lt;ref&amp;gt;Damit ist der mögliche Speicherbereich für Flash-Konstanten auf 64kiB begrenzt. Einige pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher, intern via Assembler-Anweisung ELPM. Die Initialisierungswerte des Speicherinhalts jenseits der 64kiB-Marke müssen dann jedoch auf anderem Weg angelegt werden, d.h. nicht per PROGMEM. Evtl. eigene Section und Linker-Optionen. Alt und nicht ganz korrekt: Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kiB Flash bei Controllern mit mehr als 64kiB.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| {{Tabelle}}&lt;br /&gt;
|+ Übersicht der &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt; Funktionen aus&amp;lt;br/&amp;gt;dem Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; der avr-libc&lt;br /&gt;
|-&lt;br /&gt;
! Gelesener Wert || &amp;lt;tt&amp;gt;pgm_read_xxx&amp;lt;/tt&amp;gt; || Anzahl Bytes&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint8_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_byte&amp;lt;/tt&amp;gt; || 1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint16_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; || 2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint32_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_dword&amp;lt;/tt&amp;gt; || 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;float&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_float&amp;lt;/tt&amp;gt;&amp;lt;ref&amp;gt;ab avr-libc 1.7.0&amp;lt;/ref&amp;gt; || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Soll ein Zeiger gelesen werden, so verwendet man &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; und castet das Ergebnis zum gewünschten Zeiger-Typ.&lt;br /&gt;
&lt;br /&gt;
;Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t aByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* int-Array */&lt;br /&gt;
const int anArray[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
  /* Zeiger */&lt;br /&gt;
  static const uint8_t* const aPointer PROGMEM = &amp;amp;aByte;&lt;br /&gt;
&lt;br /&gt;
  uint8_t a        = pgm_read_byte (&amp;amp;aByte);&lt;br /&gt;
  int a2           = (int) pgm_read_word (&amp;amp;anArray[2]);&lt;br /&gt;
  const uint8_t* p = (const uint8_t*) pgm_read_word (&amp;amp;aPointer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
In den Flash-Funktionen der avr-libc sind keine der pgm_read_xxxx Nomenklatur folgenden Funktionen, die Speicherblöcke auslesen oder vergleichen. Die enstprechende Funktionen sind Varianten von &amp;lt;tt&amp;gt;memcpy&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp&amp;lt;/tt&amp;gt; und heißt &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, usw.  Für weitere Funktionen und deren Prototypen siehe die Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Strings sind in C nichts anderes als eine Abfolge von Zeichen und einem &amp;lt;tt&amp;gt;&#039;\0&#039;&amp;lt;/tt&amp;gt; als Stringende. Der prinzipielle Weg ist daher identisch zum  Lesen von Bytes, wobei auf die [[FAQ#Wie funktioniert String-Verarbeitung in C?|Besonderheiten von Strings]] wie 0-Terminierung geachtet werden muss.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
size_t my_string_length (const char* addr)&lt;br /&gt;
{&lt;br /&gt;
    size_t length = 0;&lt;br /&gt;
&lt;br /&gt;
    while (pgm_read_byte (addr++))&lt;br /&gt;
    {&lt;br /&gt;
        len++;&lt;br /&gt;
    }&lt;br /&gt;
    return length;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Unterstützung des Programmierers steht das Repertoire der str-Funktionen auch in jeweils eine Variante zur Verfügung, die mit dem Flash-Speicher arbeiten kann. Die Funktionsnamen tragen den Suffix &amp;lt;tt&amp;gt;_P&amp;lt;/tt&amp;gt;. Darüber hinaus gibt es das Makro &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt;, das ein String-Literal im Flash-Speicher ablegt und die Adresse des Strings liefert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Liefert true zurück, wenn string_im_ram gleich &amp;quot;Hallo Welt&amp;quot; ist. */&lt;br /&gt;
int foo (const char* string_im_ram)&lt;br /&gt;
{&lt;br /&gt;
    return strcmp_P (string_im_ram, PSTR (&amp;quot;Hallo Welt&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; nur innerhalb von Funktionen verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
; Array aus Strings:&lt;br /&gt;
&lt;br /&gt;
Arrays aus Strings im Flash-Speicher werden in zwei Schritten angelegt:&lt;br /&gt;
&lt;br /&gt;
# Zuerst die einzelnen Elemente des Arrays und&lt;br /&gt;
# im Anschluss ein Array, in dem die Startaddressen der Strings abgelegt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen wird zuerst die Adresse des gewünschten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, um auf das Element (den String) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const char str1[] PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char str2[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char str3[] PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const array[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   str1, str2, str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Lese die Adresse des i-ten Strings aus array[]&lt;br /&gt;
    const char *parray = (const char*) pgm_read_word (&amp;amp;array[i]);&lt;br /&gt;
&lt;br /&gt;
    // Kopiere den Inhalt der Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, parray);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist, die Strings in einem 2-dimensionalen char-Array abzulegen anstatt deren Adresse in einem 1-dimensionalen Adress-Array zu speichern.&lt;br /&gt;
&lt;br /&gt;
Vorteil ist, dass der Code einfacher wird.  Nachteil ist, dass bei unterschiedlich langen Strings Speicherplatz verschwendet wird, weil sich die Array-Dimension and der Länge des längsten Strings orientieret.  Bei in etwa gleich langen Strings kann es aber sogar Speicherplatz sparen, denn es die Adressen der einzelnen Strings müssen nicht abgespeichert werden.&amp;lt;ref&amp;gt;In unserem Hund-Katze-Maus Beispiel belegt die erste Variante 22 Bytes Daten und 18 Bytes Code, die zweite Variante mit 2-dimensionalem Array belegt 18 Bytes Daten und 20 Bytes Code. Gemessen wurde mit avr-gcc 4.8 -Os für ATmega8.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Die &amp;quot;6&amp;quot; ist 1 plus die Länge des längsten Strings (&amp;quot;Katze&amp;quot;)&lt;br /&gt;
const char array[][6] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere den Inhalt der i-ten Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, array[i]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_array How do I put an array of strings completely in ROM?]&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so kompliziert ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm (Flash) und Datenspeicher (RAM) auf. Der C-Standard sieht keine unterschiedlichen Adressräume vor.&lt;br /&gt;
&lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart (const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette, dann weiß die Funktion nicht, ob die Adresse in den Flash-Speicher oder das RAM zeigt. Weder aus dem Pointer-Wert, also dem Zahlenwert, noch aus dem &amp;quot;const&amp;quot; kann auf den Ort der Ablage geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler bilden die Harvard-Architektur ab, indem sie in einen Pointer nicht nur die Adresse speichern, sondern auch den Ablageort wie &#039;&#039;Flash&#039;&#039; oder &#039;&#039;RAM&#039;&#039;. In einem Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben.&lt;br /&gt;
&lt;br /&gt;
Dies hat jedoch auch Nachteile, denn bei jedem Zugriff über einen Zeiger muss zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden, wie der Zugriff auszuführen ist und entsprechend länglicher und langsamer wird der erzeugte Code.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
=== Variablenzugriff &amp;gt;64kB ===&lt;br /&gt;
&lt;br /&gt;
Die Zeiger beim avr-gcc sind nur 16 Bit breit, können somit also nur 64kiB Datenspeicher adressieren. Als Funktionspointer können sie beim AVR bis zu 128 kiB Programmspeicher adressieren, weil Funktionsadressen immer 16-Bit Worte adressieren und nicht Bytes. Um Flashzugriff jenseits von 64KiB zu bewerkstelligen gibt es mehrere Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Address-Spaces wie &amp;lt;tt&amp;gt;__flash1&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;, siehe Abschnitt &amp;quot;[[#Jenseits von flash|Jenseits von __flash]]&amp;quot;.&lt;br /&gt;
* Die Funktionen bzw. Makros &amp;lt;tt&amp;gt;pgm_read_xxx_far&amp;lt;/tt&amp;gt; der AVR-Libc, wie im folgenden beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unverständlicherweise gibt es in der AVR-Libc keine Funktion, um 32-Bit Pointer zu erhalten. Hier schafft ein eigenes GNU-C99 Makro Abhilfe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Macro to access strings defined in PROGMEM above 64kB&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR(var)                     \&lt;br /&gt;
({ uint_farptr_t tmp;                \&lt;br /&gt;
   __asm__ (                         \&lt;br /&gt;
       &amp;quot;ldi    %A0, lo8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %B0, hi8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %C0, hh8(%1)&amp;quot;         \&lt;br /&gt;
       : &amp;quot;=d&amp;quot; (tmp)                  \&lt;br /&gt;
       : &amp;quot;i&amp;quot;  (&amp;amp;(var)));             \&lt;br /&gt;
   tmp;                              \&lt;br /&gt;
})&lt;br /&gt;
//-------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//===================================================================&lt;br /&gt;
// Define a section above 64kiB (FAR_SECTION)&lt;br /&gt;
// and add the required linker argument below&lt;br /&gt;
// -Wl,--section-start=.far_section=0x10000&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR_SECTION   __attribute__((__section__(&amp;quot;.far_section&amp;quot;)))&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Just a Sample&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
const char MyString[] FAR_SECTION = &amp;quot;Hier liegt mein FAR-Teststring!&amp;quot;;&lt;br /&gt;
const char MyBmp64[]  FAR_SECTION = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  char MyChar;&lt;br /&gt;
  DDRC = 0xFF;&lt;br /&gt;
  do&lt;br /&gt;
  {&lt;br /&gt;
    MyChar = pgm_read_byte_far(FAR(MyBmp64));&lt;br /&gt;
    PORTC  = MyChar;&lt;br /&gt;
  }&lt;br /&gt;
  while(MyChar);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. man muss&lt;br /&gt;
* Das Makro &amp;lt;tt&amp;gt;FAR&amp;lt;/tt&amp;gt; im Quellcode einfügen&lt;br /&gt;
* Die Definition der neuen Section &amp;lt;tt&amp;gt;FAR_SECTION&amp;lt;/tt&amp;gt; einfügen&lt;br /&gt;
* Die Variablen mit dieser Section kennzeichnen&lt;br /&gt;
* Dem Linker mittels Kommandozeilenoption die Startadrese dieser Section mitteilen&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf diese Variablen kann nur mittels direkter Pointerarithmetik erfolgen, eine Indizierung von Arrays mit variablem Index ist nicht möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int n=3;&lt;br /&gt;
MyChar = pgm_read_byte_far(FAR(MyBmp64)+n);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flash mit __flash und Embedded-C ==&lt;br /&gt;
&lt;br /&gt;
Ab Version 4.7 unterstützt avr-gcc &#039;&#039;Adress-Spaces&#039;&#039; gemäß dem Embedded-C Dokument ISO/IEC TR18037.  Der geläufigste Adress-Space ist &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;, der im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; kein GCC-Attribut ist, sondern ein Qualifier und damit syntaktisch ähnlich verwendet wird wie &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;volatile&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GCC kennt keine eigene Option zum Aktivieren von Embedded-C, es wird als GNU-C Erweiterung behandelt. Daher müssen C-Module, die Address-Spaces verwenden, mit &amp;lt;tt&amp;gt;-std=gnu99&amp;lt;/tt&amp;gt; o.ä. compiliert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash int value = 10;&lt;br /&gt;
&lt;br /&gt;
int get_value (void)&lt;br /&gt;
{&lt;br /&gt;
  return value;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; sind keine speziellen Bibliotheksfunktionen oder -makros für den Zugriff mehr notwendig: Der Code zum Lesen der Variable ist &amp;quot;normales&amp;quot; C.&lt;br /&gt;
# Die Variable wird im richtigen Speicherbereich (Flash) angelegt.&lt;br /&gt;
# &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; ist nur zusammen mit read-only Objekten oder Zeigern, d.h. nur zusammen mit &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt;, erlaubt.&lt;br /&gt;
# Zugriffe wie im obigen Beispiel können (weg)optimiert werden.  Das Beispiel entspricht einem &amp;quot;&amp;lt;tt&amp;gt;return 10&amp;lt;/tt&amp;gt;&amp;quot;.  Es besteht keine Notwendigkeit, für &amp;lt;tt&amp;gt;value&amp;lt;/tt&amp;gt; überhaupt Flash-Speicher zu reservieren.&lt;br /&gt;
&lt;br /&gt;
Auch Zeiger-Indirektionen sind problemlos möglich.  Zu beachten ist, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; auf der richtigen Seite des &amp;quot;&amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;&amp;quot; in der Zeigerdeklaration bzw. -definition steht:&lt;br /&gt;
* &#039;&#039;&#039;Rechts vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger selbst liegt im Flash&lt;br /&gt;
* &#039;&#039;&#039;Links vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger enthält eine Flash-Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// val ist eine Variable im Flash&lt;br /&gt;
const __flash int val = 42;&lt;br /&gt;
&lt;br /&gt;
// pval liegt auch im Flash und enthält die Adresse von val&lt;br /&gt;
const __flash int* const __flash pval = &amp;amp;val;&lt;br /&gt;
&lt;br /&gt;
char get_val (void)&lt;br /&gt;
{&lt;br /&gt;
  // liest den Wert von val über die in pval abgelegte Adresse&lt;br /&gt;
  return *pval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
Um Speicherbereiche vom Flash in den RAM zu kopieren, gibt es zwei Möglichkeiten: Zum einen können wie bei &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; beschreiben die Funktionen der avr-libc wie &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;movmem_P&amp;lt;/tt&amp;gt;, etc. verwendet werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // buf wird auf dem Stack angelegt&lt;br /&gt;
    data_t buf;&lt;br /&gt;
    &lt;br /&gt;
    // Kopiere Daten vom Flash nach buf ins RAM&lt;br /&gt;
    memcpy_P (&amp;amp;buf, pdata, sizeof (data_t));&lt;br /&gt;
 &lt;br /&gt;
    // Sende die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum anderen kann eine Struktur auch über direktes Kopieren ins RAM geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere Daten ins RAM.  buf wird auf dem Stack angelegt&lt;br /&gt;
    const data_t buf = *pdata;&lt;br /&gt;
    &lt;br /&gt;
    // Verwendet die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Natürlich können auch Strings im Flash abgelegt werden und auch mit Funktionen wie &amp;lt;tt&amp;gt;strcpy_P&amp;lt;/tt&amp;gt; aus der avr-libc verarbeitet werden.  Zudem ist es möglich, Flash-Zeiger mit der Adresse eines String-Literals zu initialisieren:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define FSTR(X) ((const __flash char[]) { X } )&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    FSTR (&amp;quot;Hund&amp;quot;), FSTR (&amp;quot;Katze&amp;quot;), FSTR (&amp;quot;Maus&amp;quot;)&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
size_t get_len (uint8_t tier)&lt;br /&gt;
{&lt;br /&gt;
    return strlen_P (array[tier]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider sieht der Embedded-C Draft nicht vor, String-Literale direkt in einem anderen Adress-Space als &#039;&#039;generic&#039;&#039; anzulegen, so dass hier der Umweg über &amp;lt;tt&amp;gt;FSTR&amp;lt;/tt&amp;gt; genommen werden muss.  Dieses Konstrukt ist nur ausserhalb von Funktionen möglich und kann daher nicht als Ersatz für &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; aus der avr-libc dienen.&lt;br /&gt;
&lt;br /&gt;
Soll &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; ein 2-dimensonales Array sein anstatt ein 1-dimensionales Array von Zeigern, dann geht das ohne große Verrenkungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Die 6 ergibt sich aus 1 plus der Länge des längsten Strings &amp;quot;Katze&amp;quot;&lt;br /&gt;
const __flash char array[][6] = &lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiters besteht die Möglichkeit, &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; analog anzulegen, wie man es mit &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; machen würde:  Jeder String wird explizit angelegt und seine Adresse bei der Initialisierung von &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; verwendet.  Dies entspricht dem ersten Beispiel eines 1-dimensionalen Zeigerarrays:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char strHund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char strKatze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char strMaus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    strHund, strKatze, strMaus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Casts ===&lt;br /&gt;
&lt;br /&gt;
Embedded C fordert, dass zwei Adress-Spaces entweder disjunkt sind – d.h. sie enthalten keine gemeinsamen Adressen – oder aber ein Space komplett im anderen enthalten ist, also eine Teilmengen-Beziehung besteht.  Die Adress-Spaces von avr-gcc sind so implementiert, dass jeder Space Teilmenge jedes anderes ist.  Zwar haben Spaces wie RAM und Flash physikalisch keinen Speicherbereich gemein, allerdings ermöglicht diese Implementierung das Casten von Zeigern zu unterschiedlichen Adress-Spaces&amp;lt;ref&amp;gt;Im Gegensatz zu einem Attribute wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist ein (Adress Space) Qualifier Teil des Zeiger-Typs.&amp;lt;/ref&amp;gt;:  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
char read_char (const char *address, bool data_in_flash)&lt;br /&gt;
{&lt;br /&gt;
    if (data_in_flash)&lt;br /&gt;
        return *(const __flash char*) address;&lt;br /&gt;
    else&lt;br /&gt;
        return *address;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Cast selbst erzeugt keinen zusätzlichen Code, da eine RAM-Adresse und eine Flash-Adresse die gleiche Binärdarstellung haben.  Allerdings wird über den nach &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gecasteten Zeiger anders zugegriffen, nämlich per LPM.&lt;br /&gt;
&lt;br /&gt;
=== Jenseits von __flash ===&lt;br /&gt;
&lt;br /&gt;
Ausser &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gibt es auch folgende Address-Spaces:&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;: Mit &#039;&#039;N&#039;&#039; = 1..5 sind fünf weitere Spaces, die analog zu &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; funktionieren und deren Zeiger ebenfalls 16 Bit breit sind.  avr-gcc erwartet, dass die zugehörigen Daten, welche in die Section &amp;lt;tt&amp;gt;.progmem&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;&amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; abgelegt werden, so lokatiert sind, dass das high-Byte der Adresse (Bits 16..23) gerade &#039;&#039;N&#039;&#039; ist.  Dies wird in Binutils noch nicht unterstützt&amp;lt;ref&amp;gt;[http://sourceware.org/PR14406 Binutils PR14406]: Support .progmem&amp;lt;N&amp;gt;.data sections to work with GCC&#039;s [http://gcc.gnu.org/PR49868 PR49868].&amp;lt;/ref&amp;gt; (Stand Binutils 2.24) Die Unterstützung kann durch ein eigenes Linker-Skript erreicht werden, welches diese Sections wie vom Compiler erwartet lokatiert.&lt;br /&gt;
; &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;: Dieser Address-Space implementiert 3-Byte Zeiger und unterstützt Lesen über 64KiB-Segmentgrenzen hinweg.  Das MSB (Bit 23) gibt dabei an, ob der &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger eine Flash-Adresse enthält (Bit23 = 0) oder eine RAM-Adresse (Bit23 = 1), was folgenden Code erlaubt:&lt;br /&gt;
&amp;lt;!-- Bitte die Tabelle belassen, damit der Code richtig eingerückt wird! --&amp;gt;&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const __memx int a_flash = 42;&lt;br /&gt;
const        int a_ram   = 100;&lt;br /&gt;
&lt;br /&gt;
static int get_a (const __memx int* pa)&lt;br /&gt;
{&lt;br /&gt;
    return *pa;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    return get_a (&amp;amp;a_flash) + get_a (&amp;amp;a_ram);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}Dies bedeutet, dass erst zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden kann, ob aus dem RAM oder aus dem Flash gelesen werden soll, was &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt; im Vergleich zu den anderen Address-Spaces langsamer macht. Ausserdem ist zu beachten, dass &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger zwar 24-Bit Zeiger sind, die zugrundeliegende Adress-Arithmetik jedoch gemäß dem C-Standard erfolgt, also als 16-Bit Arithmetik.&lt;br /&gt;
&lt;br /&gt;
=== __flash, progmem und Portierbarkeit ===&lt;br /&gt;
&lt;br /&gt;
Da ab er aktuellen Compilerversion 4.7 sowohl &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; als auch &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; und die &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen zur Verfügung stehen, ergibt sich die Frage, welche Variante &amp;quot;besser&amp;quot; ist und wie zwischen ihnen hin- und her zu portieren ist.&lt;br /&gt;
&lt;br /&gt;
Zunächst sei erwähnt, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; kein Ersatz für &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; ist, sondern lediglich eine Alternative dazu.  Das &amp;quot;alte&amp;quot; progmem wird weiterhin mir gleicher Semantik unterstützt, so dass alter Code ohne Änderungen mit den neueren Compilerversionen übersetzbar bleibt.&lt;br /&gt;
&lt;br /&gt;
Von der Codegüte her dürften sich keine großen Unterschiede ergeben.  Es ist nicht zu erwarten, dass die eine oder die andere Variante wesentlich besseren oder schlechteren Code erzeugt — von einer Ausnahme abgesehen:  Der Wert beim Zugriff ist zur Compilezeit bekannt und kann daher eliminiert werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char x[] = { &#039;A&#039;, &#039;V&#039;, &#039;R&#039; };&lt;br /&gt;
&lt;br /&gt;
char foo (void)&lt;br /&gt;
{&lt;br /&gt;
    return x[2];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies wird übersetzt wie &amp;quot;&amp;lt;tt&amp;gt;return &#039;R&#039;;&amp;lt;/tt&amp;gt;&amp;quot;, und das Array &amp;lt;tt&amp;gt;x[]&amp;lt;/tt&amp;gt; kann komplett wegoptimiert werden und entfallen.&lt;br /&gt;
&lt;br /&gt;
==== progmem → __flash ====&lt;br /&gt;
&lt;br /&gt;
Portierung in diese Richtung bedeutet, alten Code anzupassen.  Zwingend ist die Portierung nicht, da &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; weiterhin unterstützt wird.&lt;br /&gt;
Allerdings ist eine Quelle mit &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; besser lesbar, denn der Code wird von den &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen befreit, die vor allem bei Mehrfach-Indirektion den Code ziemlich verunstalten und unleserlich machen können.&lt;br /&gt;
Weiterer Vorteil von &amp;lt;tt&amp;gt;_flash&amp;lt;/tt&amp;gt; ist, daß eine striktere Typprüfung erfolgen kann.&lt;br /&gt;
&lt;br /&gt;
Eine Portierung wird man in zwei Schritten vornehmen:&lt;br /&gt;
&lt;br /&gt;
;1. Definitionen von Flash-Variablen werden angepasst:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
static const char hund[]  PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char katze[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char maus[]  PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const tier[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char hund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char katze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char maus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
const __flash char * const __flash tier[] = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; wird nicht mehr benötigt.  Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; müssen Qualifier immer links von der definierten Variablen stehen; bei Attributen wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist das mehr oder weniger egal.&lt;br /&gt;
&lt;br /&gt;
Nachdem diese Anpassung erfolgreich abgeschlossen ist, folgt Schritt&lt;br /&gt;
&lt;br /&gt;
; 2. Der Code wird von &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Aufrufen bereinigt:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const char *tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    const char* ptier = (const char*) pgm_read_word (&amp;amp;tier[i]);&lt;br /&gt;
    return (char) pgm_read_byte (&amp;amp;ptier[0]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const __flash char * const __flash tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    return tier[i][0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Dateien direkt im Flash einbinden ==&lt;br /&gt;
&lt;br /&gt;
Wenn man größere Dateien direkt im Programm einbinden will, ohne sie vorher in C Quelltext umzuwandeln, muss man das mit dem Linker machen. Wie das geht steht hier.&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/webdoc/avrlibcreferencemanual/FAQ_1faq_binarydata.html Atmel, avr gcc Dokumentation]&lt;br /&gt;
* [http://nongnu.org/avr-libc/user-manual/FAQ.html#faq_binarydata Nongnu avr gcc Dokumentation]&lt;br /&gt;
&lt;br /&gt;
Wie man das dann praktisch umsetzt, sieht man in diesem Beitrag.&lt;br /&gt;
&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/361429?goto=4056910#4056910 Forumsbeitrag]: Binärdateien mittels Linker einbinden&lt;br /&gt;
&lt;br /&gt;
== Flash in der Anwendung schreiben ==&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option – auch bekannt als [[Bootloader]]-Support – können Teile des Flash-Speichers vom Anwendungsprogramm beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktion in einem besonderen Speicherbereich, der Boot-Section des Programmspeichers/Flash, abgelegt ist.&lt;br /&gt;
&lt;br /&gt;
Bei einigen kleinen AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* Forumsbeitrag [http://www.mikrocontroller.net/topic/163632#1561622 Daten in Programmspeicher speichern]&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Möchte man Werte aus einem Programm heraus so speichern, dass sie auch nach dem Abschalten der Versorgungsspannung noch erhalten bleiben und nach dem Wiederherstellen der Versorgungsspannung bei erneutem Programmstart wieder zur Verfügung stehen, dann benutzt man das EEPROM.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h der avr-libc definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16 Bit), Fließkommawerte (32 Bit, single-precision, float) und Datenblöcke geschrieben und gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen kümmern sich auch um diverse Details, die bei der Benutzung des EEPROMs normalerweise notwendig sind:&lt;br /&gt;
* EEPROM-Operationen sind im Vergleich relativ langsam. Man muss daher darauf achten, dass eine vorhergehende Operation abgeschlossen ist, ehe die nächste Operation mit dem EEPROM gestartet wird. Die in der avr-libc implementierten Funktionen aus eeprom.h berücksichtigten dies. Soll beim Aufruf einer EEPROM-Funktion sichergestellt werden, dass diese nicht intern in einer Warteschleife auf den Abschluss der vorherigen Operation wartet, kann vorher per eeprom_is_ready testen, ob der Zugriff auf den EEPROM-Speicher sofort möglich ist.&lt;br /&gt;
* Es ist darauf zu achten, dass die EEPROM-Funktionen nicht durch einen Interrupt unterbrochen werden. Einige Phasen des Zugriffs sind zeitkritisch und müssen in einer definierten bzw. begrenzten Anzahl von Takten durchgeführt werden. Durch einen unterbrechenden Interrupt würde diese Restriktion nicht mehr eingehalten. Auch dieses Detail wird von den avr-libc Funktionen berücksichtigt, so dass man sich als C-Programmierer nicht darum kümmern muss. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. Dies gilt für jede einzelne Zelle. &lt;br /&gt;
&lt;br /&gt;
Bei geschickter Programmierung (z.&amp;amp;nbsp;B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den gesamten EEPROM-Speicher, erreichen. Auf jeden Fall sollte man aber eine Abschätzung über die zu erwartende Lebensdauer des EEPROM durchführen. Wird ein Wert im EEPROM im Durchschnitt nur einmal pro Woche verändert, wird die garantierte Anzahl der Schreibzyklen innerhalb der voraussichtlichen Verwendungszeit des Controllers sicherlich nicht erreicht werden. Welcher Controller ist schon 100000 / 52 = 1923 Jahre im Einsatz? In diesem Fall lohnt es sich daher nicht, erweiterte Programmfunktionen zu implementieren, mit denen die Anzahl der Schreibzugriffe minimiert wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit, Schreibzyklen einzusparen, besteht in der Vorabprüfung, ob der zu speichernde Wert im EEPROM bereits enthalten ist und nur veränderte Werte zu schreiben. In aktuelleren Versionen der avr-libc sind bereits Funktionen enthalten, die solche Prüfungen enthalten (eeprom_update_*).&lt;br /&gt;
&lt;br /&gt;
Lesezugriffe können beliebig oft durchgeführt werden. Sie unterliegen keinen Einschränkungen in Bezug auf deren Anzahl. &lt;br /&gt;
&lt;br /&gt;
=== EEMEM ===&lt;br /&gt;
Um eine Variable im EEPROM anzulegen, stellt die avr-libc das Makro EEMEM zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Array */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3, 70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7, 79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die grundsätzliche Vorgehensweise ist identisch zur Verwendung von PROGMEM. Auch hier erzeugt man sich spezielle attributierte Variablen (EEMEM erledigt das), die vom Compiler/Linker nicht wie normale Variablen behandelt werden. Compiler/Linker kümmern sich zwar darum, dass diesen Variablen eine Adresse zugewiesen wird, diese Adresse ist dann aber die Adresse der &#039;Variablen&#039; im EEPROM. Um die dort gespeicherten Werte zu lesen bzw. zu schreiben, übergibt man diese Adresse an spezielle Funktionen, die die entsprechenden Werte aus dem EEPROM holen bzw. das EEPROM neu beschreiben.&lt;br /&gt;
&lt;br /&gt;
Die mittels EEMEM erzeugten &#039;Variablen&#039; sind also mehr als Platzhalter zu verstehen, denn als echte Variablen. Es geht nur darum, im C-Programm symbolische Namen zur Verfügung zu haben, anstatt mit echten EEPROM-Adressen hantieren zu müssen, etwas, das grundsätzlich aber auch genauso gut möglich ist. Nur muss man sich in diesem Fall dann selbst darum kümmern, dass mehrere &#039;Variablen&#039; ohne Überschneidung im EEPROM angeordnet werden.&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EEPROM_DEF 0xFF&lt;br /&gt;
&lt;br /&gt;
void eeprom_example (void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    // myByte lesen (Wert = 123)&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByte);&lt;br /&gt;
&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // Variablen eeFooByte geschrieben&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
&lt;br /&gt;
    // Beispiel fuer eeprom_update_byte: die EEPROM-Zelle wird nur&lt;br /&gt;
    // dann beschrieben, wenn deren Inhalt sich vom Parameterwert&lt;br /&gt;
    // unterscheidet. In diesem Beispiel erfolgt also kein Schreib-&lt;br /&gt;
    // zugriff, da die Werte gleich sind.&lt;br /&gt;
    eeprom_update_byte(&amp;amp;eeFooByte, myByte);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z. B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ((myByte = eeprom_read_byte (&amp;amp;eeFooByte)) == EEPROM_DEF)&lt;br /&gt;
    {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // lesen&lt;br /&gt;
    uint16_t myWord = eeprom_read_word (&amp;amp;eeFooWord);&lt;br /&gt;
&lt;br /&gt;
    // schreiben&lt;br /&gt;
    eeprom_write_word (&amp;amp;eeFooWord, 2222);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &amp;lt;code&amp;gt;eeprom_read_block()&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;eeprom_write_block()&amp;lt;/code&amp;gt;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Adresse und die Länge des Datenblocks in Bytes als &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t  myByteBuffer[3];&lt;br /&gt;
uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
void eeprom_block_example (void)&lt;br /&gt;
{&lt;br /&gt;
    /* Datenblock aus EEPROM lesen  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, 3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit 16-Bit Array */&lt;br /&gt;
    eeprom_read_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock in EEPROM schreiben */&lt;br /&gt;
    eeprom_write_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fließkommawerte lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
In der avr-libc stehen auch EEPROM-Funktionen für Variablen des Typs float (Fließkommazahlen mit &amp;quot;einfacher&amp;quot; Genauigkeit) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
float eeFloat EEMEM = 12.34f;&lt;br /&gt;
&lt;br /&gt;
float void eeprom_float_example(float value)&lt;br /&gt;
{&lt;br /&gt;
   /* float in EEPROM schreiben */&lt;br /&gt;
   eeprom_write_float(&amp;amp;eeFloat, value);&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM lesen */&lt;br /&gt;
   return  eeprom_read_float(&amp;amp;eeFloat);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklarationen abgeleitete EEPROM-Inhalt in eine Datei geschrieben werden. Die übliche Dateiendung ist .eep, Daten im Intel Hex-Format. Damit können Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. &lt;br /&gt;
&lt;br /&gt;
Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen, siehe dazu die Erläuterungen im [[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]].&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden, wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse&amp;lt;ref&amp;gt;vgl. Datenblatt Abschnitt Fuse Bits&amp;lt;/ref&amp;gt; nicht die korrekten Werte:&lt;br /&gt;
; EESAVE = 0 (programmed): Die Daten im EEPROM bleiben erhalten. Werden sie nicht neu geschrieben, so enthält das EEPROM evtl. Daten, die nicht mehr zum Programm passen.&lt;br /&gt;
; EESAVE = 1 (unprogrammed): Beim Programmieren werden die Daten im EEPROM gelöscht, also auf 0xff gesetzt.&lt;br /&gt;
&lt;br /&gt;
Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
=== Direkter Zugriff auf EEPROM-Adressen ===&lt;br /&gt;
&lt;br /&gt;
Will man direkt auf bestimmte EEPROM Adressen zugreifen, dann sind folgende Funktionen hilfreich, um sich die Typecasts zu ersparen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Byte aus dem EEPROM lesen&lt;br /&gt;
uint8_t EEPReadByte(uint16_t addr)&lt;br /&gt;
{&lt;br /&gt;
  return eeprom_read_byte((uint8_t *)addr);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Byte in das EEPROM schreiben&lt;br /&gt;
void EEPWriteByte(uint16_t addr, uint8_t val)&lt;br /&gt;
{&lt;br /&gt;
  eeprom_write_byte((uint8_t *)addr, val);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder als Makro:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define   EEPReadByte(addr)         eeprom_read_byte((uint8_t *)addr)     &lt;br /&gt;
#define   EEPWriteByte(addr, val)   eeprom_write_byte((uint8_t *)addr, val)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EEPWriteByte(0x20, 128);   // Byte an die Adresse 0x20 schreiben&lt;br /&gt;
...&lt;br /&gt;
Val=EEPReadByte(0x20);     // EEPROM-Wert von Adresse 0x20 lesen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Was steckt dahinter? - EEPROM-Register ===&lt;br /&gt;
Um das EEPROM anzusteuern, sind drei Register von Bedeutung:&lt;br /&gt;
;EEAR: Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL, da in einem 8-Bit-Register keine 512 Adressen adressiert werden können.&lt;br /&gt;
;EEDR: Hier werden die Daten eingetragen, die geschrieben werden sollen, bzw. es enthält die gelesenen Daten.&lt;br /&gt;
;EECR: Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Aufbau des EECR-Registers&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
| - || - || - ||- || EERIE || EEMWE || EEWE || EERE&lt;br /&gt;
|-&lt;br /&gt;
! Read/Write&lt;br /&gt;
| R || R || R || R || R/W || R/W || R/W || R/W&lt;br /&gt;
|-&lt;br /&gt;
!Init Value&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bedeutung der Bits&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Bit 4-7: nicht belegt&lt;br /&gt;
&lt;br /&gt;
;Bit 3 (EERIE): &#039;&#039;EEPROM Ready Interrupt Enable&#039;&#039;: Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7), wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0, wird kein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
;Bit 2 EEMWE): &#039;&#039;EEPROM Master Write Enable&#039;&#039;: Dieses Bit bestimmt, dass, wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE = 0 ist und EEWE = 1 gesetzt wird, hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst. Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&lt;br /&gt;
&lt;br /&gt;
;Bit 1 (EEWE): &#039;&#039;EEPROM Write Enable&#039;&#039;: Dieses Bit löst den Schreibvorgang aus, wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist, wird dieses Bit automatisch wieder auf 0 gesetzt und, sofern EERIE gesetzt ist, ein Interrupt ausgelöst. Ein Schreibvorgang sieht typischerweise wie folgt aus:&lt;br /&gt;
:# EEPROM-Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Daten übergeben an EEDR&lt;br /&gt;
:# Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1&lt;br /&gt;
:# (Optional) Warten, bis Schreibvorgang abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
;Bit 0 EERE: &#039;&#039;EEPROM Read Enable&#039;&#039;: Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden, wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen, die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit, wenn das Bit EEWE=0 ist. Ist der Lesevorgang abgeschlossen, wird das Bit wieder auf 0 gesetzt, und das EEPROM ist für neue Lese- und Schreibbefehle wieder bereit. Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&lt;br /&gt;
:# Bereitschaft zum Lesen prüfen (EEWE=0)&lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Lesezyklus auslösen mit EERE = 1&lt;br /&gt;
:# Warten, bis Lesevorgang abgeschlossen EERE = 0&lt;br /&gt;
:# Daten abholen aus EEDR&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von sprintf und printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel, d.h. formatiert, Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bieten sich &#039;&#039;&#039;sprintf&#039;&#039;&#039; oder &#039;&#039;&#039;printf&#039;&#039;&#039; an. Alle *printf-Varianten sind jedoch ziemlich speicherintensiv und der Einsatz in einem Mikrocontroller mit knappem Speicher muss sorgsam abgewogen werden.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;sprintf&#039;&#039;&#039; wird die Ausgabe zunächst in einem Puffer vorbereitet und anschließend mit einfachen Funktionen zeichenweise ausgegeben. Es liegt in der Verantwortung des Programmierers, genügend Platz im Puffer für die erwarteten Zeichen bereitzuhalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
// Ausgabe eines unsigned Integerwertes&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t puffer[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( puffer, &amp;quot;Zählerstand: %u&amp;quot;, value );&lt;br /&gt;
    uart_puts( puffer );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, den STREAM stdout (Standardausgabe) auf eine eigene Ausgabefunktion umzuleiten. Dazu wird dem Ausgabemechanismus der C-Bibliothek eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben. Wohin die Ausgabe dann tatsächlich stattfindet, ist Sache der Ausgabefunktion. Im Beispiel unten wird auf UART ausgegeben. Alle anderen, höheren Funktionen wie z.&amp;amp;nbsp;B. &#039;&#039;&#039;printf&#039;&#039;&#039;, greifen letztendlich auf diese primitive Ausgabefunktion zurück. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
&lt;br /&gt;
// a. Deklaration der primitiven Ausgabefunktion&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
// b. Umleiten der Standardausgabe stdout (Teil 1)&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
// c. Definition der Ausgabefunktion&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t antwort = 42;&lt;br /&gt;
    uart_init();&lt;br /&gt;
&lt;br /&gt;
    // b. Umleiten der Standardausgabe stdout (Teil 2)&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
&lt;br /&gt;
    // Anwendung&lt;br /&gt;
    printf( &amp;quot;Die Antwort ist %d.\n&amp;quot;, antwort );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
= Anmerkungen =&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:avr-gcc Tutorial| ]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90656</id>
		<title>AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=90656"/>
		<updated>2015-12-11T14:25:17Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* EEPROM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieses Tutorial soll den Einstieg in die Programmierung von Atmel [[AVR]]-Mikrocontrollern in der Programmiersprache [[C]] mit dem freien C-Compiler [[avr-gcc]] aus der [http://gcc.gnu.org/ GNU Compiler Collection] (GCC) erleichtern.&lt;br /&gt;
&lt;br /&gt;
Vorausgesetzt werden Grundkenntnisse der Programmiersprache C. Diese Kenntnisse kann man sich online erarbeiten, z. B. mit dem [http://www.schellong.de/c.htm C Tutorial von Helmut Schellong] ([[C|Liste von C-Tutorials]]). Nicht erforderlich sind Vorkenntnisse in der Programmierung von Mikrocontrollern.&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek avr-libc verwiesen, für die es eine [http://www.nongnu.org/avr-libc/user-manual/index.html Online-Dokumentation] gibt, in der sich auch viele nützliche Informationen zum Compiler und zur Programmierung von AVR-Controllern finden. Beim Paket [[WinAVR]] gehört die avr-libc Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
Der Compiler und die Standardbibliothek avr-libc werden ständig weiterentwickelt. Einige Unterschiede, die sich im Verlauf der Entwicklung ergeben haben, werden hier und im Artikel [[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen]] zwar angesprochen, Anfängern und Umsteigern sei jedoch empfohlen, eine aktuelle Versionen zu nutzen.&lt;br /&gt;
&lt;br /&gt;
Das ursprüngliche Tutorial stammt von Christian Schifferle, viele neue Abschnitte und aktuelle Anpassungen von Martin Thomas.&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial ist in [[Media:AVR-GCC-Tutorial.pdf|PDF-Form]] erhältlich (nicht immer auf aktuellem Stand).&lt;br /&gt;
&lt;br /&gt;
== Weiterführende Kapitel ==&lt;br /&gt;
&lt;br /&gt;
Um dieses riesige Tutorial etwas überschaubarer zu gestalten, wurden einige Kapitel ausgelagert, die nicht unmittelbar mit den Grundlagen von avr-gcc in Verbindung stehen. All diese Seiten gehören zur [[:Kategorie:avr-gcc Tutorial]].&lt;br /&gt;
&lt;br /&gt;
;UART: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der UART|Der UART]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;ADC: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Analoge Ein- und Ausgabe|Analoge Ein- und Ausgabe (ADC)]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Timer: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Die Timer und Zähler des AVR|Die Timer und Zähler des AVR]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;LCD: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Ansteuerung]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Watchdog: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Der Watchdog|Der Watchdog]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Assembler: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Assembler und Inline-Assembler|Assembler und Inline-Assembler]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;alte Quellen anpassen: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Alte Quellen|Alte Quellen anpassen]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Makefiles: → Hauptartikel: &#039;&#039;[[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]]&#039;&#039; sowie als Alternative für sehr kleine Projekte → Hauptartikel: &#039;&#039;[[C ohne Makefile]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um eigene Programme für AVRs mittels einer AVR-Toolchain zu erstellen wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Eine AVR-Toolchain bestehend aus avr-gcc, den avr-Binutils (Assembler, Linker, etc) und einer Standard-C Bibliothek.  Üblich ist die AVR-LibC, die auch quasi in allen avr-gcc Distributionen enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Hardware wird keine benötigt – bis auf einen PC natürlich, auf dem der Compiler ablaufen kann.  Selbst ohne AVR-Hardware kann man also bereits C-Programme für AVRs schreiben, compiliern und sich das Look-and-Feel von avr-gcc sowie von IDEs wie [[Atmel Studio]], Eclipse oder leichtgewichtigeren Entwicklungsumbgebungen anschauen. Selbst das Debuggen und Simulieren ist mithilfe entsprechender Tools wie Debugger und Simulator in gewissen Grenzen möglich.&lt;br /&gt;
&lt;br /&gt;
Um Programme für AVRs mittels einer AVR-Toolchain zu testen, wird folgende Hard- und Software benötigt:&lt;br /&gt;
&lt;br /&gt;
* Platine oder Versuchsaufbau für die Aufnahme eines AVR-Controllers, der vom avr-gcc Compiler unterstützt wird.&amp;lt;ref&amp;gt;Für eine Liste der unterstützten COntroller siehe die Dokumentation des Compilers oder [http://www.nongnu.org/avr-libc/user-manual/index.html#supported_devices AVR-Libc: Supported Devices].&amp;lt;/ref&amp;gt; Dieses Testboard kann durchaus auch selbst gelötet oder auf einem Steckbrett aufgebaut werden. Einige Registerbeschreibungen dieses Tutorials beziehen sich auf den inzwischen veralteten AT90S2313. Der weitaus größte Teil des Textes ist aber für alle Controller der AVR-Familie gültig. &lt;br /&gt;
&lt;br /&gt;
:Brauchbare Testplattformen sind auch das [[STK500]] und der [[AVR Butterfly]] von Atmel. Weitere Infos findet man in den Artikeln [[AVR#Starterkits|AVR Starterkits]] und [[AVR-Tutorial: Equipment]].&lt;br /&gt;
&lt;br /&gt;
* Programmiersoftware und -[[AVR In System Programmer |hardware]] z. B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], Atmel AVRISP, [[AVR-Studio]]).&lt;br /&gt;
&lt;br /&gt;
* Nicht unbedingt erforderlich, aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]].&lt;br /&gt;
&lt;br /&gt;
* Wer unter Windows und Linux gleichermassen entwickeln will, der sollte sich die [http://www.eclipse.org/ IDE Eclipse for C/C++ Developers] und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] ansehen. Beide sind unter Windows und Linux einfach zu installieren, siehe auch [[AVR Eclipse]]. Ebenfalls unter Linux und Windows verfügbar ist die Entwicklungsumgebung [http://www.codeblocks.org/ Code::Blocks]&amp;lt;ref&amp;gt;Aktuelle, stabile Versionen sind als Nightly Builds regelmäßig im [http://forums.codeblocks.org/ Forum] verfügbar.&amp;lt;/ref&amp;gt;. Innerhalb dieser Entwicklungsumgebung können ohne die Installation zusätzlicher Plugins &amp;quot;AVR-Projekte&amp;quot; angelegt werden. Für Linux gibt es auch noch das [http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=25220 KontrollerLab].&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht klappt? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder nur die eigenen C-Kenntnisse einer Auffrischung bedürfen. Allgemeine C-Fragen kann man eventuell &amp;quot;beim freundlichen Programmierer zwei Büro-, Zimmer- oder Haustüren weiter&amp;quot; loswerden. Ansonsten: [[C]]-Buch (gibt&#039;s auch &amp;quot;gratis&amp;quot; online) lesen.&lt;br /&gt;
&lt;br /&gt;
* Die [[AVR Checkliste]] durcharbeiten.&lt;br /&gt;
&lt;br /&gt;
* Die &#039;&#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc]&#039;&#039;&#039; lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/&#039;&#039;&#039;Frequently Asked Questions&#039;&#039;&#039; = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://www.mikrocontroller.net/forum/gcc GCC-Forum auf  www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net AVRfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Das [http://lists.gnu.org/archive/html/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem im &#039;&#039;Projects&#039;&#039;-Bereich von [http://www.avrfreaks.net AVRfreaks] (anmelden).&lt;br /&gt;
&lt;br /&gt;
* Google oder yahoo befragen schadet nie.&lt;br /&gt;
&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal). Datenblätter sind  auf den [http://www.atmel.com Atmel Webseiten] als pdf-Dateien verfügbar. Das komplette Datenblatt (complete) und nicht die Kurzfassung (summary) verwenden.&lt;br /&gt;
&lt;br /&gt;
* Die Beispielprogramme im [[AVR-Tutorial]] sind zwar in AVR-Assembler verfasst, Erläuterungen und Vorgehensweisen sind aber auch auf C-Programme übertragbar.&lt;br /&gt;
&lt;br /&gt;
* Einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei möglichst viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode oder besser ein [http://www.mikrocontroller.net/topic/72767#598986 Testprojekt] mit allen notwendigen Dateien, um das Problem nachzuvollziehen, sowie genaue Fehlermeldungen bzw. Beschreibung des Fehlverhaltens. Bei Ansteuerung externer Geräte die Beschaltung beschreiben oder skizzieren (z. B. mit [http://www.tech-chat.de/ Andys ASCII Circuit]). Siehe dazu auch: &#039;&#039;&#039;[http://www.tty1.net/smart-questions_de.html &amp;quot;Wie man Fragen richtig stellt&amp;quot;]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Erzeugen von Maschinencode =&lt;br /&gt;
&lt;br /&gt;
Aus dem C-Quellcode erzeugt der avr-gcc Compiler (zusammen mit Hilfsprogrammen wie z.&amp;amp;nbsp;B. Präprozessor, Assembler und Linker) Maschinencode für den AVR-Controller. Üblicherweise liegt dieser Code dann im Intel Hex-Format vor (&amp;quot;Hex-Datei&amp;quot;). Die Programmiersoftware (z.&amp;amp;nbsp;B. [[AVRDUDE]], PonyProg oder AVRStudio/STK500-plugin) liest diese Datei ein und überträgt die enthaltene Information (den Maschinencode) in den Speicher des Controllers. Im Prinzip sind also &amp;quot;nur&amp;quot; der avr-gcc-Compiler (und wenige Hilfsprogramme) mit den &amp;quot;richtigen&amp;quot; Optionen aufzurufen, um aus C-Code eine &amp;quot;Hex-Datei&amp;quot; zu erzeugen. Grundsätzlich stehen dazu drei verschiedene Ansätze zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* Die Verwendung einer integrierten Entwicklungsumgebung (IDE = &#039;&#039;&#039;I&#039;&#039;&#039;ntegrated &#039;&#039;&#039;D&#039;&#039;&#039;evelopment &#039;&#039;&#039;E&#039;&#039;&#039;nvironment), bei der alle Einstellungen z.&amp;amp;nbsp;B. in Dialogboxen durchgeführt werden können. Unter Anderem kann AVRStudio ab Version 4.12 (kostenlos auf [http://www.atmel.com/ atmel.com]) zusammen mit WinAVR als integrierte Entwicklungsumgebung für den Compiler avr-gcc genutzt werden (dazu müssen AVRStudio und WinAVR auf dem Rechner installiert sein). Weitere IDEs (ohne Anspruch auf Vollständigkeit): [http://www.eclipse.org/ Eclipse for C/C++ Developers] (d.h. inkl. CDT) und das [http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin AVR-Eclipse Plugin] (für diverse Plattformen, u.a. Linux und MS Windows, IDE und Plugin kostenlos), [http://sourceforge.net/projects/kontrollerlab KontrollerLab] (Linux/KDE, kostenlos). [http://www.atmanecl.com/EnglishSite/SoftwareEnglish.htm AtmanAvr] (MS Windows, relativ günstig), KamAVR (MS-Windows, kostenlos, wird augenscheinlich nicht mehr weiterentwickelt), [http://www.amctools.com/vmlab.htm VMLab] (MS Windows, ab Version 3.12 ebenfalls kostenlos). Integrierte Entwicklungsumgebungen unterscheiden sich stark in Ihrer Bedienung und stehen auch nicht für alle Plattformen zur Verfügung, auf denen der Compiler  ausführbar ist (z.&amp;amp;nbsp;B. AVRStudio nur für MS-Windows). Zur Anwendung des avr-gcc Compilers mit IDEs sei hier auf deren Dokumentation verwiesen. &lt;br /&gt;
&lt;br /&gt;
* Die Nutzung des Programms make mit passenden Makefiles. In den folgenden Abschnitten wird die Generierung von Maschinencode für einen AVR (&amp;quot;hex-Datei&amp;quot;) aus C-Quellcode (&amp;quot;c-Dateien&amp;quot;) anhand von &amp;quot;make&amp;quot; und den &amp;quot;Makefiles&amp;quot; näher erläutert. Viele der darin beschriebenen Optionen findet man auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio (AVRStudio generiert ein makefile in einem Unterverzeichnis des Projektverzeichnisses). &lt;br /&gt;
&lt;br /&gt;
* Das Generieren des Programms ohne IDE und ohne Makefile. In diesem Fall muss die Quellcodedatei durch eine vorgefertigte Kommandofolge an den Compiler übergeben werden. Der Artikel [[C ohne Makefile]] zeigt, wie das funktioniert. Diese Vorgehensweise empfiehlt sich jedoch nur für kleine Programme, die nicht auf verschiedene Quellcodedateien verteilt sind.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel vom makefile-Ansatz nach WinAVR-Vorlage zu AVRStudio ist darauf zu achten, dass AVRStudio (Stand: AVRStudio Version 4.13) bei einem neuen Projekt die Optimierungsoption (vgl. Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|AVR-GCC-Tutorial/Exkurs: Makefiles]], typisch: -Os) nicht einstellt und die mathematische Bibliothek der avr-libc (libm.a, Linker-Option -lm) nicht einbindet. (Hinweis: Bei Version 4.16 wird beides bereits gesetzt). Beides ist Standard bei Verwendung von makefiles nach WinAVR-Vorlage und sollte daher auch im Konfigurationsdialog des avr-gcc-Plugins von AVRStudio &amp;quot;manuell&amp;quot; eingestellt werden, um auch mit AVRStudio kompakten Code zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
= Einführungsbeispiel =&lt;br /&gt;
&lt;br /&gt;
Zum Einstieg ein kleines Beispiel, an dem die Nutzung des Compilers und der Hilfsprogramme (der sogenannten &#039;&#039;Toolchain&#039;&#039;) demonstriert wird. Detaillierte Erläuterungen folgen in den weiteren Abschnitten dieses Tutorials.&lt;br /&gt;
&lt;br /&gt;
Das Programm soll auf einem AVR Mikrocontroller einige Ausgänge ein- und andere ausschalten. Das Beispiel ist für einen ATmega16 programmiert ([http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf Datenblatt]), kann aber sinngemäß für andere Controller der AVR-Familie modifiziert werden. &lt;br /&gt;
&lt;br /&gt;
Ein kurzes Wort zur Hardware: Bei diesem Programm werden alle Pins von PORTB auf Ausgang gesetzt, und einige davon werden auf HIGH andere auf LOW gesetzt. Das kann je nach angeschlossener Hardware an diesen Pins kritisch sein. Am ungefährlichsten ist es, wenn nichts an den Pins angeschlossen ist und man die Funktion des Programmes durch eine Spannungsmessung mit einem Multimeter kontrolliert. Die Spannung wird dabei zwischen GND-Pin und den einzelnen Pins von PORTB gemessen.&lt;br /&gt;
&lt;br /&gt;
Zunächst der Quellcode der Anwendung, der in einer Text-Datei mit dem Namen &#039;&#039;main.c&#039;&#039; abgespeichert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* Alle Zeichen zwischen Schrägstrich-Stern &lt;br /&gt;
   und Stern-Schrägstrich sind Kommentare */&lt;br /&gt;
&lt;br /&gt;
// Zeilenkommentare sind ebenfalls möglich&lt;br /&gt;
// alle auf die beiden Schrägstriche folgenden&lt;br /&gt;
// Zeichen einer Zeile sind Kommentar&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;          // (1)&lt;br /&gt;
&lt;br /&gt;
int main (void) {            // (2)&lt;br /&gt;
&lt;br /&gt;
   DDRB  = 0xFF;             // (3)&lt;br /&gt;
   PORTB = 0x03;             // (4)&lt;br /&gt;
&lt;br /&gt;
   while(1) {                // (5)&lt;br /&gt;
     /* &amp;quot;leere&amp;quot; Schleife*/   // (6)&lt;br /&gt;
   }                         // (7)&lt;br /&gt;
&lt;br /&gt;
   /* wird nie erreicht */&lt;br /&gt;
   return 0;                 // (8)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# In dieser Zeile wird eine sogenannte Header-Datei eingebunden. In &amp;lt;code&amp;gt;avr/io.h&amp;lt;/code&amp;gt; sind die Registernamen definiert, die im späteren Verlauf genutzt werden. Auch unter Windows wird ein&amp;amp;nbsp;&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; zur Kennzeichnung von Unterverzeichnissen in Include-Dateinamen verwendet und kein&amp;amp;nbsp;&amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Hier beginnt das eigentliche Programm. Jedes C-Programm beginnt mit den Anweisungen in der Funktion &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Die Anschlüsse eines AVR (Pins) werden zu Blöcken zusammengefasst, einen solchen Block bezeichnet man als Port. Beim ATmega16 hat jeder Port 8 Anschlüsse, bei kleineren AVRs können einem Port auch weniger als 8 Anschlüsse zugeordnet sein. Da per Definition (Datenblatt) alle gesetzten Bits in einem Datenrichtungsregister den entsprechenden Anschluss auf Ausgang schalten, werden mit DDRB=0xff alle Anschlüsse des Ports B als Ausgänge eingestellt.&lt;br /&gt;
# Die den ersten beiden Bits des Ports zugeordneten Anschlüsse (PB0 und PB1) werden 1, alle anderen Anschlüsse des Ports B (PB2-PB7) zu 0. Aktivierte Ausgänge (logisch 1 oder &amp;quot;high&amp;quot;) liegen auf Betriebsspannung (VCC, meist 5 Volt), nicht aktivierte Ausgänge führen 0 Volt (GND, Bezugspotential). Es ist sinnvoll, sich möglichst frühzeitig eine alternative Schreibweise beizubringen, die wegen der leichteren Überprüfbarkeit und Portierbarkeit oft im weiteren Tutorial und in Forenbeiträgen benutzt wird. Die Zuordnung sieht in diesem Fall so aus, Näheres dazu im Artikel [[Bitmanipulation]]:&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;PORTB = (1&amp;lt;&amp;lt;PB1) | (1&amp;lt;&amp;lt;PB0);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# ist der Beginn der sogenannte &#039;&#039;Hauptschleife&#039;&#039; (main-loop). Dies ist eine Endlosschleife, welche kontinuierlich wiederkehrende Befehle enthält.&lt;br /&gt;
# In diesem Beispiel ist die Hauptschleife leer. Der Controller durchläuft die Schleife immer wieder, ohne dass etwas passiert. Eine solche Schleife ist notwendig, da es auf dem Controller kein Betriebssystem gibt, das nach Beendigung des Programmes die Kontrolle übernehmen könnte. Ohne diese Schleife kehrt das Programm aus &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; zurück, alle Interrupts werden deaktiviert und eine Endlosschleife betreten.&lt;br /&gt;
# Ende der Hauptschleife und Sprung zur passenden, öffnenden Klammer, also zu 5.&lt;br /&gt;
# ist das Programmende. Die Zeile ist nur aus Gründen der C-Kompatibilität enthalten: &amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int main(void)&amp;lt;/syntaxhighlight&amp;gt; besagt, dass die Funktion einen int-Wert zurückgibt. Die Anweisung wird aber nicht erreicht, da das Programm die Hauptschleife nie verlässt.&lt;br /&gt;
&lt;br /&gt;
Um diesen Quellcode in ein lauffähiges Programm zu übersetzen, wird hier ein Makefile genutzt. Das verwendete Makefile findet sich auf der Seite [[Beispiel Makefile]] und basiert auf der Vorlage, die in WinAVR mitgeliefert wird und wurde bereits angepasst (Controllertyp ATmega16). Man kann das Makefile bearbeiten und an andere Controller anpassen oder sich mit dem Programm MFile menügesteuert ein Makefile &amp;quot;zusammenklicken&amp;quot;. Das Makefile speichert man unter dem Namen &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; (ohne Endung) im selben Verzeichnis, in dem auch die Datei &amp;lt;code&amp;gt;main.c&amp;lt;/code&amp;gt; mit dem Programmcode abgelegt ist. Detailliertere Erklärungen zur Funktion von Makefiles finden sich im Artikel [[AVR-GCC-Tutorial/Exkurs_Makefiles|Exkurs: Makefiles]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;dir&lt;br /&gt;
&lt;br /&gt;
 Verzeichnis von D:\beispiel&lt;br /&gt;
&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          .&lt;br /&gt;
28.11.2006  22:53    &amp;lt;DIR&amp;gt;          ..&lt;br /&gt;
28.11.2006  20:06               118 main.c&lt;br /&gt;
28.11.2006  20:03            16.810 Makefile&lt;br /&gt;
               2 Datei(en)         16.928 Bytes&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt man &#039;&#039;make all&#039;&#039; ein. Falls das mit WinAVR installierte Programmers Notepad genutzt wird, gibt es dazu einen Menüpunkt im Tools Menü. Sind alle Einstellungen korrekt, entsteht eine Datei &amp;lt;code&amp;gt;main.hex&amp;lt;/code&amp;gt;, in welcher der Code für den AVR enthalten ist. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:\beispiel&amp;gt;make all&lt;br /&gt;
&lt;br /&gt;
-------- begin --------&lt;br /&gt;
avr-gcc (GCC) 3.4.6&lt;br /&gt;
Copyright (C) 2006 Free Software Foundation, Inc.&lt;br /&gt;
This is free software; see the source for copying conditions.  There is NO&lt;br /&gt;
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;br /&gt;
&lt;br /&gt;
Compiling C: main.c&lt;br /&gt;
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -f&lt;br /&gt;
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef&lt;br /&gt;
 -Wa,-adhlns=obj/main.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/main.o.d main.c -&lt;br /&gt;
o obj/main.o&lt;br /&gt;
&lt;br /&gt;
Linking: main.elf&lt;br /&gt;
avr-gcc -mmcu=atmega16 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funs&lt;br /&gt;
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -W&lt;br /&gt;
a,-adhlns=obj/main.o  -std=gnu99 -Wundef -MD -MP -MF .dep/main.elf.d obj/main.o&lt;br /&gt;
--output main.elf -Wl,-Map=main.map,--cref    -lm&lt;br /&gt;
&lt;br /&gt;
Creating load file for Flash: main.hex&lt;br /&gt;
avr-objcopy -O ihex -R .eeprom main.elf main.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der hex-Datei kann nun zum Controller übertragen werden. Dies kann z.&amp;amp;nbsp;B. über In-System-Programming ([[ISP]]) erfolgen, das im [[AVR-Tutorial: Equipment]] beschrieben ist. Makefiles nach der WinAVR/MFile-Vorlage sind für die Nutzung des Programms [[AVRDUDE]] vorbereitet. Wenn man den Typ und Anschluss des Programmiergerätes richtig eingestellt hat, kann mit &#039;&#039;make program&#039;&#039; die Übertragung mittels AVRDUDE gestartet werden. Jede andere Software, die hex-Dateien lesen und zu einem AVR übertragen kann&amp;lt;ref&amp;gt;z.&amp;amp;nbsp;B. [[Pony-Prog_Tutorial|Ponyprog]], yapp, AVRStudio&amp;lt;/ref&amp;gt;, kann natürlich ebenfalls genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Startet man nun den Controller (Reset-Taster oder Stromzufuhr aus/an), werden vom Programm die Anschlüsse PB0 und PB1 auf 1 gesetzt. Man kann mit einem Messgerät nun an diesem Anschluss die Betriebsspannung messen oder eine [[LED]] leuchten lassen (Anode an den Pin, Vorwiderstand nicht vergessen). An den Anschlüssen PB2-PB7 misst man 0 Volt. Eine mit der Anode mit einem dieser Anschlüsse verbundene LED leuchtet nicht.&lt;br /&gt;
&lt;br /&gt;
= Ganzzahlige Datentypen (Integer) =&lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung von Mikrokontrollern ist die Definition einiger ganzzahliger Datentypen sinnvoll, an denen eindeutig die Bit-Länge abgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; definiert, die folgendermaßen eingebunden werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;stdint.h&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|+ &#039;&#039;&#039;int-Typen aus &amp;lt;code&amp;gt;stdint.h&amp;lt;/code&amp;gt; (C99)&#039;&#039;&#039;&amp;lt;br/&amp;gt;&amp;amp;nbsp;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenbehaftete int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || −128 ⋯ 127 || −2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || −32768 ⋯ 32767 || −2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;15&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;signed int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || −2147483648 ⋯ 2147483647 || −2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;31&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;int64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || −9223372036854775808 ⋯ 9223372036854775807 || −2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt; ⋯ 2&amp;lt;sup&amp;gt;63&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;signed long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#d0d0ff&amp;quot;&lt;br /&gt;
!colspan=&amp;quot;5&amp;quot;| Vorzeichenlose int-Typen&lt;br /&gt;
|- bgcolor=&amp;quot;#e8e8ff&amp;quot;&lt;br /&gt;
! Typname || Bit-Breite ||colspan=&amp;quot;2&amp;quot;| Wertebereich&lt;br /&gt;
|align=&amp;quot;center| &#039;&#039;&#039;C-Entsprechung&#039;&#039;&#039; (avr-gcc)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 8 || 0 ⋯ 255 || 0 ⋯ 2&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned char&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 16 || 0 ⋯ 65535 || 0 ⋯ 2&amp;lt;sup&amp;gt;16&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned short&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;unsigned int&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 32 || 0 ⋯ 4294967295 || 0 ⋯ 2&amp;lt;sup&amp;gt;32&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;uint64_t&amp;lt;/code&amp;gt; ||align=&amp;quot;right&amp;quot;| 64 || 0 ⋯ 18446744073709551615 || 0 ⋯ 2&amp;lt;sup&amp;gt;64&amp;lt;/sup&amp;gt;−1 || &amp;lt;code&amp;gt;unsigned long long&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Neben den Typen gibt es auch Makros für die Bereichsgrenzen wie &amp;lt;code&amp;gt;INT8_MIN&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;UINT16_MAX&amp;lt;/code&amp;gt;. Siehe dazu auch: [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html Dokumentation der avr-libc: Standard Integer Types].&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines µC-Programms =&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden zwischen 2 verschiedenen Methoden, um ein&lt;br /&gt;
Mikrocontroller-Programm zu schreiben, und zwar völlig unabhängig davon, in&lt;br /&gt;
welcher Programmiersprache das Programm geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
== Sequentieller Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif|left]]&lt;br /&gt;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat. Es wird hier nach dem sogenannten EVA-Prinzip gehandelt. EVA steht für &amp;quot;Eingabe, Verarbeitung, Ausgabe&amp;quot;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif|left]]&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher Dinge erledigt werden können, welche nicht zeitkritisch sind. Wenn ein Interrupt ausgelöst wird, so wird automatisch die zugeordnete Interruptfunktion ausgeführt.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Register =&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller verfügen über eine Vielzahl von Registern. Die meisten&lt;br /&gt;
davon sind sogenannte Schreib-/Leseregister. Das heißt, das Programm kann die&lt;br /&gt;
Inhalte der Register sowohl auslesen als auch beschreiben.&lt;br /&gt;
&lt;br /&gt;
Register haben einen besonderen Stellenwert bei den AVR Controllern. Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers. Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir die 8-Bit Register.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVRs vorhanden, andere wiederum nur bei bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff auf den UART notwendig sind, selbstverständlich nur bei denjenigen Modellen vorhanden, welche über einen integrierten Hardware UART bzw. USART verfügen.&lt;br /&gt;
&lt;br /&gt;
Die Namen der Register sind in den Headerdateien zu den entsprechenden AVR-Typen definiert. Dazu muss man den Namen der controllerspezifischen Headerdatei nicht kennen. Es reicht aus, die allgemeine Headerdatei &#039;&#039;avr/io.h&#039;&#039; einzubinden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU-Typ z.&amp;amp;nbsp;B. mit dem Inhalt atmega8 definiert (und wird somit per -mmcu=atmega8 an den Compiler übergeben), wird beim Einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wohl besser als Anhang - spaeter... --&amp;gt;&lt;br /&gt;
Intern wird diese &amp;quot;Automatik&amp;quot; wie folgt realisiert: Der Controllertyp wird dem Compiler als Parameter übergeben (vgl. &#039;&#039;avr-gcc -c -mmcu=atmega16 [...]&#039;&#039; im Einführungsbeispiel). Wird ein Makefile nach der WinAVR/mfile-Vorlage verwendet, setzt man die Variable &#039;&#039;MCU&#039;&#039;, der Inhalt dieser Variable wird dann an passender Stelle für die Compilerparameter verwendet. Der Compiler definiert intern eine dem mmcu-Parameter zugeordnete &amp;quot;Variable&amp;quot; (genauer: ein Makro) mit dem Namen des Controllers, vorangestelltem &#039;&#039;__AVR_&#039;&#039; und angehängten Unterstrichen (z.&amp;amp;nbsp;B. wird bei &#039;&#039;-mmcu=atmega16&#039;&#039; das Makro &#039;&#039;__AVR_ATmega16__&#039;&#039; definiert). Beim Einbinden der Header-Datei &#039;&#039;avr/io.h&#039;&#039; wird geprüft, ob das jeweilige Makro definiert ist und die zum Controller passende Definitionsdatei eingelesen. Zur Veranschaulichung einige Ausschnitte aus einem Makefile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[...]&lt;br /&gt;
# MCU Type (&amp;quot;name&amp;quot;) setzen:&lt;br /&gt;
MCU = atmega16&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Verwendung des Inhalts von MCU (hier atmega16) fuer die &lt;br /&gt;
## Compiler- und Assembler-Parameter&lt;br /&gt;
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)&lt;br /&gt;
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)&lt;br /&gt;
[...]&lt;br /&gt;
&lt;br /&gt;
[...]&lt;br /&gt;
## Aufruf des Compilers:&lt;br /&gt;
## mit den Parametern ($(ALL_CFLAGS) ist -mmcu=$(MCU)[...] = -mmcu=atmega16[...]&lt;br /&gt;
$(OBJDIR)/%.o : %.c&lt;br /&gt;
	@echo&lt;br /&gt;
	@echo $(MSG_COMPILING) $&amp;lt;&lt;br /&gt;
	$(CC) -c $(ALL_CFLAGS) $&amp;lt; -o $@ &lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da --mmcu=atmega16 übergeben wurde, wird __AVR_ATmega16__ definiert und kann in avr/io.h zur Fallunterscheidung genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// avr/io.h &lt;br /&gt;
// (bei WinAVR-Standardinstallation in C:\WinAVR\avr\include\avr)&lt;br /&gt;
[...]&lt;br /&gt;
#if defined (__AVR_AT94K__)&lt;br /&gt;
#  include &amp;lt;avr/ioat94k.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#elif defined (__AVR_ATmega16__)&lt;br /&gt;
// da __AVR_ATmega16__ definiert ist, wird avr/iom16.h eingebunden:&lt;br /&gt;
#  include &amp;lt;avr/iom16.h&amp;gt;&lt;br /&gt;
// [...]&lt;br /&gt;
#else&lt;br /&gt;
#  if !defined(__COMPILING_AVR_LIBC__)&lt;br /&gt;
#    warning &amp;quot;device type not defined&amp;quot;&lt;br /&gt;
#  endif&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Beispiele in den folgenden Abschnitten demonstrieren den Zugriff auf Register anhand der Register für I/O-Ports (PORTx, DDRx, PINx), die Vorgehensweise ist jedoch für alle Register (z.&amp;amp;nbsp;B. die des UART, ADC, SPI) analog.&lt;br /&gt;
&lt;br /&gt;
== Schreiben in Register ==&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man Register einfach wie eine Variable setzen.&amp;lt;ref&amp;gt;In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Schreibzugriff über die Funktion outp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt, outp() ist nicht mehr erforderlich.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    /* Setzt das Richtungsregister des Ports A auf 0xff &lt;br /&gt;
       (alle Pins als Ausgang, vgl. Abschnitt Zugriff auf Ports): */&lt;br /&gt;
    DDRA = 0xff;    &lt;br /&gt;
&lt;br /&gt;
    /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot;: */&lt;br /&gt;
    PORTA = 0x03;   &lt;br /&gt;
&lt;br /&gt;
    // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
    // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
    DDRB = 0x1F;    /* direkte Zuweisung - unübersichtlich */&lt;br /&gt;
&lt;br /&gt;
    /* Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
       aber übersichtlicher und selbsterklärend: */&lt;br /&gt;
    DDRB = (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4);&lt;br /&gt;
&lt;br /&gt;
    while (1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ausführliche Schreibweise sollte bevorzugt verwendet werden, da dadurch die Zuweisungen selbsterklärend sind und somit der Code leichter nachvollzogen werden kann. Atmel verwendet sie auch bei Beispielen in Datenblätten und in den allermeisten Quellcodes zu Application-Notes. Mehr zu der Schreibweise mit &amp;quot;|&amp;quot; und &amp;quot;&amp;lt;&amp;lt;&amp;quot; findet man unter [[Bitmanipulation]].&lt;br /&gt;
&lt;br /&gt;
Der gcc C-Compiler unterstützt ab Version 4.3.0 Konstanten im Binärformat, z.&amp;amp;nbsp;B. DDRB&amp;amp;nbsp;=&amp;amp;nbsp;0b00011111. Diese Schreibweise ist jedoch nur in GNU-C verfügbar und nicht in ISO-C definiert. Man sollte sie daher nicht verwenden, wenn Code mit anderen ausgetauscht oder mit anderen Compilern bzw. älteren Versionen des gcc genutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
== Verändern von Registerinhalten ==&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt und löscht man &amp;quot;Standard-C-konform&amp;quot; mittels logischer (Bit-) Operationen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 x |= (1 &amp;lt;&amp;lt; Bitnummer);  // Hiermit wird ein Bit in x gesetzt&lt;br /&gt;
 x &amp;amp;= ~(1 &amp;lt;&amp;lt; Bitnummer); // Hiermit wird ein Bit in x geloescht&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird jeweils nur der Zustand des angegebenen Bits geändert, der vorherige Zustand der anderen Bits bleibt erhalten. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#define MEINBIT 2&lt;br /&gt;
...&lt;br /&gt;
PORTA |= (1 &amp;lt;&amp;lt; MEINBIT);    /* setzt Bit 2 an PortA auf 1 */&lt;br /&gt;
PORTA &amp;amp;= ~(1 &amp;lt;&amp;lt; MEINBIT);   /* loescht Bit 2 an PortA */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode lassen sich auch mehrere Bits eines Registers gleichzeitig setzen und löschen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRA &amp;amp;= ~( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* PA0 und PA3 als Eingaenge */&lt;br /&gt;
PORTA |= ( (1&amp;lt;&amp;lt;PA0) | (1&amp;lt;&amp;lt;PA3) );  /* Interne Pull-Up fuer beide einschalten */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten AVR Registern mit Bits, die durch Beschreiben mit einer logischen 1 gelöscht werden, muss eine absolute Zuweisung benutzt werden. Ein ODER löscht in diesen Registern ALLE gesetzten Bits!&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TIFR2 = (1&amp;lt;&amp;lt;OCF2A); // Nur Bit OCF2A löschen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
&lt;br /&gt;
== Lesen aus Registern ==&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf Register einfach wie auf eine Variable zugreifen. In Quellcodes, die für ältere Versionen des avr-gcc/der avr-libc entwickelt wurden, erfolgt der Lesezugriff über die Funktion inp(). Aktuelle Versionen des Compilers unterstützen den Zugriff nun direkt und inp() ist nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    /* kopiert den Status der Eingabepins an PortB &lt;br /&gt;
       in die Variable foo: */&lt;br /&gt;
    foo = PINB;    &lt;br /&gt;
    //...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände von Bits erfolgt durch Einlesen des gesamten Registerinhalts und ausblenden der Bits deren Zustand nicht von Interesse ist. Einige Beispiele zum Prüfen ob Bits gesetzt oder gelöscht sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define MEINBIT0 0 &lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
extern test1();&lt;br /&gt;
&lt;br /&gt;
// Funkion test1 aufrufen, wenn Bit 0 in Register PINA gesetzt (1) ist&lt;br /&gt;
i = PINA;         // Inhalt in Arbeitsvariable&lt;br /&gt;
i = i &amp;amp; 0x01;     // alle Bits bis auf Bit 0 ausblenden (bitweise und)&lt;br /&gt;
                  // falls das Bit gesetzt war, hat i den Inhalt 1&lt;br /&gt;
if ( i != 0 ) {   // Ergebnis ungleich 0 (wahr)? &lt;br /&gt;
  test1();         // dann muss Bit 0 in i gesetzt sein -&amp;gt; Funktion aufrufen&lt;br /&gt;
}&lt;br /&gt;
// verkürzt:&lt;br /&gt;
if ( ( PINA &amp;amp; 0x01 ) != 0 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( PINA &amp;amp; 0x01 ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
// mit definierter Bitnummer:&lt;br /&gt;
if ( PINA &amp;amp; ( 1 &amp;lt;&amp;lt; MEINBIT0 ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und/oder Bit 2 gesetzt ist. (Bit 0 und 2 also Wert 5) &lt;br /&gt;
// (Bedenke: Bit 0 hat Wert 1, Bit 1 hat Wert 2 und Bit 2 hat Wert 4)&lt;br /&gt;
if ( PINA &amp;amp; 0x05 ) {&lt;br /&gt;
  test1();  // Vergleich &amp;lt;&amp;gt; 0 (wahr), also mindestens eines der Bits gesetzt&lt;br /&gt;
}&lt;br /&gt;
// mit definierten Bitnummern:&lt;br /&gt;
if ( PINA &amp;amp; ( ( 1 &amp;lt;&amp;lt; MEINBIT0 ) | ( 1 &amp;lt;&amp;lt; MEINBIT2 ) ) ) {&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion aufrufen, wenn Bit 0 und Bit 2 gesetzt sind&lt;br /&gt;
if ( ( PINA &amp;amp; 0x05 ) == 0x05 ) {  // nur wahr, wenn beide Bits gesetzt&lt;br /&gt;
  test1();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Funktion test2() aufrufen, wenn Bit 0 gelöscht (0) ist&lt;br /&gt;
i = PINA;        // einlesen in temporäre Variable&lt;br /&gt;
i = i &amp;amp; 0x01;    // maskieren von Bit 0&lt;br /&gt;
if ( i == 0 ) {  // Vergleich ist wahr, wenn Bit 0 nicht gesetzt ist&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// analog mit not-Operator&lt;br /&gt;
if ( !i ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
// nochmals verkürzt:&lt;br /&gt;
if ( !( PINA &amp;amp; 0x01 ) ) {&lt;br /&gt;
  test2();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Warten auf einen bestimmten Zustand ==&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek avr-libc Funktionen, die warten, bis ein bestimmter Zustand eines Bits erreicht ist. Es ist allerdings normalerweise eine eher unschöne Programmiertechnik, da in diesen Funktionen &amp;quot;blockierend&amp;quot; gewartet wird. Der Programmablauf bleibt also an dieser Stelle stehen, bis das maskierte Ereignis erfolgt ist. Setzt man den [[Watchdog]] ein, muss man darauf achten, dass dieser auch noch getriggert wird (Zurücksetzen des Watchdogtimers). &lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_set&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits gesetzt ist, wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 2 (das dritte Bit) in Register PINA gesetzt (1) ist */&lt;br /&gt;
&lt;br /&gt;
#define WARTEPIN PINA&lt;br /&gt;
#define WARTEBIT PA2&lt;br /&gt;
&lt;br /&gt;
// mit der avr-libc Funktion:&lt;br /&gt;
loop_until_bit_is_set(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// _nicht_ ungleich 0 (also 0) ist.&lt;br /&gt;
while ( !(WARTEPIN &amp;amp; (1 &amp;lt;&amp;lt; WARTEBIT)) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;loop_until_bit_is_clear&#039;&#039;&#039; wartet in einer Schleife, bis das definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits gelöscht ist, wird die Funktion sofort wieder verlassen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Warten bis Bit Nr. 4 (das fuenfte Bit) in Register PINB geloescht (0) ist */&lt;br /&gt;
#define WARTEPIN PINB&lt;br /&gt;
#define WARTEBIT PB4&lt;br /&gt;
&lt;br /&gt;
// avr-libc-Funktion:&lt;br /&gt;
loop_until_bit_is_clear(WARTEPIN, WARTEBIT);&lt;br /&gt;
&lt;br /&gt;
// dito in &amp;quot;C-Standard&amp;quot;:&lt;br /&gt;
// Durchlaufe die (leere) Schleife solange das WARTEBIT in Register WARTEPIN&lt;br /&gt;
// gesetzt (1) ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Plattformen besser übertragbar ist die Verwendung von C-Standardoperationen.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Special Function Registers&lt;br /&gt;
* [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Register (ADC, ICR1, OCR1x, TCNT1, UBRR) ==&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (Low-Byte) und &amp;quot;H&amp;quot; (High-Byte) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne &amp;quot;L&amp;quot; oder &amp;quot;H&amp;quot;. Auf diese Register kann dann direkt zugegriffen werden. Dies ist zum Beispiel der Fall für Register wie ADC oder TCNT1.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t foo;&lt;br /&gt;
&lt;br /&gt;
    /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
    foo = ADC; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei anderen Registern, wie zum Beispiel Baudraten-Register, liegen High- und Low-Teil nicht direkt nebeneinander im SFR-Bereich, so dass ein 16-Bit Zugriff nicht möglich ist und der Zugriff zusammengebastelt werden muss:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
   uint16_t baud = F_CPU / (UART_BAUD_RATE * 16L) -1;&lt;br /&gt;
&lt;br /&gt;
   UBRRH = (uint8_t) (baud &amp;gt;&amp;gt; 8);&lt;br /&gt;
   UBRRL = (uint8_t) baud;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einigen AVR-Typen wie ATmega8 oder ATmega16 teilen sich UBRRH und UCSRC die gleiche Speicher-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL), welches Register tatsächlich beschrieben werden soll. &#039;&#039;1000 0011&#039;&#039; (0x83) adressiert demnach UCSRC und übergibt den Wert &#039;&#039;3&#039;&#039;. Und &#039;&#039;0000 0011&#039;&#039; (0x3) adressiert UBRRH und übergibt ebenfalls den Wert &#039;&#039;3&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Speziell bei den 16-Bit-Timern und auch beim ADC ist es bei allen Zugriffen auf Datenregister erforderlich, dass diese Daten synchronisiert sind. Wenn z.&amp;amp;nbsp;B. bei einem 16-Bit-Timer das High-Byte des Zählregisters gelesen wurde und vor dem Lesezugriff auf das Low-Byte ein Überlauf des Low-Bytes stattfindet, erhält man einen völlig unsinnigen Wert. Auch die Compare-Register müssen synchron geschrieben werden, da es ansonsten zu unerwünschten Compare-Ereignissen kommen kann. &lt;br /&gt;
&lt;br /&gt;
Beim ADC besteht das Problem darin, dass zwischen den Zugriffen auf die beiden Teilregister eine Wandlung beendet werden kann und der ADC ein neues Ergebnis in ADCL und ADCH schreiben will, wodurch High- und Low-Byte nicht zusammenpassen.&lt;br /&gt;
&lt;br /&gt;
Um diese Datenmüllproduktion zu verhindern, gibt es in beiden Fällen eine Synchronisation, die jeweils durch den Zugriff auf das Low-Byte ausgelöst wird:&lt;br /&gt;
* Bei den Timer-Registern (das gilt für alle TCNT-, OCR- und ICR-Register bei den 16-Bit-Timern) wird bei einem &#039;&#039;Lesezugriff&#039;&#039; auf das Low-Byte automatisch das High-Byte in ein temporäres Register, das ansonsten nach außen nicht sichtbar ist, geschoben. Greift man nun &#039;&#039;anschließend&#039;&#039; auf das High-Byte zu, dann wird eben dieses temporäre Register gelesen.&lt;br /&gt;
* Bei einem &#039;&#039;Schreibzugriff&#039;&#039; auf eines der genannten Register wird das High-Byte in besagtem temporären Register zwischengespeichert und erst beim Schreiben des Low-Bytes werden &#039;&#039;beide&#039;&#039; gleichzeitig in das eigentliche Register übernommen.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet für die Reihenfolge:&lt;br /&gt;
* Lesezugriff: Erst Low-Byte, dann High-Byte&lt;br /&gt;
* Schreibzugriff: Erst High-Byte, dann Low-Byte&lt;br /&gt;
&lt;br /&gt;
Des weiteren ist zu beachten, dass es für all diese 16-Bit-Register nur ein einziges temporäres Register gibt, so dass das Auftreten eines Interrupts, in dessen Handler ein solches Register manipuliert wird, bei einem durch ihn unterbrochenen Zugriff i.d.R. zu Datenmüll führt. 16-Bit-Zugriffe sind generell nicht atomar! Wenn mit Interrupts gearbeitet wird, kann es erforderlich sein, vor einem solchen Zugriff auf ein 16-Bit-Register die Interrupt-Bearbeitung zu deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Beim ADC-Datenregister ADCH/ADCL ist die Synchronisierung anders gelöst. Hier wird beim Lesezugriff (ADCH/ADCL sind logischerweise read-only) auf das Low-Byte ADCL beide Teilregister für Zugriffe seitens des ADC so lange gesperrt, bis das High-Byte ADCH ausgelesen wurde. Dadurch kann der ADC nach einem Zugriff auf ADCL keinen neuen Wert in ADCH/ADCL ablegen, bis ADCH gelesen wurde. Ergebnisse von Wandlungen, die zwischen einem Zugriff auf ADCL und ADCH beendet werden, gehen verloren!&lt;br /&gt;
&lt;br /&gt;
Nach einem Zugriff auf ADCL muss grundsätzlich ADCH gelesen werden!&lt;br /&gt;
&lt;br /&gt;
In beiden Fällen – also sowohl bei den Timern als auch beim ADC – werden vom C-Compiler 16-Bit Pseudo-Register zur Verfügung gestellt (z.&amp;amp;nbsp;B. TCNT1H/TCNT1L → TCNT1, ADCH/ADCL → ADC bzw. ADCW), bei deren Verwendung der Compiler automatisch die richtige Zugriffsreihenfolge regelt. In C-Programmen sollten grundsätzlich diese 16-Bit-Register verwendet werden! Sollte trotzdem ein Zugriff auf ein Teilregister erforderlich sein, sind obige Angaben zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass auch ein Zugriff auf die 16-Bit-Register vom Compiler in zwei 8-Bit-Zugriffe aufgeteilt wird und dementsprechend genauso nicht-atomar ist wie die Einzelzugriffe. Auch hier gilt, dass u.U. die Interrupt-Bearbeitung gesperrt werden muss, um Datenmüll zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Beim ADC gibt es für den Fall, dass eine Auflösung von 8 Bit ausreicht, die Möglichkeit, das Ergebnis &amp;quot;linksbündig&amp;quot; in ADCH/ADCL auszurichten, so dass die relevanten 8 MSB in ADCH stehen. In diesem Fall muss bzw. sollte nur ADCH ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
ADC und ADCW sind unterschiedliche Bezeichner für das selbe Registerpaar. Üblicherweise kann man in C-Programmen ADC verwenden, was analog zu den anderen 16-Bit-Registern benannt ist. ADCW (ADC Word) existiert nur deshalb, weil die Headerdateien auch für Assembler vorgesehen sind und es bereits einen Assembler-Befehl namens &#039;&#039;adc&#039;&#039; gibt. &lt;br /&gt;
&lt;br /&gt;
Im Umgang mit 16-Bit Registern siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Related Pages/Frequently Asked Questions/Nr. 8&lt;br /&gt;
* Datenblatt Abschnitt &#039;&#039;Accessing 16-bit Registers&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== IO-Register als Parameter und Variablen ==&lt;br /&gt;
&lt;br /&gt;
Um Register als Parameter für eigene Funktionen übergeben zu können, muss man sie als einen volatile uint8_t Pointer übergeben. Zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t key_pressed (volatile uint8_t *inputreg, uint8_t inputbit)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_state = 0;&lt;br /&gt;
 &lt;br /&gt;
  if (last_state == (*inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit)))&lt;br /&gt;
     return 0; /* keine Änderung */&lt;br /&gt;
 &lt;br /&gt;
  /* Wenn doch, warten bis etwaiges Prellen vorbei ist: */&lt;br /&gt;
  _delay_ms(20);&lt;br /&gt;
&lt;br /&gt;
  /* Zustand für nächsten Aufruf merken: */&lt;br /&gt;
  last_state = *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
 &lt;br /&gt;
  /* und den entprellten Tastendruck zurückgeben: */&lt;br /&gt;
  return *inputreg &amp;amp; (1&amp;lt;&amp;lt;inputbit);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Beispiel für einen Funktionsaufruf: */&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t i = key_pressed (&amp;amp;PINB, PB1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Aufruf der Funktion mit call by value würde Folgendes bewirken: Beim Funktionseintritt wird nur eine Kopie des momentanen Portzustandes angefertigt, die sich unabhängig vom tatsächlichen Zustand das Ports nicht mehr ändert, womit die Funktion wirkungslos wäre. Die Übergabe eines Zeigers wäre die Lösung, wenn der Compiler nicht optimieren würde. Denn dadurch wird im Programm nicht von der Hardware gelesen, sondern wieder nur von einem Abbild im Speicher. Das Ergebnis wäre das gleiche wie oben. Mit dem Schlüsselwort volatile sagt man nun dem Compiler, dass die entsprechende Variable entweder durch andere Softwareroutinen (Interrupts) oder durch die Hardware verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass avr-libc FAQ: &amp;quot;How do I pass an IO port as a parameter to a function?&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf IO-Ports =&lt;br /&gt;
&lt;br /&gt;
Jeder AVR implementiert eine unterschiedliche Menge an GPIO-Registern&lt;br /&gt;
(GPIO - General Purpose Input/Output). Diese Register dienen dazu:&lt;br /&gt;
* einzustellen welche der Anschlüsse (&amp;quot;Beinchen&amp;quot;) des Controllers als Ein- oder Ausgänge dienen&lt;br /&gt;
* bei Ausgängen deren Zustand festzulegen&lt;br /&gt;
* bei Eingängen deren Zustand zu erfassen&lt;br /&gt;
&lt;br /&gt;
Mittels GPIO werden digitale Zustände gesetzt und erfasst, d.h. die Spannung an einem Ausgang wird ein- oder ausgeschaltet und an einem Eingang wird erfasst, ob die anliegende Spannung über oder unter einem bestimmten Schwellwert liegt. Im Datenblatt Abschnitt Electrical Characteristics/DC Characteristics finden sich die Spannungswerte (V_OL, V_OH für Ausgänge, V_IL, V_IH für Eingänge).&lt;br /&gt;
&lt;br /&gt;
Die Verarbeitung von analogen Eingangswerten und die Ausgabe von Analogwerten wird in Kapitel [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|Analoge Ein- und Ausgabe]] behandelt.&lt;br /&gt;
&lt;br /&gt;
Die physischen Ein- und Ausgänge werden bei AVR-Controllern zu logischen Ports gruppiert.&lt;br /&gt;
&lt;br /&gt;
Alle Ports werden über Register gesteuert. Dazu sind jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! DDRx&lt;br /&gt;
| Datenrichtungsregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&#039;&#039;&#039;x&#039;&#039;&#039; entspricht &#039;&#039;&#039;A&#039;&#039;&#039;, &#039;&#039;&#039;B&#039;&#039;&#039;, &#039;&#039;&#039; C&#039;&#039;&#039;, &#039;&#039;&#039;D&#039;&#039;&#039; usw. (abhängig von der Anzahl der Ports des verwendeten AVR). Bit im Register gesetzt (1) für Ausgang, Bit gelöscht (0) für Eingang.&lt;br /&gt;
|- &lt;br /&gt;
! PINx&lt;br /&gt;
| Eingangsadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der als Eingang definierten Portpins. Bit 1 wenn Pin &amp;quot;high&amp;quot;, Bit 0 wenn Portpin low.&lt;br /&gt;
|-&lt;br /&gt;
! PORTx&lt;br /&gt;
| Datenregister für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
Dieses Register wird verwendet, um die Ausgänge eines Ports anzusteuern. Bei Pins, die mittels DDRx auf Eingang geschaltet wurden, können über PORTx&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die folgenden Beispiele gehen von einem AVR aus, der sowohl Port A als auch Port B besitzt. Sie müssen für andere AVRs (zum Beispiel ATmega8/48/88/168) entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Datenrichtung bestimmen ==&lt;br /&gt;
&lt;br /&gt;
Zuerst muss die Datenrichtung der verwendeten Pins bestimmt werden. Um dies zu erreichen, wird das Datenrichtungsregister des entsprechenden Ports beschrieben.&lt;br /&gt;
&lt;br /&gt;
Für jeden Pin, der als Ausgang verwendet werden soll, muss dabei das&lt;br /&gt;
entsprechende Bit auf dem Port gesetzt werden. Soll der Pin als Eingang&lt;br /&gt;
verwendet werden, muss das entsprechende Bit gelöscht sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
Angenommen am Port B sollen die Pins 0 bis 4 als Ausgänge definiert werden, die noch verbleibenden Pins 5 bis 7 sollen als Eingänge fungieren. Dazu ist es daher notwendig, im für das Port B zuständigen Datenrichtungsregister DDRB folgende Bitkonfiguration einzutragen&lt;br /&gt;
&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |&lt;br /&gt;
&lt;br /&gt;
In C liest sich das dann so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// in io.h wird u.a. DDRB definiert:&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // Setzen der Bits 0,1,2,3 und 4&lt;br /&gt;
  // Binär 00011111 = Hexadezimal 1F&lt;br /&gt;
  // direkte Zuweisung - standardkonform */&lt;br /&gt;
  DDRB = 0x1F;    /* &lt;br /&gt;
&lt;br /&gt;
  // übersichtliche Alternative - Binärschreibweise, aber kein ISO-C&lt;br /&gt;
  DDRB = 0b00011111;&lt;br /&gt;
&lt;br /&gt;
  // Ausführliche Schreibweise: identische Funktionalität, mehr Tipparbeit&lt;br /&gt;
  // aber übersichtlicher und selbsterklärend:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB1) | (1 &amp;lt;&amp;lt; DDB2) | (1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet. Weitere Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Alle Pins des Ports B als Ausgang definieren:&lt;br /&gt;
  DDRB = 0xff; &lt;br /&gt;
  // Pin0 wieder auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~(1 &amp;lt;&amp;lt; DDB0);&lt;br /&gt;
  // Pin 3 und 4 auf Eingang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB &amp;amp;= ~((1 &amp;lt;&amp;lt; DDB3) | (1 &amp;lt;&amp;lt; DDB4));&lt;br /&gt;
  // Pin 0 und 3 wieder auf Ausgang und andere im ursprünglichen Zustand belassen:&lt;br /&gt;
  DDRB |= (1 &amp;lt;&amp;lt; DDB0) | (1 &amp;lt;&amp;lt; DDB3);&lt;br /&gt;
  // Alle Pins auf Eingang:&lt;br /&gt;
  DDRB = 0x00;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vordefinierte Bitnummern für I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die Bitnummern (z.&amp;amp;nbsp;B. PCx, PINCx und DDCx für den Port C) sind in den io*.h-Dateien der avr-libc definiert und dienen lediglich der besseren Lesbarkeit. Man muss diese Definitionen nicht verwenden oder kann auch einfach &amp;quot;immer&amp;quot; PAx, PBx, PCx usw. nutzen, auch wenn der Zugriff auf Bits in DDRx- oder PINx-Registern erfolgt. Für den Compiler sind die Ausdrücke (1&amp;lt;&amp;lt;PC7), (1&amp;lt;&amp;lt;DDC7) und (1&amp;lt;&amp;lt;PINC7) identisch zu (1&amp;lt;&amp;lt;7) (genauer: der Präprozessor ersetzt die Ausdrücke (1&amp;lt;&amp;lt;PC7),... zu (1&amp;lt;&amp;lt;7)). Ein Ausschnitt der Definitionen für Port C eines ATmega32 aus der iom32.h-Datei zur Verdeutlichung (analog für die weiteren Ports):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* PORTC */&lt;br /&gt;
#define PC7     7&lt;br /&gt;
#define PC6     6&lt;br /&gt;
#define PC5     5&lt;br /&gt;
#define PC4     4&lt;br /&gt;
#define PC3     3&lt;br /&gt;
#define PC2     2&lt;br /&gt;
#define PC1     1&lt;br /&gt;
#define PC0     0&lt;br /&gt;
&lt;br /&gt;
/* DDRC */&lt;br /&gt;
#define DDC7    7&lt;br /&gt;
#define DDC6    6&lt;br /&gt;
#define DDC5    5&lt;br /&gt;
#define DDC4    4&lt;br /&gt;
#define DDC3    3&lt;br /&gt;
#define DDC2    2&lt;br /&gt;
#define DDC1    1&lt;br /&gt;
#define DDC0    0&lt;br /&gt;
&lt;br /&gt;
/* PINC */&lt;br /&gt;
#define PINC7   7&lt;br /&gt;
#define PINC6   6&lt;br /&gt;
#define PINC5   5&lt;br /&gt;
#define PINC4   4&lt;br /&gt;
#define PINC3   3&lt;br /&gt;
#define PINC2   2&lt;br /&gt;
#define PINC1   1&lt;br /&gt;
#define PINC0   0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Digitale Signale ==&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, digitale Signale mit dem Mikrocontroller zu erfassen bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
Will man als Ausgang definierte Pins (entsprechende DDRx-Bits = 1) auf Logisch 1 setzen, setzt man die  entsprechenden Bits im Portregister.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = 0x04; /* besser PORTB=(1&amp;lt;&amp;lt;PB2) */&lt;br /&gt;
&lt;br /&gt;
    // übersichtliche Alternative - Binärschreibweise&lt;br /&gt;
    PORTB = 0b00000100;    /* direkte Zuweisung - übersichtlich */&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird also der Ausgang an Pin PB2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039; gezählt werden, das niederwertigste Bit ist also Bitnummer 0 und nicht etwa Bitnummer 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; immer alle Pins gleichzeitig angegeben werden. Man sollte also, wenn nur bestimmte Ausgänge geschaltet werden sollen, zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfließen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an Port B auf &amp;quot;high&amp;quot; setzen und den Status der anderen Ausgänge unverändert lassen, nutze man diese Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;PB2 ) */&lt;br /&gt;
    /* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB2);&lt;br /&gt;
&lt;br /&gt;
    /* auch mehrere &amp;quot;gleichzeitig&amp;quot;: */&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5); /* Pins PB4 und PB5 &amp;quot;high&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Ausschalten&amp;quot;, also  Ausgänge auf &amp;quot;low&amp;quot; setzen, erfolgt analog:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB2); /* löscht Bit 2 in PORTB und setzt damit Pin PB2 auf low */ &lt;br /&gt;
    PORTB &amp;amp;= ~( (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5) ); /* Pin PB4 und Pin PB5 &amp;quot;low&amp;quot; */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
In Quellcodes, die für ältere Version den des avr-gcc/der avr-libc entwickelt wurden, werden einzelne Bits mittels der Funktionen sbi und cbi gesetzt bzw. gelöscht. Beide Funktionen sind in aktuellen Versionen der avr-libc nicht mehr enthalten und auch nicht mehr erforderlich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Falls der Anfangszustand von Ausgängen kritisch ist, muss die Reihenfolge beachtet werden, mit der die Datenrichtung (DDRx) eingestellt und der Ausgabewert (PORTx) gesetzt wird:&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für Ausgangspins, die mit Anfangswert &amp;quot;high&amp;quot; initialisiert werden sollen:&lt;br /&gt;
* zuerst die Bits im PORTx-Register setzen&lt;br /&gt;
* anschließend die Datenrichtung auf Ausgang stellen&lt;br /&gt;
&lt;br /&gt;
Daraus ergibt sich die Abfolge für einen Pin, der bisher als Eingang mit abgeschaltetem Pull-Up konfiguriert war:&lt;br /&gt;
* setze PORTx: interner Pull-Up aktiv&lt;br /&gt;
* setze DDRx: Ausgang (&amp;quot;high&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Bei der Reihenfolge erst DDRx und dann PORTx kann es zu einem kurzen &amp;quot;low-Puls&amp;quot; kommen, der auch externe Pull-Up-Widerstände &amp;quot;überstimmt&amp;quot;. Die (ungünstige) Abfolge: Eingang -&amp;gt; setze DDRx: Ausgang (auf &amp;quot;low&amp;quot;, da PORTx nach Reset 0) -&amp;gt; setze PORTx: Ausgang auf high. Vergleiche dazu auch das Datenblatt Abschnitt &#039;&#039;Configuring the Pin&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Eingänge (Wie kommen Signale in den &amp;amp;micro;C) ==&lt;br /&gt;
&lt;br /&gt;
Die digitalen Eingangssignale können auf verschiedene Arten zu unserer Logik gelangen.&lt;br /&gt;
&lt;br /&gt;
=== Signalkopplung ===&lt;br /&gt;
&lt;br /&gt;
Am einfachsten ist es, wenn die Signale direkt aus einer anderen digitalen Schaltung übernommen werden können. Hat der Ausgang der entsprechenden Schaltung TTL-Pegel dann können wir sogar direkt den Ausgang der Schaltung mit einem Eingangspin von unserem Controller verbinden.&lt;br /&gt;
&lt;br /&gt;
Hat der Ausgang der anderen Schaltung keinen TTL-Pegel so müssen wir den Pegel über entsprechende Hardware (z.&amp;amp;nbsp;B. Optokoppler, [[Widerstand#Spannungsteiler|Spannungsteiler]], &amp;quot;Levelshifter&amp;quot; aka [[Pegelwandler]]) anpassen.&lt;br /&gt;
&lt;br /&gt;
Die Masse der beiden Schaltungen muss selbstverständlich miteinander verbunden werden. Der Software selber ist es natürlich letztendlich egal, wie das Signal eingespeist wird. Wir können ja ohnehin lediglich prüfen, ob an einem Pin unseres Controllers eine logische 1 (Spannung größer ca. 0,7*Vcc) oder eine logische 0 (Spannung kleiner ca. 0,2*Vcc) anliegt. Detaillierte Informationen darüber, ab welcher Spannung ein Eingang als 0 (&amp;quot;low&amp;quot;) bzw. 1 (&amp;quot;high&amp;quot;) erkannt wird, liefert die Tabelle DC Characteristics im Datenblatt des genutzten Controllers.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Spannungstabelle&#039;&#039;&#039; &amp;lt;br /&amp;gt; &amp;lt;small&amp;gt;(ca. Grenzwerte)&amp;lt;/small&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
! Low || High&lt;br /&gt;
|-&lt;br /&gt;
! bei 5 V&lt;br /&gt;
| 1 V || 3,5 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 3,3 V&lt;br /&gt;
| 0,66 V || 2,31 V&lt;br /&gt;
|-&lt;br /&gt;
! bei 1,8 V&lt;br /&gt;
| 0,36 V || 1,26 V&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
{{Warnung|Dabei ist wichtig, zur Abfrage der Eingänge &#039;&#039;nicht&#039;&#039; etwa Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern Eingangsregister &#039;&#039;&#039;PINx&#039;&#039;&#039;. Ansonsten liest man nicht den Zustand der Eingänge, sondern den Status der internen Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx ist ein häufiger Fehler beim AVR-&amp;quot;Erstkontakt&amp;quot;.}}&lt;br /&gt;
&lt;br /&gt;
Will man also die aktuellen Signalzustände von Port D abfragen und in eine Variable namens bPortD abspeichern, schreibt man folgende Befehlszeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint8_t bPortD;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den C-Bitoperationen kann man den Status der Bits abfragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 1 (das &amp;quot;zweite&amp;quot; Bit) in PINC gesetzt (1) ist */&lt;br /&gt;
if ( PINC &amp;amp; (1&amp;lt;&amp;lt;PINC1) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Fuehre Aktion aus, wenn Bit Nr. 2 (das &amp;quot;dritte&amp;quot; Bit) in PINB geloescht (0) ist */&lt;br /&gt;
if ( !(PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2)) ) {&lt;br /&gt;
  /* Aktion */&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Siehe auch [[Bitmanipulation#Bits_prüfen]]&lt;br /&gt;
&lt;br /&gt;
=== Interne Pull-Up Widerstände ===&lt;br /&gt;
&lt;br /&gt;
Portpins für Ein- und Ausgänge (GPIO) eines AVR verfügen über zuschaltbare interne Pull-Up Widerstände (nominal mehrere 10kOhm, z.&amp;amp;nbsp;B. ATmega16 20-50kOhm). Diese können in vielen Fällen statt externer Widerstände genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039; geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt, so ist der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up Widerstand nicht aktiv. Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel werden alle Pins des Ports D als Eingänge geschaltet und alle Pull-Up Widerstände aktiviert. Weiterhin wird Pin PC7 als Eingang geschaltet und dessen interner Pull-Up Widerstand aktiviert, ohne die Einstellungen für die anderen Portpins (PC0-PC6) zu verändern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
DDRD  = 0x00; /* alle Pins von Port D als Eingang */&lt;br /&gt;
PORTD = 0xff; /* interne Pull-Ups an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;PC7);  /* Pin PC7 als Eingang */&lt;br /&gt;
PORTC |= (1&amp;lt;&amp;lt;PC7);    /* internen Pull-Up an PC7 aktivieren */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Taster und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller, ist zwischen zwei unterschiedliche Methoden zu unterscheiden: &#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300&amp;quot; heights=&amp;quot;300&amp;quot; caption=&amp;quot;Anschluss mechanischer Kontakte an einen µC&amp;quot;&amp;gt;&lt;br /&gt;
Image:Active Low.gif|&#039;&#039;&#039;Active Low:&#039;&#039;&#039; Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet. Damit bei offenem Schalter der Controller kein undefiniertes Signal bekommt, wird zwischen die Versorgungsspannung und den Eingangspin ein sogenannter &#039;&#039;&#039;Pull-Up&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffnetem Schalter auf logisch 1 zu ziehen.&lt;br /&gt;
Image:Active High.gif|&#039;&#039;&#039;Active High:&#039;&#039;&#039; Hier wird der Kontakt zwischen die Versorgungsspannung und den Eingangspin geschaltet. Damit bei offener Schalterstellung kein undefiniertes Signal am Controller ansteht, wird zwischen den Eingangspin und die Masse ein &#039;&#039;&#039;Pull-Down&#039;&#039;&#039; Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter Schalterstellung auf logisch 0 zu halten. &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Widerstandswert von Pull-Up- und Pull-Down-Widerständen ist an sich nicht kritisch. Wird er allerdings zu hoch gewählt, ist die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10 kOhm eingebürgert. Die AVRs verfügen an den meisten Pins über zuschaltbare interne Pull-Up Widerstände (vgl. Abschnitt [[AVR-GCC-Tutorial#Interne Pull-Up Widerstände|Interne Pull-Up Widerstände]]), welche insbesondere wie hier bei Tastern und ähnlichen Bauteilen (z.&amp;amp;nbsp;B. Drehgebern) statt externer Bauteile verwendet werden können. Interne Pull-Down-Widerstand sind nicht verfügbar und müssen daher in Form zusätzlicher Bauteile in die Schaltung eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
==== Taster entprellen ====&lt;br /&gt;
&lt;br /&gt;
Siehe: &#039;&#039;[[Entprellung#Warteschleifen-Verfahren|Entprellung: Warteschleifen-Verfahren]]&lt;br /&gt;
&lt;br /&gt;
= Warteschleifen (delay.h) =&lt;br /&gt;
&lt;br /&gt;
Der Programmablauf kann verschiedene Arten von Wartefunktionen erfordern:&lt;br /&gt;
&lt;br /&gt;
* Warten im Sinn von Zeitvertrödeln&lt;br /&gt;
* Warten auf einen bestimmten Zustand an den I/O-Pins&lt;br /&gt;
* Warten auf einen bestimmten Zeitpunkt (siehe Timer)&lt;br /&gt;
* Warten auf einen bestimmten Zählerstand (siehe Counter)&lt;br /&gt;
&lt;br /&gt;
Der einfachste Fall, das Zeitvertrödeln, kann in vielen Fällen und mit großer Genauigkeit anhand der avr-libc Bibliotheksfunktionen _delay_ms() und _delay_us() erledigt werden. Die Bibliotheksfunktionen sind einfachen Zählschleifen (Warteschleifen) vorzuziehen, da leere Zählschleifen ohne besondere Vorkehrungen sonst bei eingeschalteter Optimierung vom avr-gcc-Compiler wegoptimiert werden. Weiterhin sind die Bibliotheksfunktionen bereits darauf vorbereitet, die in F_CPU definierte Taktfrequenz zu verwenden. Außerdem sind die Funktionen der Bibliothek wirklich getestet.&lt;br /&gt;
&lt;br /&gt;
Einfach!? Schon, aber während gewartet wird, macht der µC nichts anderes mehr (abgesehen von möglicherweise auftretenden Interrupts, falls welche aktiviert sind). Die Wartefunktion blockiert den Programmablauf. Möchte man einerseits warten, um z.&amp;amp;nbsp;B. eine LED blinken zu lassen und gleichzeitig andere Aktionen ausführen z.&amp;amp;nbsp;B. weitere LED bedienen, sollten die Timer/Counter des AVR verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bibliotheksfunktionen funktionieren allerdings nur dann korrekt, wenn sie mit zur Übersetzungszeit (beim Compilieren) bekannten konstanten Werten aufgerufen werden. Der Quellcode muss mit eingeschalteter Optimierung übersetzt werden, sonst wird sehr viel Maschinencode erzeugt, und die Wartezeiten stimmen nicht mehr mit dem Parameter überein.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Einschränkung liegt darin, daß sie möglicherweise länger warten, als erwartet, nämlich in dem Fall, daß Interrupts auftreten und die _delay...()-Funktion unterbrechen. Genau genommen warten diese nämlich nicht eine bestimmte Zeit, sondern verbrauchen eine bestimmte Anzahl von Prozessortakten. Die wiederum ist so bemessen, daß ohne Unterbrechung durch Interrupts die gewünschte Wartezeit erreicht wird.&lt;br /&gt;
Wird das Warten aber durch eine oder mehrere ISR unterbrochen, die zusammen 1% Prozessorzeit verbrauchen, dann dauert das Warten etwa 1% länger. Bei 50% Last durch die ISR dauert das Warten doppelt solange wie gewünscht, bei 90% zehnmal solange...&lt;br /&gt;
&lt;br /&gt;
Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen bis 1.6 ==&lt;br /&gt;
&lt;br /&gt;
Die Wartezeit der Funktion _delay_ms() ist auf 262,14ms/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 13,1ms warten. Die Wartezeit der Funktion _delay_us() ist auf 768us/F_CPU (in MHz) begrenzt, d.h. bei 20 MHz kann man nur max. 38,4µs warten. Längere Wartezeiten müssen dann über einen mehrfachen Aufruf in einer Schleife gelöst werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.&amp;amp;nbsp;B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* in älteren avr-libc Versionen &amp;lt;avr/delay.h&amp;gt; */ &lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 lange, variable Verzögerungszeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
Die maximale Zeit pro Funktionsaufruf ist begrenzt auf &lt;br /&gt;
262.14 ms / F_CPU in MHz (im Beispiel: &lt;br /&gt;
262.1 / 3.6864 = max. 71 ms) &lt;br /&gt;
&lt;br /&gt;
Daher wird die kleine Warteschleife mehrfach aufgerufen,&lt;br /&gt;
um auf eine längere Wartezeit zu kommen. Die zusätzliche &lt;br /&gt;
Prüfung der Schleifenbedingung lässt die Wartezeit geringfügig&lt;br /&gt;
ungenau werden (macht hier vielleicht 2-3ms aus).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms)&lt;br /&gt;
{&lt;br /&gt;
    for(; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 )                  // Endlosschleife&lt;br /&gt;
    {                &lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.&amp;amp;nbsp;B. angeschlossene LED&lt;br /&gt;
        long_delay(1000);       // Eine Sekunde warten...&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== avr-libc Versionen ab 1.7 ==&lt;br /&gt;
&lt;br /&gt;
_delay_ms() kann mit einem Argument bis 6553,5 ms (= 6,5535 Sekunden) benutzt werden. Es ist nicht möglich, eine Variable als Argument zu übergeben. Wird die früher gültige Grenze von 262,14 ms/F_CPU (in MHz) überschritten, so arbeitet _delay_ms() einfach etwas ungenauer und zählt nur noch mit einer Auflösung von 1/10 ms. Eine Verzögerung von 1000,10 ms ließe sich nicht mehr von einer von 1000,19 ms unterscheiden. Ein Verlust, der sich im Allgemeinen verschmerzen lässt. Dem Programmierer wird keine Rückmeldung gegeben, dass die Funktion ggf. gröber arbeitet, d.h. wenn es darauf ankommt, bitte den Parameter wie bisher geschickt wählen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion _delay_us() wurde ebenfalls erweitert. Wenn deren maximal als genau behandelbares Argument überschritten wird, benutzt diese intern _delay_ms(). Damit gelten in diesem Fall die _delay_ms() Einschränkungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Blinken einer LED an PORTB Pin PB0 im ca. 1s Rhythmus, avr-libc ab Version 1.6&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert &lt;br /&gt;
   (z.B. durch Übergabe als Parameter zum Compiler innerhalb &lt;br /&gt;
   des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die&lt;br /&gt;
   &amp;quot;nachträgliche&amp;quot; Definition hinweist */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        _delay_ms(1000);        // Eine Sekunde +/-1/10000 Sekunde warten...&lt;br /&gt;
                                // funktioniert nicht mit Bibliotheken vor 1.6&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Trick zur Übergabe einer Variablen an _delay_ms():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void sleep ( uint8_t ms )&lt;br /&gt;
{&lt;br /&gt;
    for(; ms &amp;gt; 0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
    int x = 0;                  // Variable als Wartezeit erstellen&lt;br /&gt;
    DDRB = ( 1 &amp;lt;&amp;lt; PB0 );        // PB0 an PORTB als Ausgang setzen&lt;br /&gt;
&lt;br /&gt;
    while( 1 ) {                // Endlosschleife&lt;br /&gt;
        PORTB ^= ( 1 &amp;lt;&amp;lt; PB0 );  // Toggle PB0 z.B. angeschlossene LED&lt;br /&gt;
        sleep(x); &lt;br /&gt;
        x++;       &lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die _delay_ms() und die _delay_us aus &#039;&#039;&#039;avr-libc 1.7.0&#039;&#039;&#039; sind fehlerhaft. _delay_ms () läuft 4x schneller als erwartet. Abhilfe ist eine korrigierte Includedatei: [http://www.mikrocontroller.net/topic/196738#1943039]&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; margin:2em;&amp;quot;&amp;gt;&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Nachdem wir nun alles Wissenswerte für die serielle Programmerstellung&lt;br /&gt;
gelernt haben nehmen wir jetzt ein völlig anderes Thema in Angriff, nämlich&lt;br /&gt;
die Programmierung unter Zuhilfenahme der Interrupts des AVR.&lt;br /&gt;
&lt;br /&gt;
Tritt ein Interrupt auf, unterbricht (engl. interrupts) der Controller die Verarbeitung des Hauptprogramms und verzweigt zu einer Interruptroutine. Das Hauptprogramm wird also beim Eintreffen eines Interrupts unterbrochen, die Interruptroutine ausgeführt und danach erst wieder das Hauptprogramm an der Unterbrechungsstelle fortgesetzt (vgl. die Abbildung).&lt;br /&gt;
&lt;br /&gt;
Um Interrupts verarbeiten zu können, ist folgendes zu beachten:&lt;br /&gt;
&lt;br /&gt;
* Für jede aktivierte Interruptquelle ist eine Funktion zu programmieren, in der die beim Auftreten des jeweiligen Interrupts erforderlichen Verarbeitungsschritte enthalten sind. Für diese Funktion existieren verschiedene Bezeichnungen. Üblich sind die englischen Begriffe Interrupt-Handler oder Interrupt-Service-Routinen (ISR), man findet aber auch die Bezeichnungen Interruptverarbeitungs- oder -behandlungsroutine oder auch kurz Interruptroutine. Zum Beispiel wird üblicherweise in der ISR zur Verarbeitung des Empfangsinterrupts eines UARTs (UART-RX Interrupt) das empfangene Zeichen in einen Zwischenspeicher (FIFO-Buffer) kopiert, dessen Inhalt später von anderen Programmteilen geleert wird. Sofern der Zwischenspeicher ausreichend groß ist, geht also kein Zeichen verloren, auch wenn im Hauptprogramm zeitintensive Operationen durchgeführt werden.&lt;br /&gt;
* Die benötigten Interrupts sind in den jeweiligen Funktionsbausteinen einzuschalten. Dies erfolgt über das jeweilige Aktivierungsbit (Interrupt Enable) in einem der Hardwareregister (z.B. RX(Complete)Interrupt Enable eines UARTs)&lt;br /&gt;
* Sämtliche Interrupts werden über einen weiteren globalen Schalter aktiviert und deaktiviert. Zur Verarbeitung der Interrupts ist dieser Schalter zu aktivieren (sei(), siehe unten).&lt;br /&gt;
 &lt;br /&gt;
Alle Punkte sind zu beachten. Fehlt z.B. die globale Aktivierung, werden Interruptroutinen auch dann nicht aufgerufen, wenn sie im Funktionsbaustein eingeschaltet sind und eine Behandlungsroutine verhanden ist.&lt;br /&gt;
&lt;br /&gt;
Siehe auch&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-235092.html#new Ausführlicher Thread im Forum]&lt;br /&gt;
* Artikel [[Interrupt]]&lt;br /&gt;
* Artikel [[Multitasking]]&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen, sollten einige Grundregeln bei der Implementierung der Interruptroutinen beachtet werden. Interruptroutinen sollten möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
&lt;br /&gt;
* Keine umfangreichen Berechnungen innerhalb der Interruptroutine. (*)&lt;br /&gt;
* Keine langen Programmschleifen.&lt;br /&gt;
* Obwohl es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen, wird davon ohne genaue Kenntnis der internen Abläufe dringend abgeraten.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten also möglichst kurz sein und keine Schleifen mit vielen Durchläufen enthalten. Längere Operationen können meist in einen &amp;quot;Interrupt-Teil&amp;quot; in einer ISR und einen &amp;quot;Arbeitsteil&amp;quot; im Hauptprogramm aufgetrennt werden. Z.B. Speichern des Zustands aller Eingänge im EEPROM in bestimmten Zeitabständen: ISR-Teil: Zeitvergleich (Timer,RTC) mit Logzeit/-intervall. Bei Übereinstimmung ein globales Flag setzen (volatile bei Flag-Deklaration nicht vergessen, s.u.). Dann im Hauptprogramm prüfen, ob das Flag gesetzt ist. Wenn ja: die Daten im EEPROM ablegen und Flag löschen.&lt;br /&gt;
&lt;br /&gt;
(*)&lt;br /&gt;
Hinweis: &lt;br /&gt;
Es gibt allerdings die seltene Situation, dass man gerade eingelesene&lt;br /&gt;
ADC-Werte sofort verarbeiten muss. Besonders dann, wenn man mehrere Werte sehr&lt;br /&gt;
schnell hintereinander bekommt. Dann bleibt einem nichts anderes übrig, als die&lt;br /&gt;
Werte noch in der ISR zu verarbeiten. Kommt aber sehr selten vor und sollte&lt;br /&gt;
durch geeignete Wahl des Systemtaktes bzw. Auswahl des Controllers vermieden werden!&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf einem AVR AT90S2313 auslösen, wobei die Reihenfolge der Auflistung auch die Priorität der Interrupts aufzeigt.&lt;br /&gt;
&lt;br /&gt;
* Reset&lt;br /&gt;
* Externer Interrupt 0&lt;br /&gt;
* Externer Interrupt 1&lt;br /&gt;
* Timer/Counter 1 Capture Ereignis&lt;br /&gt;
* Timer/Counter 1 Compare Match&lt;br /&gt;
* Timer/Counter 1 Überlauf&lt;br /&gt;
* Timer/Counter 0 Überlauf&lt;br /&gt;
* UART Zeichen empfangen&lt;br /&gt;
* UART Datenregister leer&lt;br /&gt;
* UART Zeichen gesendet&lt;br /&gt;
* Analoger Komparator&lt;br /&gt;
&lt;br /&gt;
Die Anzahl der möglichen Interruptquellen variiert zwischen den verschiedenen Microcontroller-Typen. Im Zweifel hilft ein Blick ins Datenblatt (&amp;quot;Interrupt Vectors&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register die mit den&lt;br /&gt;
Interrupts zusammenhängen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039; Register.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit &lt;br /&gt;
| 7 || 6|| 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039; || &#039;&#039;&#039;INT0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INT0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Interrupt ausgelöst, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&lt;br /&gt;
:Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
:Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang geschaltet ist. Auf diese Weise bietet sich die Möglichkeit, Software-Interrupts zu realisieren.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;GIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;G&#039;&#039;&#039;eneral &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039; || &#039;&#039;&#039;INTF0&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039; || &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W || R/W || R || R || R || R || R || R&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF1&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;INTF0&#039;&#039;&#039; (External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Bedingung, entsprechend der Konfiguration, als eingetreten erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist, wird die Interruptroutine angesprungen.&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist. Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039; eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MCUCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;MCU&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;-&#039;&#039;&#039;|| &#039;&#039;&#039;SE&#039;&#039;&#039;|| &#039;&#039;&#039;SM&#039;&#039;&#039;|| &#039;&#039;&#039;ISC11&#039;&#039;&#039;|| &#039;&#039;&#039;ISC10&#039;&#039;&#039;|| &#039;&#039;&#039;ISC01&#039;&#039;&#039;|| &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R || R || R/W || R/W || R/W || R/W || R/W || R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SE&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl in den Schlafzustand versetzen zu können.&lt;br /&gt;
:Um den Schlafmodus nicht irrtümlich einzuschalten, wird empfohlen, das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu setzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SM&#039;&#039;&#039; (&#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit bestimmt über den Schlafmodus.&lt;br /&gt;
:Ist das Bit gelöscht, so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt. Ist das Bit gesetzt, so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt. (für andere AVR Controller siehe Abschnitt &amp;quot;Sleep-Mode&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC11&#039;&#039;&#039;, &#039;&#039;&#039;ISC10&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;1&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC11 || ISC10 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ISC01&#039;&#039;&#039;, &#039;&#039;&#039;ISC00&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ense &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;0&#039;&#039;&#039; Bits)&lt;br /&gt;
:Diese beiden Bits bestimmen, ob die steigende oder die fallende Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet wird.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! ISC01 || ISC00 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Low Level an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird getriggert, solange der Pin auf 0 bleibt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Reserviert&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Die fallende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Die steigende Flanke an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Allgemeines über die Interrupt-Abarbeitung ==&lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt eintrifft, wird automatisch das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle weiteren Interrupts unterbunden. Dieses wird automatisch wieder gesetzt, wenn die Interruptroutine beendet wird. Wenn in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden Interrupt-Routine in der Reihenfolge ihrer Priorität ausgeführt. Dies kann&lt;br /&gt;
eigentlich nur dann zu Problemen führen, wenn ein hoch priorisierter Interrupt ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe, weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.  Es ist möglich das GIE-Bit in der ISR zu setzen und so schon wieder weitere Interrupts zuzulassen - allerdings sollte man damit vorsichtig sein und genau wissen was man damit macht. Kritisch wird es vor allem wenn der gleiche Interrupt noch einmal kommt, bevor die ISR abgearbeitet ist. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom Programmierer selber vorgesehen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit avr-gcc ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;Anmerkung eines Nutzers: Ich habe mir das Thema hier angearbeitet und hatte am Anfang starke Probleme: Jeder Interrupt muss nochmals einzeln aktiviert werden. Es reicht nicht nur per &#039;&#039;sei()&#039;&#039; die Interrupts global zu aktiveren.&#039;&#039; - mthomas: Hoffentlich duch die modifizerte Einleitung etwas offensichtlicher erläutert. Ansonsten bitte per Eintrag auf die Diskussionseite nochmals melden) --&amp;gt; &lt;br /&gt;
&amp;lt;!-- Selbstverständlich können alle interruptspezifischen Registerzugriffe wie gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.--&amp;gt;&lt;br /&gt;
Funktionen zur Interrupt-Verarbeitung werden in den Includedateien &#039;&#039;interrupt.h&#039;&#039;  der avr-libc zur Verfügung gestellt (bei älterem Quellcode zusätzlich &#039;&#039;signal.h&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und ISR():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf. Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen. Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen  Zustands die Interrupts aktivieren, was zu unerwünschten Seiteneffekten führen kann. Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
void NichtUnterbrechenBitte(void)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_sreg;  // temporaerer Speicher fuer das Statusregister&lt;br /&gt;
&lt;br /&gt;
   tmp_sreg = SREG;   // Statusregister (also auch das I-Flag darin) sichern&lt;br /&gt;
   cli();             // Interrupts global deaktivieren&lt;br /&gt;
&lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Anfang&lt;br /&gt;
     JTAG-Interface eines ATmega16 per Software deaktivieren &lt;br /&gt;
     und damit die JTAG-Pins an PORTC für &amp;quot;general I/O&amp;quot; nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern. Dazu ist eine &amp;quot;timed sequence&amp;quot;&lt;br /&gt;
     einzuhalten (vgl Datenblatt ATmega16, Stand 10/04, S. 229): &lt;br /&gt;
     Das JTD-Bit muss zweimal innerhalb von 4 Taktzyklen geschrieben &lt;br /&gt;
     werden. Ein Interrupt zwischen den beiden Schreibzugriffen wuerde &lt;br /&gt;
     die erforderliche Sequenz &amp;quot;brechen&amp;quot;, das JTAG-Interface bliebe&lt;br /&gt;
     weiterhin aktiv und die IO-Pins weiterhin für JTAG reserviert. */&lt;br /&gt;
&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCSR |= (1&amp;lt;&amp;lt;JTD); // 2 mal in Folge ,vgl. Datenblatt fuer mehr Information&lt;br /&gt;
&lt;br /&gt;
   /* Beispiel Ende */&lt;br /&gt;
  &lt;br /&gt;
   SREG = tmp_sreg;     // Status-Register wieder herstellen &lt;br /&gt;
                      // somit auch das I-Flag auf gesicherten Zustand setzen&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void NichtSoGut(void)&lt;br /&gt;
{&lt;br /&gt;
   cli();&lt;br /&gt;
   &lt;br /&gt;
   /* hier &amp;quot;unterbrechnungsfreier&amp;quot; Code */&lt;br /&gt;
   &lt;br /&gt;
   sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // auch nach Aufruf der Funktion deaktiviert&lt;br /&gt;
&lt;br /&gt;
   sei();&lt;br /&gt;
   // Interrupts global aktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtUnterbrechenBitte();&lt;br /&gt;
   // weiterhin aktiviert&lt;br /&gt;
   //...&lt;br /&gt;
&lt;br /&gt;
   /* Verdeutlichung der unguenstigen Vorgehensweise mit cli/sei: */&lt;br /&gt;
   cli();  &lt;br /&gt;
   // Interrupts jetzt global deaktiviert &lt;br /&gt;
&lt;br /&gt;
   NichtSoGut();&lt;br /&gt;
   // nach Aufruf der Funktion sind Interrupts global aktiviert &lt;br /&gt;
   // dies ist mglw. ungewollt!&lt;br /&gt;
   //...&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- mt: besser so nicht(?), lieber &amp;quot;datenblattkonform&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;timer_enable_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet Timerbezogene Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle&lt;br /&gt;
Timerinterrupts ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden,&lt;br /&gt;
welche Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;timer_enable_int (1 &amp;lt;&amp;lt; TOIE1));&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Achtung: Wenn ein Timerinterrupt eingeschaltet wird während ein&lt;br /&gt;
anderer Timerinterrupt bereits läuft, dann müssen beide Bits angegeben werden&lt;br /&gt;
sonst wird der andere Timerinterrupt versehentlich ausgeschaltet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;enable_external_int (unsigned char ints);&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;/font&amp;gt;Schaltet die externen Interrupts ein bzw. aus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn als Argument &#039;&#039;&#039;ints&#039;&#039;&#039; der Wert 0 übergeben wird so werden alle externen&lt;br /&gt;
Interrrups ausgeschaltet, ansonsten muss in &#039;&#039;&#039;ints&#039;&#039;&#039; angegeben werden, welche&lt;br /&gt;
Interrupts zu aktivieren sind. Dabei müssen einfach die entsprechend zu&lt;br /&gt;
setzenden Bits definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Beispiel: &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;enable_external_int ((1&amp;lt;&lt;br /&gt;
&amp;lt;/font&amp;gt;&#039;&#039;&#039;Schaltet die externen Interrupts 0 und 1 ein.&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind, braucht es selbstverständlich noch den auszuführenden Code, der ablaufen soll, wenn ein Interrupt eintrifft.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Zu den aktivierten Interrupts ist eine Funktion zu programmieren, deren Code aufgerufen wird, wenn der betreffende Interrupt auftritt (Interrupt-Handler, Interrupt-Service-Routine). Dazu existiert die Definition (ein Makro) &#039;&#039;&#039;ISR&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== ISR ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;ISR()&#039;&#039; ersetzt bei neueren Versionen der avr-libc &#039;&#039;SIGNAL()&#039;&#039;. SIGNAL sollte nicht mehr genutzt werden, zur Portierung von SIGNAL nach ISR siehe den [[AVR-GCC-Tutorial#Anhang|Anhang]].)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(Vectorname) /* vormals: SIGNAL(siglabel) dabei Vectorname != siglabel ! */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;ISR&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektors angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Die Bezeichnung entspricht dem Namen aus dem Datenblatt, bei dem die Leerzeichen durch Unterstriche ersetzt sind und ein &#039;&#039;_vect&#039;&#039; angehängt ist.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel ein Ausschnitt aus der Datei für den ATmega8 (bei WinAVR Standardinstallation in C:\WinAVR\avr\include\avr\iom8.h) in der neben den aktuellen Namen für &#039;&#039;ISR&#039;&#039; (*_vect) noch die Bezeichnungen für das inzwischen nicht mehr aktuelle &#039;&#039;SIGNAL&#039;&#039; (SIG_*) enthalten sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
/* $Id: iom8.h,v 1.13 2005/10/30 22:11:23 joerg_wunsch Exp $ */&lt;br /&gt;
&lt;br /&gt;
/* avr/iom8.h  - definitions for ATmega8 */&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
/* Interrupt vectors */&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 0 */&lt;br /&gt;
#define INT0_vect                       _VECTOR(1)&lt;br /&gt;
#define SIG_INTERRUPT0                  _VECTOR(1)&lt;br /&gt;
&lt;br /&gt;
/* External Interrupt Request 1 */&lt;br /&gt;
#define INT1_vect                       _VECTOR(2)&lt;br /&gt;
#define SIG_INTERRUPT1                  _VECTOR(2)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Compare Match */&lt;br /&gt;
#define TIMER2_COMP_vect                _VECTOR(3)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE2             _VECTOR(3)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter2 Overflow */&lt;br /&gt;
#define TIMER2_OVF_vect                 _VECTOR(4)&lt;br /&gt;
#define SIG_OVERFLOW2                   _VECTOR(4)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Capture Event */&lt;br /&gt;
#define TIMER1_CAPT_vect                _VECTOR(5)&lt;br /&gt;
#define SIG_INPUT_CAPTURE1              _VECTOR(5)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match A */&lt;br /&gt;
#define TIMER1_COMPA_vect               _VECTOR(6)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1A            _VECTOR(6)&lt;br /&gt;
&lt;br /&gt;
/* Timer/Counter1 Compare Match B */&lt;br /&gt;
#define TIMER1_COMPB_vect               _VECTOR(7)&lt;br /&gt;
#define SIG_OUTPUT_COMPARE1B            _VECTOR(7)&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--Vor Nutzung von SIGNAL muss ebenfalls die Header-Datei signal.h eingebunden werden.--&amp;gt; &lt;br /&gt;
Mögliche Funktionsrümpfe für Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
/* veraltet: #include &amp;lt;avr/signal.h&amp;gt; */&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)       /* veraltet: SIGNAL(SIG_INTERRUPT0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) /* veraltet: SIGNAL(SIG_UART_RECV) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// und so weiter und so fort...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf die korrekte Schreibweise der Vektorbezeichnung ist zu achten. Der gcc-Compiler prüft erst ab Version 4.x, ob ein Signal/Interrupt der angegebenen Bezeichnung tatsächlich in der Includedatei definiert ist und gibt andernfalls eine Warnung aus. Bei WinAVR (ab 2/2005) wurde die Überprüfung auch in den mitgelieferten Compiler der Version 3.x integriert. Aus dem gcc-Quellcode Version 3.x selbst erstellte Compiler enthalten die Prüfung nicht (vgl. [[AVR-GCC]]). &lt;br /&gt;
&lt;br /&gt;
Während der Ausführung der Funktion sind alle weiteren Interrupts automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt (gleiche oder andere Interruptquelle) auftreten, so wird das entsprechende Bit im zugeordneten Interrupt Flag Register gesetzt und die entsprechende Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden sollten.&lt;br /&gt;
&lt;br /&gt;
=== Unterbrechbare Interruptroutinen ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Faustregel&amp;quot;: im Zweifel &#039;&#039;&#039;ISR&#039;&#039;&#039;. Die nachfolgend beschriebene Methode nur dann verwenden, wenn man sich über die unterschiedliche Funktionsweise im Klaren ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR(XXX,ISR_NOBLOCK) /* veraltet: INTERRUPT(SIG_OVERFLOW0) */&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt-Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei steht XXX für den oben beschriebenen Namen des Vektors (also z.&amp;amp;nbsp;B. &#039;&#039;TIMER0_OVF_vect&#039;&#039;). Der Unterschied im Vergleich zu einer herkömmlichen ISR ist, dass hier beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit durch Einfügen einer SEI-Anweisung direkt wieder gesetzt und somit alle Interrupts zugelassen werden &amp;amp;ndash; auch XXX-Interrupts. &lt;br /&gt;
&lt;br /&gt;
Bei unsachgemässer Handhabung kann dies zu erheblichen Problemen durch Rekursion wie einem Stack-Overflow oder anderen unerwarteten Effekten führen und sollte wirklich nur dann eingesetzt werden, wenn man sich sicher ist, das Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
Insbesondere sollte möglichst am ISR-Anfang die auslösende IRQ-Quelle deaktiviert und erst am Ende der ISR wieder aktiviert werden. Robuster als die Verwendung einer NOBLOCK-ISR ist daher folgender ISR-Aufbau:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR (XXX) &lt;br /&gt;
{&lt;br /&gt;
    // Implementiere die ISR ohne zunaechst weitere IRQs zuzulassen&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Deaktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Erlaube alle Interrupts (ausser XXX)&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
    //... Code ...&lt;br /&gt;
&lt;br /&gt;
    // IRQs global deaktivieren um die XXX-IRQ wieder gefahrlos &lt;br /&gt;
    // aktivieren zu koennen&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;&amp;lt;Aktiviere die XXX-IRQ&amp;gt;&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Weise kann sich die XXX-IRQ nicht selbst unterbrechen, was zu einer Art Endlosschleife führen würde.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
siehe dazu: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen, die sowohl in Interrupt-Routinen (ISR = Interrupt Service Routine(s)) als auch vom übrigen Programmcode geschrieben oder gelesen werden, müssen mit einem &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert werden. Damit wird dem Compiler mitgeteilt, dass der Inhalt der Variablen vor jedem Lesezugriff aus dem Speicher gelesen und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte der Compiler den Code so optimieren, dass der Wert der Variablen nur in Prozessorregistern zwischengespeichert wird, die nichts von der Änderung woanders mitbekommen.&lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung ein Codefragment für eine Tastenentprellung mit Erkennung einer &amp;quot;lange gedrückten&amp;quot; Taste.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// Schwellwerte&lt;br /&gt;
// Entprellung: &lt;br /&gt;
#define CNTDEBOUNCE 10&lt;br /&gt;
// &amp;quot;lange gedrueckt:&amp;quot;&lt;br /&gt;
#define CNTREPEAT 200&lt;br /&gt;
&lt;br /&gt;
// hier z.&amp;amp;nbsp;B. Taste an Pin2 PortA &amp;quot;active low&amp;quot; = 0 wenn gedrueckt&lt;br /&gt;
#define KEY_PIN  PINA&lt;br /&gt;
#define KEY_PINNO PA2&lt;br /&gt;
&lt;br /&gt;
// beachte: volatile! &lt;br /&gt;
volatile uint8_t gKeyCounter;&lt;br /&gt;
&lt;br /&gt;
// Timer-Compare Interrupt ISR, wird z.B. alle 10ms ausgefuehrt&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert. Die übrigen&lt;br /&gt;
   // Programmteile müssen diese Aenderung &amp;quot;sehen&amp;quot;:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer in den Speicher schreiben&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
    /* hier: Initialisierung der Ports und des Timer-Interrupts */&lt;br /&gt;
//... &lt;br /&gt;
   // hier wird auf gKeyCounter zugegriffen. Dazu muss der in der&lt;br /&gt;
   // ISR geschriebene Wert bekannt sein:&lt;br /&gt;
   // volatile -&amp;gt; aktuellen Wert immer aus dem Speicher lesen&lt;br /&gt;
   if ( gKeyCounter &amp;gt; CNTDEBOUNCE ) { // Taste mind. 10*10 ms &amp;quot;prellfrei&amp;quot;&lt;br /&gt;
       if (gKeyCounter == CNTREPEAT) {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste lange gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
          /* hier: Code fuer &amp;quot;Taste kurz gedrueckt&amp;quot; */&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird innerhalb einer ISR mehrfach auf eine mit volatile deklarierte Variable zugegriffen, wirkt sich dies ungünstig auf die Verarbeitungsgeschwindigkeit aus, da bei jedem Zugriff mit dem Speicherinhalt abgeglichen wird. Da bei AVR-Controllern &#039;&#039;innerhalb&#039;&#039; einer ISR keine Unterbrechungen zu erwarten sind, bietet es sich an, einen Zwischenspeicher in Form einer lokalen Variable zu verwenden, deren Inhalt zu Beginn und am Ende mit dem der volatile Variable synchronisiert wird. Lokale Variable werden bei eingeschalteter Optimierung mit hoher Wahrscheinlichkeit in Prozessorregistern verwaltet und der Zugriff darauf ist daher nur mit wenigen internen Operationen verbunden. Die ISR aus dem vorherigen Beispiel lässt sich so optimieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
&lt;br /&gt;
   tmp_kc = gKeyCounter; // Uebernahme in lokale Arbeitsvariable&lt;br /&gt;
&lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   gKeyCounter = tmp_kc; // Zurueckschreiben&lt;br /&gt;
}&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich die Disassemblies (Ausschnitte der &amp;quot;lss-Dateien&amp;quot;, compiliert für ATmega162) im Anschluss. Man erkennt den viermaligen Zugriff auf die Speicheraddresse von &#039;&#039;gKeyCounter&#039;&#039; (hier 0x032A) in der ISR ohne &amp;quot;Cache&amp;quot;-Variable und den zweimaligen Zugriff in der Variante mit Zwischenspeicher. Im Beispiel ist der Vorteil gering, bei komplexeren Routinen kann die Zwischenspeicherung in lokalen Variablen jedoch zu deutlicheren Verbesserungen führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
    if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     876:	ca 99       	sbic	0x19, 2	; 25&lt;br /&gt;
     878:	0a c0       	rjmp	.+20     	; 0x88e &amp;lt;__vector_13+0x24&amp;gt;&lt;br /&gt;
      if (gKeyCounter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
     87a:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     87e:	88 3c       	cpi	r24, 0xC8	; 200 &lt;br /&gt;
     880:	40 f4       	brcc	.+16     	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
     882:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	02 c0       	rjmp	.+4      	; 0x892 &amp;lt;__vector_13+0x28&amp;gt;&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      gKeyCounter = 0;&lt;br /&gt;
     88e:	10 92 2a 03 	sts	0x032A, r1&lt;br /&gt;
     892:	8f 91       	pop	r24&lt;br /&gt;
     894:	0f 90       	pop	r0&lt;br /&gt;
     896:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     898:	0f 90       	pop	r0&lt;br /&gt;
     89a:	1f 90       	pop	r1&lt;br /&gt;
     89c:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
     86a:	1f 92       	push	r1&lt;br /&gt;
     86c:	0f 92       	push	r0&lt;br /&gt;
     86e:	0f b6       	in	r0, 0x3f	; 63&lt;br /&gt;
     870:	0f 92       	push	r0&lt;br /&gt;
     872:	11 24       	eor	r1, r1&lt;br /&gt;
     874:	8f 93       	push	r24&lt;br /&gt;
   uint8_t tmp_kc;&lt;br /&gt;
 &lt;br /&gt;
   tmp_kc = gKeyCounter;&lt;br /&gt;
     876:	80 91 2a 03 	lds	r24, 0x032A&lt;br /&gt;
 &lt;br /&gt;
   if ( !(KEY_PIN &amp;amp; (1&amp;lt;&amp;lt;KEY_PINNO)) ) {&lt;br /&gt;
     87a:	ca 9b       	sbis	0x19, 2	; 25&lt;br /&gt;
     87c:	02 c0       	rjmp	.+4      	; 0x882 &amp;lt;__vector_13+0x18&amp;gt;&lt;br /&gt;
     87e:	80 e0       	ldi	r24, 0x00	; 0&lt;br /&gt;
     880:	03 c0       	rjmp	.+6      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
      if (tmp_kc &amp;lt; CNTREPEAT) {&lt;br /&gt;
     882:	88 3c       	cpi	r24, 0xC8	; 200&lt;br /&gt;
     884:	08 f4       	brcc	.+2      	; 0x888 &amp;lt;__vector_13+0x1e&amp;gt;&lt;br /&gt;
         tmp_kc++;&lt;br /&gt;
     886:	8f 5f       	subi	r24, 0xFF	; 255&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
      tmp_kc = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   gKeyCounter = tmp_kc;&lt;br /&gt;
     888:	80 93 2a 03 	sts	0x032A, r24&lt;br /&gt;
     88c:	8f 91       	pop	r24&lt;br /&gt;
     88e:	0f 90       	pop	r0&lt;br /&gt;
     890:	0f be       	out	0x3f, r0	; 63&lt;br /&gt;
     892:	0f 90       	pop	r0&lt;br /&gt;
     894:	1f 90       	pop	r1&lt;br /&gt;
     896:	18 95       	reti&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== volatile und Pointer ===&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;volatile&#039;&#039;&#039; in Verbindung mit Pointern ist zu beachten, ob der Pointer selbst oder die Variable, auf die der Pointer zeigt, &#039;&#039;&#039;volatile&#039;&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t *a;   // das Ziel von a ist volatile&lt;br /&gt;
&lt;br /&gt;
uint8_t *volatile a;   // a selbst ist volatile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls der Pointer volatile ist (zweiter Fall im Beispiel), ist zu beachten, dass der Wert des Pointers, also eine Speicheradresse, intern in mehr als einem Byte verwaltet wird. Lese- und Schreibzugriffe im Hauptprogramm (außerhalb von Interrupt-Routinen) sind daher so zu implementieren, dass alle Teilbytes der Adresse konsistent bleiben, vgl. dazu den folgenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
=== Variablen größer 1 Byte ===&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte, auf die in Interrupt-Routinen und im Hauptprogramm zugegriffen wird, muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes außerhalb der ISR nicht durch einen Interrupt unterbrochen werden. (Allgemeinplatz: AVRs sind 8-bit Controller). Zur Veranschaulichung ein Codefragment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
volatile uint16_t gMyCounter16bit;&lt;br /&gt;
//...&lt;br /&gt;
ISR(...)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
   gMyCounter16Bit++;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uint16_t tmpCnt;&lt;br /&gt;
//...&lt;br /&gt;
   // nicht gut: Mglw. hier ein Fehler, wenn ein Byte von MyCounter &lt;br /&gt;
   // schon in tmpCnt kopiert ist aber vor dem Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt, der den Inhalt von MyCounter verändert.&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Änderungen &amp;quot;außerhalb&amp;quot; verhindern -&amp;gt; alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interrupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
&lt;br /&gt;
   // oder: vorheriger Status des globalen Interrupt-Flags bleibt erhalten&lt;br /&gt;
   uint8_t sreg_tmp;&lt;br /&gt;
   sreg_tmp = SREG;    /* Sichern */&lt;br /&gt;
   cli()&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   SREG = sreg_tmp;    /* Wiederherstellen */&lt;br /&gt;
&lt;br /&gt;
   // oder: mehrfach lesen, bis man konsistente Daten hat&lt;br /&gt;
   uint16_t count1 = gMyCounter16Bit;&lt;br /&gt;
   uint16_t count2 = gMyCounter16Bit;&lt;br /&gt;
   while (count1 != count2) {&lt;br /&gt;
       count1 = count2;&lt;br /&gt;
       count2 = gMyCounter16Bit;&lt;br /&gt;
   }&lt;br /&gt;
   tmpCnt = count1;&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die avr-libc bietet ab Version 1.6.0(?) einige Hilfsfunktionen/Makros, mit der im Beispiel oben gezeigten Funktionalität, die zusätzlich auch sogenannte [http://en.wikipedia.org/wiki/Memory_barrier memory barriers] beinhalten. Diese stehen nach #include &amp;lt;util/atomic.h&amp;gt; zur Verfügung.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
    // analog zu cli, Zugriff, sei:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_FORCEON) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
// oder:&lt;br /&gt;
&lt;br /&gt;
    // analog zu Sicherung des SREG, cli, Zugriff und Zurückschreiben des SREG:&lt;br /&gt;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {&lt;br /&gt;
        tmpCnt = gMyCounter16Bit;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch [http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html Dokumentation der avr-libc zu atomic.h]&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Routinen und Registerzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Falls Register sowohl im Hauptprogramm als auch in Interrupt-Routinen verändert werden, ist darauf zu achten, dass diese Zugriffe sich nicht überlappen. Nur wenige Anweisungen lassen sich in sogenannte &amp;quot;atomare&amp;quot; Zugriffe übersetzen, die nicht von Interrupt-Routinen unterbrochen werden können. &lt;br /&gt;
&lt;br /&gt;
Zur Veranschaulichung eine Anweisung, bei der ein Bit und im Anschluss drei Bits in einem Register gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
//...&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
	&lt;br /&gt;
	PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
//...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Compiler übersetzt diese Anweisungen für einen ATmega128 bei Optimierungsstufe &amp;quot;S&amp;quot; nach:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA0);&lt;br /&gt;
  d2:	d8 9a       	sbi	0x1b, 0	; 27 (a)&lt;br /&gt;
	&lt;br /&gt;
        PORTA |= (1&amp;lt;&amp;lt;PA2)|(1&amp;lt;&amp;lt;PA3)|(1&amp;lt;&amp;lt;PA4);&lt;br /&gt;
  d4:	8b b3       	in	r24, 0x1b	; 27 (b)&lt;br /&gt;
  d6:	8c 61       	ori	r24, 0x1C	; 28 (c)&lt;br /&gt;
  d8:	8b bb       	out	0x1b, r24	; 27 (d)&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Setzen des einzelnen Bits wird bei eingeschalteter Optimierung für Register im unteren Speicherbereich in eine einzige Assembler-Anweisung (sbi) übersetzt und ist nicht anfällig für Unterbrechungen durch Interrupts. Die Anweisung zum Setzen von drei Bits wird jedoch in drei abhängige Assembler-Anweisungen übersetzt und bietet damit zwei &amp;quot;Angriffspunkte&amp;quot; für Unterbrechungen. Eine Interrupt-Routine könnte nach dem Laden des Ausgangszustands in den Zwischenspeicher (hier Register 24) den Wert des Registers ändern, z.&amp;amp;nbsp;B. ein Bit löschen. Damit würde der Zwischenspeicher nicht mehr mit dem tatsächlichen Zustand übereinstimmen aber dennoch nach der Bitoperation (hier ori) in das Register zurückgeschrieben. &lt;br /&gt;
&lt;br /&gt;
Beispiel: PORTA sei anfangs 0b00000000. Die erste Anweisung (a) setzt Bit 0 auf &#039;&#039;&#039;1&#039;&#039;&#039;, PORTA ist danach 0b0000000&#039;&#039;&#039;1&#039;&#039;&#039;. Nun wird im ersten Teil der zweiten Anweisung der Portzustand in ein Register eingelesen (b). Unmittelbar darauf (vor (c)) &amp;quot;feuert&amp;quot; ein Interrupt, in dessen Interrupt-Routine Bit 0 von PORTA gelöscht wird. Nach Verlassen der Interrupt-Routine hat PORTA den Wert 0b00000000. In den beiden noch folgenden Anweisungen des Hauptprogramms wird nun der zwischengespeicherte &amp;quot;alte&amp;quot; Zustand 0b00000001 mit 0b00011100 logisch-&#039;&#039;&#039;ODER&#039;&#039;&#039;-verknüft (c) und das Ergebnis 0b00011101 in PortA geschrieben (d). Obwohl zwischenzeitlich Bit 0 gelöscht wurde, ist es nach (d) wieder gesetzt. &lt;br /&gt;
&lt;br /&gt;
Lösungsmöglichkeiten:&lt;br /&gt;
* Register ohne besondere Vorkehrungen nicht in Interruptroutinen &#039;&#039;und&#039;&#039; im Hauptprogramm verändern.&lt;br /&gt;
* Interrupts vor Veränderungen in Registern, die auch in ISRs verändert werden, deaktivieren (&amp;quot;cli&amp;quot;).&lt;br /&gt;
* Bits einzeln löschen oder setzen. sbi und cbi können nicht unterbrochen werden. Vorsicht: nur Register im unteren Speicherbereich sind mittels sbi/cbi ansprechbar. Der Compiler kann nur für diese sbi/cbi-Anweisungen generieren. Für Register außerhalb dieses Adressbereichs (&amp;quot;Memory-Mapped&amp;quot;-Register) werden auch zur Manipulation einzelner Bits abhängige Anweisungen erzeugt (lds,...,sts).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Frequently asked Questions/Fragen Nr. 1 und 8. (Stand: avr-libc Vers. 1.0.4)&lt;br /&gt;
&lt;br /&gt;
== Interruptflags löschen ==&lt;br /&gt;
&lt;br /&gt;
Beim Löschen von Interruptflags haben AVRs eine Besonderheit, die auch im Datenblatt beschrieben ist: Es wird zum Löschen eine 1 in das betreffende Bit geschrieben. &lt;br /&gt;
&lt;br /&gt;
Hinweis:&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei Registern mit mehreren Interrupt-Flag-Bits (wie die Timer Interrupt Flag Register)  &#039;&#039;&#039;nicht&#039;&#039;&#039;  die übliche bitweise VerODERung nehmen, sondern eine direkte Zuweisung machen. Da sonst weitere Flags, als nur das gewünschte, ebenfalls gelöscht werden könnten.&amp;lt;br /&amp;gt;&lt;br /&gt;
([http://www.mikrocontroller.net/topic/171148#1640133 Erklärung]).&lt;br /&gt;
&lt;br /&gt;
== Was macht das Hauptprogramm? ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten (Ausnahme-)Fall gar nichts mehr. Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der main-Funktion lediglich noch die Interrupts aktiviert und dann in einer Endlosschleife verharrt. Sämtliche Funktionen werden dann in den ISRs abgearbeitet. Diese Vorgehensweise ist jedoch bei den meisten Anwendungen schlecht: man verschenkt eine Verarbeitungsebene und hat außerdem möglicherweise Probleme durch Interruptroutinen, die zu viel Verarbeitungszeit benötigen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man in den Interruptroutinen nur die bei Auftreten des jeweiligen Interruptereignisses unbedingt notwendigen Operationen ausführen lassen. Alle weniger kritischen Aufgaben werden dann im Hauptprogramm abgearbeitet.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Interrupts and Signals&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogenannten [[Sleep Mode |&#039;&#039;Sleep-Modes&#039;&#039;]] (&amp;quot;Schlaf-Modi&amp;quot;). Diese ermöglichen es, Teile des Controllers abzuschalten. Zum Einen kann damit besonders bei Batteriebetrieb Strom gespart werden, zum Anderen können Komponenten des Controllers deaktiviert werden, die die Genauigkeit des Analog-Digital-Wandlers bzw. des Analog-Comparators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrupts den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen (eigentlich Makros) der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
;set_sleep_mode (uint8_t mode): Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.&amp;amp;nbsp;B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
;sleep_enable(): Aktiviert den gesetzten Schlafmodus, versetzt den Controller aber noch nicht in den Schlafmodus&lt;br /&gt;
;sleep_cpu(): Versetzt den Controller in den Schlafmodus .sleep_cpu wird im Prinzip durch die Assembler-Anweisung &#039;&#039;sleep&#039;&#039; ersetzt.&lt;br /&gt;
;sleep_disable(): Deaktiviert den gesetzten Schlafmodus&lt;br /&gt;
;sleep_mode(): Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus. Das Makro entspricht sleep_enable()+sleep_cpu()+sleep_disable(), beinhaltet also nicht die Aktivierung von Interrupts (besser nicht benutzen).&lt;br /&gt;
&lt;br /&gt;
Bei Anwendung von sleep_cpu() müssen Interrupts also bereits freigeben sein (sei()), da der Controller sonst nicht mehr &amp;quot;aufwachen&amp;quot; kann. sleep_mode() ist nicht geeignet für die Verwendung in ISR Interrupt-Service-Routinen, da bei deren Abarbeitung Interrupts global deaktiviert sind und somit auch die möglichen &amp;quot;Aufwachinterrupts&amp;quot;. Abhilfe: stattdessen sleep_enable(), sei(), sleep_cpu(), sleep_disable() und evtl. cli() verwenden (vgl. Dokumentation der avr-libc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
...&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      sleep_mode();&lt;br /&gt;
   &lt;br /&gt;
      // Code hier wird erst nach Auftreten eines entsprechenden&lt;br /&gt;
      // &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In älteren Versionenen der avr-libc wurden nicht alle AVR-Controller durch die sleep-Funktionen richtig angesteuert. Mit avr-libc 1.2.0 wurde die Anzahl der unterstützten Typen jedoch deutlich erweitert. Bei nicht-unterstützten Typen erreicht man die gewünschte Funktionalität durch direkte &amp;quot;[[Bitmanipulation]]&amp;quot; der entsprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler oder sleep_cpu():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   // Sleep-Mode &amp;quot;Power-Save&amp;quot; beim ATmega169 &amp;quot;manuell&amp;quot; aktivieren&lt;br /&gt;
   SMCR = (3&amp;lt;&amp;lt;SM0) | (1&amp;lt;&amp;lt;SE);&lt;br /&gt;
   asm volatile (&amp;quot;sleep&amp;quot;::); // alternativ sleep_cpu() aus sleep.h&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sleep Modi ==&lt;br /&gt;
Zu beachten ist, dass unterschiedliche Prozessoren aus der AVR Familie unterschiedliche Sleep-Modi unterstützen oder nicht unterstützen. Auskunft über die tatsächlichen Gegebenheiten gibt, wie immer, das zum Prozessor gehörende Datenblatt. Die unterschiedlichen Modi unterscheiden sich dadurch, welche Bereiche des Prozessors abgeschaltet werden. Damit korrespondiert unmittelbar welche Möglichkeiten es gibt, den Prozessor aus den jeweiligen Sleep Modus wieder aufzuwecken.&lt;br /&gt;
&lt;br /&gt;
;Idle Mode (SLEEP_MODE_IDLE): Die CPU kann durch SPI, USART, Analog Comperator, ADC, TWI, Timer, Watchdog und irgendeinen anderen Interrupt wieder aufgeweckt werden.&lt;br /&gt;
&lt;br /&gt;
;ADC Noise Reduction Mode (SLEEP_MODE_ADC): In diesem Modus liegt das Hauptaugenmerk darauf, die CPU soweit stillzulegen, dass der ADC möglichst keine Störungen aus dem inneren der CPU auffangen kann. Aufwachen aus diesem Modus kann ausgelöst werden durch den ADC, externe Interrupts, TWI, Timer und Watchdog.&lt;br /&gt;
&lt;br /&gt;
;Power-Down Mode (SLEEP_MODE_PWR_DOWN): In diesem Modus wird ein externer Oszillator (Quarz, Quarzoszillator) gestoppt. Geweckt werden kann die CPU durch einen externen Level Interrupt, TWI, Watchdog, Brown-Out-Reset&lt;br /&gt;
&lt;br /&gt;
;Power-Save-Mode (SLEEP_MODE_PWR_SAVE): Power-Save ist identisch zu Power-Down mit einer Ausnahme: Ist der Timer 2 auf die Verwendung eines externen Taktes konfiguriert, so läuft dieser Timer auch im Power-Save weiter und kann die CPU mit einem Interrupt aufwecken.&lt;br /&gt;
&lt;br /&gt;
;Standby-Mode (SLEEP_MODE_STANDBY, SLEEP_MODE_EXT_STANDBY): Voraussetzung für den Standby-Modus ist die Verwendung eines Quarzes oder eines Quarzoszillators (also einer externen Taktquelle). Ansonsten ist dieser Modus identisch zum Power-Down Modus. Vorteil dieses Modus ist eine kürzere Aufwachzeit.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/96369#832712 Forenbeitrag] zur &amp;quot;Nichtverwendung&amp;quot; von sleep_mode in ISRs.&lt;br /&gt;
&lt;br /&gt;
= Zeiger =&lt;br /&gt;
Zeiger (engl. &#039;&#039;Pointer&#039;&#039;) sind Variablen, die die Adresse von Daten oder Funktionen enthalten und belegen 16 Bits. Die Größe hängt mit dem adressierbaren Speicherbereich zusammen und der GCC reserviert dann den entsprechenden Platz.&lt;br /&gt;
Ggf. ist es also günstiger, Indizes auf Arrays (Listen) zu verwenden, so dass der GCC für die Zeigerarithmetik den erforderlichen RAM nur temporär benötigt.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: [[Zeiger]]&lt;br /&gt;
&lt;br /&gt;
= Speicherzugriffe =&lt;br /&gt;
&lt;br /&gt;
Atmel AVR-Controller verfügen typisch über drei Speicher:&lt;br /&gt;
&lt;br /&gt;
* [[RAM]]: Im RAM (genauer statisches RAM/SRAM) wird vom gcc-Compiler Platz für Variablen reserviert. Auch der Stack befindet sich im RAM. Dieser Speicher ist &amp;quot;flüchtig&amp;quot;, d.h. der Inhalt der Variablen geht beim Ausschalten oder einem Zusammenbruch der Spannungsversorgung verloren.&lt;br /&gt;
&lt;br /&gt;
* Programmspeicher: Ausgeführt als FLASH-Speicher, seitenweise wiederbeschreibbar. Darin ist das Anwendungsprogramm abgelegt.&lt;br /&gt;
&lt;br /&gt;
* [[EEPROM]]: Nichtflüchtiger Speicher, d.h. der einmal geschriebene Inhalt bleibt auch ohne Stromversorgung erhalten. Byte-weise schreib/lesbar. Im EEPROM werden typischerweise gerätespezifische Werte wie z.&amp;amp;nbsp;B. Kalibrierungswerte von Sensoren abgelegt.&lt;br /&gt;
&lt;br /&gt;
Einige AVRs besitzen keinen RAM-Speicher, lediglich die Register können als &amp;quot;Arbeitsvariablen&amp;quot;&lt;br /&gt;
genutzt werden. Da die Anwendung des avr-gcc auf solch &amp;quot;kleinen&amp;quot; Controllern ohnehin selten sinnvoll ist und auch nur bei einigen RAM-losen Typen nach [http://lightner.net/avr/ATtinyAvrGcc.html &amp;quot;Bastelarbeiten&amp;quot;] möglich ist, werden diese Controller hier nicht weiter berücksichtigt. Auch EEPROM-Speicher ist nicht auf allen Typen verfügbar. Generell sollten die nachfolgenden Erläuterungen auf alle ATmega-Controller und die größeren AT90-Typen übertragbar sein. Für die Typen ATtiny2313, ATtiny26 und viele weitere der &amp;quot;ATtiny-Reihe&amp;quot; gelten die Ausführungen ebenfalls.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[Binäre Daten zum Programm hinzufügen]]&lt;br /&gt;
== RAM ==&lt;br /&gt;
&lt;br /&gt;
Die Verwaltung des RAM-Speichers erfolgt durch den Compiler, im Regelfall ist beim Zugriff auf Variablen im RAM nichts Besonderes zu beachten. Die Erläuterungen in jedem brauchbaren C-Buch gelten auch für den vom avr-gcc-Compiler erzeugten Code.&lt;br /&gt;
&lt;br /&gt;
Um Speicher dynamisch (während der Laufzeit) zu reservieren, kann &#039;&#039;&#039;malloc()&#039;&#039;&#039; verwendet werden. malloc(size) &amp;quot;alloziert&amp;quot; (~reserviert) einen gewissen Speicherblock mit &#039;&#039;&#039;size&#039;&#039;&#039; Bytes. Ist kein Platz für den neuen Block, wird NULL (0) zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wird der angelegte Block zu klein (groß), kann die Größe mit realloc() verändert werden. Den allozierten Speicherbereich kann man mit free() wieder freigeben. Wenn das Freigeben eines Blocks vergessen wird spricht man von einem &amp;quot;Speicherleck&amp;quot; (memory leak).&lt;br /&gt;
&lt;br /&gt;
malloc() legt Speicherblöcke im &#039;&#039;&#039;Heap&#039;&#039;&#039; an, belegt man zuviel Platz, dann wächst der Heap zu weit nach oben und überschreibt den Stack, und der Controller kommt in Teufels Küche. Das kann leider nicht nur passieren wenn man insgesamt zu viel Speicher anfordert, sondern auch wenn man Blöcke unterschiedlicher Größe in ungünstiger Reihenfolge alloziert/freigibt (siehe Artikel [[Heap-Fragmentierung]]). Aus diesem Grund sollte man malloc() auf Mikrocontrollern sehr sparsam (am besten gar nicht) verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel zur Verwendung von malloc():&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void foo(void) {&lt;br /&gt;
  // neuen speicherbereich anlegen,&lt;br /&gt;
  // platz für 10 uint16&lt;br /&gt;
  uint16_t* pBuffer = malloc(10 * sizeof(uint16_t));&lt;br /&gt;
&lt;br /&gt;
  // darauf zugreifen, als wärs ein gewohnter Buffer&lt;br /&gt;
  pBuffer[2] = 5;&lt;br /&gt;
&lt;br /&gt;
  // Speicher (unbedingt!) wieder freigeben&lt;br /&gt;
  free(pBuffer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn (wie in obigem Beispiel) dynamischer Speicher nur für die Dauer einer Funktion benötigt und am Ende wieder freigegeben wird, bietet es sich an, statt malloc() &#039;&#039;&#039;alloca()&#039;&#039;&#039; zu verwenden. Der Unterschied zu malloc() ist, dass der Speicher auf dem Stack reserviert wird, und beim Verlassen der Funktion automatisch wieder freigegeben wird. Es kann somit kein Speicherleck und keine Fragmentierung entstehen.&lt;br /&gt;
&lt;br /&gt;
siehe auch:&lt;br /&gt;
* http://www.nongnu.org/avr-libc/user-manual/malloc.html&lt;br /&gt;
&lt;br /&gt;
== Flash mit PROGMEM und pgm_read ==&lt;br /&gt;
&lt;br /&gt;
→ [http://nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html avr-libc: Doku zu avr/pgmspace.h]&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc erst ab Version 4.7 &amp;quot;transparent&amp;quot; möglich. Um Daten aus dem Flash zu lesen, muss die AVR-Instruktion LPM (&#039;&#039;Load from Program Memory&#039;&#039;) erzeugt werden, bei Controllern mit mehr als 64kiB Flash auch ELMP.&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es das AVR-spezifische GCC-Attribut &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt;, mit dem eine Variablendeklaration im &#039;&#039;static storage&#039;&#039;&amp;lt;ref&amp;gt;Variablen der Speicherklasse &#039;&#039;static storage&#039;&#039; haben eine unbegrenzte Lebensdauer.  Beispiel für solche Variablen sind globale Variablen, aber auch static-Variablen innerhalb einer Funktion gehören dazu.  Beispiele für Variablen, die nicht &#039;&#039;static storage&#039;&#039; sind: auto-Variablen (&amp;quot;normale&amp;quot; lokale Variablen), register-Variablen, durch malloc geschaffene Objekte, etc.&amp;lt;/ref&amp;gt; markiert werden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const int value __attribute__((progmem)) = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Effekt ist, dass die so markierte Variable nicht im RAM sondern im Flash angelegt wird.  Wird durch &amp;quot;normalen&amp;quot; C-Code auf solch eine Variable zugegriffen, wird jedoch aus dem RAM gelesen und nicht aus dem Flash!&lt;br /&gt;
&lt;br /&gt;
Zum Lesen aus dem Flash stellt die avr-libc daher zahlreiche Makros zur Verfügung.  Zudem wird das Makro &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; definiert, das etwas Tipparbeit spart:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const int value PROGMEM = 1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; funktioniert im Wesentlichen wie ein Section-Attribut, das die Daten in der Section &amp;lt;tt&amp;gt;.progmem.data&amp;lt;/tt&amp;gt; ablegt.  Im Gegensatz zum Section-Attribut werden jedoch noch weitere Prüfungen unternommen, ab avr-gcc 4.6 etwa muss die entsprechende Variable &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; sein.&lt;br /&gt;
&lt;br /&gt;
=== Integer und float ===&lt;br /&gt;
&lt;br /&gt;
Zum Lesen von Skalaren stellt die avr-libc folgende Makros zu Verfügung, die jeweils ein Argument erhalten: Die 16-Bit Adresse des zu lesenden Wertes&amp;lt;ref&amp;gt;Damit ist der mögliche Speicherbereich für Flash-Konstanten auf 64kiB begrenzt. Einige pgmspace-Funktionen ermöglichen den Lesezugriff auf den gesamten Flash-Speicher, intern via Assembler-Anweisung ELPM. Die Initialisierungswerte des Speicherinhalts jenseits der 64kiB-Marke müssen dann jedoch auf anderem Weg angelegt werden, d.h. nicht per PROGMEM. Evtl. eigene Section und Linker-Optionen. Alt und nicht ganz korrekt: Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kiB Flash bei Controllern mit mehr als 64kiB.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| {{Tabelle}}&lt;br /&gt;
|+ Übersicht der &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt; Funktionen aus&amp;lt;br/&amp;gt;dem Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; der avr-libc&lt;br /&gt;
|-&lt;br /&gt;
! Gelesener Wert || &amp;lt;tt&amp;gt;pgm_read_xxx&amp;lt;/tt&amp;gt; || Anzahl Bytes&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint8_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_byte&amp;lt;/tt&amp;gt; || 1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint16_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; || 2&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;uint32_t&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_dword&amp;lt;/tt&amp;gt; || 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;tt&amp;gt;float&amp;lt;/tt&amp;gt; || &amp;lt;tt&amp;gt;pgm_read_float&amp;lt;/tt&amp;gt;&amp;lt;ref&amp;gt;ab avr-libc 1.7.0&amp;lt;/ref&amp;gt; || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Soll ein Zeiger gelesen werden, so verwendet man &amp;lt;tt&amp;gt;pgm_read_word&amp;lt;/tt&amp;gt; und castet das Ergebnis zum gewünschten Zeiger-Typ.&lt;br /&gt;
&lt;br /&gt;
;Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t aByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* int-Array */&lt;br /&gt;
const int anArray[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
&lt;br /&gt;
void foo (void)&lt;br /&gt;
{&lt;br /&gt;
  /* Zeiger */&lt;br /&gt;
  static const uint8_t* const aPointer PROGMEM = &amp;amp;aByte;&lt;br /&gt;
&lt;br /&gt;
  uint8_t a        = pgm_read_byte (&amp;amp;aByte);&lt;br /&gt;
  int a2           = (int) pgm_read_word (&amp;amp;anArray[2]);&lt;br /&gt;
  const uint8_t* p = (const uint8_t*) pgm_read_word (&amp;amp;aPointer);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
In den Flash-Funktionen der avr-libc sind keine der pgm_read_xxxx Nomenklatur folgenden Funktionen, die Speicherblöcke auslesen oder vergleichen. Die enstprechende Funktionen sind Varianten von &amp;lt;tt&amp;gt;memcpy&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp&amp;lt;/tt&amp;gt; und heißt &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, usw.  Für weitere Funktionen und deren Prototypen siehe die Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Strings sind in C nichts anderes als eine Abfolge von Zeichen und einem &amp;lt;tt&amp;gt;&#039;\0&#039;&amp;lt;/tt&amp;gt; als Stringende. Der prinzipielle Weg ist daher identisch zum  Lesen von Bytes, wobei auf die [[FAQ#Wie funktioniert String-Verarbeitung in C?|Besonderheiten von Strings]] wie 0-Terminierung geachtet werden muss.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
size_t my_string_length (const char* addr)&lt;br /&gt;
{&lt;br /&gt;
    size_t length = 0;&lt;br /&gt;
&lt;br /&gt;
    while (pgm_read_byte (addr++))&lt;br /&gt;
    {&lt;br /&gt;
        len++;&lt;br /&gt;
    }&lt;br /&gt;
    return length;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Unterstützung des Programmierers steht das Repertoire der str-Funktionen auch in jeweils eine Variante zur Verfügung, die mit dem Flash-Speicher arbeiten kann. Die Funktionsnamen tragen den Suffix &amp;lt;tt&amp;gt;_P&amp;lt;/tt&amp;gt;. Darüber hinaus gibt es das Makro &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt;, das ein String-Literal im Flash-Speicher ablegt und die Adresse des Strings liefert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Liefert true zurück, wenn string_im_ram gleich &amp;quot;Hallo Welt&amp;quot; ist. */&lt;br /&gt;
int foo (const char* string_im_ram)&lt;br /&gt;
{&lt;br /&gt;
    return strcmp_P (string_im_ram, PSTR (&amp;quot;Hallo Welt&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; nur innerhalb von Funktionen verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
; Array aus Strings:&lt;br /&gt;
&lt;br /&gt;
Arrays aus Strings im Flash-Speicher werden in zwei Schritten angelegt:&lt;br /&gt;
&lt;br /&gt;
# Zuerst die einzelnen Elemente des Arrays und&lt;br /&gt;
# im Anschluss ein Array, in dem die Startaddressen der Strings abgelegt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen wird zuerst die Adresse des gewünschten Elements aus dem Array im Flash-Speicher gelesen, die im Anschluss dazu genutzt wird, um auf das Element (den String) selbst zuzugreifen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static const char str1[] PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char str2[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char str3[] PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const array[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   str1, str2, str3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Lese die Adresse des i-ten Strings aus array[]&lt;br /&gt;
    const char *parray = (const char*) pgm_read_word (&amp;amp;array[i]);&lt;br /&gt;
&lt;br /&gt;
    // Kopiere den Inhalt der Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, parray);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist, die Strings in einem 2-dimensionalen char-Array abzulegen anstatt deren Adresse in einem 1-dimensionalen Adress-Array zu speichern.&lt;br /&gt;
&lt;br /&gt;
Vorteil ist, dass der Code einfacher wird.  Nachteil ist, dass bei unterschiedlich langen Strings Speicherplatz verschwendet wird, weil sich die Array-Dimension and der Länge des längsten Strings orientieret.  Bei in etwa gleich langen Strings kann es aber sogar Speicherplatz sparen, denn es die Adressen der einzelnen Strings müssen nicht abgespeichert werden.&amp;lt;ref&amp;gt;In unserem Hund-Katze-Maus Beispiel belegt die erste Variante 22 Bytes Daten und 18 Bytes Code, die zweite Variante mit 2-dimensionalem Array belegt 18 Bytes Daten und 20 Bytes Code. Gemessen wurde mit avr-gcc 4.8 -Os für ATmega8.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Die &amp;quot;6&amp;quot; ist 1 plus die Länge des längsten Strings (&amp;quot;Katze&amp;quot;)&lt;br /&gt;
const char array[][6] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Liest den i-ten String von array[] und kopiert ihn ins RAM nach buf[]&lt;br /&gt;
void read_string (char* buf, size_t i)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere den Inhalt der i-ten Zeichenkette vom Flash ins RAM&lt;br /&gt;
    strcpy_P (buf, array[i]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu auch die avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_array How do I put an array of strings completely in ROM?]&lt;br /&gt;
&lt;br /&gt;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so kompliziert ist, sei hier nur kurz erläutert: Die Harvard-Architektur des AVR weist getrennte Adressräume für Programm (Flash) und Datenspeicher (RAM) auf. Der C-Standard sieht keine unterschiedlichen Adressräume vor.&lt;br /&gt;
&lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart (const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette, dann weiß die Funktion nicht, ob die Adresse in den Flash-Speicher oder das RAM zeigt. Weder aus dem Pointer-Wert, also dem Zahlenwert, noch aus dem &amp;quot;const&amp;quot; kann auf den Ort der Ablage geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler bilden die Harvard-Architektur ab, indem sie in einen Pointer nicht nur die Adresse speichern, sondern auch den Ablageort wie &#039;&#039;Flash&#039;&#039; oder &#039;&#039;RAM&#039;&#039;. In einem Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben.&lt;br /&gt;
&lt;br /&gt;
Dies hat jedoch auch Nachteile, denn bei jedem Zugriff über einen Zeiger muss zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden, wie der Zugriff auszuführen ist und entsprechend länglicher und langsamer wird der erzeugte Code.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitte Modules/Program Space String Utilities und Abschnitt Modules/Bootloader Support Utilities&lt;br /&gt;
&lt;br /&gt;
=== Variablenzugriff &amp;gt;64kB ===&lt;br /&gt;
&lt;br /&gt;
Die Zeiger beim avr-gcc sind nur 16 Bit breit, können somit also nur 64kiB Datenspeicher adressieren. Als Funktionspointer können sie beim AVR bis zu 128 kiB Programmspeicher adressieren, weil Funktionsadressen immer 16-Bit Worte adressieren und nicht Bytes. Um Flashzugriff jenseits von 64KiB zu bewerkstelligen gibt es mehrere Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
* Address-Spaces wie &amp;lt;tt&amp;gt;__flash1&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;, siehe Abschnitt &amp;quot;[[#Jenseits von flash|Jenseits von __flash]]&amp;quot;.&lt;br /&gt;
* Die Funktionen bzw. Makros &amp;lt;tt&amp;gt;pgm_read_xxx_far&amp;lt;/tt&amp;gt; der AVR-Libc, wie im folgenden beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unverständlicherweise gibt es in der AVR-Libc keine Funktion, um 32-Bit Pointer zu erhalten. Hier schafft ein eigenes GNU-C99 Makro Abhilfe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Macro to access strings defined in PROGMEM above 64kB&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR(var)                     \&lt;br /&gt;
({ uint_farptr_t tmp;                \&lt;br /&gt;
   __asm__ (                         \&lt;br /&gt;
       &amp;quot;ldi    %A0, lo8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %B0, hi8(%1)&amp;quot;  &amp;quot;\n\t&amp;quot; \&lt;br /&gt;
       &amp;quot;ldi    %C0, hh8(%1)&amp;quot;         \&lt;br /&gt;
       : &amp;quot;=d&amp;quot; (tmp)                  \&lt;br /&gt;
       : &amp;quot;i&amp;quot;  (&amp;amp;(var)));             \&lt;br /&gt;
   tmp;                              \&lt;br /&gt;
})&lt;br /&gt;
//-------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//===================================================================&lt;br /&gt;
// Define a section above 64kiB (FAR_SECTION)&lt;br /&gt;
// and add the required linker argument below&lt;br /&gt;
// -Wl,--section-start=.far_section=0x10000&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
#define FAR_SECTION   __attribute__((__section__(&amp;quot;.far_section&amp;quot;)))&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
//====================================================================&lt;br /&gt;
// Just a Sample&lt;br /&gt;
//--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
const char MyString[] FAR_SECTION = &amp;quot;Hier liegt mein FAR-Teststring!&amp;quot;;&lt;br /&gt;
const char MyBmp64[]  FAR_SECTION = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00};&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  char MyChar;&lt;br /&gt;
  DDRC = 0xFF;&lt;br /&gt;
  do&lt;br /&gt;
  {&lt;br /&gt;
    MyChar = pgm_read_byte_far(FAR(MyBmp64));&lt;br /&gt;
    PORTC  = MyChar;&lt;br /&gt;
  }&lt;br /&gt;
  while(MyChar);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
D.h. man muss&lt;br /&gt;
* Das Makro &amp;lt;tt&amp;gt;FAR&amp;lt;/tt&amp;gt; im Quellcode einfügen&lt;br /&gt;
* Die Definition der neuen Section &amp;lt;tt&amp;gt;FAR_SECTION&amp;lt;/tt&amp;gt; einfügen&lt;br /&gt;
* Die Variablen mit dieser Section kennzeichnen&lt;br /&gt;
* Dem Linker mittels Kommandozeilenoption die Startadrese dieser Section mitteilen&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf diese Variablen kann nur mittels direkter Pointerarithmetik erfolgen, eine Indizierung von Arrays mit variablem Index ist nicht möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int n=3;&lt;br /&gt;
MyChar = pgm_read_byte_far(FAR(MyBmp64)+n);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flash mit __flash und Embedded-C ==&lt;br /&gt;
&lt;br /&gt;
Ab Version 4.7 unterstützt avr-gcc &#039;&#039;Adress-Spaces&#039;&#039; gemäß dem Embedded-C Dokument ISO/IEC TR18037.  Der geläufigste Adress-Space ist &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;, der im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; kein GCC-Attribut ist, sondern ein Qualifier und damit syntaktisch ähnlich verwendet wird wie &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;volatile&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
GCC kennt keine eigene Option zum Aktivieren von Embedded-C, es wird als GNU-C Erweiterung behandelt. Daher müssen C-Module, die Address-Spaces verwenden, mit &amp;lt;tt&amp;gt;-std=gnu99&amp;lt;/tt&amp;gt; o.ä. compiliert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash int value = 10;&lt;br /&gt;
&lt;br /&gt;
int get_value (void)&lt;br /&gt;
{&lt;br /&gt;
  return value;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; sind keine speziellen Bibliotheksfunktionen oder -makros für den Zugriff mehr notwendig: Der Code zum Lesen der Variable ist &amp;quot;normales&amp;quot; C.&lt;br /&gt;
# Die Variable wird im richtigen Speicherbereich (Flash) angelegt.&lt;br /&gt;
# &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; ist nur zusammen mit read-only Objekten oder Zeigern, d.h. nur zusammen mit &amp;lt;tt&amp;gt;const&amp;lt;/tt&amp;gt;, erlaubt.&lt;br /&gt;
# Zugriffe wie im obigen Beispiel können (weg)optimiert werden.  Das Beispiel entspricht einem &amp;quot;&amp;lt;tt&amp;gt;return 10&amp;lt;/tt&amp;gt;&amp;quot;.  Es besteht keine Notwendigkeit, für &amp;lt;tt&amp;gt;value&amp;lt;/tt&amp;gt; überhaupt Flash-Speicher zu reservieren.&lt;br /&gt;
&lt;br /&gt;
Auch Zeiger-Indirektionen sind problemlos möglich.  Zu beachten ist, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; auf der richtigen Seite des &amp;quot;&amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;&amp;quot; in der Zeigerdeklaration bzw. -definition steht:&lt;br /&gt;
* &#039;&#039;&#039;Rechts vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger selbst liegt im Flash&lt;br /&gt;
* &#039;&#039;&#039;Links vom &amp;lt;tt&amp;gt;*&amp;lt;/tt&amp;gt;:&#039;&#039;&#039; Der Zeiger enthält eine Flash-Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// val ist eine Variable im Flash&lt;br /&gt;
const __flash int val = 42;&lt;br /&gt;
&lt;br /&gt;
// pval liegt auch im Flash und enthält die Adresse von val&lt;br /&gt;
const __flash int* const __flash pval = &amp;amp;val;&lt;br /&gt;
&lt;br /&gt;
char get_val (void)&lt;br /&gt;
{&lt;br /&gt;
  // liest den Wert von val über die in pval abgelegte Adresse&lt;br /&gt;
  return *pval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Blöcke ===&lt;br /&gt;
&lt;br /&gt;
Um Speicherbereiche vom Flash in den RAM zu kopieren, gibt es zwei Möglichkeiten: Zum einen können wie bei &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; beschreiben die Funktionen der avr-libc wie &amp;lt;tt&amp;gt;memcpy_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;memcmp_P&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;movmem_P&amp;lt;/tt&amp;gt;, etc. verwendet werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // buf wird auf dem Stack angelegt&lt;br /&gt;
    data_t buf;&lt;br /&gt;
    &lt;br /&gt;
    // Kopiere Daten vom Flash nach buf ins RAM&lt;br /&gt;
    memcpy_P (&amp;amp;buf, pdata, sizeof (data_t));&lt;br /&gt;
 &lt;br /&gt;
    // Sende die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum anderen kann eine Struktur auch über direktes Kopieren ins RAM geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Eine Datenstruktur&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
    int id;&lt;br /&gt;
    char buf[10];&lt;br /&gt;
} data_t;&lt;br /&gt;
&lt;br /&gt;
extern void uart_send (const void*, size_t);&lt;br /&gt;
&lt;br /&gt;
void send_data (const __flash data_t *pdata)&lt;br /&gt;
{&lt;br /&gt;
    // Kopiere Daten ins RAM.  buf wird auf dem Stack angelegt&lt;br /&gt;
    const data_t buf = *pdata;&lt;br /&gt;
    &lt;br /&gt;
    // Verwendet die Daten in buf&lt;br /&gt;
    uart_send (&amp;amp;buf, sizeof (data_t));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Strings ===&lt;br /&gt;
&lt;br /&gt;
Natürlich können auch Strings im Flash abgelegt werden und auch mit Funktionen wie &amp;lt;tt&amp;gt;strcpy_P&amp;lt;/tt&amp;gt; aus der avr-libc verarbeitet werden.  Zudem ist es möglich, Flash-Zeiger mit der Adresse eines String-Literals zu initialisieren:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define FSTR(X) ((const __flash char[]) { X } )&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    FSTR (&amp;quot;Hund&amp;quot;), FSTR (&amp;quot;Katze&amp;quot;), FSTR (&amp;quot;Maus&amp;quot;)&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
size_t get_len (uint8_t tier)&lt;br /&gt;
{&lt;br /&gt;
    return strlen_P (array[tier]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Leider sieht der Embedded-C Draft nicht vor, String-Literale direkt in einem anderen Adress-Space als &#039;&#039;generic&#039;&#039; anzulegen, so dass hier der Umweg über &amp;lt;tt&amp;gt;FSTR&amp;lt;/tt&amp;gt; genommen werden muss.  Dieses Konstrukt ist nur ausserhalb von Funktionen möglich und kann daher nicht als Ersatz für &amp;lt;tt&amp;gt;PSTR&amp;lt;/tt&amp;gt; aus der avr-libc dienen.&lt;br /&gt;
&lt;br /&gt;
Soll &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; ein 2-dimensonales Array sein anstatt ein 1-dimensionales Array von Zeigern, dann geht das ohne große Verrenkungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Die 6 ergibt sich aus 1 plus der Länge des längsten Strings &amp;quot;Katze&amp;quot;&lt;br /&gt;
const __flash char array[][6] = &lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;Hund&amp;quot;, &amp;quot;Katze&amp;quot;, &amp;quot;Maus&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiters besteht die Möglichkeit, &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; analog anzulegen, wie man es mit &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; machen würde:  Jeder String wird explizit angelegt und seine Adresse bei der Initialisierung von &amp;lt;tt&amp;gt;array&amp;lt;/tt&amp;gt; verwendet.  Dies entspricht dem ersten Beispiel eines 1-dimensionalen Zeigerarrays:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char strHund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char strKatze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char strMaus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const __flash char * const __flash array[] = &lt;br /&gt;
{&lt;br /&gt;
    strHund, strKatze, strMaus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Casts ===&lt;br /&gt;
&lt;br /&gt;
Embedded C fordert, dass zwei Adress-Spaces entweder disjunkt sind – d.h. sie enthalten keine gemeinsamen Adressen – oder aber ein Space komplett im anderen enthalten ist, also eine Teilmengen-Beziehung besteht.  Die Adress-Spaces von avr-gcc sind so implementiert, dass jeder Space Teilmenge jedes anderes ist.  Zwar haben Spaces wie RAM und Flash physikalisch keinen Speicherbereich gemein, allerdings ermöglicht diese Implementierung das Casten von Zeigern zu unterschiedlichen Adress-Spaces&amp;lt;ref&amp;gt;Im Gegensatz zu einem Attribute wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist ein (Adress Space) Qualifier Teil des Zeiger-Typs.&amp;lt;/ref&amp;gt;:  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdbool.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
char read_char (const char *address, bool data_in_flash)&lt;br /&gt;
{&lt;br /&gt;
    if (data_in_flash)&lt;br /&gt;
        return *(const __flash char*) address;&lt;br /&gt;
    else&lt;br /&gt;
        return *address;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Cast selbst erzeugt keinen zusätzlichen Code, da eine RAM-Adresse und eine Flash-Adresse die gleiche Binärdarstellung haben.  Allerdings wird über den nach &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gecasteten Zeiger anders zugegriffen, nämlich per LPM.&lt;br /&gt;
&lt;br /&gt;
=== Jenseits von __flash ===&lt;br /&gt;
&lt;br /&gt;
Ausser &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; gibt es auch folgende Address-Spaces:&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;: Mit &#039;&#039;N&#039;&#039; = 1..5 sind fünf weitere Spaces, die analog zu &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; funktionieren und deren Zeiger ebenfalls 16 Bit breit sind.  avr-gcc erwartet, dass die zugehörigen Daten, welche in die Section &amp;lt;tt&amp;gt;.progmem&amp;lt;/tt&amp;gt;&#039;&#039;N&#039;&#039;&amp;lt;tt&amp;gt;.data&amp;lt;/tt&amp;gt; abgelegt werden, so lokatiert sind, dass das high-Byte der Adresse (Bits 16..23) gerade &#039;&#039;N&#039;&#039; ist.  Dies wird in Binutils noch nicht unterstützt&amp;lt;ref&amp;gt;[http://sourceware.org/PR14406 Binutils PR14406]: Support .progmem&amp;lt;N&amp;gt;.data sections to work with GCC&#039;s [http://gcc.gnu.org/PR49868 PR49868].&amp;lt;/ref&amp;gt; (Stand Binutils 2.24) Die Unterstützung kann durch ein eigenes Linker-Skript erreicht werden, welches diese Sections wie vom Compiler erwartet lokatiert.&lt;br /&gt;
; &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;: Dieser Address-Space implementiert 3-Byte Zeiger und unterstützt Lesen über 64KiB-Segmentgrenzen hinweg.  Das MSB (Bit 23) gibt dabei an, ob der &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger eine Flash-Adresse enthält (Bit23 = 0) oder eine RAM-Adresse (Bit23 = 1), was folgenden Code erlaubt:&lt;br /&gt;
&amp;lt;!-- Bitte die Tabelle belassen, damit der Code richtig eingerückt wird! --&amp;gt;&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const __memx int a_flash = 42;&lt;br /&gt;
const        int a_ram   = 100;&lt;br /&gt;
&lt;br /&gt;
static int get_a (const __memx int* pa)&lt;br /&gt;
{&lt;br /&gt;
    return *pa;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    return get_a (&amp;amp;a_flash) + get_a (&amp;amp;a_ram);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}Dies bedeutet, dass erst zur &#039;&#039;Laufzeit&#039;&#039; entschieden werden kann, ob aus dem RAM oder aus dem Flash gelesen werden soll, was &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt; im Vergleich zu den anderen Address-Spaces langsamer macht. Ausserdem ist zu beachten, dass &amp;lt;tt&amp;gt;__memx&amp;lt;/tt&amp;gt;-Zeiger zwar 24-Bit Zeiger sind, die zugrundeliegende Adress-Arithmetik jedoch gemäß dem C-Standard erfolgt, also als 16-Bit Arithmetik.&lt;br /&gt;
&lt;br /&gt;
=== __flash, progmem und Portierbarkeit ===&lt;br /&gt;
&lt;br /&gt;
Da ab er aktuellen Compilerversion 4.7 sowohl &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; als auch &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; und die &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen zur Verfügung stehen, ergibt sich die Frage, welche Variante &amp;quot;besser&amp;quot; ist und wie zwischen ihnen hin- und her zu portieren ist.&lt;br /&gt;
&lt;br /&gt;
Zunächst sei erwähnt, dass &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; kein Ersatz für &amp;lt;tt&amp;gt;PROGMEM&amp;lt;/tt&amp;gt; ist, sondern lediglich eine Alternative dazu.  Das &amp;quot;alte&amp;quot; progmem wird weiterhin mir gleicher Semantik unterstützt, so dass alter Code ohne Änderungen mit den neueren Compilerversionen übersetzbar bleibt.&lt;br /&gt;
&lt;br /&gt;
Von der Codegüte her dürften sich keine großen Unterschiede ergeben.  Es ist nicht zu erwarten, dass die eine oder die andere Variante wesentlich besseren oder schlechteren Code erzeugt — von einer Ausnahme abgesehen:  Der Wert beim Zugriff ist zur Compilezeit bekannt und kann daher eliminiert werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char x[] = { &#039;A&#039;, &#039;V&#039;, &#039;R&#039; };&lt;br /&gt;
&lt;br /&gt;
char foo (void)&lt;br /&gt;
{&lt;br /&gt;
    return x[2];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies wird übersetzt wie &amp;quot;&amp;lt;tt&amp;gt;return &#039;R&#039;;&amp;lt;/tt&amp;gt;&amp;quot;, und das Array &amp;lt;tt&amp;gt;x[]&amp;lt;/tt&amp;gt; kann komplett wegoptimiert werden und entfallen.&lt;br /&gt;
&lt;br /&gt;
==== progmem → __flash ====&lt;br /&gt;
&lt;br /&gt;
Portierung in diese Richtung bedeutet, alten Code anzupassen.  Zwingend ist die Portierung nicht, da &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; weiterhin unterstützt wird.&lt;br /&gt;
Allerdings ist eine Quelle mit &amp;lt;tt&amp;gt;__flash&amp;lt;/tt&amp;gt; besser lesbar, denn der Code wird von den &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Funktionen befreit, die vor allem bei Mehrfach-Indirektion den Code ziemlich verunstalten und unleserlich machen können.&lt;br /&gt;
Weiterer Vorteil von &amp;lt;tt&amp;gt;_flash&amp;lt;/tt&amp;gt; ist, daß eine striktere Typprüfung erfolgen kann.&lt;br /&gt;
&lt;br /&gt;
Eine Portierung wird man in zwei Schritten vornehmen:&lt;br /&gt;
&lt;br /&gt;
;1. Definitionen von Flash-Variablen werden angepasst:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
static const char hund[]  PROGMEM = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const char katze[] PROGMEM = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const char maus[]  PROGMEM = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const char * const tier[] PROGMEM = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const __flash char hund[]  = &amp;quot;Hund&amp;quot;;&lt;br /&gt;
static const __flash char katze[] = &amp;quot;Katze&amp;quot;;&lt;br /&gt;
static const __flash char maus[]  = &amp;quot;Maus&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
const __flash char * const __flash tier[] = &lt;br /&gt;
{&lt;br /&gt;
   hund, katze, maus&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Header &amp;lt;tt&amp;gt;avr/pgmspace.h&amp;lt;/tt&amp;gt; wird nicht mehr benötigt.  Im Gegensatz zu &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; müssen Qualifier immer links von der definierten Variablen stehen; bei Attributen wie &amp;lt;tt&amp;gt;progmem&amp;lt;/tt&amp;gt; ist das mehr oder weniger egal.&lt;br /&gt;
&lt;br /&gt;
Nachdem diese Anpassung erfolgreich abgeschlossen ist, folgt Schritt&lt;br /&gt;
&lt;br /&gt;
; 2. Der Code wird von &amp;lt;tt&amp;gt;pgm_read&amp;lt;/tt&amp;gt;-Aufrufen bereinigt:&lt;br /&gt;
&lt;br /&gt;
Vorher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const char *tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    const char* ptier = (const char*) pgm_read_word (&amp;amp;tier[i]);&lt;br /&gt;
    return (char) pgm_read_byte (&amp;amp;ptier[0]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachher:&lt;br /&gt;
:{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
extern const __flash char * const __flash tier[];&lt;br /&gt;
 &lt;br /&gt;
char first_letter (uint8_t i)&lt;br /&gt;
{&lt;br /&gt;
    return tier[i][0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Dateien direkt im Flash einbinden ==&lt;br /&gt;
&lt;br /&gt;
Wenn man größere Dateien direkt im Programm einbinden will, ohne sie vorher in C Quelltext umzuwandeln, muss man das mit dem Linker machen. Wie das geht steht hier.&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/webdoc/avrlibcreferencemanual/FAQ_1faq_binarydata.html Atmel, avr gcc Dokumentation]&lt;br /&gt;
* [http://nongnu.org/avr-libc/user-manual/FAQ.html#faq_binarydata Nongnu avr gcc Dokumentation]&lt;br /&gt;
&lt;br /&gt;
Wie man das dann praktisch umsetzt, sieht man in diesem Beitrag.&lt;br /&gt;
&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/361429?goto=4056910#4056910 Forumsbeitrag]: Binärdateien mittels Linker einbinden&lt;br /&gt;
&lt;br /&gt;
== Flash in der Anwendung schreiben ==&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit &amp;quot;self-programming&amp;quot;-Option – auch bekannt als [[Bootloader]]-Support – können Teile des Flash-Speichers vom Anwendungsprogramm beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktion in einem besonderen Speicherbereich, der Boot-Section des Programmspeichers/Flash, abgelegt ist.&lt;br /&gt;
&lt;br /&gt;
Bei einigen kleinen AVRs gibt es keine gesonderte Boot-Section, bei diesen kann der Flashspeicher von jeder Stelle des Programms geschrieben werden. Für Details sei hier auf das jeweilige Controller-Datenblatt und die Erläuterungen zum Modul boot.h der avr-libc verwiesen. Es existieren auch Application-Notes dazu bei atmel.com, die auf avr-gcc-Code übertragbar sind.&lt;br /&gt;
&lt;br /&gt;
Siehe auch: &lt;br /&gt;
* Forumsbeitrag [http://www.mikrocontroller.net/topic/163632#1561622 Daten in Programmspeicher speichern]&lt;br /&gt;
&lt;br /&gt;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Möchte man Werte aus einem Programm heraus so speichern, dass sie auch nach dem Abschalten der Versorgungsspannung noch erhalten bleiben und nach dem Wiederherstellen der Versorgungsspannung bei erneutem Programmstart wieder zur Verfügung stehen, dann benutzt man das EEPROM.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h der avr-libc definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16 Bit), Fließkommawerte (32 Bit, single-precision, float) und Datenblöcke geschrieben und gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen kümmern sich auch um diverse Details, die bei der Benutzung des EEPROMs normalerweise notwendig sind:&lt;br /&gt;
* EEPROM-Operationen sind im Vergleich relativ langsam. Man muss daher darauf achten, dass eine vorhergehende Operation abgeschlossen ist, ehe die nächste Operation mit dem EEPROM gestartet wird. Die in der avr-libc implementierten Funktionen aus eeprom.h berücksichtigten dies. Soll beim Aufruf einer EEPROM-Funktion sichergestellt werden, dass diese nicht intern in einer Warteschleife auf den Abschluss der vorherigen Operation wartet, kann vorher per eeprom_is_ready testen, ob der Zugriff auf den EEPROM-Speicher sofort möglich ist.&lt;br /&gt;
* Es ist darauf zu achten, dass die EEPROM-Funktionen nicht durch einen Interrupt unterbrochen werden. Einige Phasen des Zugriffs sind zeitkritisch und müssen in einer definierten bzw. begrenzten Anzahl von Takten durchgeführt werden. Durch einen unterbrechenden Interrupt würde diese Restriktion nicht mehr eingehalten. Auch dieses Detail wird von den avr-libc Funktionen berücksichtigt, so dass man sich als C-Programmierer nicht darum kümmern muss. Innerhalb der Funktionen werden Interrupts vor der &amp;quot;EEPROM-Sequenz&amp;quot; global deaktiviert und im Anschluss, falls vorher auch schon eingeschaltet, wieder aktiviert.&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass der EEPROM-Speicher nur eine begrenzte Anzahl von Schreibzugriffen zulässt. Beschreibt man eine EEPROM-Zelle öfter als die im Datenblatt zugesicherte Anzahl (typisch 100.000), wird die Funktion der Zelle nicht mehr garantiert. Dies gilt für jede einzelne Zelle. &lt;br /&gt;
&lt;br /&gt;
Bei geschickter Programmierung (z.&amp;amp;nbsp;B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmäßig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den gesamten EEPROM-Speicher, erreichen. Auf jeden Fall sollte man aber eine Abschätzung über die zu erwartende Lebensdauer des EEPROM durchführen. Wird ein Wert im EEPROM im Durchschnitt nur einmal pro Woche verändert, wird die garantierte Anzahl der Schreibzyklen innerhalb der voraussichtlichen Verwendungszeit des Controllers sicherlich nicht erreicht werden. Welcher Controller ist schon 100000 / 52 = 1923 Jahre im Einsatz? In diesem Fall lohnt es sich daher nicht, erweiterte Programmfunktionen zu implementieren, mit denen die Anzahl der Schreibzugriffe minimiert wird.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit, Schreibzyklen einzusparen, besteht in der Vorabprüfung, ob der zu speichernde Wert im EEPROM bereits enthalten ist und nur veränderte Werte zu schreiben. In aktuelleren Versionen der avr-libc sind bereits Funktionen enthalten, die solche Prüfungen enthalten (eeprom_update_*).&lt;br /&gt;
&lt;br /&gt;
Lesezugriffe können beliebig oft durchgeführt werden. Sie unterliegen keinen Einschränkungen in Bezug auf deren Anzahl. &lt;br /&gt;
&lt;br /&gt;
=== EEMEM ===&lt;br /&gt;
Um eine Variable im EEPROM anzulegen, stellt die avr-libc das Makro EEMEM zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWord EEMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEMEM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Array */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEMEM = { 18, 3, 70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEMEM = { 30, 7, 79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEMEM;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die grundsätzliche Vorgehensweise ist identisch zur Verwendung von PROGMEM. Auch hier erzeugt man sich spezielle attributierte Variablen (EEMEM erledigt das), die vom Compiler/Linker nicht wie normale Variablen behandelt werden. Compiler/Linker kümmern sich zwar darum, dass diesen Variablen eine Adresse zugewiesen wird, diese Adresse ist dann aber die Adresse der &#039;Variablen&#039; im EEPROM. Um die dort gespeicherten Werte zu lesen bzw. zu schreiben, übergibt man diese Adresse an spezielle Funktionen, die die entsprechenden Werte aus dem EEPROM holen bzw. das EEPROM neu beschreiben.&lt;br /&gt;
&lt;br /&gt;
Die mittels EEMEM erzeugten &#039;Variablen&#039; sind also mehr als Platzhalter zu verstehen, denn als echte Variablen. Es geht nur darum, im C-Programm symbolische Namen zur Verfügung zu haben, anstatt mit echten EEPROM-Adressen hantieren zu müssen, etwas, das grundsätzlich aber auch genauso gut möglich ist. Nur muss man sich in diesem Fall dann selbst darum kümmern, dass mehrere &#039;Variablen&#039; ohne Überschneidung im EEPROM angeordnet werden.&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heißt eeprom_read_byte. Parameter ist die Adresse des Bytes im EEPROM. Geschrieben wird über die Funktion eeprom_write_byte mit den Parametern Adresse und Inhalt. Anwendungsbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EEPROM_DEF 0xFF&lt;br /&gt;
&lt;br /&gt;
void eeprom_example (void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    // myByte lesen (Wert = 123)&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByte);&lt;br /&gt;
&lt;br /&gt;
    // der Wert 99 wird im EEPROM an die Adresse der&lt;br /&gt;
    // Variablen eeFooByte geschrieben&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte (&amp;amp;eeFooByteArray1[1]); &lt;br /&gt;
    // myByte hat nun den Wert 3&lt;br /&gt;
&lt;br /&gt;
    // Beispiel fuer eeprom_update_byte: die EEPROM-Zelle wird nur&lt;br /&gt;
    // dann beschrieben, wenn deren Inhalt sich vom Parameterwert&lt;br /&gt;
    // unterscheidet. In diesem Beispiel erfolgt also kein Schreib-&lt;br /&gt;
    // zugriff, da die Werte gleich sind.&lt;br /&gt;
    eeprom_update_byte(&amp;amp;eeFooByte, myByte);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Beispiel zur &amp;quot;Sicherung&amp;quot; gegen leeres EEPROM nach &amp;quot;Chip Erase&amp;quot;&lt;br /&gt;
    // (z. B. wenn die .eep-Datei nach Programmierung einer neuen Version&lt;br /&gt;
    // des Programms nicht in den EEPROM uebertragen wurde und EESAVE&lt;br /&gt;
    // deaktiviert ist (unprogrammed/1)&lt;br /&gt;
    // &lt;br /&gt;
    // Vorsicht: wenn EESAVE &amp;quot;programmed&amp;quot; ist, hilft diese Sicherung nicht&lt;br /&gt;
    // weiter, da die Speicheraddressen in einem neuen/erweiterten Programm&lt;br /&gt;
    // moeglicherweise verschoben wurden. An der Stelle &amp;amp;eeFooByte steht&lt;br /&gt;
    // dann u.U. der Wert einer anderen Variable aus einer &amp;quot;alten&amp;quot; Version.&lt;br /&gt;
&lt;br /&gt;
    uint8_t fooByteDefault = 222;&lt;br /&gt;
    if ((myByte = eeprom_read_byte (&amp;amp;eeFooByte)) == EEPROM_DEF)&lt;br /&gt;
    {&lt;br /&gt;
        myByte = fooByteDefault;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Schreiben und Lesen von Datenworten erfolgt analog zur Vorgehensweise bei Bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // lesen&lt;br /&gt;
    uint16_t myWord = eeprom_read_word (&amp;amp;eeFooWord);&lt;br /&gt;
&lt;br /&gt;
    // schreiben&lt;br /&gt;
    eeprom_write_word (&amp;amp;eeFooWord, 2222);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Lesen und Schreiben von Datenblöcken erfolgt über die Funktionen &amp;lt;code&amp;gt;eeprom_read_block()&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;eeprom_write_block()&amp;lt;/code&amp;gt;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Adresse und die Länge des Datenblocks in Bytes als &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t  myByteBuffer[3];&lt;br /&gt;
uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
void eeprom_block_example (void)&lt;br /&gt;
{&lt;br /&gt;
    /* Datenblock aus EEPROM lesen  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes ab der von eeFooByteArray1 definierten EEPROM-Adresse&lt;br /&gt;
       in das RAM-Array myByteBuffer */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, 3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Länge */&lt;br /&gt;
    eeprom_read_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit 16-Bit Array */&lt;br /&gt;
    eeprom_read_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock in EEPROM schreiben */&lt;br /&gt;
    eeprom_write_block (myByteBuffer, eeFooByteArray1, sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block (myWordBuffer, eeFooWordArray1, sizeof(myWordBuffer));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fließkommawerte lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
In der avr-libc stehen auch EEPROM-Funktionen für Variablen des Typs float (Fließkommazahlen mit &amp;quot;einfacher&amp;quot; Genauigkeit) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
float eeFloat EEMEM = 12.34f;&lt;br /&gt;
&lt;br /&gt;
float void eeprom_float_example(float value)&lt;br /&gt;
{&lt;br /&gt;
   /* float in EEPROM schreiben */&lt;br /&gt;
   eeprom_write_float(&amp;amp;eeFloat, value);&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM lesen */&lt;br /&gt;
   return  eeprom_read_float(&amp;amp;eeFloat);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Mit den zum Compiler gehörenden Werkzeugen kann der aus den Variablendeklarationen abgeleitete EEPROM-Inhalt in eine Datei geschrieben werden. Die übliche Dateiendung ist .eep, Daten im Intel Hex-Format. Damit können Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. &lt;br /&gt;
&lt;br /&gt;
Makefiles nach WinAVR/MFile-Vorlage enthalten bereits die notwendigen Einstellungen, siehe dazu die Erläuterungen im [[AVR-GCC-Tutorial/Exkurs Makefiles|Exkurs Makefiles]].&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der eep-Datei muss ebenfalls zum Mikrocontroller übertragen werden, wenn die Initialisierungswerte aus der Deklaration vom Programm erwartet werden. Ansonsten enthält der EEPROM-Speicher nach der Übertragung des Programmers mittels ISP abhängig von der Einstellung der EESAVE-Fuse&amp;lt;ref&amp;gt;vgl. Datenblatt Abschnitt Fuse Bits&amp;lt;/ref&amp;gt; nicht die korrekten Werte:&lt;br /&gt;
; EESAVE = 0 (programmed): Die Daten im EEPROM bleiben erhalten. Werden sie nicht neu geschrieben, so enthält das EEPROM evtl. Daten, die nicht mehr zum Programm passen.&lt;br /&gt;
; EESAVE = 1 (unprogrammed): Beim Programmieren werden die Daten im EEPROM gelöscht, also auf 0xff gesetzt.&lt;br /&gt;
&lt;br /&gt;
Als Sicherung kann man im Programm nochmals die Standardwerte vorhalten, beim Lesen auf 0xFF prüfen und gegebenenfalls einen Standardwert nutzen.&lt;br /&gt;
&lt;br /&gt;
=== Direkter Zugriff auf EEPROM-Adressen ===&lt;br /&gt;
&lt;br /&gt;
Will man direkt auf bestimmte EEPROM Adressen zugreifen, dann sind folgende Funktionen hilfreich, um sich die Typecasts zu ersparen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Byte aus dem EEPROM lesen&lt;br /&gt;
uint8_t EEPReadByte(uint16_t addr)&lt;br /&gt;
{&lt;br /&gt;
  return eeprom_read_byte((uint8_t *)addr);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Byte in das EEPROM schreiben&lt;br /&gt;
void EEPWriteByte(uint16_t addr, uint8_t val)&lt;br /&gt;
{&lt;br /&gt;
  eeprom_write_byte((uint8_t *)addr, val);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder als Makro:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define   EEPReadByte(addr)         eeprom_read_byte((uint8_t *)addr)     &lt;br /&gt;
#define   EEPWriteByte(addr, val)   eeprom_write_byte((uint8_t *)addr, val)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EEPWriteByte(0x20, 128);   // Byte an die Adresse 0x20 schreiben&lt;br /&gt;
...&lt;br /&gt;
Val=EEPReadByte(0x20);     // EEPROM-Wert von Adresse 0x20 lesen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Register ===&lt;br /&gt;
Um das EEPROM anzusteuern, sind drei Register von Bedeutung:&lt;br /&gt;
;EEAR: Hier werden die Adressen eingetragen zum Schreiben oder Lesen. Dieses Register unterteilt sich nochmal in EEARH und EEARL, da in einem 8-Bit-Register keine 512 Adressen adressiert werden können.&lt;br /&gt;
;EEDR: Hier werden die Daten eingetragen, die geschrieben werden sollen, bzw. es enthält die gelesenen Daten.&lt;br /&gt;
;EECR: Ist das Kontrollregister für das EEPROM&lt;br /&gt;
&lt;br /&gt;
Das EECR steuert den Zugriff auf das EEPROM und ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Aufbau des EECR-Registers&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
| - || - || - ||- || EERIE || EEMWE || EEWE || EERE&lt;br /&gt;
|-&lt;br /&gt;
! Read/Write&lt;br /&gt;
| R || R || R || R || R/W || R/W || R/W || R/W&lt;br /&gt;
|-&lt;br /&gt;
!Init Value&lt;br /&gt;
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bedeutung der Bits&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;Bit 4-7: nicht belegt&lt;br /&gt;
&lt;br /&gt;
;Bit 3 (EERIE): &#039;&#039;EEPROM Ready Interrupt Enable&#039;&#039;: Wenn das Bit gesetzt ist und globale Interrupts erlaubt sind in Register SREG (Bit 7), wird ein Interrupt ausgelöst nach Beendigung des Schreibzyklus (EEPROM Ready Interrupt). Ist einer der beiden Bits 0, wird kein Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
;Bit 2 EEMWE): &#039;&#039;EEPROM Master Write Enable&#039;&#039;: Dieses Bit bestimmt, dass, wenn EEWE = 1 gesetzt wird (innerhalb von 4 Taktzyklen), das EEPROM beschrieben wird mit den Daten in EEDR bei Adresse EEAR. Wenn EEMWE = 0 ist und EEWE = 1 gesetzt wird, hat das keine Auswirkungen. Der Schreibvorgang wird dann nicht ausgelöst. Nach 4 Taktzyklen wird das Bit EEMWE automatisch wieder auf 0 gesetzt. Dieses Bit löst den Schreibvorgang nicht aus, es dient sozusagen als Sicherungsbit für EEWE.&lt;br /&gt;
&lt;br /&gt;
;Bit 1 (EEWE): &#039;&#039;EEPROM Write Enable&#039;&#039;: Dieses Bit löst den Schreibvorgang aus, wenn es auf 1 gesetzt wird, sofern vorher EEMWE gesetzt wurde und seitdem nicht mehr als 4 Taktzyklen vergangen sind. Wenn der Schreibvorgang abgeschlossen ist, wird dieses Bit automatisch wieder auf 0 gesetzt und, sofern EERIE gesetzt ist, ein Interrupt ausgelöst. Ein Schreibvorgang sieht typischerweise wie folgt aus:&lt;br /&gt;
:# EEPROM-Bereitschaft abwarten (EEWE=0) &lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Daten übergeben an EEDR&lt;br /&gt;
:# Schreibvorgang auslösen in EECR mit Bit EEMWE=1 und EEWE=1&lt;br /&gt;
:# (Optional) Warten, bis Schreibvorgang abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
;Bit 0 EERE: &#039;&#039;EEPROM Read Enable&#039;&#039;: Wird dieses Bit auf 1 gesetzt wird das EEPROM an der Adresse in EEAR ausgelesen und die Daten in EEDR gespeichert. Das EEPROM kann nicht ausgelesen werden, wenn bereits eine Schreiboperation gestartet wurde. Es ist daher zu empfehlen, die Bereitschaft vorher zu prüfen. Das EEPROM ist lesebereit, wenn das Bit EEWE=0 ist. Ist der Lesevorgang abgeschlossen, wird das Bit wieder auf 0 gesetzt, und das EEPROM ist für neue Lese- und Schreibbefehle wieder bereit. Ein typischer Lesevorgang kann wie folgt aufgebaut sein:&lt;br /&gt;
:# Bereitschaft zum Lesen prüfen (EEWE=0)&lt;br /&gt;
:# Adresse übergeben an EEAR&lt;br /&gt;
:# Lesezyklus auslösen mit EERE = 1&lt;br /&gt;
:# Warten, bis Lesevorgang abgeschlossen EERE = 0&lt;br /&gt;
:# Daten abholen aus EEDR&lt;br /&gt;
&lt;br /&gt;
= Die Nutzung von sprintf und printf =&lt;br /&gt;
&lt;br /&gt;
Um komfortabel, d.h. formatiert, Ausgaben auf ein Display oder die serielle Schnittstelle zu tätigen, bieten sich &#039;&#039;&#039;sprintf&#039;&#039;&#039; oder &#039;&#039;&#039;printf&#039;&#039;&#039; an. Alle *printf-Varianten sind jedoch ziemlich speicherintensiv und der Einsatz in einem Mikrocontroller mit knappem Speicher muss sorgsam abgewogen werden.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;sprintf&#039;&#039;&#039; wird die Ausgabe zunächst in einem Puffer vorbereitet und anschließend mit einfachen Funktionen zeichenweise ausgegeben. Es liegt in der Verantwortung des Programmierers, genügend Platz im Puffer für die erwarteten Zeichen bereitzuhalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
// nicht dargestellt: Implementierung von uart_puts (vgl. Abschnitt UART)&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
uint16_t counter;&lt;br /&gt;
&lt;br /&gt;
// Ausgabe eines unsigned Integerwertes&lt;br /&gt;
void uart_puti( uint16_t value )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t puffer[20];&lt;br /&gt;
&lt;br /&gt;
    sprintf( puffer, &amp;quot;Zählerstand: %u&amp;quot;, value );&lt;br /&gt;
    uart_puts( puffer );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  counter = 5;&lt;br /&gt;
&lt;br /&gt;
  uart_puti( counter );&lt;br /&gt;
  uart_puti( 42 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine weitere elegante Möglichkeit besteht darin, den STREAM stdout (Standardausgabe) auf eine eigene Ausgabefunktion umzuleiten. Dazu wird dem Ausgabemechanismus der C-Bibliothek eine neue Ausgabefunktion bekannt gemacht, deren Aufgabe es ist, ein einzelnes Zeichen auszugeben. Wohin die Ausgabe dann tatsächlich stattfindet, ist Sache der Ausgabefunktion. Im Beispiel unten wird auf UART ausgegeben. Alle anderen, höheren Funktionen wie z.&amp;amp;nbsp;B. &#039;&#039;&#039;printf&#039;&#039;&#039;, greifen letztendlich auf diese primitive Ausgabefunktion zurück. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void);&lt;br /&gt;
&lt;br /&gt;
// a. Deklaration der primitiven Ausgabefunktion&lt;br /&gt;
int uart_putchar(char c, FILE *stream);&lt;br /&gt;
&lt;br /&gt;
// b. Umleiten der Standardausgabe stdout (Teil 1)&lt;br /&gt;
static FILE mystdout = FDEV_SETUP_STREAM( uart_putchar, NULL, _FDEV_SETUP_WRITE );&lt;br /&gt;
&lt;br /&gt;
// c. Definition der Ausgabefunktion&lt;br /&gt;
int uart_putchar( char c, FILE *stream )&lt;br /&gt;
{&lt;br /&gt;
    if( c == &#039;\n&#039; )&lt;br /&gt;
        uart_putchar( &#039;\r&#039;, stream );&lt;br /&gt;
&lt;br /&gt;
    loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
    UDR = c;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* hier µC spezifischen Code zur Initialisierung */&lt;br /&gt;
    /* des UART einfügen... s.o. im AVR-GCC-Tutorial */&lt;br /&gt;
&lt;br /&gt;
    // Beispiel: &lt;br /&gt;
    //&lt;br /&gt;
    // myAVR Board 1.5 mit externem Quarz Q1 3,6864 MHz&lt;br /&gt;
    // 9600 Baud 8N1&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 3686400&lt;br /&gt;
#endif&lt;br /&gt;
#define UART_BAUD_RATE 9600&lt;br /&gt;
&lt;br /&gt;
// Hilfsmakro zur UBRR-Berechnung (&amp;quot;Formel&amp;quot; laut Datenblatt)&lt;br /&gt;
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)&lt;br /&gt;
&lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;TXEN) | (1&amp;lt;&amp;lt;RXEN);    // UART TX und RX einschalten&lt;br /&gt;
    UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);    // Asynchron 8N1 &lt;br /&gt;
 &lt;br /&gt;
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) &amp;gt;&amp;gt; 8 );&lt;br /&gt;
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    int16_t antwort = 42;&lt;br /&gt;
    uart_init();&lt;br /&gt;
&lt;br /&gt;
    // b. Umleiten der Standardausgabe stdout (Teil 2)&lt;br /&gt;
    stdout = &amp;amp;mystdout;&lt;br /&gt;
&lt;br /&gt;
    // Anwendung&lt;br /&gt;
    printf( &amp;quot;Die Antwort ist %d.\n&amp;quot;, antwort );&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Quelle: avr-libc-user-manual-1.4.3.pdf, S.74&lt;br /&gt;
//         + Ergänzungen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollen Fließkommazahlen ausgegeben werden, muss im Makefile eine andere (größere) Version der [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|printflib]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
= Anmerkungen =&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
* Aktualisierung Register- und Bitbeschreibungen an aktuelle AVR&lt;br /&gt;
* &amp;quot;naked&amp;quot;-Funktionen&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:avr-gcc Tutorial| ]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Power_Management&amp;diff=90413</id>
		<title>AVR-Tutorial: Power Management</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Power_Management&amp;diff=90413"/>
		<updated>2015-11-27T14:30:02Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: schliessendes Tag nach dem Assembler Beispiel ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;ACHTUNG!&#039;&#039;&#039;&lt;br /&gt;
Dieser Artikel befindet sich noch im Aufbau!&lt;br /&gt;
&lt;br /&gt;
Vorallem in batteriebetriebenen Systemen spielt die Leistungsaufnahme eine wichtige Rolle, d.h. sie soll so niedrig wie möglich gehalten werden um eine lange Laufzeit zu erreichen. Den sparsamen Umgang mit der verfügbaren el. Ladung nennt man &#039;&#039;&#039;Power Management&#039;&#039;&#039; (dt. Energiesparen).&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Power Managements stehen uns beispielsweise die Sleep-Modi zur Verfügung, mit denen wir bestimmte Module zeitweise deaktivieren können. Andere garnicht genutzte Module können wir durch entsprechende Konfiguration (z.B. in den Fuses) auch komplett deaktivieren.&lt;br /&gt;
&lt;br /&gt;
== Theorie ==&lt;br /&gt;
&lt;br /&gt;
Der Stromverbrauch einer CMOS-Schaltung (was auch Mikrocontroller sind) hängt letztlich nur davon ab, wie viele Gates bei welcher Spannung umgeladen werden. Zur Reduktion steht demnach zur Verfügung:&lt;br /&gt;
* Reduktion der Umladevorgänge pro Sekunde, also Reduktion der Taktfrequenz&lt;br /&gt;
* Reduktion der beteiligten Kapazitäten, also Abschalten nicht benutzter Funktionsblöcke&lt;br /&gt;
* Reduktion der Speisespannung (bei manchen Controllern ist das die &#039;&#039;Kernspannung&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Den statischen Reststrom kann man heutzutage vernachlässigen, auch wenn an dieser Stelle die Anmerkung fallen muss, dass AVRs da eher schlecht abschneiden. Hingegen muss Strom durch ohmsche Lasten, etwa Pull-Up-Widerstände oder LEDs, unbedingt beachtet werden! Außerdem dürfen keine offenen (CMOS!-)Eingänge „herumfliegen“ sondern müssen allesamt an definiertem Potenzial liegen. In der Regel können Mikrocontroller unbenutzte Eingänge im Schlafmodus oder Analogeingangsbetrieb automatisch abtrennen.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich sollte für Stromsparbetrieb die niedrigstmögliche Taktfrequenz gewählt werden, die möglich ist. Übliche Mikrocontroller können direkt von einem Uhrenquarz mit 32,768 kHz gespeist werden. Macht natürlich auch nur maximal 32768 Befehle pro Sekunde. Die Stromaufnahme ist dabei sehr gering und im Gegensatz zu Sleep-Mode-Tricksereien ohne hohe Spitzen.&lt;br /&gt;
&lt;br /&gt;
Da das Antwortverhalten bei derart geringen Taktfrequenzen leidet, wird man eine höhere Frequenz wählen und den Prozessor bei Nicht-Bedarf schlafen schicken. Die Strom-Spitzen sind dementsprechend höher, die Gesamtstromaufnahme bleibt jedoch gering.&lt;br /&gt;
&lt;br /&gt;
Viele Mikrocontroller können einzelne Funktionsblöcke abschalten. Bei den AVRs schlägt da vor allem der 16-Bit-Timer 1 zu Buche. Aber auch die Referenzspannungsquelle für den A/D-Wandler, Analogvergleicher oder den Brown-Out-Detektor kann man abschalten.&lt;br /&gt;
&lt;br /&gt;
Für die Kontrolle der Stromaufnahme aus einer Batterie ist ein Digitalmultimeter mit vielen Strommessbereichen bis 20 µA (Auflösung 10 nA) vorteilhaft. Außerdem ein Plättchen einer doppelseitigen Leiterplatte mit dünnen Drähtchen, das man unter die Batteriekontaktierung schieben kann. Weicht die Stromaufnahme vom Datenblatt ab, hat man wohl noch irgendetwas vergessen.&lt;br /&gt;
&lt;br /&gt;
=== Sleep Modi ===&lt;br /&gt;
&lt;br /&gt;
Welche Sleep-Modi es gibt, hängt vom verwendeten µC ab, dieser Artikel nimmt jedoch Bezug auf den ATmega32. Um einen der verfügbaren Sleep-Modi des ATmega32 zu betreten müssen folgende Schritte ausgeführt werden&lt;br /&gt;
&lt;br /&gt;
# Das SE-Bit im MCUCR-Register wird auf 1 gesetzt&lt;br /&gt;
# Die SMx-Bits im MCUCR-Register je nach gewünschtem Modus setzen&lt;br /&gt;
# Der SLEEP-Befehl wird ausgeführt&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller geht dann sofort in den SLEEP-Modus, d.h. noch vor eventuell anstehenden Interrupts, und wacht erst wieder auf wenn ein Signal eines geeigneten Moduls (je nach Modus) ihn aufweckt.&lt;br /&gt;
&lt;br /&gt;
Die Arbeit wird dann mit der ersten Anweisung hinter dem SLEEP-Befehl wieder aufgenommen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ MCUCR - MCU Control Register&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Bit&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 7&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 6&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 5&lt;br /&gt;
| style=&amp;quot;width:12%&amp;quot; | 4&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 3&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 2&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 1&lt;br /&gt;
| style=&amp;quot;width:12%; background-color:#c0c0c0&amp;quot; | 0&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Bezeichnung&lt;br /&gt;
| SE&lt;br /&gt;
| SM2&lt;br /&gt;
| SM1&lt;br /&gt;
| SM0&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC11&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC10&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC01&lt;br /&gt;
| style=&amp;quot;background-color:#c0c0c0&amp;quot; | ISC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
;Bit 7 - SE: Sleep Enable&lt;br /&gt;
:Mit diesem Bit wird bestimmt ob der Sleep-Befehl ausgeführt wird (1) oder nicht (0).&lt;br /&gt;
&lt;br /&gt;
;Bit 6..4 - SM2..0: Sleep Mode Select&lt;br /&gt;
:Mit diesen drei Bits wird der gewünschte Sleep-Modus gewählt&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! SM2&lt;br /&gt;
! SM1&lt;br /&gt;
! SM0&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Sleep Modus&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Idle&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | ADC Noise Reduction&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-down&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-save&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Reserved&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Reserved&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Extended Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt; Nur verfügbar mit externem Taktgeber&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Modi Übersicht====&lt;br /&gt;
&lt;br /&gt;
Generell ist der Modus zu wählen, der die meisten nicht benötigten Module abschaltet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;5&amp;quot; | Aktive Takte &lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Aktive Oszillatoren&lt;br /&gt;
! colspan=&amp;quot;6&amp;quot; | Weckquellen&lt;br /&gt;
|- style=&amp;quot;background-color:#f0f0f0&amp;quot;&lt;br /&gt;
| Sleep Modus&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;CPU&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;FLASH&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;IO&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;ADC&amp;lt;/sub&amp;gt;&lt;br /&gt;
| clk&amp;lt;sub&amp;gt;ASY&amp;lt;/sub&amp;gt;&lt;br /&gt;
| Haupttaktgeber&lt;br /&gt;
| Timer Oszillator&lt;br /&gt;
| INT2&amp;lt;br /&amp;gt;INT1&amp;lt;br /&amp;gt;INT0&lt;br /&gt;
| TWI Address Match&lt;br /&gt;
| Timer2&lt;br /&gt;
| SPM/EEPROM Ready&lt;br /&gt;
| ADC&lt;br /&gt;
| Andere I/O&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Idle&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | ADC Noise Reduction&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-down&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Power-save&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|- style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
| style=&amp;quot;text-align:left&amp;quot; | Extended Standby&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| x&lt;br /&gt;
| x&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt;&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;sup&amp;gt;(1)&amp;lt;/sup&amp;gt; Nur verfügbar bei externer Taktquelle&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(2)&amp;lt;/sup&amp;gt; Wenn AS2-Bit in ASSR-Register gesetzt&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(3)&amp;lt;/sup&amp;gt; Nur INT2 oder Level Interrupt INT1 und INT0&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manuelles Deaktivieren ===&lt;br /&gt;
Einzelne Module können auch manuell deaktviert werden um Strom zu sparen, das bietet sich vorallem an wenn bestimmte Module im gegebenen Projekt generell nicht benötigt werden und damit deaktiviert werden können.&lt;br /&gt;
&lt;br /&gt;
==== Analog to Digital Converter ====&lt;br /&gt;
Der ADC ist standardmäßig aktiviert. Um ihn zu deaktivieren, muss man ADEN (Bit 7) im Register ADCSRA auf Null setzen.&lt;br /&gt;
&lt;br /&gt;
==== Analog Comparator ====&lt;br /&gt;
Der Analogkomparator ist standardmäßig aktiviert. Um ihn zu deaktivieren, muss man ACD (Bit 7) im Register ACSR setzen.&lt;br /&gt;
&lt;br /&gt;
==== Brown-Out Detector ====&lt;br /&gt;
Der Brown-Out Detector lässt sich entweder durch das BODEN-Bit in den Fuses oder mit entsprechenden Befehlen aktivieren oder deaktivieren. Das Fuse-Bit ist standardmäßig gesetzt (Achtung: Umgekehrte Logik!) und der BOD damit deaktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Watchdog ====&lt;br /&gt;
Auch der Watchdog-Timer lässt sich in den Fuses standardmäßig aktivieren/deaktivieren, hier über das WDTON-Bit.&lt;br /&gt;
Natürlich geht auch das softwareseitig [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Watchdog]&lt;br /&gt;
&lt;br /&gt;
== Praxis ==&lt;br /&gt;
&lt;br /&gt;
=== Assembler ===&lt;br /&gt;
Hier mal etwas Code der nicht aufgeräumt ist aber sich gut eignet für Versuche und funktioniert.&lt;br /&gt;
Verbesserungen folgen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
;**********&lt;br /&gt;
;* Dateibeschrieb:&lt;br /&gt;
;* Interrupttest_Eingängexx.ASM&lt;br /&gt;
;* Autor Martin Kuch&lt;br /&gt;
;* V02 23.10.2014 mit Sleep und LED Blink in Go raus aus ISR ST:OK!&lt;br /&gt;
;* V03 23.10.2014 cli und sei in Go - nur 1x Flanke erkennen nach Schlußdelay ST: OK!&lt;br /&gt;
;* V04 23.10.2014 sbic warten bis alle Taster wieder frei sind ST:OK!&lt;br /&gt;
;**********&lt;br /&gt;
;* Funtionen:&lt;br /&gt;
;* 4 Taster an PB0 ... PB3 zur Eingabesteuerung&lt;br /&gt;
;* Interrupt 0B (PinChangeInt)&lt;br /&gt;
;* PB4 LED&lt;br /&gt;
;**********&lt;br /&gt;
;* Hardware Zielschaltung:&lt;br /&gt;
;* Test-Board mit ATTiny2313 PU&lt;br /&gt;
;* Steckernetzgerät Sek: 6VAC 350mA&lt;br /&gt;
;* Netzteil eingenbau: Brückengleichrichter, 330uF, 7805CT, 220uF, 6,8nF 2x, LED + 261R&lt;br /&gt;
;**********&lt;br /&gt;
;* Anschlussschema:&lt;br /&gt;
; ATTiny2313&lt;br /&gt;
; Port Pin - Pin Funktion&lt;br /&gt;
; PB1 12 - Eingang Schalter 1 mit Pullup(+LED) gegen Masse (Set)&lt;br /&gt;
; PB2 13 - Eingang Schalter 2 mit Pullup(+LED) gegen Masse (Back)&lt;br /&gt;
; PB3 14 - Eingang Schalter 3 &amp;quot; (vor)&lt;br /&gt;
; PB4 15 - Eingang Schalter 4 &amp;quot; (zurück)&lt;br /&gt;
; PB0 16 - Ausgang Lüfter Ein, LED mit Widerstand gegen +5VDC (Kathode gegen Ausgang)&lt;br /&gt;
&lt;br /&gt;
;* Controllertyp&lt;br /&gt;
.device ATtiny2313   ;für Gasm   .include Headerdatei für at2313.asm bei Gasm nicht nötig &lt;br /&gt;
;* Definitionen&lt;br /&gt;
.def temp1 = r16 ;Allzweckregister und für LCD_Routines3&lt;br /&gt;
.def temp2 = r17 ;für LCD_Routines&lt;br /&gt;
.def temp3 = r18 ;für LCD_Routines&lt;br /&gt;
.def TM = r19 ;Allzweckregister Programm&lt;br /&gt;
.def TM1 = r20 ;Allzweckregister 2&lt;br /&gt;
.def TMz = r21 ;Zähler allg.&lt;br /&gt;
.def Ct0 = r24 ;Couter Delay Vorgabe, Übergabewert für Zeitschleife&lt;br /&gt;
.def Ct1 = r25 ;Conter Delay intern&lt;br /&gt;
&lt;br /&gt;
;* Flash Programm&lt;br /&gt;
.cseg ;Flash Segment&lt;br /&gt;
; Interrupt-Handler&lt;br /&gt;
.org 0x0000&lt;br /&gt;
rjmp Main	;0x00 Reset&lt;br /&gt;
reti;rjmp SInt	;0x01 INT0 PD2 (6)&lt;br /&gt;
reti;rjmp SInt	;0x02 INT1 PD3 (7)&lt;br /&gt;
reti;rjmp SInt	;0x03 Tim1_Capt&lt;br /&gt;
reti;rjmp SInt	;0x04 Tim1_CompA&lt;br /&gt;
reti;rjmp SInt	;0x05 Tim1_OVF&lt;br /&gt;
reti;rjmp SInt	;0x06 Tim0_OVF&lt;br /&gt;
reti;rjmp SInt	;0x07 Uart0_rxc&lt;br /&gt;
reti;rjmp SInt	;0x08 Uart0_dre&lt;br /&gt;
reti;rjmp SInt	;0x09 Uart0_txc&lt;br /&gt;
reti;rjmp SInt	;0x0A ANA_Comp&lt;br /&gt;
rjmp IntPC	;0x0B PinChangeInt&lt;br /&gt;
reti;rjmp SInt	;0x0C Tim1_CompB&lt;br /&gt;
reti;rjmp SInt	;0x0D Tim0_CompA&lt;br /&gt;
reti;rjmp SInt	;0x0E Tim0_CompB&lt;br /&gt;
reti;rjmp SInt	;0x0F USI_Start&lt;br /&gt;
reti;rjmp SInt	;0x10 USI_OVERFLOW&lt;br /&gt;
reti;rjmp SInt	;0x11 EE_READ&lt;br /&gt;
reti;rjmp SInt	;0x12 WDT_OVERFLOW&lt;br /&gt;
nop&lt;br /&gt;
&lt;br /&gt;
;* Main Programm Endlos Steuerung Taster&lt;br /&gt;
Main:&lt;br /&gt;
;Stack init&lt;br /&gt;
         ldi TM, RAMEND&lt;br /&gt;
         out SPL, TM&lt;br /&gt;
;*Init&lt;br /&gt;
		ldi TM, 0b00010000           ;Port B Bit0...3 =&amp;gt; Eingang B4 =&amp;gt; Ausgang&lt;br /&gt;
		out DDRB, TM&lt;br /&gt;
		nop&lt;br /&gt;
;*Hallo blinken testen, LED-Test und schauen ob uC läuft&lt;br /&gt;
	cbi PortB,4	;LED an&lt;br /&gt;
	ldi Ct0, 100&lt;br /&gt;
	rcall delayms&lt;br /&gt;
	sbi PortB,4	;LED aus&lt;br /&gt;
	rcall delayms&lt;br /&gt;
	ldi Ct0, 255&lt;br /&gt;
&lt;br /&gt;
;*Init Interrupt PCINT&lt;br /&gt;
		ldi TM, 0b00100000	;=32 dez. &lt;br /&gt;
		out MCUCR, TM		;Bit5 = EN enable Sleep&lt;br /&gt;
		out GIMSK, TM		;PCIE PC.Int.Enable&lt;br /&gt;
		ldi TM, 0b00001111&lt;br /&gt;
		out PCMSK, TM		;Maske Int. Pin zulassen&lt;br /&gt;
		sei					;Interrupts zulassen&lt;br /&gt;
Go:&lt;br /&gt;
	sleep		;Schlafen legen = Energiesparen&lt;br /&gt;
	nop&lt;br /&gt;
	cli&lt;br /&gt;
	cbi PortB,4	;LED an&lt;br /&gt;
	rcall delayms&lt;br /&gt;
&lt;br /&gt;
Go_1:&lt;br /&gt;
	sbis PINB,0	;Taste noch gedrückt? = 0 =&amp;gt; warten&lt;br /&gt;
rjmp Go_1&lt;br /&gt;
	sbis PINB,1	;Taste noch gedrückt? = 0 =&amp;gt; warten&lt;br /&gt;
rjmp Go_1&lt;br /&gt;
	sbis PINB,2	;Taste noch gedrückt? = 0 =&amp;gt; warten&lt;br /&gt;
rjmp Go_1&lt;br /&gt;
	sbis PINB,3	;Taste noch gedrückt? = 0 =&amp;gt; warten&lt;br /&gt;
rjmp Go_1&lt;br /&gt;
&lt;br /&gt;
	sbi PortB,4	;LED aus&lt;br /&gt;
;	rcall delayms&lt;br /&gt;
	sei             ;Interrupts einschalten&lt;br /&gt;
rjmp Go&lt;br /&gt;
&lt;br /&gt;
;*ISR für PinChangeInt. (0x0B)&lt;br /&gt;
IntPC:&lt;br /&gt;
;	push TM&lt;br /&gt;
;	in TM, SREG&lt;br /&gt;
	nop&lt;br /&gt;
;	out SREG, TM&lt;br /&gt;
;	pop TM&lt;br /&gt;
reti&lt;br /&gt;
&lt;br /&gt;
; Pause Count * 5ms&lt;br /&gt;
delayms:&lt;br /&gt;
         mov Ct1, Ct0&lt;br /&gt;
delayms_:&lt;br /&gt;
         rcall delay5ms&lt;br /&gt;
         dec Ct1&lt;br /&gt;
         brne delayms_&lt;br /&gt;
         ret&lt;br /&gt;
&lt;br /&gt;
 ; Längere Pause für manche Befehle&lt;br /&gt;
delay5ms:                               ; 5ms Pause&lt;br /&gt;
           Push temp2&lt;br /&gt;
           ldi  temp1, ( XTAL * 5 / 607 ) / 1000&lt;br /&gt;
WGLOOP0:   ldi  temp2, $C9&lt;br /&gt;
WGLOOP1:   dec  temp2&lt;br /&gt;
           brne WGLOOP1&lt;br /&gt;
           dec  temp1&lt;br /&gt;
           brne WGLOOP0&lt;br /&gt;
           pop temp2&lt;br /&gt;
           ret                          ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
&lt;br /&gt;
Ein simples Testprogramm, um mit sleep modi im AVR zu spielen. Es funktioniert sehr gut mit dem ATmega8 und ist auch auf andere AVRs portierbar. Teilweise sind weitere Modi verfügbar.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, daß die Interruptroutine für den &amp;quot;Weckruf&amp;quot; vorhanden sein muss. Es müssen nicht zwingend Aktionen in ihr durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* ATmega8 with internal 4Mhz clock (6cycle + 64ms) */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   DDRC |= (1 &amp;lt;&amp;lt; PC2) | (1 &amp;lt;&amp;lt; PC1); // leds for testing&lt;br /&gt;
&lt;br /&gt;
   DDRD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD2); // INT0: input...&lt;br /&gt;
   PORTD |= (1 &amp;lt;&amp;lt; PD2); // ...with pullup.&lt;br /&gt;
&lt;br /&gt;
   // level interrupt INT0 (low level)&lt;br /&gt;
   MCUCR &amp;amp;= ~((1 &amp;lt;&amp;lt; ISC01) | (1 &amp;lt;&amp;lt; ISC00));&lt;br /&gt;
&lt;br /&gt;
   // infinite main loop&lt;br /&gt;
   while (1)&lt;br /&gt;
   {&lt;br /&gt;
      // trigger leds for testing&lt;br /&gt;
      PORTC ^= (1 &amp;lt;&amp;lt; PC1);&lt;br /&gt;
      _delay_ms(500);&lt;br /&gt;
      PORTC ^= (1 &amp;lt;&amp;lt; PC1);&lt;br /&gt;
&lt;br /&gt;
      // enable external interrupt&lt;br /&gt;
      GICR |= (1 &amp;lt;&amp;lt; INT0);&lt;br /&gt;
&lt;br /&gt;
      // set sleep mode&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
&lt;br /&gt;
      // sleep_mode() has a possible race condition&lt;br /&gt;
      sleep_enable();&lt;br /&gt;
      sei();&lt;br /&gt;
      sleep_cpu();&lt;br /&gt;
      sleep_disable();&lt;br /&gt;
&lt;br /&gt;
      // waking up...&lt;br /&gt;
      // disable external interrupt here, in case the external low pulse is too long&lt;br /&gt;
      GICR &amp;amp;= ~(1 &amp;lt;&amp;lt; INT0);&lt;br /&gt;
&lt;br /&gt;
      // disable all interrupts&lt;br /&gt;
      cli();&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect)&lt;br /&gt;
{&lt;br /&gt;
   // ISR might be empty, but is necessary nonetheless&lt;br /&gt;
   PORTC ^= (1 &amp;lt;&amp;lt; PC2); // debugging&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellen ==&lt;br /&gt;
[http://www.atmel.com/dyn/resources/prod_documents/doc2503.pdf ATMEL AVR ATmega32 Datenblatt]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
{{Navigation_zurückhoch|&lt;br /&gt;
zurücktext=Power Management|&lt;br /&gt;
zurücklink=AVR-Tutorial: Power Management|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie: AVR-Tutorial|Power Management]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Serial_Peripheral_Interface&amp;diff=89864</id>
		<title>Serial Peripheral Interface</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Serial_Peripheral_Interface&amp;diff=89864"/>
		<updated>2015-09-29T17:41:06Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das &#039;&#039;&#039;S&#039;&#039;&#039;erial &#039;&#039;&#039;P&#039;&#039;&#039;eripheral &#039;&#039;&#039;I&#039;&#039;&#039;nterface, kurz SPI oder auch Microwire genannt, ist ein Bus-System bestehend aus drei Leitungen für eine serielle synchrone Datenübertragung zwischen verschiedenen ICs. &lt;br /&gt;
&lt;br /&gt;
Der Bus besteht aus folgenden Leitungen&lt;br /&gt;
* MOSI (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;O&#039;&#039;&#039;ut -&amp;gt; &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;I&#039;&#039;&#039;n) auch SDO (&#039;&#039;&#039;S&#039;&#039;&#039;erial &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;O&#039;&#039;&#039;ut) oder DO&lt;br /&gt;
* MISO (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;I&#039;&#039;&#039;n &amp;lt;- &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;O&#039;&#039;&#039;ut) auch SDI (&#039;&#039;&#039;S&#039;&#039;&#039;erial &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;I&#039;&#039;&#039;n) oder DI&lt;br /&gt;
* SCK (Serial Clock) - Schiebetakt&lt;br /&gt;
&lt;br /&gt;
Zusätzlich zu diesen drei Leitungen wird für jeden Slave eine Slave Select (SS) oder auch Chip Select (CS) genannte Leitung benötigt, durch die der Master den Slave zur aktuellen Kommunikation selektiert. Dies geschieht dadurch, dass der Master die SS/CS-Leitung von High nach Low zieht. Oft ist mit dieser Aktivierung durch den Master auch eine Benachrichtigung für den Slave verbunden mit der ihm mitgeteilt wird, dass jetzt eine Nachricht beginnt, das nächste Byte also zum Beispiel als Kommando aufzufassen ist.&lt;br /&gt;
&lt;br /&gt;
Die Übertragung geschieht so, dass der Master seine Datenleitung (MOSI) auf den Pegel des nächsten Bits bringt und dann an der SCK Leitung einen Puls ausgibt. Gleichzeitig wird vom Master der Pegel an der Datenleitung vom Slave zum Master überwacht und ihr Zustand als nächstes einzulesendes Bit aufgefasst. Üblicherweise gibt es zumindest beim Master mehrere Einstellungen, die festlegen, welches der Grundzustand dieser SCK Leitung sein soll und welche Flanke des Clock-Pulses zur Datenübernahme herzunehmen ist (die steigende oder die fallende). Bei einigen Slaves ist diese Einstellung ebenfalls möglich, oft ist es aber so, dass per SPI anzusprechende IC eine fixe Einstellung benutzen, an die sich der Master anpassen muss.&lt;br /&gt;
&lt;br /&gt;
Für den SPI-Bus gibt es kein festgelegtes Protokoll. Die Clock-Polarität (CPOL) und Phase (CPHA) können ebenfalls von Slave zu Slave unterschiedlich sein. Der SPI-Bus kann mit einer Taktfrequenz von mehreren Megahertz betrieben werden. Es gibt viele verschiedene ICs die als Slave an dem SPI-Bus betrieben werden können, diese gehen von einfachen Schieberegistern bis hin zu RTCs oder Displaytreibern mit vorgegebenem Protokoll. Unter anderem werden die meisten [[AVR]]-Microcontroller von Atmel über SPI [[ISP|programmiert]], siehe dazu [[AVR In System Programmer]].&lt;br /&gt;
&lt;br /&gt;
== konzeptionelles Übertragungsprinzip ==&lt;br /&gt;
&lt;br /&gt;
Bei SPI verlieren die Begriffe &#039;Sender&#039; und &#039;Empfänger&#039; ihre Bedeutung. Das Übertragungsprinzip funktioniert so, dass gleichzeitig 1 Byte vom Master zum Slave und 1 Byte vom Slave zum Master übertragen wird. Die Übertragung ist als eher mit dem Begriff &amp;quot;Austausch von Bytes&amp;quot; zu beschreiben, als dass es sich um ein Senden bzw. Empfangen handelt. Je nach Sichtweise könnte man auch sagen, dass sowohl Master als auch Slave jederzeit sowohl Sender als auch Empfänger sind. Der einzige Unterschied zwischen den Busteilnehmern besteht darin, dass beim SPI Bus jegliche Kommunikation immer vom Master ausgeht. Er hat die Verwaltung der SCK Leitung inne und wenn der Master keine SCK Pulse generiert, dann kann ein Slave auch nichts übertragen. Die Generierung der SCK Pulse wird dabei üblicherweise dadurch angestossen, dass der Master einfach angewiesen wird, 1 Byte über SPI auszugeben. Weiters kann ein Slave auch eine SPI Übertragung nicht hinauszögern. Wenn vom Master die SCK Pulse kommen, dann muss ein Slave die MISO Leitung bedienen. Er hat keine Möglichkeit dies nicht zu tun, selbst wenn er noch gar kein Ergebnis für den Master vorliegen hat. Selbst wenn der Slave die MISO Leitung nicht bedient, der Master wird zum per Modus eingestellten Zeitpunkt die Polarität der MISO Leitung auswerten und sich daraus das nächste Bit bestimmen.&lt;br /&gt;
&lt;br /&gt;
Erfolgt eine Abfrage eines Masters an einen Slave dergestalt, dass der Master ein Kommando an den Slave sendet, auf das der Slave mit einem Ergebnis zu antworten hat, dann sind dazu immer mindestens 2 Übertragungen notwendig. Im ersten Byte-Austausch übermittelt der Master sein Kommando an den Slave (der zu diesem Zeitpunkt logischerweise noch kein Ergebnis vorliegen haben wird, er kennt ja das Kommando noch nicht) und im zweiten Byte-Austausch überträgt der Master einfach nur 1 Byte (meistens genügt da einfach irgendein Bytewert) um dem Slave seinerseits Gelegenheit zu geben, das zuvor angefragte Ergebnis zum Master zu übertragen. Beachten sollte man, dass ein Slave auch des öfteren etwas Zeit benötigen wird, um ein gerade erhaltenes Kommando auszuwerten und die entsprechenden Ergebnisse bereit zu stellen. Ein Master wird also gut daran tun, nach dem Byteaustausch zum Zwecke der Kommandoübermittlung eine kleine Pause einzulegen, ehe er dann mit dem zweiten Byteaustausch das Ergbnis abholt.&lt;br /&gt;
 &lt;br /&gt;
== SPI-Modi ==&lt;br /&gt;
&lt;br /&gt;
Wie schon angesprochen gibt es für das SPI verschiedene Möglichkeiten Polarität und Phase des Taktes einzustellen. Folgende vier Modi sind möglich:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Mode&lt;br /&gt;
!CPOL&lt;br /&gt;
!CPHA&lt;br /&gt;
|-&lt;br /&gt;
|0 ||0 ||0&lt;br /&gt;
|-&lt;br /&gt;
|1 ||0 ||1&lt;br /&gt;
|-&lt;br /&gt;
|2 ||1 ||0&lt;br /&gt;
|-&lt;br /&gt;
|3 ||1 ||1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;CPOL (Clock Polarity)&lt;br /&gt;
:0: Takt ist in Ruhe LOW, ein Wechsel auf HIGH zählt als steigende Taktflanke&lt;br /&gt;
:1: Takt ist invertiert: in Ruhe HIGH, ein Wechsel auf LOW zählt als steigende Taktflanke&lt;br /&gt;
;CPHA (Clock Phase)&lt;br /&gt;
:0: Daten werden bei steigender Taktflanke (=abh. von CPOL) eingelesen, bei fallender ausgegeben&lt;br /&gt;
:1: Daten werden bei fallender Taktflanke eingelesen, bei steigender ausgegeben&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass Mode 0 und Mode 3 bzw. Mode 1 und Mode 2 jeweils fast identisch sind. Der einzige Unterschied ist der Pegel des Taktes in Ruhe. In der Regel sind diese Modi deshalb austauschbar.&lt;br /&gt;
&lt;br /&gt;
CPOL und CPHA lassen sich in den Konfigurationsregistern des Controllers einstellen. Beim [[AT91SAM]] hat sich Atmel ein kleines Extra einfallen lassen: hier heißt das Bit zur Einstellung der Clock Phase &amp;quot;NCPHA&amp;quot; und entspricht genau dem invertierten Wert von CPHA.&lt;br /&gt;
&lt;br /&gt;
Es ist problemlos möglich ICs mit verschiedenen SPI-Modi an einem Bus zu betreiben, man muss nur vor dem Aktivieren des Chip Select den jeweils richtigen Modus einstellen.&lt;br /&gt;
&lt;br /&gt;
== Falle bei AVR Prozessoren ==&lt;br /&gt;
&lt;br /&gt;
Bei den AVR Prozessoren, die eine SPI Enheit besitzen, ist es eine gute Idee, den SS-Pin der SPI Einheit auf Ausgang zu setzen, wenn man den Prozessor als Master benutzen möchte.  Dies geschieht nicht automatisch, da ein auf Eingang geschalteter Pin im Multimaster Modus benötigt wird. Da dieser Fall aber recht selten vorkommt, wird er hier ignoriert. Schaltet man den Pin nicht auf Ausgang, dann hängt es vom extern angelegten Pegel ab, ob der die Master-SPI Einheit arbeitet oder nicht. Daher den Pin immer auf Ausgang schalten. In diesem Fall ist der Pegel der SS Leitung dann irrelevant, die SPI Einheit arbeitet immer, sobald sie als Master konfiguriert wurde.&lt;br /&gt;
&lt;br /&gt;
== Fehlersuche ==&lt;br /&gt;
&lt;br /&gt;
Immer wieder kommt es zu Problemen bei der Nutzung von SPI, obwohl diese Schnittstelle recht einfach aufgebaut ist. Immer wieder kommt es u.a. zu dem Effekt, dass nur bestimmte SPI-Frequenzen funktionieren, etwas höher oder niedrigere nicht. Das ist ein klares Zeichen für Fehler. Folgende Punkte sollte man mit dem [[Oszilloskop]] prüfen.&lt;br /&gt;
&lt;br /&gt;
* Stimmt jeweils der SPI-Modus?&lt;br /&gt;
* Sind die Select-Signale vorhanden?&lt;br /&gt;
* Sind die erreichten Pegel ausreichend?&lt;br /&gt;
* Sind die Clock-Flanken sauber genug? Siehe [[Wellenwiderstand]].&lt;br /&gt;
* Sind die Daten während der Datenübernahme stabil?&lt;br /&gt;
* Ist die Zeit zwischen Select-Signal und Übertragungsbeginn ausreichend?&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Porterweiterung mit SPI]]&lt;br /&gt;
* [[AVR-Tutorial: Schieberegister]]&lt;br /&gt;
* [[MMC- und SD-Karten]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/54441 SPI Codetuning]&lt;br /&gt;
* [[SPI Daisychain]]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* http://www.mct.net/faq/spi.html (Clock-Einstellungen, Slave-IC Liste, etc.)&lt;br /&gt;
* http://www.matuschek.net/atmega-spi (ATMega SPI Performance Tuning)&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2585.pdf AVR151: Setup And Use of The SPI] Atmel Application Note (PDF)&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/AVR151.zip AVR151: Examples for Setup and use of the SPI] Programming Examples for the Atmel Application Note (zip)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:SPI| ]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_UART&amp;diff=89615</id>
		<title>AVR-Tutorial: UART</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_UART&amp;diff=89615"/>
		<updated>2015-08-25T12:09:52Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Hardware Handshake */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wie viele andere Controller besitzen die meisten AVRs einen &#039;&#039;&#039;[[UART]]&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;niversal &#039;&#039;&#039;A&#039;&#039;&#039;synchronous &#039;&#039;&#039;R&#039;&#039;&#039;eceiver and &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter). Das ist eine serielle Schnittstelle, die meistens zur Datenübertragung zwischen Mikrocontroller und PC genutzt wird. Zur Übertragung werden zwei Pins am Controller benötigt: &#039;&#039;&#039;TXD&#039;&#039;&#039; und &#039;&#039;&#039;RXD&#039;&#039;&#039;. Über &#039;&#039;&#039;TXD&#039;&#039;&#039; (&amp;quot;Transmit Data&amp;quot;) werden Daten gesendet, &#039;&#039;&#039;RXD&#039;&#039;&#039; (&amp;quot;Receive Data&amp;quot;) dient zum Empfang von Daten.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Um den UART des Mikrocontrollers zu verwenden, muss der Versuchsaufbau um folgende Bauteile erweitert werden: &lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-RS232.png|framed|right|640px| UART/MAX232 Standardbeschaltung ]]&lt;br /&gt;
&lt;br /&gt;
Auf dem Board vom [http://shop.mikrocontroller.net/ Shop] sind diese Bauteile bereits enthalten, man muss nur noch die Verbindungen zwischen MAX232 (IC2) und AVR herstellen wie im [http://shop.mikrocontroller.net/images/avrplat28-app.jpg Bild] zu sehen.&lt;br /&gt;
&lt;br /&gt;
* Der MAX232 ist ein [[Pegelwandler]], der die -12V/+12V Signale an der seriellen Schnittstelle des PCs zu den 5V/0V des AVRs kompatibel macht.&lt;br /&gt;
* C1 ist ein kleiner Elektrolyt-, Tantal- oder Keramikkondensator, wie er immer wieder zur Entkopplung der Versorgungsspannungen an digitalen ICs verwendet wird. &lt;br /&gt;
* Die vier Kondensatoren C2..C5 sind Elektrolyt-, Tantal- oder Keramikkondensatoren. Falls Elkos oder Tantals verwendet werden, auf die richtige Polung achten! Der exakte Wert ist hier relativ unkritisch, in der Praxis sollte alles von ca. 1µF bis 47µF mit einer Spannungsfestigkeit von 16V und höher funktionieren.&lt;br /&gt;
* X1 ist ein weiblicher 9-poliger SUB-D-Verbinder.&lt;br /&gt;
* Die Verbindung zwischen PC und Mikrocontroller erfolgt über ein 9-poliges Modem-Kabel (also ein &#039;&#039;Verlängerungskabel&#039;&#039;, &#039;&#039;&#039;kein [http://de.wikipedia.org/wiki/Nullmodem-Kabel Nullmodem]-Kabel!)&#039;&#039;&#039;, das an den seriellen Port des PCs angeschlossen wird. Bei einem Modem-Kabel sind die Pins 2 und 3 des einen Kabelendes mit den Pins 2 und 3 des anderen Kabelendes durchverbunden. Bei einem Nullmodem-Kabel sind die Leitungen gekreuzt, sodass Pin 2 von der einen Seite mit Pin 3 auf der anderen Seite verbunden ist und umgekehrt.&lt;br /&gt;
* Als Faustregel kann man annehmen: Befinden sich an den beiden Enden des Kabels die gleiche Art von Anschlüssen (Männchen = Stecker; Weibchen = Buchse), dann benötigt man ein gekreuztes, also ein Nullmodem-Kabel. Am PC-Anschluss selbst befindet sich ein Stecker, also ein Männchen, sodaß am Kabel auf dieser Seite eine Buchse (also ein Weibchen) sitzen muss. Da am AVR laut obigem Schaltbild eine Buchse verbaut wird, muss daher an diesem Ende des Kabels ein Stecker sitzen. Das Kabel hat daher an einem Ende einen Stecker und am anderen Ende eine Buchse und ist daher ein normales Modem-Kabel ( = nicht gekreuzt).&lt;br /&gt;
&lt;br /&gt;
[[Bild:USART_Kabel.gif|framed|center| Kabelbeschaltungen]]&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== UART konfigurieren ===&lt;br /&gt;
&lt;br /&gt;
Als erstes muss die gewünschte Baudrate im Register &#039;&#039;&#039;UBRR&#039;&#039;&#039; festgelegt werden. Der in dieses Register zu schreibende Wert errechnet sich nach der folgenden Formel: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\text{UBRR} = \frac {\text{Taktfrequenz (in Hz)}} { 16 \cdot \text{Baudrate} } - 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim AT90S4433 kann man den Wert direkt in das Register &#039;&#039;&#039;UBRR&#039;&#039;&#039; laden, beim ATmega8 gibt es für &#039;&#039;&#039;UBRR&#039;&#039;&#039; zwei Register: &#039;&#039;&#039;UBRRL&#039;&#039;&#039; (Low-Byte) und &#039;&#039;&#039;UBRRH&#039;&#039;&#039; (High-Byte). Bei Baudraten über etwa 3900 Bit/s (gilt nur bei Verwendung eines Takts von 16 MHz) steht in &#039;&#039;&#039;UBRRH&#039;&#039;&#039; 0, da der berechnete Wert kleiner als 256 ist und somit in &#039;&#039;&#039;UBRRL&#039;&#039;&#039; alleine passt. Beachtet werden muss, dass das Register &#039;&#039;&#039;UBRRH&#039;&#039;&#039; vor dem Register &#039;&#039;&#039;UBRRL&#039;&#039;&#039; beschrieben werden muss. Der Schreibzugriff auf &#039;&#039;&#039;UBRRL&#039;&#039;&#039; löst das Neusetzen des internen Taktteilers aus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#ff0000;&amp;quot;&amp;gt;&#039;&#039;&#039;WICHTIGER HINWEIS 1&#039;&#039;&#039;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es empfiehlt sich statt der oben genannten Formel, die Formel der Codebeispiele zu verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\text{UBRR} = \frac{\text{Taktfrequenz (in Hz)} + (\text{Baudrate} \cdot 8)}{(\text{Baudrate} \cdot 16)}-1 \quad \left( = \frac{ \text{Taktfrequenz (in Hz)} }{ 16 \cdot \text{Baudrate} } -0.5 \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Beispiel:&amp;lt;/b&amp;gt; Bei einem ATMega mit 16MHz und 115200 Baud ist der Wert laut Datenblatt UBBRL=8. Rechnet man mit der Formel UBRRL=(F_CPU / (UART_BAUDRATE* 16L) - 1) ergibt sich ein Wert von 7,680555 und im UBRRL Register steht somit eine &amp;lt;b&amp;gt;7 statt einer 8&amp;lt;/b&amp;gt;. Die Verwendung der Formel aus dem Codebeispiel ergibt 8.180555 und im UBRRL Register steht somit der richtige Wert - nämlich 8&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#ff0000;&amp;quot;&amp;gt;&#039;&#039;&#039;WICHTIGER HINWEIS 2&#039;&#039;&#039;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf Grund permanent wiederkehrender Nachfrage sei hier &#039;&#039;&#039;AUSDRÜCKLICH&#039;&#039;&#039; darauf hingewiesen, dass bei Verwendung des UART im asynchronen Modus dringend ein Quarz oder Quarzoszillator verwendet werden sollte! Der interne RC-Oszillator der AVRs ist recht ungenau! Damit kann es in Ausnahmefällen funktionieren, muss es aber nicht! Auch ist der interne Oszillator temperaturempfindlich. Damit hat man dann den schönen Effekt, dass eine UART-Schaltung die im Winter noch funktionierte, im Sommer den Dienst verweigert.&lt;br /&gt;
&lt;br /&gt;
Außerdem muss bei der Berechnung von &#039;&#039;&#039;UBRR&#039;&#039;&#039; geprüft werden, ob mit der verwendeten Taktfrequenz die gewünschte Baudrate mit einem Fehler von &amp;lt;1% generiert werden kann. Das Datenblatt bietet hier sowohl die Formel als auch Tabellen unter der Überschrift des U(S)ART an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \text{Fehler}_{\text{Baudrate}}[%] = \left( \frac{\text{UBRR}_{\text{gerundet}}+1}{\text{UBRR}_{\text{genau}}+1} -1 \right) \cdot 100&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[Baudratenquarz]]&lt;br /&gt;
&lt;br /&gt;
Wer es ganz einfach haben will, nimmt die folgenden Macros. Die rechnen sogar den Fehler aus und brechen die Assemblierung ggf. ab. Das ist dann praktisch idiotensicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wer dennoch den internen RC-Oszillator verwenden will, muss diesen kalibrieren. Näheres findet man dazu im Datenblatt, Stichwort Register OSCCAL.  &lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Um den Sendekanal des UART zu aktivieren, muss das Bit &#039;&#039;&#039;TXEN&#039;&#039;&#039; im UART Control Register &#039;&#039;&#039;UCSRB&#039;&#039;&#039; auf 1 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Danach kann das zu sendende Byte in das Register &#039;&#039;&#039;UDR&#039;&#039;&#039; eingeschrieben werden - vorher muss jedoch sichergestellt werden, dass das Register leer ist, die vorhergehende Übertragung also schon abgeschlossen wurde. Dazu wird getestet, ob das Bit &#039;&#039;&#039;UDRE&#039;&#039;&#039; (&amp;quot;UART Data Register Empty&amp;quot;) im Register &#039;&#039;&#039;UCSRA&#039;&#039;&#039; auf 1 ist.&lt;br /&gt;
&lt;br /&gt;
Genaueres über die UART-Register findet man im Datenblatt des Controllers.&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle sei noch folgendes angemerkt: Das &#039;&#039;&#039;UDRE&#039;&#039;&#039;-Bit sagt nichts darüber aus, ob der Controller immer noch damit beschäftigt ist, Daten zu senden. Da das Senderegister mehrfach gepuffert ist, wird &#039;&#039;&#039;UDRE&#039;&#039;&#039; bereits gesetzt, obwohl das letzte Zeichen den AVR noch nicht komplett verlassen hat. Dies kann insbesondere bei der Verwendung von Sleep-Modes ein Problem werden, wenn der Controller schlafen gelegt wird, bevor das letzte Zeichen versendet wurde, da dies gezwungenermassen zu einem Frame-Error beim Empfänger führen wird. Um sicher zu gehen, dass der UART nicht mehr beschäftigt ist, kann das Bit &#039;&#039;&#039;TXC&#039;&#039;&#039; (&amp;quot;UART Transmit complete&amp;quot;) getestet werden. Dieses wird jedoch wirklich erst nach dem Senden eines Zeichens gesetzt, beinhaltet also auch nach dem Systemstart eine 0, obwohl der Controller nichts sendet.&lt;br /&gt;
&lt;br /&gt;
Der ATmega8 bietet noch viele weitere Optionen zur Konfiguration des UARTs, aber für die Datenübertragung zum PC sind im Normalfall keine anderen Einstellungen notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Senden von Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Das Beispielprogramm überträgt die Zeichenkette &amp;quot;Test!&amp;quot; in einer Endlosschleife an den PC.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn man das nachfolgende Programm laufen lässt und Hyperterminal startet, scheint es problemlos zu funktionieren. Wenn man aber das RS232 Kabel zwischenzeitlich abzieht und wieder ansteckt wird es oft passieren, dass nur noch wirre Zeichen auf dem PC erscheinen. Das liegt daran, dass der PC aus einem ununterbrochen Zeichenstrom nicht den Anfang eines Zeichens erkennen kann. Darum muss in solchen Fällen periodisch eine kleine Pause von der Länge mindestens eines Zeichens eingelegt werden, damit der PC sich wieder synchronisieren kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die folgenden Beispiele sind für den ATmega8 geschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp    = r16                              ; Register für kleinere Arbeiten&lt;br /&gt;
.def zeichen = r17                              ; in diesem Register wird das Zeichen an die&lt;br /&gt;
                                                ; Ausgabefunktion übergeben&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))      ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB,TXEN                  ; TX aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    ldi     zeichen, &#039;T&#039;&lt;br /&gt;
    rcall   serout                      ; Unterprogramm aufrufen&lt;br /&gt;
    ldi     zeichen, &#039;e&#039;&lt;br /&gt;
    rcall   serout                      ; Unterprogramm aufrufen&lt;br /&gt;
    ldi     zeichen, &#039;s&#039;&lt;br /&gt;
    rcall   serout                      ; ...&lt;br /&gt;
    ldi     zeichen, &#039;t&#039;&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    ldi     zeichen, &#039;!&#039;&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    ldi     zeichen, 10&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    ldi     zeichen, 13&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    rcall   sync                        &lt;br /&gt;
    rjmp    loop&lt;br /&gt;
&lt;br /&gt;
serout:&lt;br /&gt;
    sbis    UCSRA,UDRE                  ; Warten bis UDR für das nächste&lt;br /&gt;
                                        ; Byte bereit ist&lt;br /&gt;
    rjmp    serout&lt;br /&gt;
    out     UDR, zeichen&lt;br /&gt;
    ret                                 ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
; kleine Pause zum Synchronisieren des Empfängers, falls zwischenzeitlich&lt;br /&gt;
; das Kabel getrennt wurde&lt;br /&gt;
                                    &lt;br /&gt;
sync:&lt;br /&gt;
    ldi     r16,0&lt;br /&gt;
sync_1:&lt;br /&gt;
    ldi     r17,0&lt;br /&gt;
sync_loop:&lt;br /&gt;
    dec     r17&lt;br /&gt;
    brne    sync_loop&lt;br /&gt;
    dec     r16&lt;br /&gt;
    brne    sync_1  &lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; argin:1em&amp;quot;&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/images/hyperterminal.gif&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Der Befehl &#039;&#039;&#039;rcall serout&#039;&#039;&#039; ruft ein kleines Unterprogramm auf, das zuerst wartet bis das Datenregister &#039;&#039;&#039;UDR&#039;&#039;&#039; von der vorhergehenden Übertragung frei ist, und anschließend das in zeichen (=r17) gespeicherte Byte an &#039;&#039;&#039;UDR&#039;&#039;&#039; ausgibt. &lt;br /&gt;
&lt;br /&gt;
Bevor &amp;lt;i&amp;gt;serout&amp;lt;/i&amp;gt; aufgerufen wird, wird zeichen jedesmal mit dem ASCII-Code des zu übertragenden Zeichens geladen (so wie in Teil 4 bei der LCD-Ansteuerung). Der Assembler wandelt Zeichen in einfachen Anführungsstrichen automatisch in den entsprechenden ASCII-Wert um. Nach dem Wort &amp;quot;Test!&amp;quot; werden noch die Codes 10 (New Line) und 13 (Carriage Return) gesendet, um dem Terminalprogramm mitzuteilen, dass eine neue Zeile beginnt.&lt;br /&gt;
&lt;br /&gt;
Eine Übersicht aller ASCII-Codes gibt es auf [http://www.asciitable.com/ www.asciitable.com].&lt;br /&gt;
&lt;br /&gt;
Die Berechnung der Baudrate wird übrigens nicht im Controller während der Programmausführung durchgeführt, sondern schon beim Assemblieren, wie man beim Betrachten der Listingdatei feststellen kann. &lt;br /&gt;
&lt;br /&gt;
Zum Empfang muss auf dem PC ein Terminal-Programm wie z.&amp;amp;nbsp;B. HyperTerminal gestartet werden. Der folgende Screenshot zeigt, welche Einstellungen im Programm vorgenommen werden müssen: &lt;br /&gt;
&lt;br /&gt;
Linux-Benutzer können das entsprechende Device (z.&amp;amp;nbsp;B. /dev/ttyS0) mit stty konfigurieren und mit cat die empfangenen Daten anzeigen oder ein Terminalprogramm wie minicom nutzen.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann unter Windows und Linux [http://www.der-hammer.info/terminal/ HTerm] genutzt werden. (Freeware)&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
=== Senden von Zeichenketten ===&lt;br /&gt;
&lt;br /&gt;
Eine bequemere Methode um längere Zeichenketten (Strings) zu übertragen ist hier zu sehen. Dabei werden die Zeichenketten im Flash gespeichert. Als Abschluss des Strings wird der Wert 0x00 genutzt, so wie auch in der Programmiersprache C. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp    = r16                              ; Register für kleinere Arbeiten&lt;br /&gt;
.def zeichen = r17                              ; in diesem Register wird das Zeichen an die&lt;br /&gt;
                                                ; Ausgabefunktion übergeben&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
; hier geht unser Programm los&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB,TXEN                      ; TX aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    ldi     zl,low(my_string*2);            ; Z Pointer laden&lt;br /&gt;
    ldi     zh,high(my_string*2);&lt;br /&gt;
    rcall   serout_string&lt;br /&gt;
    rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; Ausgabe eines Strings aus dem Flash&lt;br /&gt;
&lt;br /&gt;
serout_string:&lt;br /&gt;
    lpm                             ; nächstes Byte aus dem Flash laden&lt;br /&gt;
    and     r0,r0                   ; = Null? &lt;br /&gt;
    breq    serout_string_ende      ; wenn ja, -&amp;gt; Ende&lt;br /&gt;
serout_string_wait:&lt;br /&gt;
    sbis    UCSRA,UDRE              ; Warten bis UDR für das nächste&lt;br /&gt;
                                    ; Byte bereit ist&lt;br /&gt;
    rjmp    serout_string_wait&lt;br /&gt;
    out     UDR, r0&lt;br /&gt;
    adiw    zl:zh,1                 ; Zeiger erhöhen&lt;br /&gt;
    rjmp    serout_string           ; nächstes Zeichen bearbeiten&lt;br /&gt;
serout_string_ende:&lt;br /&gt;
    ret                             ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
; Hier wird jetzt der String definiert und im Flash gespeichert&lt;br /&gt;
&lt;br /&gt;
my_string:  .db &amp;quot;Test!&amp;quot;,10,13,0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfangen von Zeichen per Polling===&lt;br /&gt;
&lt;br /&gt;
Der AVR kann nicht nur Daten seriell senden, sondern auch empfangen. Dazu muss man, nachdem die Baudrate wie oben beschrieben eingestellt wurde, das Bit &#039;&#039;&#039;RXEN&#039;&#039;&#039; setzen. &lt;br /&gt;
&lt;br /&gt;
Sobald der UART ein Byte über die serielle Verbindung empfangen hat, wird das Bit &#039;&#039;&#039;RXC&#039;&#039;&#039; im Register &#039;&#039;&#039;UCSRA&#039;&#039;&#039; gesetzt, um anzuzeigen, dass ein Byte im Register &#039;&#039;&#039;UDR&#039;&#039;&#039; zur Weiterverarbeitung bereitsteht. Sobald es aus &#039;&#039;&#039;UDR&#039;&#039;&#039; gelesen wurde, wird &#039;&#039;&#039;RXC&#039;&#039;&#039; automatisch wieder gelöscht, bis das nächste Byte angekommen ist. &lt;br /&gt;
&lt;br /&gt;
Das erste einfache Testprogramm soll das empfangene Byte auf den an Port D angeschlossenen LEDs ausgeben. Dabei sollte man daran denken, dass PD0 (RXD) bereits für die Datenübertragung zuständig ist, so dass das entsprechende Bit im Register PORTD keine Funktion hat und damit auch nicht für die Datenanzeige verwendet werden kann. &lt;br /&gt;
&lt;br /&gt;
Nachdem der UART konfiguriert ist, wartet das Programm einfach in der Hauptschleife darauf, dass ein Byte über den UART ankommt (z.&amp;amp;nbsp;B. indem man im Terminalprogramm ein Zeichen eingibt), also &#039;&#039;&#039;RXC&#039;&#039;&#039; gesetzt wird. Sobald das passiert, wird das Register &#039;&#039;&#039;UDR&#039;&#039;&#039;, in dem die empfangenen Daten stehen, nach &amp;lt;i&amp;gt;temp&amp;lt;/i&amp;gt; eingelesen und an den Port D ausgegeben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Port D = Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, 0xFF&lt;br /&gt;
    out     DDRD, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren&lt;br /&gt;
&lt;br /&gt;
receive_loop:&lt;br /&gt;
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist&lt;br /&gt;
   rjmp     receive_loop&lt;br /&gt;
   in       temp, UDR                       ; empfangenes Byte nach temp kopieren&lt;br /&gt;
   out      PORTD, temp                     ; und an Port D ausgeben.&lt;br /&gt;
   rjmp     receive_loop                    ; zurück zum Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfangen von Zeichen per Interrupt ===&lt;br /&gt;
&lt;br /&gt;
Dieses Programm lässt sich allerdings noch verfeinern. Statt in der Hauptschleife auf die Daten zu warten, kann man auch veranlassen dass ein Interrupt ausgelöst wird, sobald ein Byte angekommen ist. Das sieht in der einfachsten Form so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
.org 0x00&lt;br /&gt;
        rjmp main&lt;br /&gt;
&lt;br /&gt;
.org URXCaddr                                   ; Interruptvektor für UART-Empfang&lt;br /&gt;
        rjmp int_rxc&lt;br /&gt;
&lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Port D = Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, 0xFF&lt;br /&gt;
    out     DDRD, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, RXCIE                    ; Interrupt bei Empfang&lt;br /&gt;
    sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren&lt;br /&gt;
    &lt;br /&gt;
    sei                                     ; Interrupts global aktivieren&lt;br /&gt;
    &lt;br /&gt;
loop:&lt;br /&gt;
    rjmp loop                               ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
; Interruptroutine: wird ausgeführt sobald ein Byte über das UART empfangen wurde&lt;br /&gt;
&lt;br /&gt;
int_rxc:&lt;br /&gt;
    push    temp                            ; temp auf dem Stack sichern&lt;br /&gt;
    in      temp, UDR                       ; empfangenes Byte lesen,&lt;br /&gt;
                                            ; dadurch wird auch der Interrupt gelöscht&lt;br /&gt;
    out     PORTD, temp                     ; Daten ausgeben&lt;br /&gt;
    pop     temp                            ; temp wiederherstellen&lt;br /&gt;
    reti                                    ; Interrupt beenden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode hat den großen Vorteil, dass das Hauptprogramm (hier nur eine leere Endlosschleife) andere Dinge erledigen kann, während der Controller Daten empfängt. Auf diese Weise kann man mehrere Aktionen quasi gleichzeitig ausführen, da das Hauptprogramm nur kurz unterbrochen wird, um die empfangenen Daten zu verarbeiten. &lt;br /&gt;
&lt;br /&gt;
Probleme können allerdings auftreten, wenn in der Interruptroutine die gleichen Register verwendet werden wie im Hauptprogramm, da dieses ja an beliebigen Stellen durch den Interrupt unterbrochen werden kann. Damit sich aus der Sicht der Hauptschleife durch den Interruptaufruf nichts ändert, müssen alle in der Interruptroutine geänderten Register am Anfang der Routine gesichert und am Ende wiederhergestellt werden. Das gilt vor allem für das CPU-Statusregister (&#039;&#039;&#039;SREG&#039;&#039;&#039;)! Sobald ein einziger Befehl im Interrupt ein einziges Bit im SREG beeinflusst, muss das SREG gesichert werden. Das ist praktisch fast immer der Fall, nur in dem ganz einfachen Beispiel oben ist es überflüssig, weil die verwendeten Befehle das SREG nicht beeinflussen. In diesem Zusammenhang wird der [[Stack]] wieder interessant. Um die Register zu sichern, kann man sie mit &#039;&#039;&#039;push&#039;&#039;&#039; oben auf den Stapel legen und am Ende wieder in der umgekehrten Reihenfolge(!) mit &#039;&#039;&#039;pop&#039;&#039;&#039; vom Stapel herunternehmen.&lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispielprogramm werden die empfangenen Daten nun nicht mehr komplett angezeigt. Stattdessen kann man durch Eingabe einer 1 oder einer 0 im Terminalprogramm eine LED (an PB0) an- oder ausschalten. Dazu wird das empfangene Byte in der Interruptroutine mit den entsprechenden ASCII-Codes der Zeichen 1 und 0 (siehe [http://www.asciitable.com/ www.asciitable.com]) verglichen.&lt;br /&gt;
&lt;br /&gt;
Für den [[AVR-Tutorial:_Vergleiche|Vergleich]] eines Registers mit einer Konstanten gibt es den Befehl &#039;&#039;&#039;cpi register, konstante&#039;&#039;&#039;. Das Ergebnis dieses Vergleichs kann man mit den Befehlen &#039;&#039;&#039;breq label&#039;&#039;&#039; (springe zu label, wenn gleich) und &#039;&#039;&#039;brne label&#039;&#039;&#039; (springe zu label, wenn ungleich) auswerten. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
.org 0x00&lt;br /&gt;
        rjmp main&lt;br /&gt;
&lt;br /&gt;
.org URXCaddr&lt;br /&gt;
        rjmp int_rxc&lt;br /&gt;
&lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
main:&lt;br /&gt;
    &lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Port B = Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, 0xFF&lt;br /&gt;
    out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, RXCIE                ; Interrupt bei Empfang&lt;br /&gt;
    sbi     UCSRB, RXEN                 ; RX (Empfang) aktivieren&lt;br /&gt;
    &lt;br /&gt;
    sei                                 ; Interrupts global aktivieren&lt;br /&gt;
    &lt;br /&gt;
loop:&lt;br /&gt;
    rjmp loop                           ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
; Interruptroutine: wird ausgeführt sobald ein Byte über das UART empfangen wurde&lt;br /&gt;
&lt;br /&gt;
int_rxc:&lt;br /&gt;
    push    temp                        ; temp auf dem Stack sichern&lt;br /&gt;
    in      temp, sreg                  ; SREG sichern&lt;br /&gt;
    push    temp&lt;br /&gt;
    &lt;br /&gt;
    in      temp, UDR                   ; UART Daten lesen&lt;br /&gt;
    cpi     temp, &#039;1&#039;                   ; empfangenes Byte mit &#039;1&#039; vergleichen&lt;br /&gt;
    brne    int_rxc_1                   ; wenn nicht gleich, dann zu int_rxc_1&lt;br /&gt;
    cbi     PORTB, 0                    ; LED einschalten, low aktiv&lt;br /&gt;
    rjmp    int_rxc_2                   ; Zu int_rxc_2 springen&lt;br /&gt;
int_rxc_1:&lt;br /&gt;
    cpi     temp, &#039;0&#039;                   ; empfangenes Byte mit &#039;0&#039; vergleichen&lt;br /&gt;
    brne    int_rxc_2                   ; wenn nicht gleich, dann zu int_rxc_2&lt;br /&gt;
    sbi     PORTB, 0                    ; LED ausschalten, low aktiv&lt;br /&gt;
int_rxc_2:&lt;br /&gt;
&lt;br /&gt;
    pop     temp&lt;br /&gt;
    out     sreg, temp                  ; SREG wiederherstellen&lt;br /&gt;
    pop     temp                        ; temp wiederherstellen&lt;br /&gt;
    reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Handshake ==&lt;br /&gt;
Werden Daten schnell über eine serielle Leitung an ein langsames Gerät übertragen, dann kann es passieren, dass die Situation eintritt, dass das empfangende Gerät nicht mehr mitkommt. Das kann z.B. dadurch passieren, dass das empfangende Gerät selbst etwas Zeit für die Bearbeitung der Daten benötigt. Man denke z.B. an die Situation, dass an ein Modem Daten übertragen werden. Das Modem muss diese Daten bearbeiten und unter Umständen über eine langsame Telefonleitung absetzen. Überträgt der AVR seine Daten mit voller Geschwindigkeit an das Modem, so wird auch dem besten Modem irgendwann der interne Speicher ausgehen, in dem es die Daten zwischenspeichern kann.&lt;br /&gt;
&lt;br /&gt;
Was benötigt wird, ist also eine Möglichkeit, wie die Gegenstelle dem Sender signalisieren kann: &amp;quot;Bitte jetzt nichts senden, ich bin beschäftigt!&amp;quot;. Die einfachste Form eines derartigen Protokolls, nennt sich Handshake. Es gibt bei RS232 2 Arten, wie dieses Handshake implementiert werden kann: &amp;lt;b&amp;gt;Software-Handshake&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;Hardware-Handshake&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Hardware Handshake ===&lt;br /&gt;
Hardware Handshake benutzt die beiden Steuerleitungen &amp;lt;b&amp;gt;RTS - Request to Send&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;CTS - Clear to Send&amp;lt;/b&amp;gt; um die Flusskontrolle durchzuführen.&lt;br /&gt;
&lt;br /&gt;
Die etwas seltsam anmutenden Namen haben historische Ursache. Ursprünglich war RS232 dazu gedacht ein Modem (ein sog. Data Carrier Equipment oder DCE) an einen Endpunkt (DTE oder Data Terminal Equipment) anzuschliessen. Wenn das DTE Daten senden wollte, aktivierte es die Leitung RTS, es fragte praktisch beim DCE an: &amp;quot;Darf ich senden? (engl. Request sending)&amp;quot;. Wenn das DCE bereit war, dann aktivierte es seinerseits die CTS Leitung und signalisierte damit &amp;quot;Alles ok. Daten marsch! (engl. Clear to send)&amp;quot;. Solange das DCE nicht bereit war, Daten entgegenzunehmen, musste das DTE warten, bis es vom DCE die Freigabe zum Senden bekam.&lt;br /&gt;
&lt;br /&gt;
* Für das DTE gilt: RTS ist ein Ausgang, CTS ist ein Eingang.&lt;br /&gt;
* Für das DCE gilt: RTS ist ein Eingang, CTS ist ein Ausgang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
[[Bild:RS232_orig.png]]&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das war die ursprüngliche Idee. Heutzutage ist es aber normal, dass 2 DTE miteinander über eine RS232 Verbindung gekoppelt werden. Wird in so einem Fall Hardware Handshake benutzt, so muss jedes DTE seiner Gegenstelle eine korrekte Bedienung der RTS/CTS Leitung vortäuschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
[[Bild:RS232_dte.png]]&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Teil, dass CTS nur dann bedient wird, wenn über RTS die Anfrage nach der Sendefreigabe erfolgt entfällt dabei. Jeder Gesprächspartner überprüft ganz einfach vor dem Sendevorgang den Zustand der CTS Leitung der Gegenstelle, während der eigene RTS Ausgang zur Signalisierung der Empfangsbereitschaft für die Gegenstelle dient. Dies ist auch der Grund warum bei einem Null-Modem-Kabel nicht nur die RX/TX Leitungen, sondern auch die RTS/CTS Leitungen gekreuzt werden müssen.&lt;br /&gt;
&lt;br /&gt;
Möchte man obige Schaltung um eine Hardware-Flusskontrolle erweitern, so bietet es sich an, die beiden noch freien Kanäle des MAX232 dafür zu verwenden. Die Schaltung sieht dann wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:640px-AVR-RS232_RTS.png|framed|center| UART/MAX232 Beschaltung für RTS/CTS am Beispiel eines Mega16. Achtung: Pinbelegung an den Mega8 anpassen]]&lt;br /&gt;
&lt;br /&gt;
Am Mega8 stehen dann die Signale RTS bzw. CTS an den Pins PD4 bzw. PD5 zur Verfügung. An PD5 kann abgefragt werden, ob die Gegenstelle zum Empfang von Daten bereit ist, während der Mega8 über PD4 signalisieren kann, dass er im Moment keine Daten über die serielle Schnittstelle empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Bedenken sollte man dabei allerdings, dass nach der Rücknahme der Empfangsbereitschaft es je nach Gegenstelle dazu kommen kann, dass noch ein paar Zeichen über die UART eintreffen. Wird zb der Zustand der RTS Leitung vom Füllgrad eines Puffers abhängig gemacht, dann sollte man RTS nicht erst dann abschalten, wenn der Buffer komplett gefüllt ist, sondern schon ein paar Zeichen früher. Ursache könnte zb sein, dass die Gegenstelle über eine Hardware UART verfügt, die einen internen Buffer verfügt. Hat die Gegenstelle erst mal diesen Hardware Buffer gefüllt, dann gibt es oft keine Möglichkeit mehr für das dortige Programm diese UART-Hardware zu stoppen - die Zeichen die in die UART übertragen wurden, werden auf jeden Fall von der Hardware ausgegeben.&lt;br /&gt;
&lt;br /&gt;
=== Software Handshake ===&lt;br /&gt;
Software Handshake benutzt die Datenleitung selbst, um die Flußkontrolle von Sender/Empfänger zu erreichen. Dazu wurden im ASCII Code 2 spezielle &#039;Zeichen&#039; vorgesehen: XON (mit dem Code 0x11) und XOFF (mit dem Code 0x13).&lt;br /&gt;
&lt;br /&gt;
Bemerkt ein Empfänger, dass er in Kürze keine Daten mehr vom Sender aufnehmen kann, dann sendet er seinerseits ein XOFF, woraufhin der Sender das Senden der Daten unterbricht. Ist der Empfänger wieder aufnahmebereit, so gibt er die Übertragung durch das Senden eines XON wieder frei.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil des Software-Handshaking besteht also in mehreren Punkten&lt;br /&gt;
* zum einen können nicht mehr alle Datenbytes übertragen werden, da ja die Bytes 0x11 und 0x13 eine spezielle Bedeutung haben. Möchte man Bytes binär übertragen, muss man daher spezielle Vorkehrungen treffen, damit diese Datenbytes nicht durch das Software-Handshaking fehlinterpretiert werden.&lt;br /&gt;
* zum anderen muss jeder Sender während er sendet auch gleichzeitig einen möglichen Empfang von Daten überwachen. Die Gegenstelle könnte ja mittels XOFF eine kurzfristige Unterbrechung der Sendung anfordern. Auch muss jeder Sender exakt darüber Buch führen, ob die Leitung zur Zeit im Status XOFF liegt und ob daher Übertragungen überhaupt möglich sind.&lt;br /&gt;
* das Senden von XOFF muss rechtzeitig erfolgen. Denn meistens benötigt die Gegenstelle etwas Zeit um das Senden einzustellen. Es kann durchaus sein, dass nach einem XOFF noch ein paar Zeichen von der Gegenstelle eintreffen&lt;br /&gt;
* es besteht die Gefahr eines Deadlocks, indem sich beide Seiten gegenseitig mit einem XOFF blockieren, aus dem sie nicht mehr herauskommen.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://www.wormfood.net/avrbaudcalc.php WormFood&#039;s AVR Baud Rate Calculator] online.&lt;br /&gt;
* [http://www.gjlay.de/helferlein/avr-uart-rechner.html Online Baudraten-Rechner für ATmega AVRs] (JavaScript)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Mehrfachverzweigung|&lt;br /&gt;
zurücklink=AVR-Tutorial: Mehrfachverzweigung|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Speicher|&lt;br /&gt;
vorlink=AVR-Tutorial: Speicher}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|UART]]&lt;br /&gt;
[[Category:UART und RS232]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=89481</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=89481"/>
		<updated>2015-08-05T18:46:31Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Was hat es mit volatile auf sich */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Library [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Wertebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit einen Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
Präprozessordefinitionen besitzen keine eigene Datentypen, da sie eine reine Textersetzungen durchführen. Deren Inhalte können aber ggf. zu Werten aufgelöst werden, die datentypbehaftet sind.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File - wie geht das =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen VarExtA bzw. VarExtB, die zu den jewiligen Funktionen A bzw. B gehören. Für dieses System soll eine Aufteilung erfolgen, so dass functionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll. Bei dem Makro A_DEFAULT kann es unterschiedliche Ansichten geben. Handelt es sich um einen Wert, den ein Aufrufer kennen soll, dann wird man das #define ins Header File verschieben (und aus dem C-File rausnehmen). Sieht man dieses #define aber als &#039;Privatsache&#039; des C-Files, dann bleibt der #define dort wo er jetzt ist.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Einfach immer alle Variablen generell volatile zu machen, ist dann auch über das Ziel hinausgeschossen. Denn damit legt man in letzter Konsequenz eine wichtige Möglichkeit für Optimierungen durch den Compiler lahm.&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=89420</id>
		<title>AVR-Tutorial: Stack</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=89420"/>
		<updated>2015-07-28T12:49:15Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Motivation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Motivation ==&lt;br /&gt;
bisher war es so, dass wenn die Programmausführung an einer anderen Stelle fortgesetzt werden soll, als sich durch die Abfolge der Befehle ergibt, mittels &amp;lt;b&amp;gt;rjmp&amp;lt;/b&amp;gt; an diese andere Stelle gesprungen wurde.&lt;br /&gt;
Das ist aber oft nicht ausreichend. Oft möchte man den Fall haben, dass man aus der normalen Befehlsreihenfolge heraus eine andere Sequenz von Befehlen ausgeführt wird und wenn diese abgearbeitet ist, genau an die Aufrufstelle zurückgesprungen wird. Da diese eingeschobene Sequenz an vielen Stellen aufrufbar sein soll, gelingt es daher auch nicht, mittels eines &amp;lt;b&amp;gt;rjmp&amp;lt;/b&amp;gt; wieder zur aufrufenden Stelle zurück zu kommen, denn dann müsste ja dieser Rücksprung je nachdem von wo der Hinsprung gekommen ist entsprechend modifiziert werden.&lt;br /&gt;
&lt;br /&gt;
Die Lösung dieses Dilemmas besteht in einem eigenen Befehl &amp;lt;b&amp;gt;rcall&amp;lt;/b&amp;gt;. Ein &amp;lt;b&amp;gt;rcall&amp;lt;/b&amp;gt; macht prinzipiell auch den Sprung zu einem Ziel, legt aber gleichzeitig auch  noch die Adresse von wo der Sprung erfolgt ist in einem speziellen Speicherbereich ab, so dass sein &#039;Gegenspieler&#039;, der Befehl &amp;lt;b&amp;gt;ret&amp;lt;/b&amp;gt; (wie Return) anhand dieser abgelegten Information wieder genau zu dieser Aufrufstelle zurückspringen kann. Diesen speziellen Speicherbereich nennt man den &amp;quot;[[Stack]]&amp;quot;. Stack bedeutet übersetzt soviel wie Stapel. Damit ist ein Speicher nach dem LIFO-Prinzip (&amp;quot;last in first out&amp;quot;) gemeint. Das bedeutet, dass das zuletzt auf den Stapel gelegte Element auch zuerst wieder heruntergenommen wird. Es ist nicht möglich, Elemente irgendwo in der Mitte des Stapels herauszuziehen oder hineinzuschieben. Ein Stack (oder Stapel) funktioniert wie ein Stapel Teller. Das Teller, welches zuletzt auf den Stapel gelegt wird, ist auch das erste, welches wieder vom Stapel heruntergenommen wird. Und genau das wird in diesem Fall ja auch benötigt: jeder &amp;lt;b&amp;gt;rcall&amp;lt;/b&amp;gt; legt seine Rüclsprungadresse auf den Stack, so dass alle nachfolgenden &amp;lt;b&amp;gt;ret&amp;lt;/b&amp;gt; jeweils in umgekehrter Reihenfolge wieder die richtigen Rücksprungadressen anspringen.&lt;br /&gt;
&lt;br /&gt;
Bei allen aktuellen AVR-Controllern wird der Stack im [[Speicher#RAM|RAM]] angelegt. Der Stack wächst dabei von oben nach unten: Am Anfang wird der Stackpointer (Adresse der aktuellen Stapelposition) auf das Ende des RAMs gesetzt. Wird nun ein Element hinzugefügt, wird dieses an der momentanen Stackpointerposition abgespeichert und der Stackpointer um 1 erniedrigt. Soll ein Element vom Stack heruntergenommen werden, wird zuerst der Stackpointer um 1 erhöht und dann das Byte von der vom Stackpointer angezeigten Position gelesen.&lt;br /&gt;
&lt;br /&gt;
== Aufruf von Unterprogrammen ==&lt;br /&gt;
Dem Prozessor dient der Stack hauptsächlich dazu, Rücksprungadressen beim Aufruf von Unterprogrammen zu speichern, damit er später noch weiß, an welche Stelle zurückgekehrt werden muss, wenn das Unterprogramm mit &#039;&#039;&#039;ret&#039;&#039;&#039; oder die Interruptroutine mit &#039;&#039;&#039;reti&#039;&#039;&#039; beendet wird. &lt;br /&gt;
&lt;br /&gt;
Das folgende Beispielprogramm (AT90S4433) zeigt, wie der Stack dabei beeinflusst wird: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack.asm Download stack.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;     ; bzw. 2333def.inc&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, RAMEND  ; Stackpointer initialisieren&lt;br /&gt;
         out SP, temp&lt;br /&gt;
&lt;br /&gt;
         rcall sub1        ; sub1 aufrufen&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
                           ; hier könnten ein paar Befehle stehen&lt;br /&gt;
         rcall sub2        ; sub2 aufrufen&lt;br /&gt;
                           ; hier könnten auch ein paar Befehle stehen&lt;br /&gt;
         ret               ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
sub2:&lt;br /&gt;
                           ; hier stehen normalerweise die Befehle,&lt;br /&gt;
                           ; die in sub2 ausgeführt werden sollen&lt;br /&gt;
         ret               ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;.def temp = r16&#039;&#039;&#039; ist eine Assemblerdirektive. Diese sagt dem Assembler, dass er überall, wo er &amp;quot;temp&amp;quot; findet, stattdessen &amp;quot;r16&amp;quot; einsetzen soll. Das ist oft praktisch, damit man nicht mit den Registernamen durcheinander kommt. Eine Übersicht über die Assemblerdirektiven findet man [http://www.avr-asm-tutorial.net/avr_de/beginner/diraus.html hier]. &lt;br /&gt;
&lt;br /&gt;
Bei Controllern, die mehr als 256 Byte RAM besitzen (z.&amp;amp;nbsp;B. ATmega8), passt die Adresse nicht mehr in ein Byte. Deswegen gibt es bei diesen Controllern das Stack-Pointer-Register aufgeteilt in &#039;&#039;&#039;SPL&#039;&#039;&#039; (Low) und &#039;&#039;&#039;SPH&#039;&#039;&#039; (High), in denen das Low- und das High-Byte der Adresse gespeichert wird. Damit es funktioniert, muss das Programm dann folgendermaßen geändert werden: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack-bigmem.asm Download stack-bigmem.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, HIGH(RAMEND)            ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
         ldi temp, LOW(RAMEND)             ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
&lt;br /&gt;
         rcall sub1                        ; sub1 aufrufen&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
                                           ; hier könnten ein paar Befehle stehen&lt;br /&gt;
         rcall sub2                        ; sub2 aufrufen&lt;br /&gt;
                                           ; hier könnten auch Befehle stehen&lt;br /&gt;
         ret                               ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
sub2:&lt;br /&gt;
                                           ; hier stehen normalerweise die Befehle,&lt;br /&gt;
                                           ; die in sub2 ausgeführt werden sollen&lt;br /&gt;
         ret                               ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich ist es unsinnig, dieses Programm in einen Controller zu programmieren. Stattdessen sollte man es mal mit dem AVR-Studio simulieren, um die Funktion des Stacks zu verstehen. &lt;br /&gt;
&lt;br /&gt;
Als erstes wird mit &#039;&#039;&#039;Project/New&#039;&#039;&#039; ein neues Projekt erstellt, zu dem man dann mit &#039;&#039;&#039;Project/Add File&#039;&#039;&#039; eine Datei mit dem oben gezeigten Programm (stack.asm) hinzufügt. Nachdem man unter &#039;&#039;&#039;Project/Project Settings&#039;&#039;&#039; das &#039;&#039;&#039;Object Format for AVR-Studio&#039;&#039;&#039; ausgewählt hat, kann man das Programm mit Strg+F7 assemblieren und den Debug-Modus starten. &lt;br /&gt;
&lt;br /&gt;
Danach sollte man im Menu &#039;&#039;&#039;View&#039;&#039;&#039; die Fenster &#039;&#039;&#039;Processor&#039;&#039;&#039; und &#039;&#039;&#039;Memory&#039;&#039;&#039; öffnen und im Memory-Fenster &#039;&#039;&#039;Data&#039;&#039;&#039; auswählen. &lt;br /&gt;
&lt;br /&gt;
Das Fenster &#039;&#039;&#039;Processor&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;Program Counter&#039;&#039;: Adresse im Programmspeicher (FLASH), die gerade abgearbeitet wird&lt;br /&gt;
* &#039;&#039;Stack Pointer&#039;&#039;: Adresse im Datenspeicher (RAM), auf die der Stackpointer gerade zeigt&lt;br /&gt;
* &#039;&#039;Cycle Counter&#039;&#039;: Anzahl der Taktzyklen seit Beginn der Simulation&lt;br /&gt;
* &#039;&#039;Time Elapsed&#039;&#039;: Zeit, die seit dem Beginn der Simulation vergangen ist &lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Memory&#039;&#039;&#039; wird der Inhalt des RAMs angezeigt. &lt;br /&gt;
&lt;br /&gt;
Sind alle 3 Fenster gut auf einmal sichtbar, kann man anfangen, das Programm (in diesem Fall &amp;quot;stack.asm&amp;quot;) mit der Taste F11 langsam Befehl für Befehl zu simulieren. &lt;br /&gt;
&lt;br /&gt;
Wenn der gelbe Pfeil in der Zeile &#039;&#039;&#039;out SPL, temp&#039;&#039;&#039; vorbeikommt, kann man im Prozessor-Fenster sehen, wie der Stackpointer auf 0xDF (&#039;&#039;ATmega8&#039;&#039;: 0x45F) gesetzt wird. Wie man im Memory-Fenster sieht, ist das die letzte RAM-Adresse. &lt;br /&gt;
&lt;br /&gt;
Wenn der Pfeil auf dem Befehl &#039;&#039;&#039;rcall sub1&#039;&#039;&#039; steht, sollte man sich den Program Counter anschauen: Er steht auf 0x02 (&#039;&#039;ATmega8&#039;&#039;: 0x04). &lt;br /&gt;
&lt;br /&gt;
Drückt man jetzt nochmal auf F11, springt der Pfeil zum Unterprogramm sub1. Im RAM erscheint an der Stelle, auf die der Stackpointer vorher zeigte, die Zahl 0x03 (&#039;&#039;ATmega8&#039;&#039;: 0x05). Das ist die Adresse im ROM, an der das Hauptprogramm nach dem Abarbeiten des Unterprogramms fortgesetzt wird. Doch warum wurde der Stackpointer um 2 verkleinert? Das liegt daran, dass eine Programmspeicheradresse bis zu 2 Byte breit sein kann, und somit auch 2 Byte auf dem Stack benötigt werden, um die Adresse zu speichern. &lt;br /&gt;
&lt;br /&gt;
Das gleiche passiert beim Aufruf von sub2. &lt;br /&gt;
&lt;br /&gt;
Zur Rückkehr aus dem mit rcall aufgerufenen Unterprogramm gibt es den Befehl &#039;&#039;&#039;ret&#039;&#039;&#039;. Dieser Befehl sorgt dafür, dass der Stackpointer wieder um 2 erhöht wird und die dabei eingelesene Adresse in den &amp;quot;Program Counter&amp;quot; kopiert wird, so dass das Programm dort fortgesetzt wird. &lt;br /&gt;
&lt;br /&gt;
Apropos Program Counter: Wer sehen will, wie so ein Programm aussieht, wenn es assembliert ist, sollte mal die Datei mit der Endung &amp;quot;.lst&amp;quot; im Projektverzeichnis öffnen. Die Datei sollte ungefähr so aussehen: &lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/images/listfile.gif&lt;br /&gt;
&lt;br /&gt;
Im blau umrahmten Bereich steht die Adresse des Befehls im Programmspeicher. Das ist auch die Zahl, die im Program Counter angezeigt wird, und die beim Aufruf eines Unterprogramms auf den Stack gelegt wird. Der grüne Bereich rechts daneben ist der OP-Code des Befehls, so wie er in den Programmspeicher des Controllers programmiert wird, und im roten Kasten stehen die &amp;quot;mnemonics&amp;quot;: Das sind die Befehle, die man im Assembler eingibt.&lt;br /&gt;
Der nicht eingerahmte Rest besteht aus Assemblerdirektiven, Labels (Sprungmarkierungen) und Kommentaren, die nicht direkt in OP-Code umgewandelt werden.&lt;br /&gt;
Der grün eingerahmte Bereich ist das eigentliche Programm, so wie es der µC versteht. Die jeweils erste Zahl im grünen Bereich steht für einen Befehl, den sog. OP-Code (OP = Operation). Die zweite Zahl codiert Argumente für diesen Befehl.&lt;br /&gt;
&lt;br /&gt;
== Sichern von Registern ==&lt;br /&gt;
Eine weitere Anwendung des Stacks ist das &amp;quot;Sichern&amp;quot; von Registern. Wenn man z.&amp;amp;nbsp;B. im Hauptprogramm die Register R16, R17 und R18 verwendet, dann ist es i.d.R. erwünscht, dass diese Register durch aufgerufene Unterprogramme nicht beeinflusst werden. Man muss also nun entweder auf die Verwendung dieser Register innerhalb von Unterprogrammen verzichten, oder man sorgt dafür, dass am Ende jedes Unterprogramms der ursprüngliche Zustand der Register wiederhergestellt wird. Wie man sich leicht vorstellen kann ist ein &amp;quot;Stapelspeicher&amp;quot; dafür ideal: Zu Beginn des Unterprogramms legt man die Daten aus den zu sichernden Registern oben auf den Stapel, und am Ende holt man sie wieder (in der umgekehrten Reihenfolge) in die entsprechenden Register zurück. Das Hauptprogramm bekommt also wenn es fortgesetzt wird überhaupt nichts davon mit, dass die Register inzwischen anderweitig verwendet wurden. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack-saveregs.asm Download stack-saveregs.asm]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;            ; bzw. 2333def.inc&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, RAMEND         ; Stackpointer initialisieren&lt;br /&gt;
         out SP, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp           ; Port B = Ausgang&lt;br /&gt;
&lt;br /&gt;
         ldi R17, 0b10101010      ; einen Wert ins Register R17 laden&lt;br /&gt;
&lt;br /&gt;
         rcall sub1                ; Unterprogramm &amp;quot;sub1&amp;quot; aufrufen&lt;br /&gt;
 &lt;br /&gt;
         out PORTB, R17           ; Wert von R17 an den Port B ausgeben&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop                ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
         push R17                 ; Inhalt von R17 auf dem Stack speichern&lt;br /&gt;
&lt;br /&gt;
         ; hier kann nach belieben mit R17 gearbeitet werden,&lt;br /&gt;
         ; als Beispiel wird es hier auf 0 gesetzt&lt;br /&gt;
&lt;br /&gt;
         ldi R17, 0&lt;br /&gt;
&lt;br /&gt;
         pop R17                  ; R17 zurückholen&lt;br /&gt;
         ret                      ; wieder zurück zum Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn man dieses Programm assembliert und in den Controller lädt, dann wird man feststellen, dass jede zweite LED an Port B leuchtet. Der ursprüngliche Wert von R17 blieb also erhalten, obwohl dazwischen ein Unterprogramm aufgerufen wurde, das R17 geändert hat. &lt;br /&gt;
&lt;br /&gt;
Auch in diesem Fall kann man bei der Simulation des Programms im AVR-Studio die Beeinflussung des Stacks durch die Befehle &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; genau nachvollziehen.&lt;br /&gt;
&lt;br /&gt;
== Sprung zu beliebiger Adresse ==&lt;br /&gt;
&#039;&#039;Dieser Abschnitt ist veraltet, da nahezu alle ATmega/ATtiny Typen IJMP/ICALL unterstützen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Kleinere AVR besitzen keinen Befehl, um direkt zu einer Adresse zu springen, die in einem Registerpaar gespeichert ist. Man kann dies aber mit etwas Stack-Akrobatik erreichen. Dazu einfach zuerst den niederen Teil der Adresse, dann den höheren Teil der Adresse mit &#039;&#039;&#039;push&#039;&#039;&#039; auf den Stack legen und ein &#039;&#039;&#039;ret&#039;&#039;&#039; ausführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
	ldi ZH, high(testRoutine)&lt;br /&gt;
	ldi ZL, low(testRoutine)&lt;br /&gt;
	&lt;br /&gt;
	push ZL&lt;br /&gt;
	push ZH&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
testRoutine:&lt;br /&gt;
	rjmp testRoutine&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Art und Weise kann man auch Unterprogrammaufrufe durchführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
	ldi ZH, high(testRoutine)&lt;br /&gt;
	ldi ZL, low(testRoutine)&lt;br /&gt;
	rcall indirectZCall&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
indirectZCall:&lt;br /&gt;
	push ZL&lt;br /&gt;
	push ZH&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
testRoutine:&lt;br /&gt;
	...&lt;br /&gt;
	ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Größere AVR haben dafür die Befehle &amp;lt;code&amp;gt;ijmp&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;icall&amp;lt;/code&amp;gt;.&lt;br /&gt;
Bei diesen Befehlen muss das Sprungziel in ZH:ZL stehen.&lt;br /&gt;
&lt;br /&gt;
== Weitere Informationen (von Lothar Müller): ==&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/301/Der-Stack-1.pdf Der Stack - Funktion und Nutzen (pdf)]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/299/Der-Stack-2.pdf Der Stack - Parameterübergabe an Unterprogramme (pdf)]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/676/Der-Stack-3.pdf Der Stack - Unterprogramme mit variabler Parameteranzahl (pdf) ]&lt;br /&gt;
&lt;br /&gt;
(Der in dieser Abhandlung angegebene Befehl &#039;&#039;MOV ZLow, SPL&#039;&#039; muss &#039;&#039;IN ZL, SPL&#039;&#039; heißen, da SPL und SPH I/O-Register sind. Ggf ist auch SPH zu berücksichtigen --&amp;gt; 2byte Stack-Pointer)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=logische Operatoren|&lt;br /&gt;
zurücklink=AVR-Tutorial: Logik|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=LCD|&lt;br /&gt;
vorlink=AVR-Tutorial: LCD}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Stack]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=89419</id>
		<title>AVR-Tutorial: Stack</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=89419"/>
		<updated>2015-07-28T12:45:20Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Motivation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Motivation ==&lt;br /&gt;
bisher war es so, dass wenn die Programmausführung an einer anderen Stelle fortgesetzt werden soll, als sich durch die Abfolge der Befehle ergibt, mittels &#039;&#039;&#039;rjmp&#039;&#039;&#039; an diese andere Stelle gesprungen wurde.&lt;br /&gt;
Das ist aber oft nicht ausreichend. Oft möchte man den Fall haben, dass man aus der normalen Befehlsreihenfolge heraus eine andere Sequenz von Befehlen ausgeführt wird und wenn diese abgearbeitet ist, genau an die Aufrufstelle zurückgesprungen wird. Da diese eingeschobene Sequenz an vielen Stellen aufrufbar sein soll, gelingt es daher auch nicht, mittels eines &#039;&#039;&#039;rjmp&#039;&#039;&#039; wieder zur aufrufenden Stelle zurück zu kommen, denn dann müsste ja dieser Rücksprung je nachdem von wo der Hinsprung gekommen ist entsprechend modifiziert werden.&lt;br /&gt;
&lt;br /&gt;
Die Lösung dieses Dilemmas besteht in einem eigenen Befehl, &#039;&#039;&#039;rcall&#039;&#039;&#039;. Ein &#039;&#039;&#039;rcall&#039;&#039;&#039; macht prinzipiell auch den Sprung zu einem Ziel, legt aber gleichzeitig auch  noch die Adresse von wo der Sprung erfolgt ist in einem speziellen Speicherbereich ab, so dass sein &#039;Gegenspieler&#039;, der Befehl &#039;&#039;&#039;ret&#039;&#039;&#039; (wie Return) anhand dieser abgelegten Information wieder genau zu dieser Aufrufstelle zurückspringen kann. Diesen speziellen Speicherbereich nennt man den &amp;quot;[[Stack]]&amp;quot;. Stack bedeutet übersetzt soviel wie Stapel. Damit ist ein Speicher nach dem LIFO-Prinzip (&amp;quot;last in first out&amp;quot;) gemeint. Das bedeutet, dass das zuletzt auf den Stapel gelegte Element auch zuerst wieder heruntergenommen wird. Es ist nicht möglich, Elemente irgendwo in der Mitte des Stapels herauszuziehen oder hineinzuschieben. Ein Stack (oder Stapel) funktioniert wie ein Stapel Teller. Das Teller, welches zuletzt auf den Stapel gelegt wird, ist auch das erste, welches wieder vom Stapel heruntergenommen wird. Und genau das wird in diesem Fall ja auch benötigt: jeder &#039;&#039;&#039;rcall&#039;&#039; legt seine Rüclsprungadresse auf den Stack, so dass alle nachfolgenden &#039;&#039;&#039;ret&#039;&#039;&#039; jeweils in umgekehrter Reihenfolge wieder die richtigen Rücksprungadressen anspringen.&lt;br /&gt;
&lt;br /&gt;
Bei allen aktuellen AVR-Controllern wird der Stack im [[Speicher#RAM|RAM]] angelegt. Der Stack wächst dabei von oben nach unten: Am Anfang wird der Stackpointer (Adresse der aktuellen Stapelposition) auf das Ende des RAMs gesetzt. Wird nun ein Element hinzugefügt, wird dieses an der momentanen Stackpointerposition abgespeichert und der Stackpointer um 1 erniedrigt. Soll ein Element vom Stack heruntergenommen werden, wird zuerst der Stackpointer um 1 erhöht und dann das Byte von der vom Stackpointer angezeigten Position gelesen.&lt;br /&gt;
&lt;br /&gt;
== Aufruf von Unterprogrammen ==&lt;br /&gt;
Dem Prozessor dient der Stack hauptsächlich dazu, Rücksprungadressen beim Aufruf von Unterprogrammen zu speichern, damit er später noch weiß, an welche Stelle zurückgekehrt werden muss, wenn das Unterprogramm mit &#039;&#039;&#039;ret&#039;&#039;&#039; oder die Interruptroutine mit &#039;&#039;&#039;reti&#039;&#039;&#039; beendet wird. &lt;br /&gt;
&lt;br /&gt;
Das folgende Beispielprogramm (AT90S4433) zeigt, wie der Stack dabei beeinflusst wird: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack.asm Download stack.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;     ; bzw. 2333def.inc&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, RAMEND  ; Stackpointer initialisieren&lt;br /&gt;
         out SP, temp&lt;br /&gt;
&lt;br /&gt;
         rcall sub1        ; sub1 aufrufen&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
                           ; hier könnten ein paar Befehle stehen&lt;br /&gt;
         rcall sub2        ; sub2 aufrufen&lt;br /&gt;
                           ; hier könnten auch ein paar Befehle stehen&lt;br /&gt;
         ret               ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
sub2:&lt;br /&gt;
                           ; hier stehen normalerweise die Befehle,&lt;br /&gt;
                           ; die in sub2 ausgeführt werden sollen&lt;br /&gt;
         ret               ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;.def temp = r16&#039;&#039;&#039; ist eine Assemblerdirektive. Diese sagt dem Assembler, dass er überall, wo er &amp;quot;temp&amp;quot; findet, stattdessen &amp;quot;r16&amp;quot; einsetzen soll. Das ist oft praktisch, damit man nicht mit den Registernamen durcheinander kommt. Eine Übersicht über die Assemblerdirektiven findet man [http://www.avr-asm-tutorial.net/avr_de/beginner/diraus.html hier]. &lt;br /&gt;
&lt;br /&gt;
Bei Controllern, die mehr als 256 Byte RAM besitzen (z.&amp;amp;nbsp;B. ATmega8), passt die Adresse nicht mehr in ein Byte. Deswegen gibt es bei diesen Controllern das Stack-Pointer-Register aufgeteilt in &#039;&#039;&#039;SPL&#039;&#039;&#039; (Low) und &#039;&#039;&#039;SPH&#039;&#039;&#039; (High), in denen das Low- und das High-Byte der Adresse gespeichert wird. Damit es funktioniert, muss das Programm dann folgendermaßen geändert werden: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack-bigmem.asm Download stack-bigmem.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, HIGH(RAMEND)            ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
         ldi temp, LOW(RAMEND)             ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
&lt;br /&gt;
         rcall sub1                        ; sub1 aufrufen&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
                                           ; hier könnten ein paar Befehle stehen&lt;br /&gt;
         rcall sub2                        ; sub2 aufrufen&lt;br /&gt;
                                           ; hier könnten auch Befehle stehen&lt;br /&gt;
         ret                               ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
sub2:&lt;br /&gt;
                                           ; hier stehen normalerweise die Befehle,&lt;br /&gt;
                                           ; die in sub2 ausgeführt werden sollen&lt;br /&gt;
         ret                               ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich ist es unsinnig, dieses Programm in einen Controller zu programmieren. Stattdessen sollte man es mal mit dem AVR-Studio simulieren, um die Funktion des Stacks zu verstehen. &lt;br /&gt;
&lt;br /&gt;
Als erstes wird mit &#039;&#039;&#039;Project/New&#039;&#039;&#039; ein neues Projekt erstellt, zu dem man dann mit &#039;&#039;&#039;Project/Add File&#039;&#039;&#039; eine Datei mit dem oben gezeigten Programm (stack.asm) hinzufügt. Nachdem man unter &#039;&#039;&#039;Project/Project Settings&#039;&#039;&#039; das &#039;&#039;&#039;Object Format for AVR-Studio&#039;&#039;&#039; ausgewählt hat, kann man das Programm mit Strg+F7 assemblieren und den Debug-Modus starten. &lt;br /&gt;
&lt;br /&gt;
Danach sollte man im Menu &#039;&#039;&#039;View&#039;&#039;&#039; die Fenster &#039;&#039;&#039;Processor&#039;&#039;&#039; und &#039;&#039;&#039;Memory&#039;&#039;&#039; öffnen und im Memory-Fenster &#039;&#039;&#039;Data&#039;&#039;&#039; auswählen. &lt;br /&gt;
&lt;br /&gt;
Das Fenster &#039;&#039;&#039;Processor&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;Program Counter&#039;&#039;: Adresse im Programmspeicher (FLASH), die gerade abgearbeitet wird&lt;br /&gt;
* &#039;&#039;Stack Pointer&#039;&#039;: Adresse im Datenspeicher (RAM), auf die der Stackpointer gerade zeigt&lt;br /&gt;
* &#039;&#039;Cycle Counter&#039;&#039;: Anzahl der Taktzyklen seit Beginn der Simulation&lt;br /&gt;
* &#039;&#039;Time Elapsed&#039;&#039;: Zeit, die seit dem Beginn der Simulation vergangen ist &lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Memory&#039;&#039;&#039; wird der Inhalt des RAMs angezeigt. &lt;br /&gt;
&lt;br /&gt;
Sind alle 3 Fenster gut auf einmal sichtbar, kann man anfangen, das Programm (in diesem Fall &amp;quot;stack.asm&amp;quot;) mit der Taste F11 langsam Befehl für Befehl zu simulieren. &lt;br /&gt;
&lt;br /&gt;
Wenn der gelbe Pfeil in der Zeile &#039;&#039;&#039;out SPL, temp&#039;&#039;&#039; vorbeikommt, kann man im Prozessor-Fenster sehen, wie der Stackpointer auf 0xDF (&#039;&#039;ATmega8&#039;&#039;: 0x45F) gesetzt wird. Wie man im Memory-Fenster sieht, ist das die letzte RAM-Adresse. &lt;br /&gt;
&lt;br /&gt;
Wenn der Pfeil auf dem Befehl &#039;&#039;&#039;rcall sub1&#039;&#039;&#039; steht, sollte man sich den Program Counter anschauen: Er steht auf 0x02 (&#039;&#039;ATmega8&#039;&#039;: 0x04). &lt;br /&gt;
&lt;br /&gt;
Drückt man jetzt nochmal auf F11, springt der Pfeil zum Unterprogramm sub1. Im RAM erscheint an der Stelle, auf die der Stackpointer vorher zeigte, die Zahl 0x03 (&#039;&#039;ATmega8&#039;&#039;: 0x05). Das ist die Adresse im ROM, an der das Hauptprogramm nach dem Abarbeiten des Unterprogramms fortgesetzt wird. Doch warum wurde der Stackpointer um 2 verkleinert? Das liegt daran, dass eine Programmspeicheradresse bis zu 2 Byte breit sein kann, und somit auch 2 Byte auf dem Stack benötigt werden, um die Adresse zu speichern. &lt;br /&gt;
&lt;br /&gt;
Das gleiche passiert beim Aufruf von sub2. &lt;br /&gt;
&lt;br /&gt;
Zur Rückkehr aus dem mit rcall aufgerufenen Unterprogramm gibt es den Befehl &#039;&#039;&#039;ret&#039;&#039;&#039;. Dieser Befehl sorgt dafür, dass der Stackpointer wieder um 2 erhöht wird und die dabei eingelesene Adresse in den &amp;quot;Program Counter&amp;quot; kopiert wird, so dass das Programm dort fortgesetzt wird. &lt;br /&gt;
&lt;br /&gt;
Apropos Program Counter: Wer sehen will, wie so ein Programm aussieht, wenn es assembliert ist, sollte mal die Datei mit der Endung &amp;quot;.lst&amp;quot; im Projektverzeichnis öffnen. Die Datei sollte ungefähr so aussehen: &lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/images/listfile.gif&lt;br /&gt;
&lt;br /&gt;
Im blau umrahmten Bereich steht die Adresse des Befehls im Programmspeicher. Das ist auch die Zahl, die im Program Counter angezeigt wird, und die beim Aufruf eines Unterprogramms auf den Stack gelegt wird. Der grüne Bereich rechts daneben ist der OP-Code des Befehls, so wie er in den Programmspeicher des Controllers programmiert wird, und im roten Kasten stehen die &amp;quot;mnemonics&amp;quot;: Das sind die Befehle, die man im Assembler eingibt.&lt;br /&gt;
Der nicht eingerahmte Rest besteht aus Assemblerdirektiven, Labels (Sprungmarkierungen) und Kommentaren, die nicht direkt in OP-Code umgewandelt werden.&lt;br /&gt;
Der grün eingerahmte Bereich ist das eigentliche Programm, so wie es der µC versteht. Die jeweils erste Zahl im grünen Bereich steht für einen Befehl, den sog. OP-Code (OP = Operation). Die zweite Zahl codiert Argumente für diesen Befehl.&lt;br /&gt;
&lt;br /&gt;
== Sichern von Registern ==&lt;br /&gt;
Eine weitere Anwendung des Stacks ist das &amp;quot;Sichern&amp;quot; von Registern. Wenn man z.&amp;amp;nbsp;B. im Hauptprogramm die Register R16, R17 und R18 verwendet, dann ist es i.d.R. erwünscht, dass diese Register durch aufgerufene Unterprogramme nicht beeinflusst werden. Man muss also nun entweder auf die Verwendung dieser Register innerhalb von Unterprogrammen verzichten, oder man sorgt dafür, dass am Ende jedes Unterprogramms der ursprüngliche Zustand der Register wiederhergestellt wird. Wie man sich leicht vorstellen kann ist ein &amp;quot;Stapelspeicher&amp;quot; dafür ideal: Zu Beginn des Unterprogramms legt man die Daten aus den zu sichernden Registern oben auf den Stapel, und am Ende holt man sie wieder (in der umgekehrten Reihenfolge) in die entsprechenden Register zurück. Das Hauptprogramm bekommt also wenn es fortgesetzt wird überhaupt nichts davon mit, dass die Register inzwischen anderweitig verwendet wurden. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack-saveregs.asm Download stack-saveregs.asm]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;            ; bzw. 2333def.inc&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, RAMEND         ; Stackpointer initialisieren&lt;br /&gt;
         out SP, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp           ; Port B = Ausgang&lt;br /&gt;
&lt;br /&gt;
         ldi R17, 0b10101010      ; einen Wert ins Register R17 laden&lt;br /&gt;
&lt;br /&gt;
         rcall sub1                ; Unterprogramm &amp;quot;sub1&amp;quot; aufrufen&lt;br /&gt;
 &lt;br /&gt;
         out PORTB, R17           ; Wert von R17 an den Port B ausgeben&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop                ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
         push R17                 ; Inhalt von R17 auf dem Stack speichern&lt;br /&gt;
&lt;br /&gt;
         ; hier kann nach belieben mit R17 gearbeitet werden,&lt;br /&gt;
         ; als Beispiel wird es hier auf 0 gesetzt&lt;br /&gt;
&lt;br /&gt;
         ldi R17, 0&lt;br /&gt;
&lt;br /&gt;
         pop R17                  ; R17 zurückholen&lt;br /&gt;
         ret                      ; wieder zurück zum Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn man dieses Programm assembliert und in den Controller lädt, dann wird man feststellen, dass jede zweite LED an Port B leuchtet. Der ursprüngliche Wert von R17 blieb also erhalten, obwohl dazwischen ein Unterprogramm aufgerufen wurde, das R17 geändert hat. &lt;br /&gt;
&lt;br /&gt;
Auch in diesem Fall kann man bei der Simulation des Programms im AVR-Studio die Beeinflussung des Stacks durch die Befehle &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; genau nachvollziehen.&lt;br /&gt;
&lt;br /&gt;
== Sprung zu beliebiger Adresse ==&lt;br /&gt;
&#039;&#039;Dieser Abschnitt ist veraltet, da nahezu alle ATmega/ATtiny Typen IJMP/ICALL unterstützen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Kleinere AVR besitzen keinen Befehl, um direkt zu einer Adresse zu springen, die in einem Registerpaar gespeichert ist. Man kann dies aber mit etwas Stack-Akrobatik erreichen. Dazu einfach zuerst den niederen Teil der Adresse, dann den höheren Teil der Adresse mit &#039;&#039;&#039;push&#039;&#039;&#039; auf den Stack legen und ein &#039;&#039;&#039;ret&#039;&#039;&#039; ausführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
	ldi ZH, high(testRoutine)&lt;br /&gt;
	ldi ZL, low(testRoutine)&lt;br /&gt;
	&lt;br /&gt;
	push ZL&lt;br /&gt;
	push ZH&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
testRoutine:&lt;br /&gt;
	rjmp testRoutine&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Art und Weise kann man auch Unterprogrammaufrufe durchführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
	ldi ZH, high(testRoutine)&lt;br /&gt;
	ldi ZL, low(testRoutine)&lt;br /&gt;
	rcall indirectZCall&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
indirectZCall:&lt;br /&gt;
	push ZL&lt;br /&gt;
	push ZH&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
testRoutine:&lt;br /&gt;
	...&lt;br /&gt;
	ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Größere AVR haben dafür die Befehle &amp;lt;code&amp;gt;ijmp&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;icall&amp;lt;/code&amp;gt;.&lt;br /&gt;
Bei diesen Befehlen muss das Sprungziel in ZH:ZL stehen.&lt;br /&gt;
&lt;br /&gt;
== Weitere Informationen (von Lothar Müller): ==&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/301/Der-Stack-1.pdf Der Stack - Funktion und Nutzen (pdf)]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/299/Der-Stack-2.pdf Der Stack - Parameterübergabe an Unterprogramme (pdf)]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/676/Der-Stack-3.pdf Der Stack - Unterprogramme mit variabler Parameteranzahl (pdf) ]&lt;br /&gt;
&lt;br /&gt;
(Der in dieser Abhandlung angegebene Befehl &#039;&#039;MOV ZLow, SPL&#039;&#039; muss &#039;&#039;IN ZL, SPL&#039;&#039; heißen, da SPL und SPH I/O-Register sind. Ggf ist auch SPH zu berücksichtigen --&amp;gt; 2byte Stack-Pointer)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=logische Operatoren|&lt;br /&gt;
zurücklink=AVR-Tutorial: Logik|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=LCD|&lt;br /&gt;
vorlink=AVR-Tutorial: LCD}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Stack]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=89418</id>
		<title>AVR-Tutorial: Stack</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Stack&amp;diff=89418"/>
		<updated>2015-07-28T12:41:58Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Motivation ==&lt;br /&gt;
bisher war es so, dass wenn die Programmausführung an einer anderen Stelle fortgesetzt werden soll, als sich durch die Abfolge der Befehle ergibt, mittels RJMP an diese andere Stelle gesprungen wurde.&lt;br /&gt;
Das ist aber oft nicht ausreichend. Oft möchte man den Fall haben, dass man aus der normalen Befehlsreihenfolge heraus eine andere Sequenz von Befehlen ausgeführt wird und wenn diese abgearbeitet ist, genau an die Aufrufstelle zurückgesprungen wird. Da diese eingeschobene Sequenz an vielen Stellen aufrufbar sein soll, gelingt es daher auch nicht, mittels eines RJMP wieder zur aufrufenden Stelle zurück zu kommen, denn dann müsste ja dieser Rücksprung je nachdem von wo der Hinsprung gekommen ist, entsprechend modifiziert werden.&lt;br /&gt;
&lt;br /&gt;
Die Lösung dieses Dilemmas besteht in einem eigenen Befehl, RCALL. Ein RCALL macht prinzpiell auch den Sprung zu einem Ziel, legt aber gleichzeitig auch  noch die Adresse von wo der Sprung erfolgt ist in einem speziellen Speicherbereich ab, so dass sein &#039;Gegenspieler&#039;, der Befehl RET (wie Return) anhand dieser abgelegten Information wieder genau zu dieser Aufrufstelle zurückspringen kann. Diesen speziellen Speicherbereich nennt man den &amp;quot;[[Stack]]&amp;quot;. Stack bedeutet übersetzt soviel wie Stapel. Damit ist ein Speicher nach dem LIFO-Prinzip (&amp;quot;last in first out&amp;quot;) gemeint. Das bedeutet, dass das zuletzt auf den Stapel gelegte Element auch zuerst wieder heruntergenommen wird. Es ist nicht möglich, Elemente irgendwo in der Mitte des Stapels herauszuziehen oder hineinzuschieben. Ein Stack (oder Stapel) funktioniert wie ein Stapel Teller. Das Teller, welches zuletzt auf den Stapel gelegt wird, ist auch das erste, welches wieder vom Stapel heruntergenommen wird.&lt;br /&gt;
&lt;br /&gt;
Bei allen aktuellen AVR-Controllern wird der Stack im [[Speicher#RAM|RAM]] angelegt. Der Stack wächst dabei von oben nach unten: Am Anfang wird der Stackpointer (Adresse der aktuellen Stapelposition) auf das Ende des RAMs gesetzt. Wird nun ein Element hinzugefügt, wird dieses an der momentanen Stackpointerposition abgespeichert und der Stackpointer um 1 erniedrigt. Soll ein Element vom Stack heruntergenommen werden, wird zuerst der Stackpointer um 1 erhöht und dann das Byte von der vom Stackpointer angezeigten Position gelesen.&lt;br /&gt;
&lt;br /&gt;
== Aufruf von Unterprogrammen ==&lt;br /&gt;
Dem Prozessor dient der Stack hauptsächlich dazu, Rücksprungadressen beim Aufruf von Unterprogrammen zu speichern, damit er später noch weiß, an welche Stelle zurückgekehrt werden muss, wenn das Unterprogramm mit &#039;&#039;&#039;ret&#039;&#039;&#039; oder die Interruptroutine mit &#039;&#039;&#039;reti&#039;&#039;&#039; beendet wird. &lt;br /&gt;
&lt;br /&gt;
Das folgende Beispielprogramm (AT90S4433) zeigt, wie der Stack dabei beeinflusst wird: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack.asm Download stack.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;     ; bzw. 2333def.inc&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, RAMEND  ; Stackpointer initialisieren&lt;br /&gt;
         out SP, temp&lt;br /&gt;
&lt;br /&gt;
         rcall sub1        ; sub1 aufrufen&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
                           ; hier könnten ein paar Befehle stehen&lt;br /&gt;
         rcall sub2        ; sub2 aufrufen&lt;br /&gt;
                           ; hier könnten auch ein paar Befehle stehen&lt;br /&gt;
         ret               ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
sub2:&lt;br /&gt;
                           ; hier stehen normalerweise die Befehle,&lt;br /&gt;
                           ; die in sub2 ausgeführt werden sollen&lt;br /&gt;
         ret               ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;.def temp = r16&#039;&#039;&#039; ist eine Assemblerdirektive. Diese sagt dem Assembler, dass er überall, wo er &amp;quot;temp&amp;quot; findet, stattdessen &amp;quot;r16&amp;quot; einsetzen soll. Das ist oft praktisch, damit man nicht mit den Registernamen durcheinander kommt. Eine Übersicht über die Assemblerdirektiven findet man [http://www.avr-asm-tutorial.net/avr_de/beginner/diraus.html hier]. &lt;br /&gt;
&lt;br /&gt;
Bei Controllern, die mehr als 256 Byte RAM besitzen (z.&amp;amp;nbsp;B. ATmega8), passt die Adresse nicht mehr in ein Byte. Deswegen gibt es bei diesen Controllern das Stack-Pointer-Register aufgeteilt in &#039;&#039;&#039;SPL&#039;&#039;&#039; (Low) und &#039;&#039;&#039;SPH&#039;&#039;&#039; (High), in denen das Low- und das High-Byte der Adresse gespeichert wird. Damit es funktioniert, muss das Programm dann folgendermaßen geändert werden: &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack-bigmem.asm Download stack-bigmem.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, HIGH(RAMEND)            ; HIGH-Byte der obersten RAM-Adresse&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
         ldi temp, LOW(RAMEND)             ; LOW-Byte der obersten RAM-Adresse&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
&lt;br /&gt;
         rcall sub1                        ; sub1 aufrufen&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
                                           ; hier könnten ein paar Befehle stehen&lt;br /&gt;
         rcall sub2                        ; sub2 aufrufen&lt;br /&gt;
                                           ; hier könnten auch Befehle stehen&lt;br /&gt;
         ret                               ; wieder zurück&lt;br /&gt;
&lt;br /&gt;
sub2:&lt;br /&gt;
                                           ; hier stehen normalerweise die Befehle,&lt;br /&gt;
                                           ; die in sub2 ausgeführt werden sollen&lt;br /&gt;
         ret                               ; wieder zurück&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich ist es unsinnig, dieses Programm in einen Controller zu programmieren. Stattdessen sollte man es mal mit dem AVR-Studio simulieren, um die Funktion des Stacks zu verstehen. &lt;br /&gt;
&lt;br /&gt;
Als erstes wird mit &#039;&#039;&#039;Project/New&#039;&#039;&#039; ein neues Projekt erstellt, zu dem man dann mit &#039;&#039;&#039;Project/Add File&#039;&#039;&#039; eine Datei mit dem oben gezeigten Programm (stack.asm) hinzufügt. Nachdem man unter &#039;&#039;&#039;Project/Project Settings&#039;&#039;&#039; das &#039;&#039;&#039;Object Format for AVR-Studio&#039;&#039;&#039; ausgewählt hat, kann man das Programm mit Strg+F7 assemblieren und den Debug-Modus starten. &lt;br /&gt;
&lt;br /&gt;
Danach sollte man im Menu &#039;&#039;&#039;View&#039;&#039;&#039; die Fenster &#039;&#039;&#039;Processor&#039;&#039;&#039; und &#039;&#039;&#039;Memory&#039;&#039;&#039; öffnen und im Memory-Fenster &#039;&#039;&#039;Data&#039;&#039;&#039; auswählen. &lt;br /&gt;
&lt;br /&gt;
Das Fenster &#039;&#039;&#039;Processor&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;Program Counter&#039;&#039;: Adresse im Programmspeicher (FLASH), die gerade abgearbeitet wird&lt;br /&gt;
* &#039;&#039;Stack Pointer&#039;&#039;: Adresse im Datenspeicher (RAM), auf die der Stackpointer gerade zeigt&lt;br /&gt;
* &#039;&#039;Cycle Counter&#039;&#039;: Anzahl der Taktzyklen seit Beginn der Simulation&lt;br /&gt;
* &#039;&#039;Time Elapsed&#039;&#039;: Zeit, die seit dem Beginn der Simulation vergangen ist &lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Memory&#039;&#039;&#039; wird der Inhalt des RAMs angezeigt. &lt;br /&gt;
&lt;br /&gt;
Sind alle 3 Fenster gut auf einmal sichtbar, kann man anfangen, das Programm (in diesem Fall &amp;quot;stack.asm&amp;quot;) mit der Taste F11 langsam Befehl für Befehl zu simulieren. &lt;br /&gt;
&lt;br /&gt;
Wenn der gelbe Pfeil in der Zeile &#039;&#039;&#039;out SPL, temp&#039;&#039;&#039; vorbeikommt, kann man im Prozessor-Fenster sehen, wie der Stackpointer auf 0xDF (&#039;&#039;ATmega8&#039;&#039;: 0x45F) gesetzt wird. Wie man im Memory-Fenster sieht, ist das die letzte RAM-Adresse. &lt;br /&gt;
&lt;br /&gt;
Wenn der Pfeil auf dem Befehl &#039;&#039;&#039;rcall sub1&#039;&#039;&#039; steht, sollte man sich den Program Counter anschauen: Er steht auf 0x02 (&#039;&#039;ATmega8&#039;&#039;: 0x04). &lt;br /&gt;
&lt;br /&gt;
Drückt man jetzt nochmal auf F11, springt der Pfeil zum Unterprogramm sub1. Im RAM erscheint an der Stelle, auf die der Stackpointer vorher zeigte, die Zahl 0x03 (&#039;&#039;ATmega8&#039;&#039;: 0x05). Das ist die Adresse im ROM, an der das Hauptprogramm nach dem Abarbeiten des Unterprogramms fortgesetzt wird. Doch warum wurde der Stackpointer um 2 verkleinert? Das liegt daran, dass eine Programmspeicheradresse bis zu 2 Byte breit sein kann, und somit auch 2 Byte auf dem Stack benötigt werden, um die Adresse zu speichern. &lt;br /&gt;
&lt;br /&gt;
Das gleiche passiert beim Aufruf von sub2. &lt;br /&gt;
&lt;br /&gt;
Zur Rückkehr aus dem mit rcall aufgerufenen Unterprogramm gibt es den Befehl &#039;&#039;&#039;ret&#039;&#039;&#039;. Dieser Befehl sorgt dafür, dass der Stackpointer wieder um 2 erhöht wird und die dabei eingelesene Adresse in den &amp;quot;Program Counter&amp;quot; kopiert wird, so dass das Programm dort fortgesetzt wird. &lt;br /&gt;
&lt;br /&gt;
Apropos Program Counter: Wer sehen will, wie so ein Programm aussieht, wenn es assembliert ist, sollte mal die Datei mit der Endung &amp;quot;.lst&amp;quot; im Projektverzeichnis öffnen. Die Datei sollte ungefähr so aussehen: &lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/images/listfile.gif&lt;br /&gt;
&lt;br /&gt;
Im blau umrahmten Bereich steht die Adresse des Befehls im Programmspeicher. Das ist auch die Zahl, die im Program Counter angezeigt wird, und die beim Aufruf eines Unterprogramms auf den Stack gelegt wird. Der grüne Bereich rechts daneben ist der OP-Code des Befehls, so wie er in den Programmspeicher des Controllers programmiert wird, und im roten Kasten stehen die &amp;quot;mnemonics&amp;quot;: Das sind die Befehle, die man im Assembler eingibt.&lt;br /&gt;
Der nicht eingerahmte Rest besteht aus Assemblerdirektiven, Labels (Sprungmarkierungen) und Kommentaren, die nicht direkt in OP-Code umgewandelt werden.&lt;br /&gt;
Der grün eingerahmte Bereich ist das eigentliche Programm, so wie es der µC versteht. Die jeweils erste Zahl im grünen Bereich steht für einen Befehl, den sog. OP-Code (OP = Operation). Die zweite Zahl codiert Argumente für diesen Befehl.&lt;br /&gt;
&lt;br /&gt;
== Sichern von Registern ==&lt;br /&gt;
Eine weitere Anwendung des Stacks ist das &amp;quot;Sichern&amp;quot; von Registern. Wenn man z.&amp;amp;nbsp;B. im Hauptprogramm die Register R16, R17 und R18 verwendet, dann ist es i.d.R. erwünscht, dass diese Register durch aufgerufene Unterprogramme nicht beeinflusst werden. Man muss also nun entweder auf die Verwendung dieser Register innerhalb von Unterprogrammen verzichten, oder man sorgt dafür, dass am Ende jedes Unterprogramms der ursprüngliche Zustand der Register wiederhergestellt wird. Wie man sich leicht vorstellen kann ist ein &amp;quot;Stapelspeicher&amp;quot; dafür ideal: Zu Beginn des Unterprogramms legt man die Daten aus den zu sichernden Registern oben auf den Stapel, und am Ende holt man sie wieder (in der umgekehrten Reihenfolge) in die entsprechenden Register zurück. Das Hauptprogramm bekommt also wenn es fortgesetzt wird überhaupt nichts davon mit, dass die Register inzwischen anderweitig verwendet wurden. &lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/stack-saveregs.asm Download stack-saveregs.asm]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;            ; bzw. 2333def.inc&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
         ldi temp, RAMEND         ; Stackpointer initialisieren&lt;br /&gt;
         out SP, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp           ; Port B = Ausgang&lt;br /&gt;
&lt;br /&gt;
         ldi R17, 0b10101010      ; einen Wert ins Register R17 laden&lt;br /&gt;
&lt;br /&gt;
         rcall sub1                ; Unterprogramm &amp;quot;sub1&amp;quot; aufrufen&lt;br /&gt;
 &lt;br /&gt;
         out PORTB, R17           ; Wert von R17 an den Port B ausgeben&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop                ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub1:&lt;br /&gt;
         push R17                 ; Inhalt von R17 auf dem Stack speichern&lt;br /&gt;
&lt;br /&gt;
         ; hier kann nach belieben mit R17 gearbeitet werden,&lt;br /&gt;
         ; als Beispiel wird es hier auf 0 gesetzt&lt;br /&gt;
&lt;br /&gt;
         ldi R17, 0&lt;br /&gt;
&lt;br /&gt;
         pop R17                  ; R17 zurückholen&lt;br /&gt;
         ret                      ; wieder zurück zum Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn man dieses Programm assembliert und in den Controller lädt, dann wird man feststellen, dass jede zweite LED an Port B leuchtet. Der ursprüngliche Wert von R17 blieb also erhalten, obwohl dazwischen ein Unterprogramm aufgerufen wurde, das R17 geändert hat. &lt;br /&gt;
&lt;br /&gt;
Auch in diesem Fall kann man bei der Simulation des Programms im AVR-Studio die Beeinflussung des Stacks durch die Befehle &#039;&#039;&#039;push&#039;&#039;&#039; und &#039;&#039;&#039;pop&#039;&#039;&#039; genau nachvollziehen.&lt;br /&gt;
&lt;br /&gt;
== Sprung zu beliebiger Adresse ==&lt;br /&gt;
&#039;&#039;Dieser Abschnitt ist veraltet, da nahezu alle ATmega/ATtiny Typen IJMP/ICALL unterstützen.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Kleinere AVR besitzen keinen Befehl, um direkt zu einer Adresse zu springen, die in einem Registerpaar gespeichert ist. Man kann dies aber mit etwas Stack-Akrobatik erreichen. Dazu einfach zuerst den niederen Teil der Adresse, dann den höheren Teil der Adresse mit &#039;&#039;&#039;push&#039;&#039;&#039; auf den Stack legen und ein &#039;&#039;&#039;ret&#039;&#039;&#039; ausführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
	ldi ZH, high(testRoutine)&lt;br /&gt;
	ldi ZL, low(testRoutine)&lt;br /&gt;
	&lt;br /&gt;
	push ZL&lt;br /&gt;
	push ZH&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
testRoutine:&lt;br /&gt;
	rjmp testRoutine&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf diese Art und Weise kann man auch Unterprogrammaufrufe durchführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
	ldi ZH, high(testRoutine)&lt;br /&gt;
	ldi ZL, low(testRoutine)&lt;br /&gt;
	rcall indirectZCall&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
indirectZCall:&lt;br /&gt;
	push ZL&lt;br /&gt;
	push ZH&lt;br /&gt;
	ret&lt;br /&gt;
&lt;br /&gt;
testRoutine:&lt;br /&gt;
	...&lt;br /&gt;
	ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Größere AVR haben dafür die Befehle &amp;lt;code&amp;gt;ijmp&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;icall&amp;lt;/code&amp;gt;.&lt;br /&gt;
Bei diesen Befehlen muss das Sprungziel in ZH:ZL stehen.&lt;br /&gt;
&lt;br /&gt;
== Weitere Informationen (von Lothar Müller): ==&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/301/Der-Stack-1.pdf Der Stack - Funktion und Nutzen (pdf)]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment/299/Der-Stack-2.pdf Der Stack - Parameterübergabe an Unterprogramme (pdf)]&lt;br /&gt;
* [http://www.mikrocontroller.net/attachment.php/676/Der-Stack-3.pdf Der Stack - Unterprogramme mit variabler Parameteranzahl (pdf) ]&lt;br /&gt;
&lt;br /&gt;
(Der in dieser Abhandlung angegebene Befehl &#039;&#039;MOV ZLow, SPL&#039;&#039; muss &#039;&#039;IN ZL, SPL&#039;&#039; heißen, da SPL und SPH I/O-Register sind. Ggf ist auch SPH zu berücksichtigen --&amp;gt; 2byte Stack-Pointer)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=logische Operatoren|&lt;br /&gt;
zurücklink=AVR-Tutorial: Logik|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=LCD|&lt;br /&gt;
vorlink=AVR-Tutorial: LCD}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Stack]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Codeoptimierung&amp;diff=89286</id>
		<title>AVR-GCC-Codeoptimierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Codeoptimierung&amp;diff=89286"/>
		<updated>2015-07-10T11:02:23Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Was */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Entstanden aus diesem [http://www.mikrocontroller.net/topic/66690 Thread] sollen hier ein paar Hinweise/Erfahrungen gegeben werden, um den Quellcode in Punkto Größe und Geschwindigkeit zu optimieren. &#039;&#039;En detail&#039;&#039; ist das Thema komplex, da es stark von der Codeoptimierung des Compilers abhängt. Es ist im Einzelfall ratsam zu prüfen, ob die eigenen Maßnahmen auch erfolgreich waren. Die Diskussionen [http://www.mikrocontroller.net/topic/132624] bzw. [http://www.mikrocontroller.net/topic/180800#new] können als Anhaltspunkte dienen, wie eine solche Prüfung ablaufen kann.&lt;br /&gt;
&lt;br /&gt;
== Prinzipien der Optimierung ==&lt;br /&gt;
&lt;br /&gt;
Wie so oft sollte man nicht einfach wild drauf los optimieren und sich zunächst ein paar Dinge klar machen.&lt;br /&gt;
&lt;br /&gt;
* Warum will ich optimieren?&lt;br /&gt;
* Was kann man sinnvoll optimieren?&lt;br /&gt;
* Wieviel Rechenzeit oder Speicher soll dabei gespart werden?&lt;br /&gt;
* Wie kann optimiert werden?&lt;br /&gt;
* &amp;quot;Verfrühte Optimierung ist die Wurzel allen Übels&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Viele Optimierungen sind &amp;quot;Angst-Optimierungen&amp;quot;, die nicht wirklich nötig sind. Die Gefahr mit Optimierungen ist, den Code tot zu optimieren, sprich Lesbarkeit, Portierbarkeit und ggf. Fehlerfreiheit sinken massgeblich. Kurz und knapp in diesem [http://blogs.msdn.com/b/audiofool/archive/2007/06/14/the-rules-of-code-optimization.aspx BLOG] formuliert.&lt;br /&gt;
&lt;br /&gt;
=== Warum ===&lt;br /&gt;
&lt;br /&gt;
Optimieren sollte man nur, wenn&lt;br /&gt;
* der Speicher nicht mehr ausreicht (RAM, Flash)&lt;br /&gt;
* Die Laufzeit für bestimmte Programmteile zu groß wird und somit bestimmte (Echtzeit-)Ausgaben nicht im erforderlichen Zeitrahmen erledigt werden&lt;br /&gt;
&lt;br /&gt;
Weiter sollte man folgende Punkte gegeneinander abwägen:&lt;br /&gt;
&lt;br /&gt;
* Codeverbrauch&lt;br /&gt;
* Datenverbrauch. Statisch/Stack/Heap&lt;br /&gt;
* Mittlere Laufzeit/maximale Laufzeit&lt;br /&gt;
* Entwicklungszeit&lt;br /&gt;
* Portabilität (Compiler, Hardware, ...)&lt;br /&gt;
* Verständlichkeit der Quelle, siehe [[Strukturierte Programmierung auf Mikrocontrollern]] &lt;br /&gt;
* ABI-Konformität&lt;br /&gt;
&lt;br /&gt;
=== Was ===&lt;br /&gt;
&lt;br /&gt;
Die goldene Regel lautet: 90% der Rechenleistung werden in 10% des Codes verbraucht. Diese 10% muss man finden und zum richtigen Zeitpunkt optimieren. Der Rest muss nur sauber und lesbar geschrieben sein. Was jedoch nichts bringt, ist eine Funktion, die von 1 Minute Programmlaufzeit lediglich 1 Sekunde verbraucht, um den Faktor 10 schneller zu machen. Die Programmlaufzeit sinkt dann von 60 Sekunden auf 59.1 Sekunden. Der Aufwand, die Funktion um einen Faktor 10 schneller zu machen ist aber meistens beträchtlich!  Kann ich aber den Code, der für die 59 Sekunden verantwortlich ist um lediglich einen Faktor 2 schneller machen (was immer noch oft schwer genug ist, aber auf jeden Fall leichter als ein Faktor 10), dann sinkt die Gesamtlaufzeit von 60 Sekunden auf 30.5 Sekunden. Dort bringt Optimieren augenscheinlich viel mehr!&lt;br /&gt;
&lt;br /&gt;
Um die optimierungswürdigen Stellen zu finden, muss man sein Programm analysieren. Dazu gibt es verschiedene Möglichkeiten.&lt;br /&gt;
&lt;br /&gt;
====Speicherverbrauch nach Funktionen aufschlüsseln====&lt;br /&gt;
&lt;br /&gt;
;map-File:&lt;br /&gt;
:dort sind alle globalen und statischen Variablen enthalten. Eine Map-Datei kann mit den GNU-Tools während des Linkens angelegt werden:&lt;br /&gt;
::&amp;lt;pre&amp;gt;&amp;amp;gt; avr-gcc ... -Wl,-Map,foo.map&amp;lt;/pre&amp;gt;&lt;br /&gt;
: Die Option -Wl bewirkt, daß avr-gcc die angehängen Optionen unverändert an den Linker weiterreicht. Dieser erzeugt dann das Mapfile &amp;quot;foo.map&amp;quot;, eine Textdatei.&lt;br /&gt;
;avr-size: Mit Tools wie avr-size kann die Platzbelegung einzelner Module ermittelt werden:&lt;br /&gt;
::&amp;lt;pre&amp;gt;&amp;amp;gt; avr-size -x foo1.o foo2.o ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
:bzw. die Platzbelegung der elf-Datei:&lt;br /&gt;
::&amp;lt;pre&amp;gt;&amp;amp;gt; avr-size foo.elf&amp;lt;/pre&amp;gt;&lt;br /&gt;
;avr-nm:&lt;br /&gt;
::&amp;lt;pre&amp;gt;&amp;amp;gt; avr-nm --size-sort -S foo.elf&amp;lt;/pre&amp;gt;&lt;br /&gt;
:ergibt eine Liste mit der Größe aller Objekte: der erste Spalte enthälte die Adresse, die zweite Spalte die Größe, die dritte den Typ und die vierte Spalte den zugehörigen Symbolnamen. Der Typ ergibt sich aus der folgenden Zuordnung, wobei Großbuchstaben globale Symbole kennzeichnen und Kleinbuchstaben Symbole, die Modul-lokal sind:&lt;br /&gt;
:;T/t: Objekte in der text-Section: Funktionen, Daten im Flash&lt;br /&gt;
:;D/d: Objekte im data-Segment (initialisierte Daten)&lt;br /&gt;
:;B/b: Objekte im bss-Segment (Null-initialisierte Daten)&lt;br /&gt;
&lt;br /&gt;
;avr-gcc: Der Compiler hat bereits Informationen über die übersetzten Funktionen, die man direkt zur Analyse verwenden kann. Dazu lässt man avr-gcc die Assembler-Ausgabe, die ohne weiteres Zutun nur als temporäre Datei angelegt wird, abspeichern. Etwa für die Quelldatei foo.c:&lt;br /&gt;
::&amp;lt;pre&amp;gt;&amp;amp;gt; avr-gcc -save-temps foo.c -c ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
:Die Assembler-Datei wird damit als foo.s angelegt und nicht gelöscht. (Das ebenfalls angelegte Präcompilat foo.i wird nicht benötigt). Für jede Funktion gibt avr-gcc 3.4.x im Prolog einen Kommentar der Form&amp;lt;ref&amp;gt;Für avr-gcc 4.x sehen die Kommentare anders aus oder fehlen je nach Compilerversion ganz&amp;lt;/ref&amp;gt;&lt;br /&gt;
::&amp;lt;pre&amp;gt;/* prologue: frame size=0 */&amp;lt;/pre&amp;gt;&lt;br /&gt;
:aus, was die Größe des aktuellen Frames angibt. Dies ist der Platz auf dem Stack, der für lokale Variablen benötigt wird. Am besten ist es, wenn die Frame-Size wie im Beispiel gleich 0 ist. Ansonsten sollte man versuchen, diese Größe auf Null zu bringen. Für Variablen, die nicht in Registern gehalten werden können, müssen Speicherzugriffe in den Stack erzeugt werden. Diese machen das Programm sowohl größer aus auch langsamer. Zudem reserviert avr-gcc bei solche Funktionen das Y-Register als Frame-Pointer; das Y-Register steht damit nicht mehr für lokale Variablen zur Verfügung was sich ebenfalls ungünstig auf die Codegüte auswirkt. Ein Grund für das Anlegen eines Frames können zu viele lokale Variablen sein (zB lokale Puffer/Arrays) oder lokale Variablen/Strukturen/Parameter mit ungünstigen Größen, etwa eine 3-Byte große Struktur. &lt;br /&gt;
&lt;br /&gt;
: Neben dieser Information gibt avr-gcc Kommentare der Gestalt&lt;br /&gt;
::&amp;lt;pre&amp;gt;/* prologue end (size=2) */&amp;lt;/pre&amp;gt;&lt;br /&gt;
:aus die darüber informieren, wie viele Register auf dem Stack gesichert wurden.&lt;br /&gt;
&lt;br /&gt;
: Zusammen mit Werkzeugen wie grep, die in jedem Linux und jeder WinAVR-Distribution enthalten sind, findet man schnell Übeltäter wie Funktionen mit Frame.&lt;br /&gt;
&lt;br /&gt;
;Assembler-Code sichten: Ein kurzer Blick auf den erzeugten Assembler-Code zeigt oft, wie gut der Compiler den Code umgesetzt hat. Den erzeugten Assembler-Code zu überfliegen ist wesentlich zeitsparender als selbst in Assembler zu programmieren. Je nach Gusto verwendet man zur Einsicht den Assembler-Code, den avr-gcc ausgibt (s.o.), Assembler-Dumps des Assemblers, List-Files oder HEX-Dumps. Siehe auch&amp;lt;ref&amp;gt;[http://rn-wissen.de/index.php/Assembler-Dump_erstellen_mit_avr-gcc roboternetz.de: Assembler-Dump erstellen mit avr-gcc]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Hilfsmittel: einkaufen oder selber bauen. Es gilt herauszufinden, welche Funktion massig Stack durch lokale Variablen verbraucht. Stacktracer können das. Wenn man keinen hat, dann muss man sich eben selber einen bauen, indem man den Stackpointer mitloggt. Zur Not einen Code-Review machen: Alle Funktionen optisch durchgehen und die identifizieren, die viele Variablen anlegen. Dann die Aufrufhierarchie der Funktion feststellen: Wirken sich die vielen Variablen überhaupt aus oder entsteht mein Problem durch eine tiefe Funktionsaufrufhierarchie, bei der zwar wenige Variablen pro Funktion im Spiel sind, aber die Menge der ineinandergeschachtelten Aufrufe &#039;das Kraut fett macht&#039;&lt;br /&gt;
;Profitools: können das alles fast auf Knopfdruck, kosten aber viel Geld&lt;br /&gt;
&lt;br /&gt;
====Laufzeit messen====&lt;br /&gt;
&lt;br /&gt;
*Simulator&lt;br /&gt;
*In Echtzeit mittels Testpin, welche an Anfang einer Funktion/Blocks gesetzt wird und am Ende wieder gelöscht wird. Mit einem [[Oszilloskop]] kann man so sehr einfach die Laufzeit messen.&lt;br /&gt;
&lt;br /&gt;
; Anmerkung: Solche Messverfahren liefern immer nur eine &#039;&#039;untere&#039;&#039; Schranke für die Laufzeit, niemals eine obere Schranke. Eine obere Schranke, wie man sie etwa in sicherheitsktitischen Systemen benötigt, liefert eine statische Codeanalyse.&lt;br /&gt;
&lt;br /&gt;
=== Wieviel ===&lt;br /&gt;
&lt;br /&gt;
Der Aufwand von Optimierungen wächst exponentiell. Die letzten paar Prozent brauchen überproportional viel Aufwand.&lt;br /&gt;
&lt;br /&gt;
=== Wie ===&lt;br /&gt;
&lt;br /&gt;
Meist muss man die Wahl treffen ob man Speicher oder Rechenzeit sparen will, beides gleichzeitg geht meist nicht. Das Konzept heißt &#039;Space for Time&#039; und kann in beide Richtungen verwendet werden. Als Beispiel soll eine komplizierte Berechnung dienen. Diese kann man relativ kompakt in eine Funktion packen, welche dann aber eher langsam ist. Oder man benutzt eine sehr große Tabelle, in welcher die Ergebnisse schon für jeden Eingangswert vorausberechnet wurden. Diese Lösung ist sehr schnell, verbraucht aber sehr viel Speicher.&lt;br /&gt;
&lt;br /&gt;
* Inlining von Funktionen erhöht den Speicherverbrauch, senkt aber die Laufzeit. Beispiel: Funktion A ist 50 Byte groß und wird 10 mal im Programm aufgerufen. Ein Aufruf kostet 10 Byte:&lt;br /&gt;
** Ohne Inline: 10 * 10Byte + 50 Byte = 150 Byte Platzverbrauch&lt;br /&gt;
** Mit Inline: 10 * 50 Byte = 500 Byte&lt;br /&gt;
* Optimierer einschalten&lt;br /&gt;
* möglichst keine Floating Point Operationen, besser ist meist [[Festkommaarithmetik]]&lt;br /&gt;
* Formeln umstellen und zusammenfassen&lt;br /&gt;
* Variablen so klein wie möglich, uint8_t wo&#039;s nur geht.&lt;br /&gt;
* Wirklich zeitkritische Funktionen und Interrupts als Assemblercode in separater Datei&lt;br /&gt;
&lt;br /&gt;
== GCC-Optionen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungs-Level ===&lt;br /&gt;
&lt;br /&gt;
avr-gcc kennt mehrere Optimierungsstufen:&lt;br /&gt;
;&amp;lt;tt&amp;gt;-O0&amp;lt;/tt&amp;gt;: Keine Optimierung des erzeugten Codes. Diese Optimierungsstufe optimiert den Resourcenverbrauch des &#039;&#039;Hostrechners&#039;&#039; und die Nachvollziehbarkeit der erzeugten Codes anhand von Debug-Information. Alle lokalen Variablen werden auf dem Stack angelegt und nicht in Registern gehalten. Es werden keine komplexen Optimierungsalgorithmen angewandt; lediglich Konstanten wie 1+2 werden zu 3 gefaltet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-O1&amp;lt;/tt&amp;gt;: Je höher die Optimierungsstufe, desto schwieriger ist der erzeugte Code nachvollziehbar — auch mit Debugger. Diese O-Stufe ist ein Kompromiss zwischen aggressiver Optimierung und Nachvollziehbarkeit des erzeugten Codes. Ein ehernes Gesetz in GCC ist, dass er den gleichen Code erzeugen muss unabhängig davon, ob Debug-Information erzeugt wird oder nicht. Im Umkehrschluss erlaubt volle Debug-Unterstützung nicht alle Optimierungen, wozu diese Optimierungsstufe dient.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-O2&amp;lt;/tt&amp;gt;: Optimierung auf Geschwindigkeit. Für AVR nur mässig sinnvoll, da sich der Codezuwachs nicht in einen entsprechenden Geschwindigkeitszuwachs transformiert. Dies liegt vor allem daran, daß Sprünge und Funktionsaufrufe auf AVR im Vergleich zu anderen Architekturen sehr billig sind. Es bringt also kaum einen Geschwindigkeitszuwachs, einen Block zu kopieren um einen Sprung zu sparen. Hingegen vergrößert dies den Code deutlich.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-O3&amp;lt;/tt&amp;gt;: Dito. Auf Teufel-komm-raus Funktionen zu inlinen, Schleifen aufzurollen oder gar Funktionen mehrfach für unterschiedliche Aufruf-Szenarien zu implementieren, ist auf einem kleinen µC wie AVR der Overkill.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-Os&amp;lt;/tt&amp;gt;: Optimierung auf Codegröße. Die bevorzugte Optimierungsstufe für AVR und viele andere µC.&lt;br /&gt;
&lt;br /&gt;
Jede O-Option ist ein Sammlung von verschiedenen Schaltern, welche bestimmte Optimierungsstrategien aktivieren. Um zu sehen, welche Schalter dies genau sind, erzeugt man wie oben beschrieben mit den Schalten&lt;br /&gt;
   -save-temps -fverbose-asm&lt;br /&gt;
die Assembler-Ausgabe von gcc und schaut die Optionen im s-File nach. Einzelne Optionen lassen sich gezielt aktivieren bzw. deaktivieren und damit zum Beispiel zum &amp;lt;tt&amp;gt;-Os&amp;lt;/tt&amp;gt;-Paket hinzufügen. &lt;br /&gt;
&lt;br /&gt;
Eine Ausnahme bildet &amp;lt;tt&amp;gt;-O0&amp;lt;/tt&amp;gt;: Hier ist Code-Optimierung generell deaktiviert, und Optimierungsschalter bleiben ohne Wirkung.&lt;br /&gt;
&lt;br /&gt;
=== Feinabstimmung der Optimizer ===&lt;br /&gt;
&lt;br /&gt;
Kandidaten für Optimierungsoptionen sind folgende Schalter. &amp;lt;tt&amp;gt;-m&amp;lt;/tt&amp;gt; kennzeichnet maschinenspezifische Schalter, die nur für AVR gültig sind. &amp;lt;tt&amp;gt;-f&amp;lt;/tt&amp;gt; bzw. &amp;lt;tt&amp;gt;-fno-&amp;lt;/tt&amp;gt; sind maschinenunabhängige Schalter, die auch für andere Architekturen verfügbar sind.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-split-wide-types&amp;lt;/tt&amp;gt;: Je nach Quelle kann die Deaktivierung von &amp;lt;tt&amp;gt;-fsplit-wide-types&amp;lt;/tt&amp;gt; besseren Code ergeben.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-inline-small-functions&amp;lt;/tt&amp;gt;: Relativ kleine Funktionen /immer/ zu inlinen kann den Code unnötig vergrößern, dieser Schalter unterbindet das automatische Inlinen kleiner Funktionen.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-finline-limit=&amp;lt;/tt&amp;gt;&#039;&#039;wert&#039;&#039;: Maximalwert für automatisch geinlinte Funktionen. In einschlägigen Foren werden kleine Werte für &#039;&#039;wert&#039;&#039; vorgeschlagen, z.B.&amp;amp;nbsp;1…3&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-mcall-prologues&amp;lt;/tt&amp;gt;: Die für aufwändige Funktionen mitunter recht langen push/pop-Sequenzen werden durch Hilfsfunktionen ersetzt. Das kann vor allem bei grossen Programmen Platz sparen. Die Ausführungszeit steigt an.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-jump-tables&amp;lt;/tt&amp;gt;: Switch-Statements werden hierdurch mitunter deutlich kürzer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-move-loop-invariants&amp;lt;br/&amp;gt;-fno-tree-loop-optimize&amp;lt;/tt&amp;gt;: Einige Schleifenoptimierungen, welche die Registerlast erhöhen und für AVR kaum zu einem Geschwindigkeitszuwachs führen, werden deaktiviert.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-tree-switch-conversion&amp;lt;/tt&amp;gt;: Neue GCC-Versionen können switch/case Anweisungen u.U. in Lookup-Tabellen umwandeln, die im RAM abgelegt werden, siehe auch [http://gcc.gnu.org/PR49857 PR49857]. Dieser Optimierung ist bei RAM-Knappheit in Betracht zu ziehen, bring aber natürlich nur dann etwas, wenn diese Optimierung auch ausgeführt wurde.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-optimize-sibling-calls&amp;lt;/tt&amp;gt;: Ab 4.7 wirksam: Tailcall-Optimierung kann den Code vergrößern, wenn Epiloge mehrfach erzeugt werden. In diesem Fall deaktiviert man die Tailcall-Optimierung.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-maccumulate-args&amp;lt;/tt&amp;gt;: Ab 4.7: Funktionen, die mehrere printf-artige Aufrufe enthalten und viele Artumente per Stack an diese übergeben, werden u.U kleiner.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-mstrict-X&amp;lt;/tt&amp;gt;: Ab 4.7: Beeinflusst die Art und Weise, wie das X-Register zur Adressierung verwendet wird.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-mbranch-cost=&amp;lt;/tt&amp;gt;&#039;&#039;wert&#039;&#039;: Ab 4.7: Setzt die Kosten, mit der der Compiler einen bedingten Sprunge veranschlagt. Default-Wert ist &#039;&#039;wert&#039;&#039;&amp;lt;tt&amp;gt;=0&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-caller-saves&amp;lt;/tt&amp;gt;: Kann zu effizienteren Pro-/Epilogen beitragen.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fno-tree-ter&amp;lt;/tt&amp;gt;: Es gibt Fälle, in denen der Compiler die Berechnung temporärer Variablen über volatile-Zugriffe und Memory-Barriers zieht, siehe [http://gcc.gnu.org/PR53033 PR53033]. Von der  C-Spezifikation her ist dies zulässig, kann aber zu unerwünschter Umsortierung der volatile-Operation führe, z.B. wenn es sich dabei um eine &amp;lt;tt&amp;gt;SEI&amp;lt;/tt&amp;gt;-Instruktion handelt. Ohne diese Optimierung wird der Code evtl. etwas größer, aber Probleme wie im PR beschrieben können vermieden werden.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;--param case-values-threshold=&amp;lt;/tt&amp;gt;&#039;&#039;wert&#039;&#039;: Ab 4.7: Schwellwert an Einträgen in einem switch/case, ab dem anstatt eines binären if/else-Entscheidungsbaums eine Sprungtabelle zu den case-Labels erzeugt wird. Voreinstellung ab 4.7 ist &#039;&#039;wert&#039;&#039;&amp;lt;tt&amp;gt;=7&amp;lt;/tt&amp;gt;. Ältere Compilerversionen verwenden andere Werte. Siehe auch &amp;lt;tt&amp;gt;-f[no-]jump-tables&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Generell gilt für all diese Optionen, daß sie abhängig vom Projekt zu einer Codeverbesserung oder -verschlechterung führen können — dies ist i.d.R. vom Projektcode abhängig.&lt;br /&gt;
&lt;br /&gt;
=== Linker-Optionen ===&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-ffunction-sections&amp;lt;br/&amp;gt;-Wl,--gc-sections&amp;lt;/tt&amp;gt;: Der Linker wirft nicht referenzierte Sections weg, was die Codegröße günstig beeinflussen kann.  Diese Optimierung verkleinert den Code nur dann, wenn nicht verwendete Funktionen in der Anwendung rumgammeln. Weil der Linker nur auf Section-Ebene optimieren kann, muss zusätzlich der Compiler mit &amp;lt;tt&amp;gt;-ffunction-sections&amp;lt;/tt&amp;gt; aufgerufen werden, um die Anwendung auf möglichst viele Sections zu verteilen.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-Wl,--relax&amp;lt;br/&amp;gt;-mrelax&amp;lt;/tt&amp;gt;: Der Linker fasst Tail-Calls wie&lt;br /&gt;
::&amp;lt;tt&amp;gt;CALL some_function&amp;lt;br/&amp;gt;RET&amp;lt;/tt&amp;gt;&lt;br /&gt;
: zusammen als&lt;br /&gt;
::&amp;lt;tt&amp;gt;JMP some_function&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Siehe auch die Compiler-Option &amp;lt;tt&amp;gt;-f[no-]optimize-sibling-calls&amp;lt;/tt&amp;gt; von oben. Zudem wird &amp;lt;tt&amp;gt;CALL&amp;lt;/tt&amp;gt; umgewandelt zur kürzeren &amp;lt;tt&amp;gt;RCALL&amp;lt;/tt&amp;gt;-Instruktion falls das Sprungziel im ±4&amp;amp;nbsp;KiB-Zielbereich von &amp;lt;tt&amp;gt;RCALL&amp;lt;/tt&amp;gt; liegt. Analog für &amp;lt;tt&amp;gt;JMP&amp;lt;/tt&amp;gt; zu &amp;lt;tt&amp;gt;RJMP&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
: Die beiden Optionen sind gleichwertig; die erste Variante veranlasst den Compiler, den Linker mit &amp;lt;tt&amp;gt;--relax&amp;lt;/tt&amp;gt; aufzurufen. Die zweite Variante verwendet den allgemeinen &amp;lt;tt&amp;gt;-Wl&amp;lt;/tt&amp;gt;-Mechanismus, um eine Option von der Compiler-Kommandozeile an den Linker durchzureichen.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Optionen ===&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-mtiny-stack&amp;lt;/tt&amp;gt;: Der Compiler ändert nur das Low-Byte des Stackpointers (SP), was auf Controllern mit 16-Bit SP zu kleinerem Code führen kann.  Benötigt der Code Platz auf dem Stack, so erzeugt der Compiler u.U. Code, der den SP liest, einen Offset aufaddiert/abzieht und den SP dann zurückschreibt. Dies ist aufwändig. Für den Fall, daß sich dabei das High-Byte SP nicht ändert, kann Code eingespart werden.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;u&amp;gt;Beispiel&amp;lt;/u&amp;gt;: ATtiny44 hat RAM von &amp;lt;tt&amp;gt;0x60&amp;lt;/tt&amp;gt; bis &amp;lt;tt&amp;gt;0x15f&amp;lt;/tt&amp;gt;. Braucht die Anwendung nicht mehr als &amp;lt;tt&amp;gt;0x60&amp;lt;/tt&amp;gt;&amp;amp;nbsp;=&amp;amp;nbsp;96 Bytes an Stack, dann kann mit &amp;lt;tt&amp;gt;-mtiny-stack&amp;lt;/tt&amp;gt; compiliert werden.&lt;br /&gt;
&lt;br /&gt;
=== Änderung des Binärinterfaces per Option ===&lt;br /&gt;
&lt;br /&gt;
{{Warnung|&lt;br /&gt;
;Warnung: Im Gegensatz zu den Optionen der vorherigen Abschnitte, bei denen es sich um reine Optimierungsoptionen handelt, ändern die folgenden Optionen das Binärinterface (ABI) des vom Compiler erzeugten Codes und sind daher nur nach eingehender Prüfung anzuwenden! Wird eine Anwendung mit diesen Schaltern übersetzt, dann ist sicher zu stellen, daß &#039;&#039;alle&#039;&#039; Module inclusive Libraries damit derzeugt werden oder die ABI-Änderung sich nicht auf Code in Bibliotheken auswirkt!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Vorsicht in auch deshalb geboten, weil manche Entwicklungsumgebungen wie &amp;quot;Atmel Studio&amp;quot; das ABI &#039;&#039;per Default&#039;&#039; verändern und ohne daß der Anwender es extra anfordert.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-funsigned-char&amp;lt;/tt&amp;gt;: Anders als im avr-gcc ABI ist der Typ &amp;lt;tt&amp;gt;char&amp;lt;/tt&amp;gt; unsigned anstatt signed.&lt;br /&gt;
:&amp;lt;u&amp;gt;Besser&amp;lt;/u&amp;gt;: Verwende die C99-Typen wie &amp;lt;tt&amp;gt;uint8_t&amp;lt;/tt&amp;gt; aus dem C99-Header &amp;lt;tt&amp;gt;stdint.h&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-funsigned-bitfields&amp;lt;/tt&amp;gt;: Bitfelder mit Basetype &amp;lt;tt&amp;gt;char&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;short&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;long&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;long long&amp;lt;/tt&amp;gt; werden als unsigned implementiert anstatt als signed wie in der avr-gcc ABI.&lt;br /&gt;
:&amp;lt;u&amp;gt;Besser&amp;lt;/u&amp;gt;: Wenn ein Bitfeld unsigned sein soll, dann mach es unsigned!&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fpack-struct&amp;lt;/tt&amp;gt;: Im Gegensatz zur avr-gcc ABI werden Strukturen und Unions per default gepackt.&lt;br /&gt;
:&amp;lt;u&amp;gt;Besser&amp;lt;/u&amp;gt;: Wenn ein zusammengesetzter Typ gepackt werden soll, mache ein Typedef mit explizitem &amp;lt;tt&amp;gt;__attribute__((packed))&amp;lt;/tt&amp;gt;!&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-fshort-enums&amp;lt;/tt&amp;gt;: Enum-Typen werden so kurz wie möglich implementiert anstatt als &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; gemäß avr-gcc ABI.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;-mint8&amp;lt;/tt&amp;gt;: Ein &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; ist nur nocht 8 Bits breit. Dies entspricht nicht mehr dem C-Standard und wird nicht durch die C-Bibliotheken wie AVR-Libc oder newlib unterstützt! Die Codegröße von 8-Bit Operationen kann sich verkleinern, weil andere Integer-Promotion Regeln angewandt werden. Literals müssen ggf. angepasst bzw gecastet werden, siehe auch C99-Makros wie &amp;lt;tt&amp;gt;UINT16_C&amp;lt;/tt&amp;gt; aus &amp;lt;tt&amp;gt;stdint.h&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Anpassungen der Quelle ==&lt;br /&gt;
&lt;br /&gt;
=== Attribute noreturn, OS_main und OS_task ===&lt;br /&gt;
&lt;br /&gt;
Mikrocontroller-Programme laufen normalerweise in einer Endlosschleife, so dass die main-Routine nie verlassen wird.&lt;br /&gt;
Teilt man dies dem Compiler mit, kann er bestimmte Optimierungen durchführen.&lt;br /&gt;
So ist es zum Beispiel unnötig, Code zum Sichern und Zurücklesen von Registern zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Das Mitteilen funktioniert beim gcc über Attribute, die man der Deklaration oder bei der Implementierung einer Funktion anhängt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static void main_loop (void) __attribute__((noreturn));&lt;br /&gt;
void main_loop (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1)&lt;br /&gt;
  {&lt;br /&gt;
     // Hauptschleife&lt;br /&gt;
  }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
oder&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static void __attribute__((noreturn))&lt;br /&gt;
main_loop (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1)&lt;br /&gt;
  {&lt;br /&gt;
     // Hauptschleife&lt;br /&gt;
  }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;tt&amp;gt;main_loop&amp;lt;/tt&amp;gt; kann dann in &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; aufgerufen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int main (void)&lt;br /&gt;
{&lt;br /&gt;
  main_loop();&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das abschließende &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; wird vom Compiler wegoptimiert und belegt keinen Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; kennt weiterhin die Attribute &amp;lt;tt&amp;gt;OS_main&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;OS_task&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Die Verwendung von &amp;lt;tt&amp;gt;OS_main&amp;lt;/tt&amp;gt; kann etwa aussehen wie folgt. Natürlich kann auch wie oben die Hauptschleife in einer eigenen Funktion implementiert werden, und das &amp;lt;tt&amp;gt;return&amp;lt;/tt&amp;gt; verursacht keinen zusätzlichen Code:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int __attribute__((OS_main))&lt;br /&gt;
main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1)&lt;br /&gt;
  {&lt;br /&gt;
     // Hauptschleife&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Statische Variablen in einer Struktur sammeln ===&lt;br /&gt;
&lt;br /&gt;
Gibt es in einem Programm mehrere inhaltlich zusammengehörende Variablen, dann ist es sinnvoll diese in einer Struktur zu vereinigen.  Neben einer klareren Programm- bzw. Datenstruktur kann dies auch zu kleinerem Code führen.&lt;br /&gt;
&lt;br /&gt;
Beispiel ist die folgende kleine Routine, welche die Zeit in der globalen &amp;lt;tt&amp;gt;time&amp;lt;/tt&amp;gt;-Strukture um eine Sekunde erhöht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct &lt;br /&gt;
{&lt;br /&gt;
    uint8_t second;&lt;br /&gt;
    uint8_t minute;&lt;br /&gt;
    uint8_t hour;&lt;br /&gt;
} time_t;&lt;br /&gt;
 &lt;br /&gt;
// Globale time-Struktur enthält die Zeit&lt;br /&gt;
time_t time;&lt;br /&gt;
 &lt;br /&gt;
void next_second (void)&lt;br /&gt;
{&lt;br /&gt;
    // Zeiger auf die globale time-Struktur&lt;br /&gt;
    time_t *ptime = &amp;amp;time;&lt;br /&gt;
    &lt;br /&gt;
    // time um 1 Sekunde erhöhen&lt;br /&gt;
&lt;br /&gt;
    if (++ptime-&amp;gt;second == 60)&lt;br /&gt;
    {&lt;br /&gt;
        ptime-&amp;gt;second = 0;&lt;br /&gt;
        &lt;br /&gt;
        if (++ptime-&amp;gt;minute == 60)&lt;br /&gt;
        {&lt;br /&gt;
            ptime-&amp;gt;minute = 0;&lt;br /&gt;
            &lt;br /&gt;
            if (++ptime-&amp;gt;hour == 24)&lt;br /&gt;
                ptime-&amp;gt;hour = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion enthält mehrere indirekte Zugriffe auf die &amp;lt;tt&amp;gt;time&amp;lt;/tt&amp;gt;-Struktur, und es wäre günstig, wenn &amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; indirekte Adressierung für die Zugriffe verwendet: Alle Zugriffe geschehen über den Struktur-Zeiger &amp;lt;tt&amp;gt;ptime&amp;lt;/tt&amp;gt;, und ein indirekter Zugriff per &amp;lt;tt&amp;gt;LD&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;LDD&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ST&amp;lt;/tt&amp;gt; oder &amp;lt;tt&amp;gt;STT&amp;lt;/tt&amp;gt; kostet 2&amp;amp;nbsp;Bytes, während die direkten Spreicherzugriffe &amp;lt;tt&amp;gt;LDS&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;STS&amp;lt;/tt&amp;gt; jeweils 4&amp;amp;nbsp;Bytes verbrauchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;avr-gcc&amp;lt;/tt&amp;gt; erzeugt mit Optimierung auf Größe jedoch folgenden Code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
next_second:&lt;br /&gt;
    // if (++ptime-&amp;gt;second == 60)&lt;br /&gt;
    lds  r24, time&lt;br /&gt;
    subi r24, -1&lt;br /&gt;
    sts  time, r24&lt;br /&gt;
    cpi  r24, 60&lt;br /&gt;
    brne .L1&lt;br /&gt;
    // ptime-&amp;gt;second = 0;&lt;br /&gt;
    sts  time, __zero_reg__&lt;br /&gt;
    // if (++ptime-&amp;gt;minute == 60)&lt;br /&gt;
    lds  r24, time+1&lt;br /&gt;
    subi r24, -1&lt;br /&gt;
    sts  time+1, r24&lt;br /&gt;
    cpi  r24, 60&lt;br /&gt;
    brne .L1&lt;br /&gt;
    // ptime-&amp;gt;minute = 0;&lt;br /&gt;
    sts  time+1, __zero_reg__&lt;br /&gt;
    // if (++ptime-&amp;gt;hour == 24)&lt;br /&gt;
    lds  r24,time+2&lt;br /&gt;
    subi r24, -1&lt;br /&gt;
    sts  time+2, r24&lt;br /&gt;
    cpi  r24, 24&lt;br /&gt;
    brne .L1&lt;br /&gt;
    // ptime-&amp;gt;hour = 0;&lt;br /&gt;
    sts  time+2, __zero_reg__&lt;br /&gt;
.L1:&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
D.h. obwohl der C-Code indirekt zugreift, enthält das Compilat direkte Zugriffe und der Code belegt 56&amp;amp;nbsp;Bytes an Flash. Grund ist, daß der Compiler den Inhalt der Variablen &amp;lt;tt&amp;gt;ptime&amp;lt;/tt&amp;gt; kennt und daher die indirekten Struktur-Zugriffe in direkte umwandelt.&lt;br /&gt;
&lt;br /&gt;
Um indirekte Adressierung im erzeugten Code zu erzwingen, kann man die Struktur-Adresse als Parameter übergeben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void next_second (time_t *ptime)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
oder – als hässliche Lösung – dem Compiler des Wissen um den Inhalt von &amp;lt;tt&amp;gt;ptime&amp;lt;/tt&amp;gt; per Inline-Assembler nehmen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (ptime));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies führt denn zu folgendem Code, der um 25% kleiner ist und nur noch 42&amp;amp;nbsp;Bytes Flash belegt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
next_second:&lt;br /&gt;
    // time_t *ptime = &amp;amp;time;&lt;br /&gt;
    // asm (&amp;quot;&amp;quot; : &amp;quot;+r&amp;quot; (ptime));&lt;br /&gt;
    ldi  r30, lo8(time)&lt;br /&gt;
    ldi  r31, hi8(time)&lt;br /&gt;
    // if (++ptime-&amp;gt;second == 60)&lt;br /&gt;
    ld   r24, Z&lt;br /&gt;
    subi r24, -1&lt;br /&gt;
    st   Z, r24&lt;br /&gt;
    cpi  r24, 60&lt;br /&gt;
    brne .L1&lt;br /&gt;
    // ptime-&amp;gt;second = 0;&lt;br /&gt;
    st   Z, __zero_reg__&lt;br /&gt;
    // if (++ptime-&amp;gt;minute == 60)&lt;br /&gt;
    ldd  r24, Z+1&lt;br /&gt;
    subi r24, -1&lt;br /&gt;
    std  Z+1, r24&lt;br /&gt;
    cpi  r24, 60&lt;br /&gt;
    brne .L1&lt;br /&gt;
    // ptime-&amp;gt;minute = 0;&lt;br /&gt;
    std  Z+1, __zero_reg__&lt;br /&gt;
    // if (++ptime-&amp;gt;hour == 24)&lt;br /&gt;
    ldd  r24, Z+2&lt;br /&gt;
    subi r24, -1&lt;br /&gt;
    std  Z+2, r24&lt;br /&gt;
    cpi  r24, 24&lt;br /&gt;
    brne .L1&lt;br /&gt;
    // ptime-&amp;gt;hour = 0;&lt;br /&gt;
    std  Z+2, __zero_reg__&lt;br /&gt;
.L1:&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weil AVRs nur zwei Zeiger-Register haben, die über diese Addressierungsart verfügen (Y und Z), ist diese Optimierung nur eingeschränkt anwendbar.&lt;br /&gt;
&lt;br /&gt;
Werden etwa Z oder Y für andere Zwecke benötigt – etwa für Flash-Adressierung per &amp;lt;tt&amp;gt;LPM&amp;lt;/tt&amp;gt; oder Y als Frame-Pointer gebraucht – verkleinert sich das Anwendungsfeld noch weiter. Zudem müssen genügend Struktur-Zugriffe nacheinander erfolgen, damit ein positiven Effekt auf die Codegröße zustande kommt. Immerhin muss die Adresse geladen werden, das Zeiger-Register wird belegt und steht nicht für andere Variablen zur Verfügung, und im Falle von Y kommen &amp;lt;tt&amp;gt;PUSH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POP&amp;lt;/tt&amp;gt; im Prolog/Epilog hinzu. Werden viele unterschiedliche Struktur-Pointer verwendet (&amp;quot;viele&amp;quot; relativ zur Anzahl der verfügbaren Pointer-Registern), kann die Codegröße auch ansteigen.&lt;br /&gt;
&lt;br /&gt;
Direkte Zugriffe sind in jedem Falle schneller, denn direkte und indirekte Zugriffe kosten die gleiche Zeit. Die Indirekten Zugriffe erfordern jedoch die Initialisierung des Zeiger-Registers und evl &amp;lt;tt&amp;gt;PUSH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POP&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Multiplikationen mit Konstanten ===&lt;br /&gt;
&lt;br /&gt;
Der Compiler instanziiert sofort eine teure allgemeine Bibliotheksfunktion, auch wenn es anders ginge. Ich hatte eine einzige 32-bit Multiplikation mit 10 drin, die mir ein mulsi3 beschert hat. Mit a = (b&amp;lt;&amp;lt;3) + (b&amp;lt;&amp;lt;1) geht es in dem Fall kürzer. Wie gesagt, map-File beobachten.&lt;br /&gt;
 &lt;br /&gt;
;Anmerkung: Variablen als unsigned definieren, dann sollte der Compiler das selbst machen.&lt;br /&gt;
&lt;br /&gt;
;Anmerkung: Auch Schieben ist teuer auf AVR. Schauen wir uns also mal an, was aus folgendem Code wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint32_t foo (uint32_t i)&lt;br /&gt;
{&lt;br /&gt;
    return i*10;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint32_t bar (uint32_t i)&lt;br /&gt;
{&lt;br /&gt;
    return (i &amp;lt;&amp;lt; 1) + (i &amp;lt;&amp;lt; 3);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Scrollbox|15ex;|&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
00000032 &amp;lt;foo&amp;gt;:&lt;br /&gt;
  32:	2a e0       	ldi	r18, 0x0A	; 10&lt;br /&gt;
  34:	30 e0       	ldi	r19, 0x00	; 0&lt;br /&gt;
  36:	40 e0       	ldi	r20, 0x00	; 0&lt;br /&gt;
  38:	50 e0       	ldi	r21, 0x00	; 0&lt;br /&gt;
  3a:	19 d0       	rcall	.+50     	; 0x6e &amp;lt;__mulsi3&amp;gt;&lt;br /&gt;
  3c:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
0000003e &amp;lt;bar&amp;gt;:&lt;br /&gt;
  3e:	26 2f       	mov	r18, r22&lt;br /&gt;
  40:	37 2f       	mov	r19, r23&lt;br /&gt;
  42:	48 2f       	mov	r20, r24&lt;br /&gt;
  44:	59 2f       	mov	r21, r25&lt;br /&gt;
  46:	22 0f       	add	r18, r18&lt;br /&gt;
  48:	33 1f       	adc	r19, r19&lt;br /&gt;
  4a:	44 1f       	adc	r20, r20&lt;br /&gt;
  4c:	55 1f       	adc	r21, r21&lt;br /&gt;
  4e:	e3 e0       	ldi	r30, 0x03	; 3&lt;br /&gt;
  50:	66 0f       	add	r22, r22&lt;br /&gt;
  52:	77 1f       	adc	r23, r23&lt;br /&gt;
  54:	88 1f       	adc	r24, r24&lt;br /&gt;
  56:	99 1f       	adc	r25, r25&lt;br /&gt;
  58:	ea 95       	dec	r30&lt;br /&gt;
  5a:	d1 f7       	brne	.-12     	; 0x50 &amp;lt;__SREG__+0x11&amp;gt;&lt;br /&gt;
  5c:	26 0f       	add	r18, r22&lt;br /&gt;
  5e:	37 1f       	adc	r19, r23&lt;br /&gt;
  60:	48 1f       	adc	r20, r24&lt;br /&gt;
  62:	59 1f       	adc	r21, r25&lt;br /&gt;
  64:	95 2f       	mov	r25, r21&lt;br /&gt;
  66:	84 2f       	mov	r24, r20&lt;br /&gt;
  68:	73 2f       	mov	r23, r19&lt;br /&gt;
  6a:	62 2f       	mov	r22, r18&lt;br /&gt;
  6c:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
0000006e &amp;lt;__mulsi3&amp;gt;:&lt;br /&gt;
  6e:	ff 27       	eor	r31, r31&lt;br /&gt;
  70:	ee 27       	eor	r30, r30&lt;br /&gt;
  72:	bb 27       	eor	r27, r27&lt;br /&gt;
  74:	aa 27       	eor	r26, r26&lt;br /&gt;
&lt;br /&gt;
00000076 &amp;lt;__mulsi3_loop&amp;gt;:&lt;br /&gt;
  76:	60 ff       	sbrs	r22, 0&lt;br /&gt;
  78:	04 c0       	rjmp	.+8      	; 0x82 &amp;lt;__mulsi3_skip1&amp;gt;&lt;br /&gt;
  7a:	a2 0f       	add	r26, r18&lt;br /&gt;
  7c:	b3 1f       	adc	r27, r19&lt;br /&gt;
  7e:	e4 1f       	adc	r30, r20&lt;br /&gt;
  80:	f5 1f       	adc	r31, r21&lt;br /&gt;
&lt;br /&gt;
00000082 &amp;lt;__mulsi3_skip1&amp;gt;:&lt;br /&gt;
  82:	22 0f       	add	r18, r18&lt;br /&gt;
  84:	33 1f       	adc	r19, r19&lt;br /&gt;
  86:	44 1f       	adc	r20, r20&lt;br /&gt;
  88:	55 1f       	adc	r21, r21&lt;br /&gt;
  8a:	96 95       	lsr	r25&lt;br /&gt;
  8c:	87 95       	ror	r24&lt;br /&gt;
  8e:	77 95       	ror	r23&lt;br /&gt;
  90:	67 95       	ror	r22&lt;br /&gt;
  92:	89 f7       	brne	.-30     	; 0x76 &amp;lt;__mulsi3_loop&amp;gt;&lt;br /&gt;
  94:	00 97       	sbiw	r24, 0x00	; 0&lt;br /&gt;
  96:	76 07       	cpc	r23, r22&lt;br /&gt;
  98:	71 f7       	brne	.-36     	; 0x76 &amp;lt;__mulsi3_loop&amp;gt;&lt;br /&gt;
&lt;br /&gt;
0000009a &amp;lt;__mulsi3_exit&amp;gt;:&lt;br /&gt;
  9a:	9f 2f       	mov	r25, r31&lt;br /&gt;
  9c:	8e 2f       	mov	r24, r30&lt;br /&gt;
  9e:	7b 2f       	mov	r23, r27&lt;br /&gt;
  a0:	6a 2f       	mov	r22, r26&lt;br /&gt;
  a2:	08 95       	ret&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
: Der Funktionsaufruf samt Lib-Funktion ist garnicht sooo teuer. Bereits mit zwei Multiplikationen im Programm &amp;amp;mdash; auch einer Multiplikation mit einer anderen Konstanten oder einer Variablen &amp;amp;mdash; gewinnt die lib-Version, da der Code wiederverwendet wird. Übrigens sind diese Multiplikationsroutinen und auch die in die libgcc enthaltenen Divisionen keine &amp;quot;normalen&amp;quot; Funktionen wie sie von C erzeugt werden. avr-gcc weiß genau, welche Register diese Routinen belegen und welche nicht. Damit ist der Aufruf einer solchen Funktion billiger als ein herkömmlicher Funktionsaufruf, bei dem die Funktion als Blackbox behandelt werden muss, die alle call-clobbered Register zerstört.&lt;br /&gt;
&lt;br /&gt;
=== Alle Variablen nur so breit wie nötig ===&lt;br /&gt;
&lt;br /&gt;
Hatte ich eigentlich schon, nur an einigen wenigen Stellen war ich da etwas nachlässig. Mitunter reicht ein kleinerer Typ doch, wenn man z.&amp;amp;nbsp;B. vorher geeignet skaliert. Am besten nur die skalaren Typen aus &amp;lt;stdint.h&amp;gt; verwenden, das erleichtert auch das Folgende. Bei RAM Knappheit: kann ich Strings sinnvollerweise aus dem RAM ins Flash verbannen? Kann ich es mir leisten mehrere Flag-Variablen in ein Byte zusammenzufassen, auch wenn dann die Zugriffe möglicherweise etwas langsamer werden.&lt;br /&gt;
&lt;br /&gt;
=== Logische Operatoren werden auf int-Größe erweitert ===&lt;br /&gt;
&lt;br /&gt;
Obwohl der AVR ein 8-Bit Controller ist, verlangt der C-Standard, daß ein &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; mindestens 16&amp;amp;nbsp;Bits groß ist. Wegen den Promotion-Regeln von C werden 8-Bit Operanden in Operationen auf 16&amp;amp;nbsp;Bits aufgeweitet. Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t c;&lt;br /&gt;
&lt;br /&gt;
void foo (uint8_t a, uint8_t b)&lt;br /&gt;
{&lt;br /&gt;
    if (a == ~b)&lt;br /&gt;
        c = 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Den zweiten Operanden mit dem Komplement weitet der Compiler auf 16 Bit auf, wodurch alle high-Bits von &amp;lt;tt&amp;gt;~b&amp;lt;/tt&amp;gt; gesetzt werden. Der Compiler erkennt, daß der Vergleich niemals wahr ist und optimiert ihn weg:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
foo:&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Cast verhindert dieses:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void foo (uint8_t a, uint8_t b)&lt;br /&gt;
{&lt;br /&gt;
    if (a == (uint8_t) ~b)&lt;br /&gt;
        c = 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
was übersetzt wird zu:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
foo:&lt;br /&gt;
    // if (a == (uint8_t) ~b)&lt;br /&gt;
    com  r22&lt;br /&gt;
    cpse r24, r22   &lt;br /&gt;
    rjmp .L1&lt;br /&gt;
    // c = 0&lt;br /&gt;
    sts c, __zero_reg__&lt;br /&gt;
.L1:&lt;br /&gt;
ret&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Achtung: Tatsächlich handelt es sich dabei nicht um ein Optimierungsproblem, sondern einen typischen Programmierfehler. Die beiden Varianten sind keineswegs identisch! Bei Variablen vom Typ &amp;lt;tt&amp;gt;uint8_t&amp;lt;/tt&amp;gt; ist der Ausdruck &amp;lt;tt&amp;gt;(a == ~b)&amp;lt;/tt&amp;gt; immer falsch! Dies unterstreicht mehr als eindringlich, daß eine gute Kenntnis der Programmiersprache das A und O jedes erfolgreichen Programmierers ist. Kenne zuallerst mal deine Programmiersprache - und zwar auch in den intimen Details!&lt;br /&gt;
&lt;br /&gt;
=== Speichern von globalen Flags ===&lt;br /&gt;
&lt;br /&gt;
Oft werden in den Programmen Flags verwendet um beispielsweise eingetroffene Interrupts in der main-Routine auszuwerten. Hierzu wird üblicherweise eine globale Variable verwendet.&lt;br /&gt;
&lt;br /&gt;
Um den Wert dieser Variable abzufragen, muss sie jedoch erst aus dem SRAM in ein Register geladen werden, und kann dann erst auf ihren Status hin überprüft werden. Eine Möglichkeit ist, der globalen Variablen ein einziges Register fest zuzuordnen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
register uint8_t counter8_1 asm(&amp;quot;r2&amp;quot;);&lt;br /&gt;
register uint8_t counter8_2 asm(&amp;quot;r3&amp;quot;);&lt;br /&gt;
register uint16_t counter16_1 asm(&amp;quot;r4&amp;quot;); // r4:r5&lt;br /&gt;
register uint16_t counter16_2 asm(&amp;quot;r6&amp;quot;); // r6:r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
siehe auch: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_regbind&lt;br /&gt;
&lt;br /&gt;
Als Alternative kann man ein nicht verwendetes Register des I/O-Bereichs verwenden. Dabei würde sich z.&amp;amp;nbsp;B. das Register eines zweiten UARTs, oder das  EEPROM-Register anbieten, falls diese nicht benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Neuere AVR-Modelle besitzen für diesen Zweck 3 frei verwendbare Bytes im bitadressierbaren I/O-Bereich: GPIOR0-2.&lt;br /&gt;
&lt;br /&gt;
{{Warnung|&lt;br /&gt;
;Warnung: Dieses Vorgehen verändert das ABI! Um dieses Feature fehlerfrei anzuwenden, ist einiges an Wissen über die Interna von GCC notwendig. Auch ein korrekt funktionierendes Programm ist keine Garantie dafür, daß die globalen Register fehlerfrei implementiert wurden. Unter Umständen bringen erst spätere Codeänderungen/-erweiterung den Fehler zum Vorschein, und weil der Fehler vorher nicht akut war, sucht man sich den Wolf an der falschen Stelle im Code anstatt bei der globalen Registern. Siehe auch [[Globale Register]].}}&lt;br /&gt;
&lt;br /&gt;
=== Puffern von volatile-Variablen ===&lt;br /&gt;
&lt;br /&gt;
Der Compiler behandelt volatile-Variablen bei mehreren Manipulationen wie heiße Kartoffeln. Für jeden einzelnen Vorgang wiederholt sich das Spiel:&lt;br /&gt;
&lt;br /&gt;
* aus dem Speicher holen&lt;br /&gt;
* bearbeiten&lt;br /&gt;
* zurückspeichern&lt;br /&gt;
&lt;br /&gt;
Unter Umständen ist dieses Verhalten unsinnig. Ein Minimalbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile char var;&lt;br /&gt;
&lt;br /&gt;
ISR()&lt;br /&gt;
{&lt;br /&gt;
    var++;&lt;br /&gt;
&lt;br /&gt;
    if (var &amp;gt; 100)&lt;br /&gt;
        var = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main (void)&lt;br /&gt;
{&lt;br /&gt;
    while (1)&lt;br /&gt;
        printf (var);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Hier wird &#039;&#039;&#039;var&#039;&#039;&#039; pro [[ISR]]-Ausführung zwei mal aus dem RAM geholt und zurückgeschrieben. Das ist überflüssig, weil die Interruptrountine nicht unterbrochen werden kann. Aus Sicht der ISR bräuchte man eigentlich kein volatile, kann es aber wegen des Zugriffs aus main heraus nicht weglassen. Eine Lösung findet sich im folgenden Schnipsel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile char var;&lt;br /&gt;
&lt;br /&gt;
ISR()&lt;br /&gt;
{&lt;br /&gt;
    char temp = var;&lt;br /&gt;
&lt;br /&gt;
    if (++temp &amp;gt; 100)&lt;br /&gt;
        temp=0;&lt;br /&gt;
&lt;br /&gt;
    var = temp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void main (void)&lt;br /&gt;
{&lt;br /&gt;
    while (1)&lt;br /&gt;
        printf (var);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Hier wird die globale Variable &#039;&#039;&#039;var&#039;&#039;&#039; in der lokalen Variable &#039;&#039;&#039;temp&#039;&#039;&#039; gepuffert. Ein Nachteil durch das Anlegen von &#039;&#039;&#039;temp&#039;&#039;&#039; ergibt sich nicht, da das dafür verwendete Register für die Manipulation sowieso benötigt wird. &lt;br /&gt;
&lt;br /&gt;
Wie alle Optimierungen kann dieses Vorgehen auch nach hinten losgehen: Wenn Laden und Zurückspeichern von &#039;&#039;&#039;var&#039;&#039;&#039; weit auseinanderliegen (extrem lange ISR), müllt man sich die Register zu. Im schlimmsten Fall wird &#039;&#039;&#039;temp&#039;&#039;&#039; sogar zwischenzeitlich auf dem Stack ausgelagert.&lt;br /&gt;
&lt;br /&gt;
=== Schleifen ===&lt;br /&gt;
&lt;br /&gt;
Bei Schleifen, die eine bestimmte Anzahl an Durchläufen ausgeführt werden sollen, ist es besser den Schleifenzähler vorher auf einen Wert zu setzen, und am Ende einer Do-While Schleife diesen zu dekrementieren.&lt;br /&gt;
So beschränkt sich die Sprungbedingung auf ein brne (branch if not equal).&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t counter;	&lt;br /&gt;
counter = 100;&lt;br /&gt;
do&lt;br /&gt;
{&lt;br /&gt;
    // mach irgendetwas&lt;br /&gt;
} while (--counter);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: Derartige Optimierungen sind meistens zweifelhaft und sehr Compilerabhängig. Compiler betreiben aufwendige Code- und Datenflussanalysen, in denen sie viele Dinge ins Kalkül ziehen, die ein menschlicher Programmierer im Regelfall nicht mehr überblicken kann. Von einem ordentlichen Compiler kann erwartet werden, dass er derartige Umstellungen (sofern sie möglich sind) in Eigenregie erledigt. Auch wenn es einzelne Compiler bzw. Compilerversionen gibt, in denen ein manueller Umbau einer derartigen Schleife tatsächlich eine Verbesserung bringt, sollte man immer im Hinterkopf behalten, dass sich in der nächsten Compilerversion alles umdrehen kann. Im Zweifelsfall lieber die Schleifenvariante benutzen, die der Situation angemessen ist und die den gewünschten Vorgang am klarsten beschreibt. Wenn dieser Vorgang im weitesten Sinne einen Countdown darstellt, dann ist natürlich nichts gegen eine derartige Schleifenkonstruktion einzuwenden.&lt;br /&gt;
&lt;br /&gt;
=== Schiebeoperationen ===&lt;br /&gt;
&lt;br /&gt;
Oft benötigt man Schiebeoperationen, um Daten Bit für Bit zu [[Bitmanipulation | verarbeiten]], z.B. für [http://www.mikrocontroller.net/articles/AVR-Tutorial:_Schieberegister Soft]-[[SPI]]. Hier empfiehlt es sich DRINGEND, möglichst nur Schiebeoperationen mit konstanten Verschiebungszähler durchzuführen, diese sind auf dem AVR deutlich schneller als Schiebeoperationen mit Variablen, welche meist durch mehrfache Funktionsaufrufe mit Scheifen etc. gelöst werden. Dabei verwendet man oft konstante Bitmasken, um die einzelnen Bits nacheinander zu maskieren.&lt;br /&gt;
&lt;br /&gt;
Noch schneller geht das Schieben von Konstanten um einen konstanten Wert, denn das macht der Compiler bei eingeschalteter Optimierung noch während des Compilierens. Gerade für schnelle IO-Operationen kommt dann meist nur ein einziger ASM-Befehl heraus (sbi, cbi).&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es wenig empfehlenswert, 32 oder gar 64 Bit Datentypen zu schieben, das dauert sehr lange. Besser ist es, die Daten als Array von 8 Bit Werten zu handhaben und diese zu schieben, das ist meist deutlich schneller. Gute Beispiele findet man [http://www.mikrocontroller.net/topic/179566#1729219 hier] und [http://www.mikrocontroller.net/topic/169509#1631439 hier].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define DATAPORT PORTD&lt;br /&gt;
#define IO-BIT PD4&lt;br /&gt;
&lt;br /&gt;
uint8_t a, b, i;&lt;br /&gt;
&lt;br /&gt;
i=5&lt;br /&gt;
a = b &amp;lt;&amp;lt; i;    // variabler Verschiebewert, langsam&lt;br /&gt;
a = b &amp;lt;&amp;lt; 5;    // konstanter Verschiebewert, schnell&lt;br /&gt;
&lt;br /&gt;
DATAPORT |= (1&amp;lt;&amp;lt;IO-BIT);   // alles Konstanten, sehr schnell&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Optimierung der Ausführungsgeschwindigkeit==&lt;br /&gt;
&lt;br /&gt;
Hierzu gibt es schon eine Application-Note von Atmel. Diese AppNote bezieht sich auf den IAR-Compiler. Die darin genannten &amp;quot;Optimierungen&amp;quot; sind für avr-gcc größtenteils obsolet oder bleiben bestenfalls ohne Effekt.&lt;br /&gt;
&lt;br /&gt;
Weblinks:&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf AVR035]: Efficient C Coding for AVR&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Program_optimization Program optimization] auf Wikipedia, engl.&lt;br /&gt;
* [http://bleaklow.com/2012/06/20/sensor_smoothing_and_optimised_maths_on_the_arduino.html Sensor smoothing and optimised maths on the Arduino] - Multiplikation vs. Division&lt;br /&gt;
&lt;br /&gt;
== Fußnoten ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
[http://www.atmel.com/Images/doc8453.pdf Atmel AVR4027]: Tips and Tricks to Optimize Your C Code for 8-bit AVR Microcontrollers ([http://www.atmel.com/Images/AVR4027.zip Beispiel-Code])&lt;br /&gt;
&lt;br /&gt;
[[Category:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Entprellung&amp;diff=89257</id>
		<title>Entprellung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Entprellung&amp;diff=89257"/>
		<updated>2015-07-08T12:47:57Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Problembeschreibung ==&lt;br /&gt;
Mechanische [[Schalter]] wie [[Drehgeber]] neigen beim Ein- und Ausschalten zum sogenannten &#039;&#039;&#039;Prellen&#039;&#039;&#039;, d.h sie schalten schnell mehrfach aus und ein, verursacht durch mechanische Vibrationen des Schaltkontaktes, sofern sie nicht dagegen geschützt sind. Vereinfacht dargestellt, sieht eine von einem Schalter oder Taster geschaltete Spannung beim Schalten wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Entprellen.png]]&lt;br /&gt;
&lt;br /&gt;
Für die Vermeidung bzw. Auswertung dieses unsauberen Signals gibt es verschiedene Ansätze:&lt;br /&gt;
&lt;br /&gt;
== Hardwareentprellung ==&lt;br /&gt;
&lt;br /&gt;
===Prellfreie Schalter===&lt;br /&gt;
&lt;br /&gt;
Für Spezialanwendungen hält die elektromechanische Industrie verschiedene Sonderkonstruktionen bereit, die saubere Schaltzustände nach Aussen generieren, indem sie entweder eine mechanische Dämpfung in Form eines selbsthemmenden Federmechanismus oder eine integrierte elektronische Signalverzögerung benutzen.&lt;br /&gt;
&lt;br /&gt;
Solche Systeme sind jedoch teuer und werden meist nur im Leistungsbereich eingesetzt.&lt;br /&gt;
&lt;br /&gt;
===Wechselschalter===&lt;br /&gt;
&lt;br /&gt;
Für die Entprellung von Wechselschaltern (engl. Double Throw Switch) kann ein klassisches RS-[[Flipflop]] genutzt werden. Bei dieser Variante werden neben zwei NAND-Gattern nur noch zwei Pull-Up Widerstände benötigt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NAND_debouncer.png|thumb|left|350px|&#039;&#039;&#039;Taster entprellen mit NAND-RS-Flipflop&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der gezeigten Schalterstellung liegt an der Position /S der Pegel 0 an. Damit ist das Flipflop gesetzt und der Ausgang auf dem Pegel 1. Schließt der Schalter zwischen den Kontakten 2 und 3, liegt an der Postion /R der Pegel 0 an. Dies bedeutet, dass der Ausgang des Flipflops auf den Pegel 0 geht. Sobald der Schalter von einem zum anderen Kontakt wechselt, beginnt er in der Regel zu prellen. Während des Prellens wechselt der Schalter zwischen den beiden Zuständen &amp;quot;Schalter berührt Kontakt&amp;quot; und &amp;quot;Schalter ist frei in der Luft&amp;quot;. Der Ausgang des Flipflops bleibt in dieser Prellzeit aber stabil, da der Schalter während des Prellens nie den gegenüberliegenden Kontakt berührt und das RS-Flipflop seinen Zustand allein halten kann. Die Prellzeit ist stark vom Schaltertyp abhängig und liegt zwischen 0,1 und 10ms. Die Dimensionierung der Widerstände ist relativ unkritisch. Als Richtwert können hier 100kOhm verwendet werden.&lt;br /&gt;
&lt;br /&gt;
====Wechselschalter ohne Flip-Flop====&lt;br /&gt;
&lt;br /&gt;
Wenn man mal gerade kein Flip-Flop zur Hand hat, kann man sich auch mit dieser Schaltung behelfen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:WechselEntprellC.PNG|thumb|left|350px|&#039;&#039;&#039;Wechsler entprellen mit Kondensator&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Funktionsweise:&lt;br /&gt;
Beim Umschalten wird der Kondensator immer sofort umgeladen.&lt;br /&gt;
Während der Kontakt prellt, befindet er sich in der Luft und hat keinerlei Verbindung. Während dieser Zeit übernimmt der Kondensator das halten des Pegels.&lt;br /&gt;
&lt;br /&gt;
Dimensionierung:&lt;br /&gt;
Ist der entprellte Taster an ein IC Angeschlossen, ist der &#039;&#039;Input Leakage Current&#039;&#039; der ausschlaggebende Strom. Falls weitere Ströme fließen sind diese mit zu berücksichtigen. Bei einem Mikrocontroller von Atmel sind 1µA typisch.&lt;br /&gt;
Es gilt:&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{dU}{dt} = \frac{I}{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
Da eine Prellung ca. 10ms dauert und die Spannung in dieser Zeit beispielsweise um maximal 0,5V fallen soll kommt man auf folgende Kapazität:&lt;br /&gt;
:&amp;lt;math&amp;gt; C = \frac{I \cdot dt}{dU} = \frac{1\mu A \cdot 10ms}{0,5V} = 20nF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um Stromspitzen zu verringern kann ein Widerstand mit eingefügt werden. Eine Zeitkonstante von 1µs bis 1ms scheint sinnvoll. Also 500 Ohm bis 500kOhm sind nutzbar, wobei bei niedrigem Widerstand die Stromspitzen höher sind, und bei 500kOhm der Pinstrom störend wird.&lt;br /&gt;
&lt;br /&gt;
===Einfacher Taster===&lt;br /&gt;
&lt;br /&gt;
Auch wenn das RS-Flipflop sehr effektiv ist, wird diese Variante der Entprellung nur selten angewendet. Grund dafür ist, dass in Schaltungen häufiger einfache Taster eingesetzt werden. Diese sind oft kleiner und preisgünstiger. Um einfache Taster (engl. Single Throw Switch) zu entprellen, kann ein einfacher RC-Tiefpass eingesetzt werden. Hierbei wird ein Kondensator über einen Widerstand je nach Schalterstellung auf- oder entladen. Das RC-Glied bildet einen Tiefpass, sodass die Spannung über den Kondensator nicht von einen Pegel auf den anderen springen kann.&lt;br /&gt;
&lt;br /&gt;
[[Bild:RC_debouncer.png|thumb|left|300px|Taster entprellen mit RC-Entpreller]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Entprellen1a.png|thumb|350px| Entstehender Spannungsverlauf]]&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
Wenn der Schalter geöffnet ist, lädt sich der Kondensator langsam über die beiden Widerstände R&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; und R&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; auf V&amp;lt;sub&amp;gt;cc&amp;lt;/sub&amp;gt; auf. Beim Erreichen der Umschaltschwelle springt der Ausgang auf den Pegel 0. Wird der Schalter geschlossen, entlädt sich der Kondensator langsam über den Widerstand R&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;. Demnach ändert sich der Ausgang des Inverters auf den Pegel 1. Während der Taster prellt, kann sich die Spannung über dem Kondensator nicht sprunghaft ändern, da das Auf- und Entladen eher langsam über die Widerstände erfolgt. Außerdem sind die Schaltschwellen für den Übergang LOW-&amp;gt;HIGH und HIGH-&amp;gt;LOW stark verschieden (Hysterese, siehe Artikel [[Schmitt-Trigger]]). Bei richtiger Dimensionierung der Bauelemente wird somit der Ausgang des Inverters prellfrei.&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass der Inverter &#039;&#039;&#039;unbedingt&#039;&#039;&#039; einer mit [[Schmitt-Trigger]]-Eingängen sein muss, weil bei Standard-Logikeingängen im Bereich von üblicherweise 0,8V - 2,0V der Ausgang nicht definiert ist. Als Inverter kann zum Beispiel der 74HC14 oder der CD40106 (pinkompatibel) eingesetzt werden. Alternativ kann auch ein CD4093 eingesetzt werden. Bei dem CD4093 handelt es sich um NAND-Gatter mit Schmitt-Trigger-Eingängen. Um aus einem NAND-Gatter einen Inverter zu machen, müssen einfach nur die beiden Eingänge verbunden werden oder ein Eingang fest auf HIGH gelegt werden.&lt;br /&gt;
&lt;br /&gt;
Für eine geeignete Dimensionierung muss man etwas mit den Standardformeln für einen Kondensator jonglieren. Die Spannung über den Kondensator beim Entladen berechnet sich nach:&lt;br /&gt;
:&amp;lt;math&amp;gt;U_C(t) = U_0 \cdot e^{\frac{-t}{R_2 C_1}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Ausgang des Inverters stabil ist, muss die Spannung über den Kondensator und damit die Spannung am Eingang des Inverters über der Spannung bleiben, bei welcher der Inverter umschaltet. Diese Schwellwertspannung ist genau die zeitabhängige Spannung über den Kondensator.&lt;br /&gt;
:&amp;lt;math&amp;gt;U_C(t)\!\ = U_{th}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Umstellen der Formel ergibt sich nun:&lt;br /&gt;
:&amp;lt;math&amp;gt;R_2=\frac{-t}{C_1 \cdot ln\left(\frac{U_{th}}{U_0} \right)}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Taster prellt üblicherweise etwa 10ms. Zur Sicherheit kann bei der Berechnung des Widerstandes eine Prellzeit von 20ms angenommen werden. U_0 ist die Betriebsspannung also Vcc. Die Schwellwertspannung muss aus dem Datenblatt des eingesetzten Schmitt-Triggers abgelesen werden. Beim 74HC14 beträgt der gesuchte Wert 2,0V. Nimmt man für den Kondensator 1µF und beträgt die  Betriebsspannung 5V, ergibt sich für den Widerstand ein Wert von etwa 22kOhm.&lt;br /&gt;
&lt;br /&gt;
Wird der Schalter geöffnet, lädt sich der Kondensator nach folgender Formel auf:&lt;br /&gt;
:&amp;lt;math&amp;gt;U_C(t) = U_0 \cdot \left( 1-e^{\frac{-t}{(R_1+R_2)\cdot C_1}} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit U_th=U_C ergibt das Umstellen nach (R_1+R_2):&lt;br /&gt;
:&amp;lt;math&amp;gt;R_1+R_2 = \frac{-t}{C_1 \cdot ln\left(1-\frac{U_{th}}{U_0} \right)} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Schwellspannung lässt sich aus dem Datenblatt ein Wert von 2,3V ablesen. Mit diesem Wert und den Annahmen von oben ergibt sich für R_1+R_2 ein Wert von 32kOhm. Somit ergibt sich für R_1 ein Wert von etwa 10kOhm.&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Beim 74LS14 von Hitachi z.&amp;amp;nbsp;B. sind die oberen und unteren Schaltschwellwerte unterschiedlich. Es muss darauf geachtet werden, dass U_{th} beim Entladen die untere Schwelle und U_{th} beim Laden die obere Schwelle einnimmt.&lt;br /&gt;
&lt;br /&gt;
== Softwareentprellung ==&lt;br /&gt;
&lt;br /&gt;
In den Zeiten der elektronischen Auswertung von Tastern und Schaltern ist das softwaretechnische Entprellen oft billiger, als die Benutzung eines teuren Schalters. Daher werden heute z.B. auch Computertastaturen nicht mehr mit prellarmen Tasten oder Entprellkondensatoren ausgestattet.&lt;br /&gt;
&lt;br /&gt;
Bei Verwendung des in den meisten Geräten ohnehin vorhandenen Mikrocontrollers z.B., kann man sich die zusätzliche Hardware sparen, da die Entprellung in Software praktisch genauso gut funktioniert. Dabei ist nur zu beachten, dass zusätzliche Rechenleistung und je nach Umsetzung auch einige Hardwareressourcen (z.B. Timer) benötigt werden. Dafür hat man aber den Vorteil, das man kurze Fehlpulse, die offensichtlich keine Tastenbetätigung sein können sondern zb durch Einstreuungen hervorgerufen werden, einfach ausfiltern zu können.&lt;br /&gt;
&lt;br /&gt;
=== Flankenerkennung ===&lt;br /&gt;
Bei einem Taster gibt es insgesamt 4 theoretische Zustände:&lt;br /&gt;
&lt;br /&gt;
* 1. war nicht gedrückt und ist nicht gedrückt&lt;br /&gt;
* 2. war nicht gedrückt und ist gedrückt (steigende Flanke)&lt;br /&gt;
* 3. war gedrückt und ist immer noch gedrückt&lt;br /&gt;
* 4. war gedrückt und ist nicht mehr gedrückt (fallende Flanke)&lt;br /&gt;
&lt;br /&gt;
Diese einzelnen Zustände lassen sich jetzt bequem abfragen/durchlaufen. Die Entprellung geschieht dabei durch die ganze Laufzeit des Programms. Die Taster werden hierbei als Active-Low angeschlossen, um die internen Pull-Ups zu nutzen.&lt;br /&gt;
&lt;br /&gt;
Diese Routine gibt für den Zustand &amp;quot;steigende Flanke&amp;quot; den Wert &amp;quot;1&amp;quot; zurück, sonst &amp;quot;0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define TASTERPORT PINC&lt;br /&gt;
#define TASTERBIT PINC1&lt;br /&gt;
&lt;br /&gt;
char taster(void)&lt;br /&gt;
{&lt;br /&gt;
    static unsigned char zustand;&lt;br /&gt;
    char rw = 0;&lt;br /&gt;
&lt;br /&gt;
    if(zustand == 0 &amp;amp;&amp;amp; !(TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird gedrueckt (steigende Flanke)&lt;br /&gt;
    {&lt;br /&gt;
        zustand = 1;&lt;br /&gt;
        rw = 1;&lt;br /&gt;
    }&lt;br /&gt;
    else if (zustand == 1 &amp;amp;&amp;amp; !(TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird gehalten&lt;br /&gt;
    {&lt;br /&gt;
         zustand = 2;&lt;br /&gt;
         rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else if (zustand == 2 &amp;amp;&amp;amp; (TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird losgelassen (fallende Flanke)&lt;br /&gt;
    {&lt;br /&gt;
        zustand = 3;&lt;br /&gt;
        rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else if (zustand == 3 &amp;amp;&amp;amp; (TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster losgelassen&lt;br /&gt;
    {&lt;br /&gt;
        zustand = 0;&lt;br /&gt;
        rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return rw;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Erweiterung, damit beliebig lange das Halten einer Taste erkannt wird kann man ganz einfach so implementieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // zustand kann entweder zum ersten mal als gehalten detektiert werden oder aber jedes weitere mal&lt;br /&gt;
    else if (((zustand == 1) || (zustand == 2)) &amp;amp;&amp;amp; !(TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird gehalten&lt;br /&gt;
    {&lt;br /&gt;
         zustand = 2;&lt;br /&gt;
         rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Warteschleifen-Verfahren ===&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem Mikrocontroller gezählt werden, wie oft ein Kontakt oder ein Relais geschaltet wird, muss das Prellen des Kontaktes exakt berücksichtigt - und von einem gewollten Mehrfachschalten abgegrenzt werden, da sonst  möglicherweise Fehlimpulse gezählt- oder andererseits echte Schaltvorgänge übersprungen werden. Dies muss beim Schreiben des Programms hinsichtlich des Abtastens des Kontaktes unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
Beim folgenden einfachen Beispiel für eine Entprellung ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritischen Anwendungen sollte man ein anderes Verfahren nutzen (z.&amp;amp;nbsp;B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( !(*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);   // Maximalwert des Parameters an _delay_ms &lt;br /&gt;
        _delay_ms(50);   // beachten, vgl. Dokumentation der avr-libc&lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );        /* PIN PB0 auf Eingang Taster)  */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );        /* Pullup-Widerstand aktivieren */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))&lt;br /&gt;
    {&lt;br /&gt;
        /* Falls Taster an PIN PB0 gedrueckt     */&lt;br /&gt;
        /* LED an Port PD7 an- bzw. ausschalten: */&lt;br /&gt;
        PORTD = PORTD ^ ( 1 &amp;lt;&amp;lt; PD7 );&lt;br /&gt;
    }&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die obige Routine hat leider mehrere Nachteile:&lt;br /&gt;
* sie detektiert nur das Loslassen (unergonomisch)&lt;br /&gt;
* sie verzögert die Mainloop immer um 100ms bei gedrückter Taste&lt;br /&gt;
* sie verliert Tastendrücke, je mehr die Mainloop zu tun hat.&lt;br /&gt;
&lt;br /&gt;
Eine ähnlich einfach zu benutzende Routine, aber ohne all diese Nachteile findet sich im Forenthread&lt;br /&gt;
[http://www.mikrocontroller.net/topic/164194#new Entprellung für Anfänger]&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;DEBOUNCE&#039;&#039; Befehl in dem BASIC-Dialekt BASCOM für AVR ist ebenfalls nach dem Warteschleifen-Verfahren programmiert. Die Wartezeit beträgt standardmäßig 25 ms, kann aber vom Anwender überschrieben werden. Vgl.  [http://avrhelp.mcselec.com/bascom-avr.html?DEBOUNCE BASCOM Online-Manual zu DEBOUNCE].&lt;br /&gt;
&lt;br /&gt;
Eine C-Implementierung für eine Tastenabfrage mit Warteschleife ist im Artikel [[AVR-GCC-Tutorial#IO-Register_als_Parameter_und_Variablen|AVR-GCC-Tutorial: IO-Register als Parameter und Variablen]] angeben.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil dieses Verfahrens ist, dass der Controller durch die Warteschleife blockiert wird. Günstiger ist die Implementierung mit einem Timer-Interrupt.&lt;br /&gt;
&lt;br /&gt;
==== Warteschleifenvariante mit Maske und Pointer (nach Christian Riggenbach) ====&lt;br /&gt;
&lt;br /&gt;
Hier eine weitere Funktion, um Taster zu entprellen: Durch den zusätzlichen Code kann eine Entprellzeit von durchschnittlich 1-3ms (mindestens 8*150µs = 1ms) erreicht werden. Grundsätzlich prüft die Funktion den Pegel der Pins auf einem bestimmten Port. Wenn die/der Pegel 8 Mal konstant war, wird die Schleife verlassen. Diese Funktion kann sehr gut eingesetzt werden, um in einer Endlosschleife Taster anzufragen, da sie, wie erwähnt, eine kurze Wartezeit hat.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void entprellung( volatile uint8_t *port, uint8_t maske ) {&lt;br /&gt;
  uint8_t   port_puffer;&lt;br /&gt;
  uint8_t   entprellungs_puffer;&lt;br /&gt;
&lt;br /&gt;
  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {&lt;br /&gt;
    entprellungs_puffer&amp;lt;&amp;lt;=1;&lt;br /&gt;
    port_puffer = *port;&lt;br /&gt;
    _delay_us(150);&lt;br /&gt;
    if( (*port &amp;amp; maske) == (port_puffer &amp;amp; maske) )&lt;br /&gt;
      entprellungs_puffer |= 0x01;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion wird wie folgt aufgerufen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Bugfix 20100414&lt;br /&gt;
  // http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass&lt;br /&gt;
  entprellung( &amp;amp;PINB, (1&amp;lt;&amp;lt;PINB2) ); // ggf. Prellen abwarten &lt;br /&gt;
  if( PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2) )           // dann stabilen Wert einlesen&lt;br /&gt;
  {&lt;br /&gt;
    // mach was&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    // mach was anderes&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als Maske kann ein beliebiger Wert übergeben werden. Sie verhindert, dass nichtverwendete Taster die Entprellzeit negativ beeinflussen.&lt;br /&gt;
&lt;br /&gt;
==== Debounce-Makro von Peter Dannegger ====&lt;br /&gt;
&lt;br /&gt;
Peter Dannegger hat in [http://www.mikrocontroller.net/topic/164194#1566921 &amp;quot;Entprellen für Anfänger&amp;quot;] folgende vereinfachtes Entprellverfahren beschrieben. Das Makro arbeitet in der Originalversion mit &#039;&#039;active low&#039;&#039; geschalteten Tastern, kann aber einfach für  &#039;&#039;active high&#039;&#039; geschaltete Taster angepasst werden ([[Pollin Funk-AVR-Evaluationsboard#Tasty Reloaded|Tasty Reloaded]]). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*                      Not so powerful Debouncing Example              */&lt;br /&gt;
/*                      No Interrupt needed                             */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*              Author: Peter Dannegger                                 */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
// Target: ATtiny13&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#define F_CPU 9.6e6&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define debounce( port, pin )                                         \&lt;br /&gt;
({                                                                    \&lt;br /&gt;
  static uint8_t flag = 0;     /* new variable on every macro usage */  \&lt;br /&gt;
  uint8_t i = 0;                                                      \&lt;br /&gt;
                                                                      \&lt;br /&gt;
  if( flag ){                  /* check for key release: */           \&lt;br /&gt;
    for(;;){                   /* loop ... */                         \&lt;br /&gt;
      if( !(port &amp;amp; 1&amp;lt;&amp;lt;pin) ){  /* ... until key pressed or ... */     \&lt;br /&gt;
        i = 0;                 /* 0 = bounce */                       \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
      _delay_us( 98 );         /* * 256 = 25ms */                     \&lt;br /&gt;
      if( --i == 0 ){          /* ... until key &amp;gt;25ms released */     \&lt;br /&gt;
        flag = 0;              /* clear press flag */                 \&lt;br /&gt;
        i = 0;                 /* 0 = key release debounced */        \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
    }                                                                 \&lt;br /&gt;
  }else{                       /* else check for key press: */        \&lt;br /&gt;
    for(;;){                   /* loop ... */                         \&lt;br /&gt;
      if( (port &amp;amp; 1&amp;lt;&amp;lt;pin) ){   /* ... until key released or ... */    \&lt;br /&gt;
        i = 0;                 /* 0 = bounce */                       \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
      _delay_us( 98 );         /* * 256 = 25ms */                     \&lt;br /&gt;
      if( --i == 0 ){          /* ... until key &amp;gt;25ms pressed */      \&lt;br /&gt;
        flag = 1;              /* set press flag */                   \&lt;br /&gt;
        i = 1;                 /* 1 = key press debounced */          \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
    }                                                                 \&lt;br /&gt;
  }                                                                   \&lt;br /&gt;
  i;                           /* return value of Macro */            \&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Testapplication&lt;br /&gt;
 */&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB  &amp;amp;= ~(1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
  PORTB |=   1&amp;lt;&amp;lt;PB0;&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
  DDRB  &amp;amp;= ~(1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
  PORTB |=   1&amp;lt;&amp;lt;PB1;&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  for(;;){&lt;br /&gt;
    if( debounce( PINB, PB1 ) )&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
    if( debounce( PINB, PB0 ) )&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Makro für die gleiche Taste (Pin) an mehreren Stellen aufgerufen werden soll, muss eine Funktion angelegt werden, damit beide Aufrufe an die gleiche Zustandsvariable &#039;&#039;flag&#039;&#039; auswerten [http://www.mikrocontroller.net/topic/195914#1918727]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Hilfsfunktion&lt;br /&gt;
uint8_t debounce_C1( void )&lt;br /&gt;
{&lt;br /&gt;
  return debounce(PINC, PC1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Beispielanwendung&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
  PORTC |=   1&amp;lt;&amp;lt;PC1; // Pullup für Taster&lt;br /&gt;
&lt;br /&gt;
  for(;;){&lt;br /&gt;
    if( debounce_C1() )  // nicht: debounce(PINC, PC1)&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
    if( debounce_C1() )  // nicht: debounce(PINC, PC1)&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Timer-Verfahren (nach Peter Dannegger) ===&lt;br /&gt;
&lt;br /&gt;
==== Grundroutine (AVR Assembler) ====&lt;br /&gt;
&lt;br /&gt;
Siehe dazu: [http://www.mikrocontroller.net/forum/read-4-20435.html#new Forum] &lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* besonders kurzer Code&lt;br /&gt;
* schnell&lt;br /&gt;
&lt;br /&gt;
Außerdem können 8 Tasten (aktiv low) gleichzeitig bearbeitet werden, es dürfen also&lt;br /&gt;
alle exakt zur selben Zeit gedrückt werden. Andere Routinen können z.&amp;amp;nbsp;B. nur eine Taste verarbeiten, d.h. die zuerst oder zuletzt gedrückte gewinnt, oder es kommt Unsinn heraus.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Einlese- und Entprellroutine ist nur 8 Instruktionen&lt;br /&gt;
kurz. Der entprellte Tastenzustand ist im Register &#039;&#039;key_state&#039;&#039;. Mit nur 2 weiteren Instruktionen wird dann der Wechsel von &#039;&#039;Taste offen&#039;&#039; zu&lt;br /&gt;
&#039;&#039;Taste gedrückt&#039;&#039; erkannt und im Register &#039;&#039;key_press&#039;&#039; abgelegt. Im Beispielcode werden dann damit 8 LEDs ein- und ausgeschaltet. Jede Taste entspricht einem Bit in den Registern, d.h. die Verarbeitung erfolgt bitweise mit logischen Operationen. Zum Verständnis empfiehlt es sich daher, die Logikgleichungen mit Gattern für ein Bit = eine Taste aufzumalen. Die Register kann man sich als Flipflops denken, die mit der Entprellzeit als Takt arbeiten. D.h. man kann das auch so z.&amp;amp;nbsp;B. in einem GAL22V10 realisieren.&lt;br /&gt;
&lt;br /&gt;
Als Kommentar sind neben den einzelnen Instruktionen alle 8 möglichen&lt;br /&gt;
Kombinationen der 3 Signale dargestellt.&lt;br /&gt;
&lt;br /&gt;
Beispielcode für AVR (Assembler):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.nolist&lt;br /&gt;
.include &amp;quot;c:\avr\inc\1200def.inc&amp;quot;&lt;br /&gt;
.list&lt;br /&gt;
.def  save_sreg         = r0&lt;br /&gt;
.def  iwr0              = r1&lt;br /&gt;
.def  iwr1              = r2&lt;br /&gt;
&lt;br /&gt;
.def  key_old           = r3&lt;br /&gt;
.def  key_state         = r4&lt;br /&gt;
.def  key_press         = r5&lt;br /&gt;
&lt;br /&gt;
.def  leds              = r16&lt;br /&gt;
.def  wr0               = r17&lt;br /&gt;
&lt;br /&gt;
.equ  key_port          = pind&lt;br /&gt;
.equ  led_port          = portb&lt;br /&gt;
&lt;br /&gt;
      rjmp   init&lt;br /&gt;
.org OVF0addr		;timer interrupt 24ms&lt;br /&gt;
      in     save_sreg, SREG&lt;br /&gt;
get8key:                               ;/old      state     iwr1      iwr0&lt;br /&gt;
      mov    iwr0, key_old             ;00110011  10101010            00110011&lt;br /&gt;
      in     key_old, key_port         ;11110000&lt;br /&gt;
      eor    iwr0, key_old             ;                              11000011&lt;br /&gt;
      com    key_old                   ;00001111&lt;br /&gt;
      mov    iwr1, key_state           ;                    10101010&lt;br /&gt;
      or     key_state, iwr0           ;          11101011&lt;br /&gt;
      and    iwr0, key_old             ;                              00000011&lt;br /&gt;
      eor    key_state, iwr0           ;          11101000&lt;br /&gt;
      and    iwr1, iwr0                ;                    00000010&lt;br /&gt;
      or     key_press, iwr1           ;store key press detect&lt;br /&gt;
;&lt;br /&gt;
;			insert other timer functions here&lt;br /&gt;
;&lt;br /&gt;
      out    SREG, save_sreg&lt;br /&gt;
      reti&lt;br /&gt;
;-------------------------------------------------------------------------&lt;br /&gt;
init:&lt;br /&gt;
      ldi    wr0, 0xFF&lt;br /&gt;
      out    ddrb, wr0&lt;br /&gt;
      ldi    wr0, 1&amp;lt;&amp;lt;CS02 | 1&amp;lt;&amp;lt;CS00    ;divide by 1024 * 256&lt;br /&gt;
      out    TCCR0, wr0&lt;br /&gt;
      ldi    wr0, 1&amp;lt;&amp;lt;TOIE0             ;enable timer interrupt&lt;br /&gt;
      out    TIMSK, wr0&lt;br /&gt;
&lt;br /&gt;
      clr    key_old&lt;br /&gt;
      clr    key_state&lt;br /&gt;
      clr    key_press&lt;br /&gt;
      ldi    leds, 0xFF&lt;br /&gt;
main: cli&lt;br /&gt;
      eor    leds, key_press           ;toggle LEDs&lt;br /&gt;
      clr    key_press                 ;clear, if key press action done&lt;br /&gt;
      sei&lt;br /&gt;
      out    led_port, leds&lt;br /&gt;
      rjmp   main&lt;br /&gt;
;-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Komfortroutine (C für AVR) ====&lt;br /&gt;
&lt;br /&gt;
Siehe dazu: [http://www.mikrocontroller.net/forum/read-4-310276.html Forum]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkung&#039;&#039;&#039; Wenn statt active-low (Ruhezustand High) active-high (Ruhezustand Low) verwendet wird muss eine Zeile geändert werden siehe:&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-4-310276.html gesamter Beitrag im Forum], &lt;br /&gt;
[http://www.mikrocontroller.net/topic/48465#606555 Stelle 1 im Beitrag], ([http://www.mikrocontroller.net/topic/48465#2306398 Stelle 2 im Beitrag] muss *nicht* geändert werden, da hier die Polarität gar keinen Einfluß hat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkung 2&#039;&#039;&#039; Zur Initialisierung siehe [http://www.mikrocontroller.net/topic/48465#3572793 Forum]&lt;br /&gt;
&lt;br /&gt;
Funktionsprinzip wie oben plus zusätzliche Features:  &lt;br /&gt;
* Kann Tasten sparen durch unterschiedliche Aktionen bei kurzem oder langem Drücken&lt;br /&gt;
* Wiederholfunktion, z.&amp;amp;nbsp;B. für die Eingabe von Werten&lt;br /&gt;
&lt;br /&gt;
Das Programm ist für avr-gcc/avr-libc geschrieben, kann aber mit ein paar Anpassungen auch mit anderen Compilern und Mikrocontrollern verwendet werden. Eine Portierung für den AT91SAM7 findet man [http://www.google.com/codesearch?q=show:ac2viP-2E2Y:pzkOO5QRsoc:RPICuprYy-A&amp;amp;sa=N&amp;amp;cd=1&amp;amp;ct=rc&amp;amp;cs_p=svn://mikrocontroller.net/mp3dec/trunk&amp;amp;cs_f=keys.c#a0 hier] (aus dem Projekt [[ARM MP3/AAC Player]]).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*                      Debouncing 8 Keys                               */&lt;br /&gt;
/*                      Sampling 4 Times                                */&lt;br /&gt;
/*                      With Repeat Function                            */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*              Author: Peter Dannegger                                 */&lt;br /&gt;
/*                      danni@specs.de                                  */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU           1000000                   // processor clock frequency&lt;br /&gt;
#warning kein F_CPU definiert&lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
#define KEY_DDR         DDRB&lt;br /&gt;
#define KEY_PORT        PORTB&lt;br /&gt;
#define KEY_PIN         PINB&lt;br /&gt;
#define KEY0            0&lt;br /&gt;
#define KEY1            1&lt;br /&gt;
#define KEY2            2&lt;br /&gt;
#define ALL_KEYS        (1&amp;lt;&amp;lt;KEY0 | 1&amp;lt;&amp;lt;KEY1 | 1&amp;lt;&amp;lt;KEY2)&lt;br /&gt;
 &lt;br /&gt;
#define REPEAT_MASK     (1&amp;lt;&amp;lt;KEY1 | 1&amp;lt;&amp;lt;KEY2)       // repeat: key1, key2&lt;br /&gt;
#define REPEAT_START    50                        // after 500ms&lt;br /&gt;
#define REPEAT_NEXT     20                        // every 200ms&lt;br /&gt;
&lt;br /&gt;
#define LED_DDR         DDRA&lt;br /&gt;
#define LED_PORT        PORTA&lt;br /&gt;
#define LED0            0&lt;br /&gt;
#define LED1            1&lt;br /&gt;
#define LED2            2&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t key_state;                                // debounced and inverted key state:&lt;br /&gt;
                                                  // bit = 1: key pressed&lt;br /&gt;
volatile uint8_t key_press;                                // key press detect&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t key_rpt;                                  // key long press and repeat&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_OVF_vect )                            // every 10ms&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t ct0, ct1, rpt;&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms&lt;br /&gt;
 &lt;br /&gt;
  i = key_state ^ ~KEY_PIN;                       // key changed ?&lt;br /&gt;
  ct0 = ~( ct0 &amp;amp; i );                             // reset or count ct0&lt;br /&gt;
  ct1 = ct0 ^ (ct1 &amp;amp; i);                          // reset or count ct1&lt;br /&gt;
  i &amp;amp;= ct0 &amp;amp; ct1;                                 // count until roll over ?&lt;br /&gt;
  key_state ^= i;                                 // then toggle debounced state&lt;br /&gt;
  key_press |= key_state &amp;amp; i;                     // 0-&amp;gt;1: key press detect&lt;br /&gt;
 &lt;br /&gt;
  if( (key_state &amp;amp; REPEAT_MASK) == 0 )            // check repeat function&lt;br /&gt;
     rpt = REPEAT_START;                          // start delay&lt;br /&gt;
  if( --rpt == 0 ){&lt;br /&gt;
    rpt = REPEAT_NEXT;                            // repeat delay&lt;br /&gt;
    key_rpt |= key_state &amp;amp; REPEAT_MASK;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
// check if a key has been pressed. Each pressed key is reported&lt;br /&gt;
// only once&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_press( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  cli();                                          // read and clear atomic !&lt;br /&gt;
  key_mask &amp;amp;= key_press;                          // read key(s)&lt;br /&gt;
  key_press ^= key_mask;                          // clear key(s)&lt;br /&gt;
  sei();&lt;br /&gt;
  return key_mask;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
// check if a key has been pressed long enough such that the&lt;br /&gt;
// key repeat functionality kicks in. After a small setup delay&lt;br /&gt;
// the key is reported being pressed in subsequent calls&lt;br /&gt;
// to this function. This simulates the user repeatedly&lt;br /&gt;
// pressing and releasing the key.&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_rpt( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  cli();                                          // read and clear atomic !&lt;br /&gt;
  key_mask &amp;amp;= key_rpt;                            // read key(s)&lt;br /&gt;
  key_rpt ^= key_mask;                            // clear key(s)&lt;br /&gt;
  sei();&lt;br /&gt;
  return key_mask;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
// check if a key is pressed right now&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_state( uint8_t key_mask )&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
  key_mask &amp;amp;= key_state;&lt;br /&gt;
  return key_mask;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_short( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  cli();                                          // read key state and key press atomic !&lt;br /&gt;
  return get_key_press( ~key_state &amp;amp; key_mask );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_long( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  return get_key_press( get_key_rpt( key_mask ));&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
  LED_PORT = 0xFF;&lt;br /&gt;
  LED_DDR = 0xFF;                     &lt;br /&gt;
&lt;br /&gt;
  // Configure debouncing routines&lt;br /&gt;
  KEY_DDR &amp;amp;= ~ALL_KEYS;                // configure key port for input&lt;br /&gt;
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors&lt;br /&gt;
&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS02)|(1&amp;lt;&amp;lt;CS00);         // divide by 1024&lt;br /&gt;
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms&lt;br /&gt;
  TIMSK |= 1&amp;lt;&amp;lt;TOIE0;                   // enable timer interrupt&lt;br /&gt;
&lt;br /&gt;
  sei();&lt;br /&gt;
&lt;br /&gt;
  while(1){&lt;br /&gt;
    if( get_key_short( 1&amp;lt;&amp;lt;KEY1 ))&lt;br /&gt;
      LED_PORT ^= 1&amp;lt;&amp;lt;LED1;&lt;br /&gt;
 &lt;br /&gt;
    if( get_key_long( 1&amp;lt;&amp;lt;KEY1 ))&lt;br /&gt;
      LED_PORT ^= 1&amp;lt;&amp;lt;LED2;&lt;br /&gt;
 &lt;br /&gt;
    // single press and repeat&lt;br /&gt;
 &lt;br /&gt;
    if( get_key_press( 1&amp;lt;&amp;lt;KEY2 ) || get_key_rpt( 1&amp;lt;&amp;lt;KEY2 )){&lt;br /&gt;
      uint8_t i = LED_PORT;&lt;br /&gt;
 &lt;br /&gt;
      i = (i &amp;amp; 0x07) | ((i &amp;lt;&amp;lt; 1) &amp;amp; 0xF0);&lt;br /&gt;
      if( i &amp;lt; 0xF0 )&lt;br /&gt;
        i |= 0x08;&lt;br /&gt;
      LED_PORT = i;      &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das single-press-und-repeat-Beispiel geht nicht in jeder Beschaltung; folgendes Beispiel sollte universeller sein (einzelne LED an/aus):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// single press and repeat&lt;br /&gt;
if( get_key_press( 1&amp;lt;&amp;lt;KEY2 ) || get_key_rpt( 1&amp;lt;&amp;lt;KEY2 ))&lt;br /&gt;
    LED_PORT ^=0x08;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Funktionsweise =====&lt;br /&gt;
Der Code basiert auf 8 parallelen vertikalen Zählern, die über die Variablen ct0 und ct1 aufgebaut werden&lt;br /&gt;
&lt;br /&gt;
[[Bild:VertCount.png|framed|center|&#039;&#039;&#039;8 vertikale Zähler in 2 8-Bit Variablen&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
wobei jeweils ein Bit in ct0 mit dem gleichwertigen Bit in ct1 zusammengenommen einen 2-Bit-Zähler bildet.&lt;br /&gt;
Der Code der sich um die 8 Zähler kümmert, ist so geschrieben, daß er alle 8 Zähler gemeinsam parallel behandelt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = key_state ^ ~KEY_PIN;                       // key changed ?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
i enthält an dieser Stelle für jede Taste, die sich im Vergleich mit dem vorhergehenden entprellten Zustand (keystate) verändert hat, ein 1 Bit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  ct0 = ~( ct0 &amp;amp; i );                             // reset or count ct0&lt;br /&gt;
  ct1 = ct0 ^ (ct1 &amp;amp; i);                          // reset or count ct1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese beiden Anweisungen erniedrigen den 2-Bit Zähler ct0/ct1 für jedes Bit um 1, welches in i gesetzt ist. Liegt an der entsprechenden Stelle in i ein 0 Bit vor (keine Änderung des Zustands), so wird der Zähler ct0/ct1 für dieses Bit auf 1 gesetzt.&lt;br /&gt;
Der Grundzustand des Zählers ist als ct0 == 1 und ct1 == 1 (Wert 3). Der Zähler zählt daher mit jedem ISR Aufruf, bei dem die Taste im Vergleich zu keystate als verändert erkannt wurde&lt;br /&gt;
&lt;br /&gt;
   ct1   ct0&lt;br /&gt;
     1    1   // 3&lt;br /&gt;
     1    0   // 2&lt;br /&gt;
     0    1   // 1&lt;br /&gt;
     0    0   // 0&lt;br /&gt;
     1    1   // 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i &amp;amp;= ct0 &amp;amp; ct1;                                 // count until roll over ?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
in i bleibt nur dort ein 1-Bit erhalten, wo sowohl in ct1 als auch in ct0 ein 1 Bit vorgefunden wird, der betreffende Zähler also bis 3 zählen konnte. Durch die zusätzliche Verundung mit i wird der Fall abgefangen, dass ein konstanter Zählerwert von 3 in i ein 1 Bit hinterlässt. Im Endergebnis bedeutet dass, dass nur ein Zählerwechsel von 0 auf 3 zu einem 1 Bit an der betreffenden Stelle in i führt, aber auch nur dann, wenn in i an dieser Bitposition ebenfalls ein 1 Bit war (welches wiederrum deswegen auf 1 war, weil an diesem Eingabeport eine Veränderung zum letzten bekannten entprellten Zustand festgestellt wurde). Alles zusammengenommen heißt das, dass ein Tastendruck dann erkannt wird, wenn die Taste 4 mal hintereinander in einem anderen Zustand vorgefunden wurde als dem zuletzt bekannten entprellten Tastenzustand.&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle ist i daher ein Vektor von 8 Bits, von denen jedes einzelne der Bits darüber Auskunft gibt, ob die entsprechende Taste mehrmals hintereinander im selben Zustand angetroffen wurde, der nicht mit dem zuletzt bekannten Tastenzustand übereinstimmt. Ist das der Fall, dann wird eine entsprechende Veränderung des Tastenzustands in key_state registriert&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  key_state ^= i;                                 // then toggle debounced state&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
und wenn sich in key_state das entsprechende Bit von 0 auf 1 verändert hat, wird dieses Ereignis als &#039;Taste wurde niedergedrückt&#039; gewertet.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  key_press |= key_state &amp;amp; i;                     // 0-&amp;gt;1: key press detect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit ist der Tasteneingang entprellt. Und zwar sowohl beim Drücken einer Taste als auch beim Loslassen (damit Tastenpreller beim Loslassen nicht mit dem Niederdrücken einer Taste verwechselt werden). Der weitere Code beschäftigt sich dann nur noch damit, diesen entprellten Tastenzustand weiter zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Der Codeteil sieht durch die Verwendung der vielen bitweisen Operationen relativ komplex aus. Behält man aber im Hinterkopf, dass einige der bitweisen wie ein &#039;paralles If&#039; gleichzeitig auf allen 8 Bits eingesetzt werden, dann vereinfacht sich vieles. Ein&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    key_press |= key_state &amp;amp; i;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist nichts anderes als ein&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // teste ob Bit 0 sowohl in key_state als auch in i gesetzt ist&lt;br /&gt;
    // und setze Bit 0 in key_press, wenn das der Fall ist&lt;br /&gt;
    if( ( key_state &amp;amp; ( 1 &amp;lt;&amp;lt; 0 ) ) &amp;amp;&amp;amp;&lt;br /&gt;
        ( i &amp;amp; ( 1 &amp;lt;&amp;lt; 0 ) )&lt;br /&gt;
       key_press |= ( 1 &amp;lt;&amp;lt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // Bit 1&lt;br /&gt;
    if( ( key_state &amp;amp; ( 1 &amp;lt;&amp;lt; 1 ) ) &amp;amp;&amp;amp;&lt;br /&gt;
        ( i &amp;amp; ( 1 &amp;lt;&amp;lt; 1 ) )&lt;br /&gt;
       key_press |= ( 1 &amp;lt;&amp;lt; 1 );&lt;br /&gt;
&lt;br /&gt;
    // Bit 2&lt;br /&gt;
    if( ( key_state &amp;amp; ( 1 &amp;lt;&amp;lt; 2 ) ) &amp;amp;&amp;amp;&lt;br /&gt;
        ( i &amp;amp; ( 1 &amp;lt;&amp;lt; 2 ) )&lt;br /&gt;
       key_press |= ( 1 &amp;lt;&amp;lt; 2 );&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
nur als wesentlich kompaktere Operation ausgeführt und für alle 8 Bits gleichzeitig.&lt;br /&gt;
Die Kürze und Effizienz dieser paar Codezeilen ergibt sich aus dem Umstand, dass jedes Bit in den Variablen für eine Taste steht und alle 8 (maximal möglichen) Tasten gleichzeitig die Operationen durchlaufen.&lt;br /&gt;
&lt;br /&gt;
===== Reduziert auf lediglich 1 Taste =====&lt;br /&gt;
Diskussionen im Forum zeigen immer wieder, dass viele eine Abneigung gegen diesen Code haben, weil er ihnen sehr kompliziert vorkommt.&lt;br /&gt;
&lt;br /&gt;
Der Code ist nicht leicht zu analysieren und er zieht alle Register dessen, was möglich ist, um sowohl Laufzeit als auch Speicherverbrauch einzusparen. Oft hört man auch das Argument: Ich benötige ja nur eine Entprellung für 1 Taste, gibt es da nichts Einfacheres?&lt;br /&gt;
&lt;br /&gt;
Hier ist die &#039;Langform&#039; des Codes, so wie man das für lediglich 1 Taste schreiben würde, wenn man exakt dasselbe Entprellverfahren einsetzen würde. Man sieht: Da ist keine Hexerei dabei: In key_state wird der letzte bekannte entprellte Zustand der Taste gehalten. Der Pin-Eingang wird mit diesem Zustand verglichen und wenn sich die beiden unterscheiden, dann wird ein Zähler heruntergezählt. Produziert dieses herunterzählen einen Unterlauf des Zählers, dann gilt die Taste als entprellt und wenn dann auch noch die Taste gerade gedrückt ist, dann wird dieses in key_press entsprechend vermerkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t key_state;&lt;br /&gt;
uint8_t key_counter;&lt;br /&gt;
volatile uint8_t key_press;&lt;br /&gt;
&lt;br /&gt;
ISR( ... Overflow ... )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t input = KEY_PIN &amp;amp; ( 1 &amp;lt;&amp;lt; KEY0 );&lt;br /&gt;
&lt;br /&gt;
  if( input != key_state ) {&lt;br /&gt;
    key_counter--;&lt;br /&gt;
    if( key_counter == 0xFF ) {&lt;br /&gt;
      key_counter = 3;&lt;br /&gt;
      key_state = input;&lt;br /&gt;
      if( input )&lt;br /&gt;
        key_press = TRUE;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
    key_counter = 3;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t get_key_press()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t result;&lt;br /&gt;
&lt;br /&gt;
  cli();&lt;br /&gt;
  result = key_press;&lt;br /&gt;
  key_press = FALSE;&lt;br /&gt;
  sei();&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der vollständige Entprellcode, wie weiter oben gelistet, besticht jetzt aber darin, dass er compiliert kleiner ist als diese anschaulichere Variante für lediglich 1 Taste. Und das bei gleichzeitig erhöhter Funktionalität. Denn zb. ein Autorepeat ist in diesem Code noch gar nicht eingebaut. Und spätestens wenn man dann eine 2.te Taste entprellen möchte, dann ist auch der SRAM-Speicherverbrauch dieser Langform höher als der des Originals für 8 Tasten. Daraus folgt: Selbst für lediglich 1 Taste ist die Originalroutine die bessere Wahl.&lt;br /&gt;
&lt;br /&gt;
Und wegen der Komplexität mal eine Frage: Sind Sie selbst in der Lage eine entsprechend effiziente sqrt() Funktion zu schreiben, wie die, die sie in der Standard-C-Bibliothek vorfinden? Nein? Dann dürften Sie eigentlich Ihrer Argumentation nach die Bibliotheksfunktion sqrt() nicht verwenden, sondern müssten sich statt dessen selbst eine Wurzel-Funktion schreiben.&lt;br /&gt;
&lt;br /&gt;
=== Selbstsättigender Filter (nach Jürgen Schuhmacher) ===&lt;br /&gt;
Durch die Nutzung der diskreten Signalanalyse in Software kann die Funktionalität einer einfachen Entprellung mit einem Widerstand, einem Kondensator und einem Schmitttrigger wie in Hardware nachgebildet werden, indem ein abstrakter IIR-Filter benutzt wird, der eine Kondensatorladekurve emuliert. Mit der Vorschrift Y(t) = k Y(t-1) + Input wird ein einfaches Filter erzeugt, dass dem Eingangswert träge folgt. Bei Überschreiten eines bestimmten Wertes erfolgt mit einer einfachen Abfrage das Schalten des Ausgangssignals.&lt;br /&gt;
&lt;br /&gt;
Für Assembler und VHDL bei FPGAs eignet sich aufgrund der leicht zu implementierenden binären Operationen folgende Darstellung mit einer Auflösung des Filterwertspeichers von nur 8 bit: Wert_Neu = Wert_Alt - Wert_Alt/16 + 16*(Taste = True). Der Filterwert bildet dann den gedämpften Verlauf des Eingangs (flankenverschliffen) ab und kann Prellen bis nahe an den Grenzbereich zum schnellen Tasten unterdrücken. Der Ausgangswert ist dann einfach das höchstwertige Bit des Filterwertes.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Entprellung mit IIR-Filter.gif]]&lt;br /&gt;
&lt;br /&gt;
Dazu muss das Signal des Tasters idealerweise um den Faktor 10-20 schneller abgetastet werden, als die höchste gewünschte Tippgeschwindigkeit vorgibt. Noch schneller abzutasten ist möglich, führt aber zu mehr Bedarf an Bits beim Filter. Die Schmittriggerfunktion kann dadurch gebildet werden, dass eine 1 am Ausgang bei z.B. Überschreiten einer 55% Grenze und eine 0 bei Unterschreitung der 45%-Grenze ausgeben wird. Im Zwischenbereich wird der alte Wert gehalten.&lt;br /&gt;
&lt;br /&gt;
=== Einfacher Mittelwertfilter (nach Lothar Miller) ===&lt;br /&gt;
Für digitale Schaltungen oder PLDs empfiehlt sich ein FIR-Filter mit aneinandergereihten FlipFlops. Man schiebt das Eingangssignal in eine FlipFlop-Kette und schaltet oberhalb der Mitte um:&lt;br /&gt;
&lt;br /&gt;
SignalInput -&amp;gt; FF1 -&amp;gt; FF2 -&amp;gt; FF3 -&amp;gt; FF4 -&amp;gt; FF5 -&amp;gt; FF6 -&amp;gt; FF7 -&amp;gt; FF8&lt;br /&gt;
&lt;br /&gt;
Wenn alle FFs = 1 (Summe der FFs=8) dann SignalOutput = 1&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn alle FFs = 0 (Summe der FFs=0) dann SignalOutput = 0&lt;br /&gt;
&lt;br /&gt;
Dieses Verfahren kann sehr einfach in Logik abgebildet werden, weil für die Berechnung des Ausgangs nur ein NOR bz. ein AND Gatter nötig ist.&lt;br /&gt;
&lt;br /&gt;
== Gegenüberstellung der Verfahren ==&lt;br /&gt;
* HW - &amp;quot;entprellte Schalter&amp;quot;: Sehr teuer, grosse Bauform, verschleissbelastet, geringe Haltbarkeit&lt;br /&gt;
* HW - &amp;quot;Umschalter&amp;quot; : benötigt aufwändigeren Schalter, benötigt Elektronik&lt;br /&gt;
* HW - &amp;quot;Umschalter ohne FF&amp;quot; : benötigt aufwändigeren Schalter und kleinen Kondensator&lt;br /&gt;
* HW - &amp;quot;Kondensatorentprellung&amp;quot; : benötigt etwas mehr Platz, kommt mit schlechten Schaltern zurecht&lt;br /&gt;
&lt;br /&gt;
* SW - Flankenverfahren:&lt;br /&gt;
* SW - Warteschleife: Durch die Warteschleifen eine nicht zu vernachlässigende Verzögerung im Code. Speziell wenn mehrere Tasten zu überwachen sind, nicht unproblematisch&lt;br /&gt;
* SW - Timer: Universalfunktionalität, die durch geringen Speicherverbrauch, geringen Rechenzeitverbrauch und gute Funktion besticht. Der &#039;Verbrauch&#039; eines Timers sieht auf den ersten Blick schlimmer aus, als er ist, denn in den meisten Programmen hat man sowieso einen Basistimer für die Zeitsteuerung des Programms im Einsatz, der für die Tastenentprellung mitbenutzt werden kann.&lt;br /&gt;
* SW - Filter: sehr geringer Platzbedarf in FPGAs, relativ gute Wirkung&lt;br /&gt;
* SW - Filter 2: sehr geringer Platzbedarf, gute Wirkung&lt;br /&gt;
&lt;br /&gt;
== Links zum Thema ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.edn.com/design/analog/4324067/Contact-debouncing-algorithm-emulates-Schmitt-trigger Contact-debouncing algorithm (Artikel)],  [http://www.edn.com/file/13370-70705di.pdf als PDF]&lt;br /&gt;
* [[AVR-Tutorial: Tasten]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#.28Tasten-.29Entprellung|AVR-GCC-Tutorial Tastenentprellung]]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-20435.html Beitrag im Forum, AVR Assembler]&lt;br /&gt;
* [http://www.ganssle.com/debouncing.pdf A guide to debouncing (engl.), praktische Erläuterungen zum Entprellen in Soft- und Hardware]&lt;br /&gt;
* [http://www.pololu.com/docs/0J16/all Understanding Destructive LC Voltage Spikes]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR]]&lt;br /&gt;
[[Kategorie:Signalverarbeitung]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe&amp;diff=87564</id>
		<title>AVR-GCC-Tutorial/Analoge Ein- und Ausgabe</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe&amp;diff=87564"/>
		<updated>2015-02-26T11:05:12Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Nutzung des ADC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Analoge Eingangswerte werden in der Regel über den AVR Analog-Digital-Converter (AD-Wandler, ADC) eingelesen, der in vielen Typen verfügbar ist (typisch 10bit Auflösung). Durch diesen werden analoge Signale (Spannungen) in digitale Zahlenwerte gewandelt. Bei AVRs, die über keinen internen AD-Wandler verfügen (z.&amp;amp;nbsp;B. ATmega162), kann durch externe Beschaltung (R/C-Netzwerk und Zeitmessung) die Funktion des AD-Wandlers simuliert werden.&lt;br /&gt;
&lt;br /&gt;
Es gibt innerhalb der ATMega- und ATTiny-AVR Reihe keine Typen mit eingebautem Digital-Analog-Konverter (DAC) - diese Funktion ist erst ab der XMEGA-Reihe der AVR-Familie verfügbar, die aber wegen ihrer vielen Unterschiede im Umfang dieses Tutorials nicht behandelt wird.&lt;br /&gt;
Die Umsetzung zu einer analogen Spannung muss daher durch externe Komponenten vorgenommen werden. Das kann z.&amp;amp;nbsp;B. durch PWM und deren Filterung zu (fast) DC, oder einem sogenannten R2R-Netzwerk erfolgen.&lt;br /&gt;
&lt;br /&gt;
Unabhängig davon besteht natürlich immer die Möglichkeit, spezielle Bausteine zur Analog-Digital- bzw. Digital-Analog-Wandlung zu nutzen und diese über eine digitale Schnittstelle (z.b. SPI oder I2C) mit einem AVR anzusteuern.&lt;br /&gt;
&lt;br /&gt;
== AC (Analog Comparator) ==&lt;br /&gt;
&lt;br /&gt;
Der Comparator vergleicht 2 Spannungen an den Pins AIN0 und AIN1 und gibt einen Status aus welche der beiden Spannungen größer ist. AIN0 Dient dabei als Referenzspannung (Sollwert) und AIN1 als Vergleichsspannung (Istwert). Als Referenzspannung kann auch alternativ eine interne Referenzspannung ausgewählt werden.&lt;br /&gt;
&lt;br /&gt;
Liegt die Vergleichsspannung (IST) unter der der Referenzspannung (SOLL) gibt der Comparator eine logische 1 aus. Ist die Vergleichsspannung hingegen größer als die Referenzspannung wird eine logische 0 ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Der Comparator arbeitet völlig autark bzw. parallel zum Prozessor. Für mobile Anwendungen empfiehlt es sich ihn abzuschalten sofern er nicht benötigt wird, da er ansonsten Strom benötigt. Der Comparator kann interruptgesteuert abgefragt werden oder im Pollingbetrieb.&lt;br /&gt;
&lt;br /&gt;
Das Steuer- bzw. Statusregister ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;ACSR - Analog Comparator Status Register&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7|| 6|| 5|| 4|| 3|| 2|| 1|| 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;ACD&#039;&#039;&#039;|| &#039;&#039;&#039;ACBG&#039;&#039;&#039;|| &#039;&#039;&#039;ACO&#039;&#039;&#039;|| &#039;&#039;&#039;ACI&#039;&#039;&#039;|| &#039;&#039;&#039;ACIE&#039;&#039;&#039;|| &#039;&#039;&#039;ACIC&#039;&#039;&#039;|| &#039;&#039;&#039;ACIS1&#039;&#039;&#039;|| &#039;&#039;&#039;ACIS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W|| R/W|| R|| R/W|| R/W|| R/W|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| n/a|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
;Bit 7 ACD: Analog Comparator Disable: 0 = Comparator ein, 1 = Comparator aus. Wird dieses Bit geändert kann ein Interrupt ausgelöst werden. Soll dies vermieden werden muss das Bit 3 ACIE ggf. abgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
;Bit 6 ACBG: Analog Comparator Bandgap Select: Ermöglicht das umschalten zwischen interner und externer Referenzspannung. 1 = interne (~1,3 Volt), 0 = externe Referenzspannung (an Pin AIN0)&lt;br /&gt;
&lt;br /&gt;
;Bit 5 ACO: Analog Comparator Output: Hier wird das Ergebnis des Vergleichs angezeigt. Es liegt typischerweise nach 1-2 Taktzyklen vor.&lt;br /&gt;
:: IST &amp;lt; SOLL &amp;amp;rarr; 1&lt;br /&gt;
:: IST &amp;gt; SOLL &amp;amp;rarr; 0&lt;br /&gt;
&lt;br /&gt;
;Bit 4 ACI: Analog Comparator Interrupt Flag: Dieses Bit wird von der Hardware gesetzt, wenn ein Interruptereignis, das in Bit 0 und 1 definiert ist, eintritt. Dieses Bit löst noch keinen Interrupt aus! Die Interruptroutine wird nur dann ausgeführt, wenn das Bit 3 ACIE gesetzt ist und global Interrupts erlaubt sind (I-Bit in SREG gesetzt). Das Bit 4 ACI wird wieder gelöscht, wenn die Interruptroutine ausgeführt wurde oder wenn es manuell auf 1! gesetzt wird. Das Bit kann für Abfragen genutzt werden, steuert oder konfiguriert aber nicht den Comparator.&lt;br /&gt;
&lt;br /&gt;
;Bit 3 ACIE: Analog Comparator Interrupt Enable: Ist das Bit auf 1 gesetzt, wird immer ein Interrupt ausgelöst, wenn das Ereignis das in Bit 1 und 0 definiert ist, eintritt.&lt;br /&gt;
&lt;br /&gt;
;Bit 2 ACIC: Analog Comparator Input Capture Enable: Wird das Bit gesetzt, wird der Comparatorausgang intern mit dem Counter 1 verbunden. Es könnten damit z.b. die Anzahl der Vergleiche im Counter1 gezählt werden. Um den Comparator an den Timer1 Input Capture Interrupt zu verbinden, muss im Timerregister das TICIE1 Bit auf 1 gesetzt werden. Der Trigger wird immer dann ausgelöst, wenn das in Bit 1 und 0 definierte Ereignis eintritt.&lt;br /&gt;
&lt;br /&gt;
;Bit 1,0 ACIS1,ACIS0: Analog Comparator Interrupt select: Hier wird definiert, welche Ereignisse einen Interrupt auslösen sollen:&lt;br /&gt;
:* 00 = Interrupt auslösen bei jedem Flankenwechsel&lt;br /&gt;
:* 10 = Interrupt auslösen bei fallender Flanke&lt;br /&gt;
:* 11 = Interrupt auslösen bei steigender Flanke&lt;br /&gt;
&lt;br /&gt;
Werden diese Bit geändert, kann ein Interrupt ausgelöst werden. Soll dies vermieden werden, muss das Bit 3 gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der Analog-Digital-Konverter (ADC) wandelt analoge Signale in digitale Werte um, welche vom Controller interpretiert werden können. Einige AVR-Typen haben bereits einen mehrkanaligen Analog-Digital-Konverter eingebaut. Die Feinheit, mit welcher ein analoges Signal aufgelöst werden kann, wird durch die Auflösung des ADC, d.h. durch die Anzahl der verwendeten Bits angegeben. So sind derzeit bspw. 8-Bit- oder 10-Bit-ADC im Einsatz. ADCs, die in AVRs enthalten sind, haben zur Zeit eine maximale Auflösung von 10-Bit.&lt;br /&gt;
&lt;br /&gt;
Ein ADC mit 8 Bit Auflösung kann somit das analoge Signal in Abstufungen von 1/256 des Maximalwertes digitalisieren. Wenn wir nun mal annehmen, wir hätten eine Eingangspannung zwischen 0 und 5 Volt, eine Referenzspannung von 5&amp;amp;nbsp;V und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
Intervalle mit den Grenzen 0&amp;amp;nbsp;V, 0.625&amp;amp;nbsp;V, 1.25&amp;amp;nbsp;V, 1.875&amp;amp;nbsp;V, 2.5&amp;amp;nbsp;V, 3.125&amp;amp;nbsp;V, 3.75&amp;amp;nbsp;V, 4.375&amp;amp;nbsp;V, 5&amp;amp;nbsp;V entsprechend folgender Tabelle unterschieden werden:&lt;br /&gt;
&lt;br /&gt;
::{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
! Eingangsspannung am ADC / V || Entsprechender Messwert&lt;br /&gt;
|-&lt;br /&gt;
| 0 – 0.625    || 0&lt;br /&gt;
|-&lt;br /&gt;
| 0.625 – 1.25 || 1&lt;br /&gt;
|-&lt;br /&gt;
| 1.25 – 1.875 || 2&lt;br /&gt;
|-&lt;br /&gt;
| 1.875 – 2.5  || 3&lt;br /&gt;
|-&lt;br /&gt;
| 2.5 – 3.125  || 4&lt;br /&gt;
|-&lt;br /&gt;
| 3.125 – 3.75 || 5&lt;br /&gt;
|-&lt;br /&gt;
| 3.75 – 4.375 || 6&lt;br /&gt;
|-&lt;br /&gt;
| 4.375 – 5    || 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des Analog-Digital-Konverters ist, also, je mehr Bits er hat, desto genauer kann der jeweilige Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Der interne ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Oft sind auch mehrere Kanäle verfügbar. Kanäle heißt in diesem Zusammenhang, dass zwar bis zu zehn analoge Eingänge am AVR vorhanden sind, aber nur ein &amp;quot;echter&amp;quot; Analog-Digital-Wandler zur Verfügung steht. Vor der eigentlichen Messung ist also festzulegen, welcher Kanal (&amp;quot;Pin&amp;quot;) mit dem Wandler verbunden und gemessen wird.&lt;br /&gt;
&lt;br /&gt;
Die Umwandlung innerhalb des AVR basiert auf der schrittweisen Näherung. Beim AVR müssen die Pins &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AVCC&#039;&#039;&#039; beschaltet werden. Für genaue Messungen sollte AVCC über ein L-C Netzwerk mit VCC verbunden werden, um Spannungsspitzen und -einbrüche vom Analog-Digital-Wandler fernzuhalten. Im Datenblatt findet sich dazu eine Schaltung, die 10µH und 100nF vorsieht.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Analog-Digital-Wandlung wird auf eine Referenzspannung bezogen. Aktuelle AVRs bieten drei Möglichkeiten zur Wahl dieser Spannung:&lt;br /&gt;
&lt;br /&gt;
* Eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; am Anschlusspin &#039;&#039;&#039;AREF&#039;&#039;&#039;. Die minimale (externe) Referenzspannung darf jedoch nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. &lt;br /&gt;
&lt;br /&gt;
* Verfügt der AVR über eine interne Referenzspannung, kann diese genutzt werden. Alle aktuellen AVRs mit internem AD-Wandler sollten damit ausgestattet sein (vgl. Datenblatt: 2,56V oder 1,1V je nach Typ). Das Datenblatt gibt auch über die Genauigkeit dieser Spannung Auskunft.&lt;br /&gt;
&lt;br /&gt;
* Es kann die Spannung AVcc als Referenzspannung herangezogen werden&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung von AVcc oder der internen Referenz wird empfohlen, einen Kondensator zwischen dem AREF-Pin und GND anzuordnen. Die Festlegung, welche Spannungsreferenz genutzt wird, erfolgt z.&amp;amp;nbsp;B. beim ATmega16 mit den Bits REFS1/REFS0 im ADMUX-Register. Die zu messende Spannung muss im Bereich zwischen &#039;&#039;&#039;AGND&#039;&#039;&#039; und &#039;&#039;&#039;AREF&#039;&#039;&#039; (egal ob intern oder extern) liegen. &lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; kann in zwei verschiedenen Betriebsarten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
; Einfache Wandlung (Single Conversion) : In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestoßen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
; Frei laufend (Free Running) : In dieser Betriebsart erfasst der Wandler permanent die anliegende Spannung und schreibt diese in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Die Register des ADC ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; verfügt über eigene Register. Im Folgenden die Registerbeschreibung eines  ATMega16, welcher über 8 ADC-Kanäle verfügt. Die Register unterscheiden sich jedoch nicht erheblich von denen anderer AVRs (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| width=&amp;quot;100&amp;quot; | &#039;&#039;&#039;ADCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister A.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den &#039;&#039;&#039;ADC&#039;&#039;&#039; verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7|| 6|| 5|| 4|| 3|| 2|| 1|| 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;|| &#039;&#039;&#039;ADSC&#039;&#039;&#039;|| &#039;&#039;&#039;ADFR&#039;&#039;&#039;|| &#039;&#039;&#039;ADIF&#039;&#039;&#039;|| &#039;&#039;&#039;ADIE&#039;&#039;&#039;|| &#039;&#039;&#039;ADPS2&#039;&#039;&#039;|| &#039;&#039;&#039;ADPS1&#039;&#039;&#039;|| &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| 0|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADEN&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able)&lt;br /&gt;
:Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren. Wenn das Bit nicht gesetzt ist, können die Pins wie normale I/O-Pins verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADSC&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion)&lt;br /&gt;
:Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden Betriebsart muss das Bit gesetzt werden, um die kontinuierliche Messung zu aktivieren.&lt;br /&gt;
:Wenn das Bit nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal gesetzt wird, führt der Controller zuerst eine zusätzliche Wandlung und erst dann die eigentliche Wandlung aus. Diese zusätzliche Wandlung wird zu Initialisierungszwecken durchgeführt.&lt;br /&gt;
:Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung erfolgt ist und geht danach auf 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADFR&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;F&#039;&#039;&#039;ree &#039;&#039;&#039;R&#039;&#039;&#039;un select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Ist das Bit auf 1 gesetzt arbeitet der ADC im &amp;quot;Free Running&amp;quot;-Modus. Dabei wird das Datenregister permanent aktualisiert. Ist das Bit hingegen auf 0 gesetzt, macht der ADC nur eine &amp;quot;Single Conversion&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIF&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag)&lt;br /&gt;
:Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt, sobald eine Umwandlung erfolgt ist und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert wurde.&lt;br /&gt;
:Wenn das &#039;&#039;&#039;ADIE&#039;&#039;&#039; Bit sowie das &#039;&#039;&#039;I-Bit&#039;&#039;&#039; im AVR &#039;&#039;&#039;Statusregister&#039;&#039;&#039; gesetzt ist, wird der &#039;&#039;&#039;ADC Interrupt&#039;&#039;&#039; ausgelöst und die Interrupt-Behandlungsroutine aufgerufen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn die Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische &#039;&#039;&#039;1&#039;&#039;&#039;! in das Register geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADIE&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;, dann wird der &#039;&#039;&#039; ADC-Interrupt&#039;&#039;&#039; aktiviert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADPS2...ADPS0&#039;&#039;&#039; (&#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;P&#039;&#039;&#039;rescaler &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
:Der &#039;&#039;&#039;ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz liegen.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert im Bereich &#039;&#039;&#039;(50-200)kHz&#039;&#039;&#039; ergibt.&lt;br /&gt;
:Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
TF_{min}=\frac{CLK}{200\,\mathrm{kHz}}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50\,\mathrm{kHz}}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im Interesse der schnelleren Wandlungszeit werden wir hier den Faktor 32 einstellen.&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! &#039;&#039;&#039;ADPS2&#039;&#039;&#039;|| &#039;&#039;&#039;ADPS1&#039;&#039;&#039;|| &#039;&#039;&#039;ADPS0&#039;&#039;&#039;|| &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 0|| 2&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| 1|| 2&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 1|| 0|| 4&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 1|| 1|| 8&lt;br /&gt;
|-&lt;br /&gt;
| 1|| 0|| 0|| 16&lt;br /&gt;
|-&lt;br /&gt;
| 1|| 0|| 1|| 32&lt;br /&gt;
|-&lt;br /&gt;
| 1|| 1|| 0|| 64&lt;br /&gt;
|-&lt;br /&gt;
| 1|| 1|| 1|| 128&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCL&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ADCH&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;ADC &#039;&#039;&#039; Data Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn eine Umwandlung abgeschlossen ist, befindet sich der gemessene Wert in&lt;br /&gt;
diesen beiden Registern. Von &#039;&#039;&#039;ADCH&#039;&#039;&#039; werden nur die beiden niederwertigsten Bits verwendet. Es müssen immer beide Register ausgelesen werden, und zwar immer &#039;&#039;&#039;in der Reihenfolge: ADCL, ADCH&#039;&#039;&#039;. &lt;br /&gt;
Der effektive Messwert ergibt sich dann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCL;       // mit uint16_t x&lt;br /&gt;
x += (ADCH&amp;lt;&amp;lt;8); // in zwei Zeilen (LSB/MSB-Reihenfolge und&lt;br /&gt;
                // C-Operatorpriorität sichergestellt)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADMUX&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Mu&#039;&#039;&#039;ltiple&#039;&#039;&#039;x&#039;&#039;&#039;er Select Register&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Register wird der zu messende Kanal ausgewählt. Beim 90S8535&lt;br /&gt;
kann jeder Pin von Port A als &#039;&#039;&#039;ADC&#039;&#039;&#039;-Eingang verwendet werden (=8 Kanäle).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7|| 6|| 5|| 4|| 3|| 2|| 1|| 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;REFS1&#039;&#039;&#039;|| &#039;&#039;&#039;REFS0&#039;&#039;&#039;|| &#039;&#039;&#039;ADLAR&#039;&#039;&#039;|| &#039;&#039;&#039;MUX4&#039;&#039;&#039;|| &#039;&#039;&#039;MUX3&#039;&#039;&#039;|| &#039;&#039;&#039;MUX2&#039;&#039;&#039;|| &#039;&#039;&#039;MUX1&#039;&#039;&#039;|| &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| 0|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;REFS1...REFS0&#039;&#039;&#039; (&#039;&#039;&#039;Ref&#039;&#039;&#039;erence&#039;&#039;&#039;S&#039;&#039;&#039;election Bits)&lt;br /&gt;
:Mit diesen Bits kann die Referenzspannung eingestellt werden. Bei der Umstellung sind Wartezeiten zu beachten, bis die ADC-Hardware einsatzfähig ist (Datenblatt und  [http://www.mikrocontroller.net/topic/165513]):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! &#039;&#039;&#039;REFS1&#039;&#039;&#039;|| &#039;&#039;&#039;REFS0&#039;&#039;&#039;|| &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 0|| Externes AREF&lt;br /&gt;
|-&lt;br /&gt;
| 0|| 1|| AVCC als Referenz&lt;br /&gt;
|-&lt;br /&gt;
| 1|| 0|| Reserviert&lt;br /&gt;
|-&lt;br /&gt;
| 1|| 1|| Interne 2,56 Volt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
:Beim ATtiny25, ATtiny45 und ATtiny85 gilt:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! &#039;&#039;&#039;REFS2&#039;&#039;&#039;||&#039;&#039;&#039;REFS1&#039;&#039;&#039;|| &#039;&#039;&#039;REFS0&#039;&#039;&#039;|| &#039;&#039;&#039;Referenzspanung&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| X || 0|| 0|| VCC als Referenz&lt;br /&gt;
|-&lt;br /&gt;
| X || 0|| 1|| Externes AREF&lt;br /&gt;
|-&lt;br /&gt;
| X || 1|| 0|| Interne 1,1 Volt&lt;br /&gt;
|-&lt;br /&gt;
| X || 1|| 1|| Reserviert&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1|| 0|| Interne 2,56 Volt&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1|| 1|| Interne 2,56 Volt mit bypass Kapazität am AREF Pin&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ADLAR&#039;&#039;&#039; (&#039;&#039;&#039;ADC &#039;&#039;&#039; &#039;&#039;&#039;L&#039;&#039;&#039;eft &#039;&#039;&#039;A&#039;&#039;&#039;djust &#039;&#039;&#039;R&#039;&#039;&#039;esult)&lt;br /&gt;
:Das ADLAR Bit verändert das Aussehen des Ergebnisses der AD-Wandlung. Bei einer logischen 1 wird das Ergebnis linksbündig ausgegeben, bei einer 0 rechtsbündig. Eine Änderung in diesem Bit beeinflusst das Ergebnis sofort, ganz egal ob bereits eine Wandlung läuft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX4...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesen 5 Bits wird der zu messende Kanal bestimmt. Wenn man einen einfachen 1-kanaligen ADC verwendet wird einfach die entsprechende Pinnummer des Ports in die Bits 0...4 eingeschrieben (je nach Anzahl der Wandler des AVR, bei 8 AD-Kanälen halt nur 0...2).&lt;br /&gt;
:Wenn das Register beschrieben wird, während eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
:Eine Empfehlung ist deswegen diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Nutzung des ADC ====&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039; ADC&#039;&#039;&#039; zu aktivieren, müssen wir das &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bit im &#039;&#039;&#039;ADCSRA&#039;&#039;&#039;-Register setzen. Im gleichen Schritt legen wir auch die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
Im Folgenden ein kleines Beispiel für den &amp;quot;single conversion&amp;quot;-Mode bei einem ATmega169 und Nutzung der internen Referenzspannung (beim &#039;169 1,1V bei anderen AVRs auch 2,56V, vgl. Datenblatt). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls muss es mit einem [[Spannungsteiler]] verringert werden. Das Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1023 für V_ref-Volt. Beim Programmstart wir der ADC konfiguriert und aktiviert und dann auf verschiedenen Kanälen die Spannung gemessen. Initialisierung des ADC und die Wandlungen sollte man trennen, denn das Einschalten des ADC und vor allem der Referenzspannung dauert ein paar Dutzend Mikrosekunden. Außerdem ist das erste Ergebnis nach dem Einschalten ungültig und muss verworfen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Diese Beispiel zeigt die Anwendung des ADC eines ATmega169&lt;br /&gt;
// unter Verwendung der internen Referenzspannung von nominell 1,1V.&lt;br /&gt;
// Zur Anpassung an andere AVR und/oder andere Referenzspannungen&lt;br /&gt;
// siehe Erläuterungen in diesem Tutorial und im Datenblatt&lt;br /&gt;
&lt;br /&gt;
/* ADC initialisieren */&lt;br /&gt;
void ADC_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  // die Versorgungsspannung AVcc als Refernz wählen:&lt;br /&gt;
  ADMUX = (1&amp;lt;&amp;lt;REFS0);    &lt;br /&gt;
  // oder interne Referenzspannung als Referenz für den ADC wählen:&lt;br /&gt;
  // ADMUX = (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0);&lt;br /&gt;
&lt;br /&gt;
  // Bit ADFR (&amp;quot;free running&amp;quot;) in ADCSRA steht beim Einschalten&lt;br /&gt;
  // schon auf 0, also single conversion&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);     // Frequenzvorteiler&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);                  // ADC aktivieren&lt;br /&gt;
&lt;br /&gt;
  /* nach Aktivieren des ADC wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen, man liest&lt;br /&gt;
     also einen Wert und verwirft diesen, um den ADC &amp;quot;warmlaufen zu lassen&amp;quot; */&lt;br /&gt;
&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);                  // eine ADC-Wandlung &lt;br /&gt;
  while (ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {         // auf Abschluss der Konvertierung warten&lt;br /&gt;
  }&lt;br /&gt;
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten&lt;br /&gt;
     Wandlung nicht übernommen. */&lt;br /&gt;
  (void) ADCW;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ADC Einzelmessung */&lt;br /&gt;
uint16_t ADC_Read( uint8_t channel )&lt;br /&gt;
{&lt;br /&gt;
  // Kanal waehlen, ohne andere Bits zu beeinflußen&lt;br /&gt;
  ADMUX = (ADMUX &amp;amp; ~(0x1F)) | (channel &amp;amp; 0x1F);&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);            // eine Wandlung &amp;quot;single conversion&amp;quot;&lt;br /&gt;
  while (ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) {   // auf Abschluss der Konvertierung warten&lt;br /&gt;
  }&lt;br /&gt;
  return ADCW;                    // ADC auslesen und zurückgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ADC Mehrfachmessung mit Mittelwertbbildung */&lt;br /&gt;
/* beachte: Wertebereich der Summenvariablen */&lt;br /&gt;
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )&lt;br /&gt;
{&lt;br /&gt;
  uint32_t sum = 0;&lt;br /&gt;
&lt;br /&gt;
  for (uint8_t i = 0; i &amp;lt; nsamples; ++i ) {&lt;br /&gt;
    sum += ADC_Read( channel );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return (uint16_t)( sum / nsamples );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Beispielaufrufe: */&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
  ADC_Init();&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    adcval = ADC_Read(0);  // Kanal 0&lt;br /&gt;
    // mach was mit adcval&lt;br /&gt;
&lt;br /&gt;
    adcval = ADC_Read_Avg(2, 4);  // Kanal 2, Mittelwert aus 4 Messungen&lt;br /&gt;
    // mach was mit adcval&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel läuft der ADC ständig. Für den Fall, dass man Strom sparen will, z.B. mittels Verwendung des [[Sleep Mode]]s, muss man den ADC nach jeder Messung abschalten und vor der nächsten Messung wieder einschalten, wobei auch dann wieder eine kleine Pause und Anfangswandlung nötig sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Das Löschen des ADIF-Flags sollte, &#039;&#039;&#039;entgegen&#039;&#039;&#039; der [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_intbits FAQ], mit&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADIF);&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erfolgen. Die Methode in der FAQ eignet sich nur für Register, in denen &#039;&#039;&#039;nur&#039;&#039;&#039; Interrupt-Flags stehen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Externe Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Die minimale (externe) Referenzspannung des ADC darf nicht beliebig niedrig sein, vgl. dazu das (aktuellste) Datenblatt des verwendeten Controllers. z.&amp;amp;nbsp;B. beim ATmega8 darf sie laut Datenblatt&amp;lt;ref&amp;gt;ATmega8: S.245, Tabelle 103, Zeile &amp;quot;VREF&amp;quot;&amp;lt;/ref&amp;gt; 2,0V nicht unterschreiten.&amp;lt;ref&amp;gt;Diese Information findet sich erst in der letzten Revision (Rev. 2486O-10/04) des Datenblatts.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Meiner &amp;lt;!-- Wer? - es gibt inzwischen x Leute die mehr oder weniger viel in diesem Artikel geschrieben haben --&amp;gt; eigenen Erfahrung nach kann man aber (auf eigene Gefahr und natürlich nicht für Seriengeräte) durchaus noch ein klein wenig weiter heruntergehen, bei dem von mir unter die Lupe genommenen ATmega8L (also die Low-Voltage-Variante) funktioniert der ADC bei 5V Betriebsspannung mit bis zu VREF=1,15V hinunter korrekt, ab 1,1V und darunter digitalisiert er jedoch nur noch Blödsinn). Ich würde sicherheitshalber nicht unter 1,5V gehen und bei niedrigeren Betriebsspannungen mag sich die Untergrenze für VREF am Pin AREF ggf. nach &#039;&#039;oben&#039;&#039; verschieben.&lt;br /&gt;
&lt;br /&gt;
In der letzten Revision des Datenblatts ist außerdem korrigiert, dass ADC4 und ADC5 sehr wohl 10 Bit Genauigkeit bieten — und nicht bloß 8 Bit, wie in älteren Revisionen irrtümlich angegeben.&lt;br /&gt;
&lt;br /&gt;
=== Analog-Digital-Wandlung ohne internen ADC ===&lt;br /&gt;
&lt;br /&gt;
==== Messen eines Widerstandes ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif|framed|right]]&lt;br /&gt;
Analoge Werte lassen sich ohne Analog-Digital-Wandler auch indirekt ermitteln. Im Folgenden wird die Messung des an einem Potentiometer eingestellten Widerstands anhand der Ladekurve eines Kondensators erläutert. Bei dieser Methode wird nur ein Portpin benötigt, ein Analog-Digital-Wandler oder Analog-Comparator ist nicht erforderlich. Es wird dazu ein Kondensator und der Widerstand (das Potentiometer) in Reihe zwischen Vorsorgungsspannung und Masse/GND geschaltet (sogen. RC-Netzwerk). Zusätzlich wird eine Verbindung der Leitung zwischen Kondensator und Potentiometer zu einem Portpin des Controllers hergestellt. Die folgende Abbildung verdeutlicht die erforderliche Schaltung. &lt;br /&gt;
&lt;br /&gt;
Wird der Portpin des Controllers auf Ausgang konfiguriert (im Beispiel &#039;&#039;DDRD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) und dieser Ausgang auf Logisch 1 (&amp;quot;High&amp;quot;, &#039;&#039;PORTD&amp;amp;nbsp;|=&amp;amp;nbsp;(1&amp;lt;&amp;lt;PD2)&#039;&#039;) geschaltet, liegt an beiden &amp;quot;Platten&amp;quot; des Kondensators das gleiche Potential &#039;&#039;&#039;VCC&#039;&#039;&#039; an und der Kondensator somit entladen. (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so, da an beiden Seiten des Kondensators das gleiche Potential anliegt und somit eine Potentialdifferenz von 0V besteht =&amp;gt; Kondensator ist entladen).&lt;br /&gt;
&lt;br /&gt;
Nach einer gewissen Zeit ist der Kondensator entladen und der Portpin wird als Eingang konfiguriert (&#039;&#039;DDRD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2); PORTD&amp;amp;nbsp;&amp;amp;=&amp;amp;nbsp;~(1&amp;lt;&amp;lt;PD2)&#039;&#039;), wodurch dieser hochohmig wird. Der Status des Eingangspin (in PIND) ist Logisch 1 (High). Der Kondensator lädt sich jetzt über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti unter die Threshold-Spannung des Eingangspins (2/5 Vcc, also ca. 2V), wird das Eingangssignal als LOW erkannt (Bit in PIND wird 0). Die Zeitspanne zwischen der Umschaltung von Entladung auf Aufladung und dem Wechsel des Eingangssignals von High auf Low ist ein Maß für den am Potentiometer eingestellten Widerstand. Zur Zeitmessung kann einer der im Controller vorhandenen Timer genutzt werden. Der 220Ω Widerstand dient dem Schutz des Controllers. Es würde sonst bei Maximaleinstellung des Potentionmeters (hier 0Ω) ein zu hoher Strom fließen, der die Ausgangsstufe des Controllers zerstört. &lt;br /&gt;
&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B: 0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Link 404 =&amp;gt; auskommentiert, mthomas 9.2.2008 &lt;br /&gt;
Ein Beispielprogramm findet sich auf [http://www.mypage.bluewin.ch/ch_schifferle/ Christian Schifferles Web-Seite] im Archiv &#039;&#039;ATMEL.ZIP&#039;&#039;, welches unter den Titel &#039;&#039;Tutorial &amp;quot;Programmieren mit C für Atmel Mikrocontroller&#039;&#039; heruntergeladen werden kann. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ADC über Komparator ====&lt;br /&gt;
&lt;br /&gt;
[[Image:ADC ueber Komparator.gif|framed|right]]&lt;br /&gt;
Es gibt einen weiteren Weg, eine analoge Spannung mit Hilfe des&lt;br /&gt;
Komparators, welcher in fast jedem AVR integriert ist, zu messen. Siehe dazu&lt;br /&gt;
auch die Application Note AVR400 von Atmel.&lt;br /&gt;
&lt;br /&gt;
Dabei wird das zu messende Signal auf den invertierenden Eingang&lt;br /&gt;
des Komparators geführt. Zusätzlich wird ein Referenzsignal an den nicht&lt;br /&gt;
invertierenden Eingang des Komparators angeschlossen. Das Referenzsignal wird&lt;br /&gt;
hier auch wieder über ein RC-Glied erzeugt, allerdings mit festen Werten für R&lt;br /&gt;
und C.&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Messung ist nun dem vorhergehenden recht&lt;br /&gt;
ähnlich. Durch Anlegen eines LOW-Pegels an Pin 2 wird der Kondensator zuerst&lt;br /&gt;
einmal entladen. Auch hier muss darauf geachtet werden, dass der Entladevorgang&lt;br /&gt;
genügend lang dauert.&lt;br /&gt;
Nun wird Pin 2 auf HIGH gelegt. Der Kondensator wird geladen. Wenn die Spannung&lt;br /&gt;
über dem Kondensator die am Eingangspin anliegende Spannung erreicht hat,&lt;br /&gt;
schaltet der Komparator durch. Die Zeit, welche benötigt wird, um den&lt;br /&gt;
Kondensator zu laden, kann nun auch wieder als Maß für die Spannung an Pin 1&lt;br /&gt;
herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Ich habe es mir gespart, diese Schaltung auch aufzubauen, und zwar aus mehreren Gründen:&lt;br /&gt;
&lt;br /&gt;
# 3 Pins notwendig.&lt;br /&gt;
# Genauigkeit vergleichbar mit einfacherer Lösung.&lt;br /&gt;
# War einfach zu faul.&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung liegt allerdings darin, dass damit direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines Digital-Analog-Konverters (&#039;&#039;&#039;DAC&#039;&#039;&#039;) können wir nun auch Analogsignale ausgeben. Es gibt hier mehrere Verfahren. &amp;lt;!-- Wenn wir beim ADC die Möglichkeit haben, mit externen Komponenten zu operieren, müssen wir bei der DAC-Wandlung mit dem auskommen, was der Controller selber zu bieten hat. --mt: hmm, richtig? verstaendlich? redundant? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DAC über mehrere digitale Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir an den Ausgängen des Controllers ein entsprechendes&lt;br /&gt;
Widerstandsnetzwerk aufbauen haben wir die Möglichkeit, durch die Ansteuerung&lt;br /&gt;
der Ausgänge über den Widerständen einen Addierer aufzubauen, mit dessen&lt;br /&gt;
Hilfe wir eine dem Zahlenwert proportionale Spannung erzeugen können. Das&lt;br /&gt;
Schaltbild dazu kann etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
[[Image:DAC R2R.gif]]&lt;br /&gt;
&lt;br /&gt;
Es sollten selbstverständlich möglichst genaue Widerstände verwendet&lt;br /&gt;
werden, also nicht unbedingt solche mit einer Toleranz von 10% oder mehr.&lt;br /&gt;
Weiterhin empfiehlt es sich, je nach Anwendung den Ausgangsstrom über einen&lt;br /&gt;
Operationsverstärker zu verstärken.&lt;br /&gt;
&lt;br /&gt;
=== PWM (Pulsweitenmodulation) ===&lt;br /&gt;
&lt;br /&gt;
Wir kommen nun zu einem Thema, welches in aller Munde ist, aber viele&lt;br /&gt;
Anwender verstehen nicht ganz, wie [[PWM]] eigentlich funktioniert.&lt;br /&gt;
&lt;br /&gt;
Wie wir alle wissen, ist ein Mikrocontroller ein rein digitales Bauteil.&lt;br /&gt;
Definieren wir einen Pin als Ausgang, dann können wir diesen Ausgang entweder&lt;br /&gt;
auf HIGH setzen, worauf am Ausgang die Versorgungsspannung &#039;&#039;&#039; Vcc&#039;&#039;&#039; anliegt, oder aber wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt. Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen HIGH und LOW umschalten? - Richtig, wir erhalten eine Rechteckspannung, wie die folgende Abbildung zeigt:&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 1.gif]]&lt;br /&gt;
&lt;br /&gt;
Diese Rechteckspannung hat nun einen arithmetischen Mittelwert, &lt;br /&gt;
der je nach Pulsbreite kleiner oder größer ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 2.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun diese pulsierende Ausgangsspannung noch über ein RC-Glied filtern/&amp;quot;glätten&amp;quot;, dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVRs können wir direkt PWM-Signale erzeugen. &lt;br /&gt;
Dazu dient der 16-Bit Zähler, welcher im sogenannten PWM-Modus betrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
;Hinweis: In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt, weshalb ein Blick ins entsprechende Datenblatt dringend angeraten wird.&lt;br /&gt;
&lt;br /&gt;
Um den PWM-Modus zu aktivieren, müssen im Timer/Counter1 Control&lt;br /&gt;
Register A TCCR1A die Pulsweiten-Modulatorbits PWM10 bzw. PWM11 entsprechend nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! PWM11 || PWM10 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| 0 || 0 || PWM-Modus des Timers ist nicht aktiv&lt;br /&gt;
|- &lt;br /&gt;
| 0 || 1 || 8-Bit PWM&lt;br /&gt;
|- &lt;br /&gt;
| 1 || 0 || 9-Bit PWM&lt;br /&gt;
|- &lt;br /&gt;
| 1 || 1 || 10-Bit PWM&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Timer/Counter zählt nun permanent von 0 bis zur Obergrenze&lt;br /&gt;
und wieder zurück, er wird also als sogenannter Auf-/Ab Zähler betrieben. &lt;br /&gt;
Die Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Auflösung || Obergrenze || Frequenz&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 255&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 510&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 9&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 511&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 1022&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 10&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1023&lt;br /&gt;
| f&amp;lt;sub&amp;gt;TC1&amp;lt;/sub&amp;gt; / 2046&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss mit den Bits &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; desselben&lt;br /&gt;
Registers die gewünschte Ausgabeart des Signals definiert werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! COM1A1 || COM1A0 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Keine Wirkung, Pin wird nicht geschaltet.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Nicht invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Hochzählen und gesetzt beim&lt;br /&gt;
Herunterzählen.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Invertierende PWM.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Ausgangspin wird gelöscht beim Herunterzählen und gesetzt beim&lt;br /&gt;
Hochzählen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl, um beispielsweise den Timer/Counter als&lt;br /&gt;
nicht invertierenden 10-Bit PWM zu verwenden, heißt dann:&lt;br /&gt;
&lt;br /&gt;
alte Schreibweise (PWMxx wird nicht mehr akzeptiert)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;PWM11)|(1&amp;lt;&amp;lt;PWM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
neue Schreibweise&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
TCCR1A = (1&amp;lt;&amp;lt;WGM11)|(1&amp;lt;&amp;lt;WGM10)|(1&amp;lt;&amp;lt;COM1A1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Timer/Counter überhaupt läuft, müssen wir im Control&lt;br /&gt;
Register B &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; noch den gewünschten Takt (Vorteiler) einstellen und&lt;br /&gt;
somit auch die Frequenz des &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signals bestimmen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! CS12 || CS11 || CS10 || Bedeutung&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Stop. Der Timer/Counter wird gestoppt.&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 8&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 64&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| CK / 256&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| CK / 1024&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| Externer Pin 1, negative Flanke&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| Externer Pin 1, positive Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Also um einen Takt von CK / 1024 zu generieren, verwenden wir&lt;br /&gt;
folgenden Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
TCCR1B = (1&amp;lt;&amp;lt;CS12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt muss nur noch der Vergleichswert festgelegt werden. Diesen&lt;br /&gt;
schreiben wir in das 16-Bit Timer/Counter Output Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem Vergleichswert und dem generierten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal aufzeigen.&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 3.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:PWM Theorie 4.gif]]&lt;br /&gt;
&lt;br /&gt;
Ach ja, fast hätte ich&#039;s vergessen. Das generierte &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal&lt;br /&gt;
wird am Output Compare Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; des Timers ausgegeben und leider können wir&lt;br /&gt;
deshalb auch beim AT90S2313 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren. Andere AVR-Typen verfügen über bis zu vier PWM-Ausgänge. Zu beachten ist außerdem, das wenn der OC Pin aktiviert ist, er nichtmehr wie üblich funktioniert und z.&amp;amp;nbsp;B. nicht einfach über PINx ausgelesen werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein Programm, welches an einem ATmega8 den Fast-PWM Modus verwendet, den Modus 14, könnte so aussehen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // OC1A auf Ausgang&lt;br /&gt;
  DDRB = (1 &amp;lt;&amp;lt; PB1 );  //ATMega8&lt;br /&gt;
  // DDRD = (1 &amp;lt;&amp;lt; PD5 ); //ATMega16&lt;br /&gt;
  //&lt;br /&gt;
  // Timer 1 einstellen&lt;br /&gt;
  //  &lt;br /&gt;
  // Modus 14:&lt;br /&gt;
  //    Fast PWM, Top von ICR1&lt;br /&gt;
  //&lt;br /&gt;
  //    WGM13    WGM12   WGM11    WGM10&lt;br /&gt;
  //      1        1       1        0&lt;br /&gt;
  //&lt;br /&gt;
  //    Timer Vorteiler: 1&lt;br /&gt;
  //     CS12     CS11    CS10&lt;br /&gt;
  //       0        0       1&lt;br /&gt;
  //&lt;br /&gt;
  //  Steuerung des Ausgangsport: Set at BOTTOM, Clear at match&lt;br /&gt;
  //     COM1A1   COM1A0&lt;br /&gt;
  //       1        0&lt;br /&gt;
 &lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;WGM11);&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM13) | (1&amp;lt;&amp;lt;WGM12) | (1&amp;lt;&amp;lt;CS10);&lt;br /&gt;
 &lt;br /&gt;
  //  den Endwert (TOP) für den Zähler setzen&lt;br /&gt;
  //  der Zähler zählt bis zu diesem Wert&lt;br /&gt;
&lt;br /&gt;
  ICR1 = 0x6FFF;&lt;br /&gt;
 &lt;br /&gt;
  // der Compare Wert&lt;br /&gt;
  // Wenn der Zähler diesen Wert erreicht, wird mit&lt;br /&gt;
  // obiger Konfiguration der OC1A Ausgang abgeschaltet&lt;br /&gt;
  // Sobald der Zähler wieder bei 0 startet, wird der&lt;br /&gt;
  // Ausgang wieder auf 1 gesetzt&lt;br /&gt;
  //&lt;br /&gt;
  // Durch Verändern dieses Wertes, werden die unterschiedlichen&lt;br /&gt;
  // PWM Werte eingestellt.&lt;br /&gt;
&lt;br /&gt;
  OCR1A = 0x3FFF;&lt;br /&gt;
&lt;br /&gt;
  while (1) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;PWM-Mode Tabelle aus dem Datenblatt des ATmega8515&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
!Mode || WGM13 || WGM12 || WGM11 || WGM10 || Timer/Counter Mode of Operation&lt;br /&gt;
! TOP|| Update of OCR1x at || TOV1 Flag set on&lt;br /&gt;
|- &lt;br /&gt;
! 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| Normal&lt;br /&gt;
| 0xFFFF&lt;br /&gt;
| Immediate&lt;br /&gt;
| MAX&lt;br /&gt;
|-&lt;br /&gt;
! 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| PWM, Phase Correct, 8-Bit&lt;br /&gt;
| 0x00FF&lt;br /&gt;
| TOP&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|- &lt;br /&gt;
! 2&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| PWM, Phase Correct, 9-Bit&lt;br /&gt;
| 0x01FF&lt;br /&gt;
| TOP&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
! 3&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| PWM, Phase Correct, 10-Bit&lt;br /&gt;
| 0x03FF&lt;br /&gt;
| TOP&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
! 4&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| CTC&lt;br /&gt;
| OCR1A&lt;br /&gt;
| Immediate&lt;br /&gt;
| MAX&lt;br /&gt;
|-&lt;br /&gt;
! 5&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| Fast PWM, 8-Bit&lt;br /&gt;
| 0x00FF&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| TOP&lt;br /&gt;
|-&lt;br /&gt;
! 6&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| Fast PWM, 9-Bit&lt;br /&gt;
| 0x01FF&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| TOP&lt;br /&gt;
|-&lt;br /&gt;
! 7&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| Fast PWM, 10-Bit&lt;br /&gt;
| 0x03FF&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| TOP&lt;br /&gt;
|-&lt;br /&gt;
! 8&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| PWM, Phase an Frequency Correct&lt;br /&gt;
| ICR1&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|-    &lt;br /&gt;
! 9&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| PWM, Phase an Frequency Correct&lt;br /&gt;
| OCR1A&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
! 10&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| PWM, Phase Correct&lt;br /&gt;
| ICR1&lt;br /&gt;
| TOP&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
! 11&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| PWM, Phase an Frequency Correct&lt;br /&gt;
| OCR1A&lt;br /&gt;
| TOP&lt;br /&gt;
| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
! 12&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 0&lt;br /&gt;
| CTC&lt;br /&gt;
| ICR1&lt;br /&gt;
| Immediate&lt;br /&gt;
| MAX&lt;br /&gt;
|-&lt;br /&gt;
! 13&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| 1&lt;br /&gt;
| Reserved&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
! 14&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 0&lt;br /&gt;
| Fast PWM&lt;br /&gt;
| ICR1&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| TOP&lt;br /&gt;
|-&lt;br /&gt;
! 15&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
| Fast PWM&lt;br /&gt;
| OCR1A&lt;br /&gt;
| BOTTOM&lt;br /&gt;
| TOP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für Details der PWM-Möglichkeiten muss immer das jeweilige Datenblatt des Prozessors konsultiert werden, da sich die unterschiedlichen Prozessoren in ihren Möglichkeiten doch stark unterscheiden. Auch muss man aufpassen, welches zu setzende Bit in welchem Register ist. Auch hier kann es sein, dass gleichnamige Konfigurationsbits in unterschiedlichen Konfigurationsregistern (je nach konkretem Prozessortyp) sitzen.&lt;br /&gt;
&lt;br /&gt;
== Anmerkungen ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie: Avr-gcc Tutorial]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87548</id>
		<title>FIFO</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87548"/>
		<updated>2015-02-24T09:01:46Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein FIFO (&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;n-&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;O&amp;lt;/b&amp;gt;ut) ist ein Pufferspeicher nach dem &amp;quot;Warteschlangen-Prinzip&amp;quot;. Pufferspeicher dienen dazu, für einen Prozessor Daten aufzufangen, die er noch nicht verarbeiten kann. Ein FIFO funktioniert definitionsgemäß so, dass das erste Element (First In), welches &amp;quot;hinten&amp;quot; in die Warteschlange eingefügt wird, später auch als erstes &amp;quot;vorne&amp;quot; herausgeholt wird (First Out). Das Gegenstück zum FIFO ist [[LIFO]].&lt;br /&gt;
&lt;br /&gt;
Ein bekanntes Beispiel für einen FIFO-Speicher ist der des [[UART]]s im PC. Dieser sammelt ankommende Daten solange, bis er fast voll ist (meist 14 Bytes). Dann wird ein [[Interrupt]] ausgelöst und der Prozessor kann &amp;quot;auf einen Rutsch&amp;quot; alle Daten auf einmal auslesen. Ganz am Anfang hatten UARTs keinen FIFO und erzeugten für jedes empfangene Byte einen Interrupt, so wie es heute noch die meisten Mikrocontroller machen. Damit ist aber die CPU-Belastung wesentlich höher, was bei höheren Datenraten zu Problemen führen kann.&lt;br /&gt;
&lt;br /&gt;
Die Umsetzung eines FIFOs in Software heißt Ringpuffer und weist auch eine feste Puffergröße auf. Wesentlicher Vorteil gegenüber der verketteten Liste ist die schnellere Ausführungszeit und der geringere Aufwand.&lt;br /&gt;
&lt;br /&gt;
== Standard-Ringpuffer ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Simpel&lt;br /&gt;
* Leicht verständlich&lt;br /&gt;
* Daten können auch vom Typ struct sein&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Schnell aber nicht optimal&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Ringpuffer.png|300px]]&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Circular buffer.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Inhalte des Puffers werden in einem schlichten Array gespeichert und der Zugriff erfolgt über einen Integer-Index. Erreicht ein Index die Obergrenze springt dieser auf Null zurück. Topologisch ist dies äquivalent zu einem Ringschluss, bei dem man ja auch nach dem letzten Element wieder beim ersten angelangt ist. Man denke zb an das Zifferblatt einer Uhr, bei der nach der 59 wieder die 0 kommt. Ein größer-gleich statt nur eines ist-gleich Vergleichs ist sicherer gegenüber Programmfehlern, bei denen der Index verstellt wurde.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
write = write + 1;&lt;br /&gt;
if (write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  write = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Haben Lese- und Schreib-Index den gleichen Wert wird der Puffer als leer angesehen und es kann kein Element aus dem Puffer entnommen werden. Wenn write+1 und read identisch sind wird der Puffer als voll angesehen und es kann kein weiteres Element mehr im Puffer gespeichert werden. Nun fehlt noch der Sonderfall bei dem read gleich Null ist, hier funktioniert der write+1-Vergleich nicht mehr (wegen dem &#039;Überlauf&#039; am Ende des Arrays, der den Ringschluss bewirkt) und die Abfrage einer zusätzlichen Bedingung ist zur Voll-Abfrage erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == write =&amp;gt; leer&lt;br /&gt;
&lt;br /&gt;
write + 1 == read || read == 0 &amp;amp;&amp;amp; write+1 == BUFFER_SIZE =&amp;gt; voll&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigem Zugriff zwischen dem Befüller (zb. einer UART-ISR) und dem Entnahmecode (zb. der Code in der Hauptschleife) muss ausgeschlossen werden, dass der Entnahmecode beim Lesen aus dem Puffer nicht durch den Interrupt unterbrochen werden kann. Denn während diese Funktion läuft ist zwischenzeitlich der read-Index in einem inkonsistenten Zustand. Erst nach Beendigung der Funktion spiegelt der read-Index wieder die Realität des Füllgrades wieder. Als Abhilfe dienen [[Interrupt#Atomarer_Datenzugriff|Atomare Abschnitte]], solche können durch keinen Interrupt unterbrochen werden. Die Radialkur deaktiviert alle Interrupts, besser ist die Deaktivierung eines einzelnen Interrupts. Es geht aber meistens auch so, da die FIFO Funktionen sehr kurz sind und daher auch die Interrupts nur kurz abgeschaltet werden. Im Allgemeinen werden versäumte Interrupts nach erneuter Aktivierung nachgeholt und gehen nicht verloren, so dass bereits anstehende Interrupts nur kurz verzögert werden. Solange die Interruptrate größer als die Ausführungsgeschwindigkeit der FIFO Funktionen ist, stellt das Abschalten der Interrupts daher auch in der Praxis meist kein allzugroßes Problem dar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
cli()&lt;br /&gt;
ret = BufferOut(&amp;amp;var);&lt;br /&gt;
sei()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist die Initialisierung der Indizes (read, write) mit -1, denn damit kann man einen leere Ringpuffer von einem vollen unterscheiden. Initialisiert man nur mit 0, kann man einen leeren nicht von einem vollen mit read=write=0 unterscheiden. &lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit der Vereinfachung entsteht durch die Einführung einer &amp;quot;Counter&amp;quot; Variable, die den Füllstand des Buffers wiedergibt. Bei Einschreiben wird diese um +1 erhöht, beim Auslesen um 1 erniedrigt. Ist sie 0 ist der Buffer leer. Damit lässt sich die Codeeffizienz steigern, da die obigen Vergleiche kürzer ausfallen und sich auf nur diese eine Variable beschränken. Nachteilig ist allerdings, dass man eine weitere Variable im System hat und natürlich benötigen die entsprechenden Operationen zum Erhöhen bzw. Erniedrigen dieser Variable ja auch Ausführungszeit. Benötigt man im restlichen Code den Füllgrad der FIFO nicht als explizite Zahl, dann lohnt daher eine derartige Counter-Variable meistens nicht.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_SIZE 23&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  //if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  //  buffer.write = 0; // erhöht sicherheit&lt;br /&gt;
&lt;br /&gt;
  if ( ( buffer.write + 1 == buffer.read ) ||&lt;br /&gt;
       ( buffer.read == 0 &amp;amp;&amp;amp; buffer.write + 1 == BUFFER_SIZE ) )&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
&lt;br /&gt;
  buffer.write++;&lt;br /&gt;
  if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.write = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//    &lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read++;&lt;br /&gt;
  if (buffer.read &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.read = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-Ringpuffer - die schnellste Lösung ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Durch Bitmaske auf den Feld-Index &#039;&#039;write&#039;&#039; kann der nicht &amp;quot;Amoklaufen&amp;quot; und beliebige Inhalte im Speicher überschreiben&lt;br /&gt;
* Sehr einfach, sehr schnell&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Nur Größenfaktoren von 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; möglich&lt;br /&gt;
* Nur in speziellen Fällen Pointerreferenz möglich&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Index am oberen Ende angekommen ist springt er an den Anfang zurück. Die Definition der Obergrenze wird nicht durch einen Vergleich realisiert, sondern durch Modulo-Arithmetik. Dies kann sehr einfach und schnell durch Maskieren des Index-Werts umgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
( write + 1 ) &amp;amp; MASK&lt;br /&gt;
&lt;br /&gt;
15 = 0b00001111&lt;br /&gt;
15 + 1 = 16 = 0b000010000&lt;br /&gt;
16 &amp;amp; MASK = 0b000010000 &amp;amp; 0b000001111 = 0b000000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt das &#039;&#039;write&#039;&#039; nur einen Wert zwischen 0 und 15 einnehmen kann. Das ganze funktioniert nur mit Zahlen der Reihe 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da der Puffer nur eine endliche Größe aufweist, muss eine voll/leer Indikation verfügbar sein. read == write bedeutet Puffer leer, entsprechend bedeutet write + 1 == read, dass keine weiteren Elemente mehr hinzugefügt werden können, denn sonst würde das Hinzufügen weiterer Daten einen Pufferüberlauf hervorrufen. Für die Behandlung des Pufferüberlaufs gibt es zwei Methoden, nichts tun und Fehler zurück geben oder ältesten Puffer-Inhalt verwerfen (read+1).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == ((write + 1) &amp;amp; MASK)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verhindert die Bitmaske einen möglichen Überlauf. Weiter zu beachten ist, dass ein Byte durch die voll/leer Indikation verloren geht, der Puffer hat hier nur eine größe von 15 statt 16 Byte&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleiner Trick. Bei einer Zahl der Reihe 2^n lässt sich auf einfache Weise immer eine passende Bitmaske erzeugen und man sieht gleichzeitig klar die Anzahl der genutzten Bytes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# define SIZE 16&lt;br /&gt;
# define MASK (SIZE-1)&lt;br /&gt;
&lt;br /&gt;
16 = 0b010000&lt;br /&gt;
16 - 1 = 0b001111&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Normalbetrieb sollte der Puffer nie voll ausgereizt sein und wenn der Puffer bereits überläuft ist es für die Systemstabilität und Fehleranlyse oft schon zu spät, denn von einem nicht mehr funktionierenden Gerät lässt sich nur wenig Information herauskitzeln. Da kann ein Frühwarnsystem mehr bieten, wobei zwei Ausprägungen typisch sind, Höchstwert merken und eine Meldeschwelle einführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 freeMemory =  (read - write - 1 ) &amp;amp; MASK);&lt;br /&gt;
 if (freeMemory &amp;lt; floodmark)&lt;br /&gt;
   floodmark = freeMemory;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 if ( (read - write - 1 ) &amp;amp; MASK) &amp;lt;= FLOODMARK;&lt;br /&gt;
   FloodmarkExcess();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine entsprechende Auswertung kann z.B. bei einer UART zur Steuerung des Handshakes benutzt werden, bei dem die Gegenstelle aufgefordert wird, das Senden kurzfristig einzustellen, damit man nicht Gefahr läuft, übertragene Zeichen zu verlieren, weil die FIFO voll wird. Dabei ist allerdings auch zu berücksichtigen, dass die Gegenstelle das Senden nicht sofort einstellen kann, da sich beispielsweise bereits Zeichen in der Sendehardware befinden können, die der Sender nicht mehr stoppen kann. Hier heißt es also rechtzeitig zu reagieren und nicht erst, wenn der FIFO propevoll ist. Fällt der Füllgrad einer darartigen UART-FIFO wieder unter eine gewisse Schwelle, dann wird der Gegenstelle signalisiert, dass sie mit dem Senden fortfahren kann.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
 &lt;br /&gt;
#define BUFFER_SIZE 16 // muss 2^n betragen (8, 16, 32, 64 ...)&lt;br /&gt;
#define BUFFER_MASK (BUFFER_SIZE-1) // Klammern auf keinen Fall vergessen&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t next = ((buffer.write + 1) &amp;amp; BUFFER_MASK);&lt;br /&gt;
&lt;br /&gt;
  if (buffer.read == next)&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
  // buffer.data[buffer.write &amp;amp; BUFFER_MASK] = byte; // absolut Sicher&lt;br /&gt;
  buffer.write = next;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read = (buffer.read+1) &amp;amp; BUFFER_MASK;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FIFO mit C-Präprozessor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef FIFO_H_&lt;br /&gt;
#define FIFO_H_&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[64];&lt;br /&gt;
} FIFO64_t;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[128];&lt;br /&gt;
} FIFO128_t;&lt;br /&gt;
&lt;br /&gt;
#define FIFO_init(fifo)         { fifo._read = 0; fifo._write = 0; }&lt;br /&gt;
&lt;br /&gt;
#define FIFO_available(fifo)    ( fifo._read != fifo._write )&lt;br /&gt;
&lt;br /&gt;
#define FIFO_read(fifo, size) (                                         \&lt;br /&gt;
        (FIFO_available(fifo)) ?                                        \&lt;br /&gt;
        fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0      \&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define FIFO_write(fifo, data, size) {                                                          \&lt;br /&gt;
        uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */    \&lt;br /&gt;
        if(tmphead != fifo._read) {                             /* if buffer is not full */     \&lt;br /&gt;
                fifo._write = tmphead;                          /* store new index */           \&lt;br /&gt;
                fifo._buffer[tmphead] = data;                   /* store data in buffer */      \&lt;br /&gt;
        }                                                                                       \&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define FIFO64_read(fifo)			FIFO_read(fifo, 64)&lt;br /&gt;
#define FIFO64_write(fifo, data)		FIFO_write(fifo, data, 64)&lt;br /&gt;
&lt;br /&gt;
#define FIFO128_read(fifo)			FIFO_read(fifo, 128)&lt;br /&gt;
#define FIFO128_write(fifo, data)		FIFO_write(fifo, data, 128)&lt;br /&gt;
&lt;br /&gt;
#endif /*FIFO_H_*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hinweis: viele C-Compiler übersetzen automatisch eine Modulo-Division mit einem Divisor einer 2-er Potenz in Form einer entsprechenden Bitmaskierung. Zum einen erspart man sich dadurch das Bestimmen der entsprechenden Maske, zum anderen hat man dann den &#039;Bonus&#039;, dass der Code immer noch korrekt funktioniert (wenn auch langsamer), wenn der Anwender einen Fehler macht und die Array-Länge nicht als 2-er Potenz definiert. Die Operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
kann dann durch die in diesem Sinne gleichwertige Operation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) % size] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ausgedrückt werden, bzw.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) % size);          /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Circular_buffer Circular buffer], englische Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87547</id>
		<title>FIFO</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87547"/>
		<updated>2015-02-24T08:58:14Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein FIFO (&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;n-&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;O&amp;lt;/b&amp;gt;ut) ist ein Pufferspeicher nach dem &amp;quot;Warteschlangen-Prinzip&amp;quot;. Pufferspeicher dienen dazu, für einen Prozessor Daten aufzufangen, die er noch nicht verarbeiten kann. Ein FIFO funktioniert definitionsgemäß so, dass das erste Element (First In), welches &amp;quot;hinten&amp;quot; in die Warteschlange eingefügt wird, später auch als erstes &amp;quot;vorne&amp;quot; herausgeholt wird (First Out). Das Gegenstück zum FIFO ist [[LIFO]].&lt;br /&gt;
&lt;br /&gt;
Ein bekanntes Beispiel für einen FIFO-Speicher ist der des [[UART]]s im PC. Dieser sammelt ankommende Daten solange, bis er fast voll ist (meist 14 Bytes). Dann wird ein [[Interrupt]] ausgelöst und der Prozessor kann &amp;quot;auf einen Rutsch&amp;quot; alle Daten auf einmal auslesen. Ganz am Anfang hatten UARTs keinen FIFO und erzeugten für jedes empfangene Byte einen Interrupt, so wie es heute noch die meisten Mikrocontroller machen. Damit ist aber die CPU-Belastung wesentlich höher, was bei höheren Datenraten zu Problemen führen kann.&lt;br /&gt;
&lt;br /&gt;
Die Umsetzung eines FIFOs in Software heißt Ringpuffer und weist auch eine feste Puffergröße auf. Wesentlicher Vorteil gegenüber der verketteten Liste ist die schnellere Ausführungszeit und der geringere Aufwand.&lt;br /&gt;
&lt;br /&gt;
== Standard-Ringpuffer ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Simpel&lt;br /&gt;
* Leicht verständlich&lt;br /&gt;
* Daten können auch vom Typ struct sein&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Schnell aber nicht optimal&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Ringpuffer.png|300px]]&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Circular buffer.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Inhalte des Puffers werden in einem schlichten Array gespeichert und der Zugriff erfolgt über einen Integer-Index. Erreicht ein Index die Obergrenze springt dieser auf Null zurück. Topologisch ist dies äquivalent zu einem Ringschluss, bei dem man ja auch nach dem letzten Element wieder beim ersten angelangt ist. Man denke zb an das Zifferblatt einer Uhr, bei der nach der 59 wieder die 0 kommt. Ein größer-gleich statt nur eines ist-gleich Vergleichs ist sicherer gegenüber Programmfehlern, bei denen der Index verstellt wurde.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
write = write + 1;&lt;br /&gt;
if (write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  write = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Haben Lese- und Schreib-Index den gleichen Wert wird der Puffer als leer angesehen und es kann kein Element aus dem Puffer entnommen werden. Wenn write+1 und read identisch sind wird der Puffer als voll angesehen und es kann kein weiteres Element mehr im Puffer gespeichert werden. Nun fehlt noch der Sonderfall bei dem read gleich Null ist, hier funktioniert der write+1-Vergleich nicht mehr (wegen dem &#039;Überlauf&#039; am Ende des Arrays, der den Ringschluss bewirkt) und die Abfrage einer zusätzlichen Bedingung ist zur Voll-Abfrage erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == write =&amp;gt; leer&lt;br /&gt;
&lt;br /&gt;
write + 1 == read || read == 0 &amp;amp;&amp;amp; write+1 == BUFFER_SIZE =&amp;gt; voll&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigem Zugriff zwischen dem Befüller (zb. einer UART-ISR) und dem Entnahmecode (zb. der Code in der Hauptschleife) muss ausgeschlossen werden, dass der Entnahmecode beim Lesen aus dem Puffer nicht durch den Interrupt unterbrochen werden kann. Denn während diese Funktion läuft ist zwischenzeitlich der read-Index in einem inkonsistenten Zustand. Erst nach Beendigung der Funktion spiegelt der read-Index wieder die Realität des Füllgrades wieder. Als Abhilfe dienen [[Interrupt#Atomarer_Datenzugriff|Atomare Abschnitte]], solche können durch keinen Interrupt unterbrochen werden. Die Radialkur deaktiviert alle Interrupts, besser ist die Deaktivierung eines einzelnen Interrupts. Es geht aber meistens auch so, da die FIFO Funktionen sehr kurz sind und daher auch die Interrupts nur kurz abgeschaltet werden. Im Allgemeinen werden versäumte Interrupts nach erneuter Aktivierung nachgeholt und gehen nicht verloren, so dass bereits anstehende Interrupts nur kurz verzögert werden. Solange die Interruptrate größer als die Ausführungsgeschwindigkeit der FIFO Funktionen ist, stellt das Abschalten der Interrupts daher auch in der Praxis meist kein allzugroßes Problem dar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
cli()&lt;br /&gt;
ret = BufferOut(&amp;amp;var);&lt;br /&gt;
sei()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist die Initialisierung der Indizes (read, write) mit -1, denn damit kann man einen leere Ringpuffer von einem vollen unterscheiden. Initialisiert man nur mit 0, kann man einen leeren nicht von einem vollen mit read=write=0 unterscheiden. &lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit der Vereinfachung entsteht durch die Einführung einer &amp;quot;Counter&amp;quot; Variable, die den Füllstand des Buffers wiedergibt. Bei Einschreiben wird diese um +1 erhöht, beim Auslesen um 1 erniedrigt. Ist sie 0 ist der Buffer leer. Damit lässt sich die Codeeffizienz steigern, da die obigen Vergleiche kürzer ausfallen und sich auf nur diese eine Variable beschränken. Nachteilig ist allerdings, dass man eine weitere Variable im System hat und natürlich benötigen die entsprechenden Operationen zum Erhöhen bzw. Erniedrigen dieser Variable ja auch Ausführungszeit. Benötigt man im restlichen Code den Füllgrad der FIFO nicht als explizite Zahl, dann lohnt daher eine derartige Counter-Variable meistens nicht.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_SIZE 23&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  //if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  //  buffer.write = 0; // erhöht sicherheit&lt;br /&gt;
&lt;br /&gt;
  if ( ( buffer.write + 1 == buffer.read ) ||&lt;br /&gt;
       ( buffer.read == 0 &amp;amp;&amp;amp; buffer.write + 1 == BUFFER_SIZE ) )&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
&lt;br /&gt;
  buffer.write++;&lt;br /&gt;
  if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.write = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//    &lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read++;&lt;br /&gt;
  if (buffer.read &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.read = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-Ringpuffer - die schnellste Lösung ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Durch Bitmaske auf den Feld-Index &#039;&#039;write&#039;&#039; kann der nicht &amp;quot;Amoklaufen&amp;quot; und beliebige Inhalte im Speicher überschreiben&lt;br /&gt;
* Sehr einfach, sehr schnell&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Nur Größenfaktoren von 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; möglich&lt;br /&gt;
* Nur in speziellen Fällen Pointerreferenz möglich&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Index am oberen Ende angekommen ist springt er an den Anfang zurück. Die Definition der Obergrenze wird nicht durch einen Vergleich realisiert, sondern durch Modulo-Arithmetik. Dies kann sehr einfach und schnell durch Maskieren des Index-Werts umgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
( write + 1 ) &amp;amp; MASK&lt;br /&gt;
&lt;br /&gt;
15 = 0b00001111&lt;br /&gt;
15 + 1 = 16 = 0b000010000&lt;br /&gt;
16 &amp;amp; MASK = 0b000010000 &amp;amp; 0b000001111 = 0b000000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt das &#039;&#039;write&#039;&#039; nur einen Wert zwischen 0 und 15 einnehmen kann. Das ganze funktioniert nur mit Zahlen der Reihe 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da der Puffer nur eine endliche Größe aufweist, muss eine voll/leer Indikation verfügbar sein. read == write bedeutet Puffer leer, entsprechend bedeutet write + 1 == read, dass keine weiteren Elemente mehr hinzugefügt werden können, denn sonst würde das Hinzufügen weiterer Daten einen Pufferüberlauf hervorrufen. Für die Behandlung des Pufferüberlaufs gibt es zwei Methoden, nichts tun und Fehler zurück geben oder ältesten Puffer-Inhalt verwerfen (read+1).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == ((write + 1) &amp;amp; MASK)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verhindert die Bitmaske einen möglichen Überlauf. Weiter zu beachten ist, dass ein Byte durch die voll/leer Indikation verloren geht, der Puffer hat hier nur eine größe von 15 statt 16 Byte&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleiner Trick. Bei einer Zahl der Reihe 2^n lässt sich auf einfache Weise immer eine passende Bitmaske erzeugen und man sieht gleichzeitig klar die Anzahl der genutzten Bytes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# define SIZE 16&lt;br /&gt;
# define MASK (SIZE-1)&lt;br /&gt;
&lt;br /&gt;
16 = 0b010000&lt;br /&gt;
16 - 1 = 0b001111&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Normalbetrieb sollte der Puffer nie voll ausgereizt sein und wenn der Puffer bereits überläuft ist es für die Systemstabilität und Fehleranlyse oft schon zu spät, denn von einem nicht mehr funktionierenden Gerät lässt sich nur wenig Information herauskitzeln. Da kann ein Frühwarnsystem mehr bieten, wobei zwei Ausprägungen typisch sind, Höchstwert merken und eine Meldeschwelle einführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 freeMemory =  (read - write - 1 ) &amp;amp; MASK);&lt;br /&gt;
 if (freeMemory &amp;lt; floodmark)&lt;br /&gt;
   floodmark = freeMemory;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 if ( (read - write - 1 ) &amp;amp; MASK) &amp;lt;= FLOODMARK;&lt;br /&gt;
   FloodmarkExcess();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine entsprechende Auswertung kann z.B. bei einer UART zur Steuerung des Handshakes benutzt werden, bei dem die Gegenstelle aufgefordert wird, das Senden kurzfristig einzustellen, damit man nicht Gefahr läuft, übertragene Zeichen zu verlieren. Fällt der Füllgrad einer darartigen UART-FIFO wieder unter eine gewisse Schwelle, dann wird der Gegenstelle signalisiert, dass sie mit dem Senden fortfahren kann.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
 &lt;br /&gt;
#define BUFFER_SIZE 16 // muss 2^n betragen (8, 16, 32, 64 ...)&lt;br /&gt;
#define BUFFER_MASK (BUFFER_SIZE-1) // Klammern auf keinen Fall vergessen&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t next = ((buffer.write + 1) &amp;amp; BUFFER_MASK);&lt;br /&gt;
&lt;br /&gt;
  if (buffer.read == next)&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
  // buffer.data[buffer.write &amp;amp; BUFFER_MASK] = byte; // absolut Sicher&lt;br /&gt;
  buffer.write = next;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read = (buffer.read+1) &amp;amp; BUFFER_MASK;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FIFO mit C-Präprozessor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef FIFO_H_&lt;br /&gt;
#define FIFO_H_&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[64];&lt;br /&gt;
} FIFO64_t;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[128];&lt;br /&gt;
} FIFO128_t;&lt;br /&gt;
&lt;br /&gt;
#define FIFO_init(fifo)         { fifo._read = 0; fifo._write = 0; }&lt;br /&gt;
&lt;br /&gt;
#define FIFO_available(fifo)    ( fifo._read != fifo._write )&lt;br /&gt;
&lt;br /&gt;
#define FIFO_read(fifo, size) (                                         \&lt;br /&gt;
        (FIFO_available(fifo)) ?                                        \&lt;br /&gt;
        fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0      \&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define FIFO_write(fifo, data, size) {                                                          \&lt;br /&gt;
        uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */    \&lt;br /&gt;
        if(tmphead != fifo._read) {                             /* if buffer is not full */     \&lt;br /&gt;
                fifo._write = tmphead;                          /* store new index */           \&lt;br /&gt;
                fifo._buffer[tmphead] = data;                   /* store data in buffer */      \&lt;br /&gt;
        }                                                                                       \&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define FIFO64_read(fifo)			FIFO_read(fifo, 64)&lt;br /&gt;
#define FIFO64_write(fifo, data)		FIFO_write(fifo, data, 64)&lt;br /&gt;
&lt;br /&gt;
#define FIFO128_read(fifo)			FIFO_read(fifo, 128)&lt;br /&gt;
#define FIFO128_write(fifo, data)		FIFO_write(fifo, data, 128)&lt;br /&gt;
&lt;br /&gt;
#endif /*FIFO_H_*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hinweis: viele C-Compiler übersetzen automatisch eine Modulo-Division mit einem Divisor einer 2-er Potenz in Form einer entsprechenden Bitmaskierung. Zum einen erspart man sich dadurch das Bestimmen der entsprechenden Maske, zum anderen hat man dann den &#039;Bonus&#039;, dass der Code immer noch korrekt funktioniert (wenn auch langsamer), wenn der Anwender einen Fehler macht und die Array-Länge nicht als 2-er Potenz definiert. Die Operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
kann dann durch die in diesem Sinne gleichwertige Operation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) % size] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ausgedrückt werden, bzw.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) % size);          /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Circular_buffer Circular buffer], englische Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87546</id>
		<title>FIFO</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87546"/>
		<updated>2015-02-24T08:56:33Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein FIFO (&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;n-&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;O&amp;lt;/b&amp;gt;ut) ist ein Pufferspeicher nach dem &amp;quot;Warteschlangen-Prinzip&amp;quot;. Pufferspeicher dienen dazu, für einen Prozessor Daten aufzufangen, die er noch nicht verarbeiten kann. Ein FIFO funktioniert definitionsgemäß so, dass das erste Element (First In), welches &amp;quot;hinten&amp;quot; in die Warteschlange eingefügt wird, später auch als erstes &amp;quot;vorne&amp;quot; herausgeholt wird (First Out). Das Gegenstück zum FIFO ist [[LIFO]].&lt;br /&gt;
&lt;br /&gt;
Ein bekanntes Beispiel für einen FIFO-Speicher ist der des [[UART]]s im PC. Dieser sammelt ankommende Daten solange, bis er fast voll ist (meist 14 Bytes). Dann wird ein [[Interrupt]] ausgelöst und der Prozessor kann &amp;quot;auf einen Rutsch&amp;quot; alle Daten auf einmal auslesen. Ganz am Anfang hatten UARTs keinen FIFO und erzeugten für jedes empfangene Byte einen Interrupt, so wie es heute noch die meisten Mikrocontroller machen. Damit ist aber die CPU-Belastung wesentlich höher, was bei höheren Datenraten zu Problemen führen kann.&lt;br /&gt;
&lt;br /&gt;
Die Umsetzung eines FIFOs in Software heißt Ringpuffer und weist auch eine feste Puffergröße auf. Wesentlicher Vorteil gegenüber der verketteten Liste ist die schnellere Ausführungszeit und der geringere Aufwand.&lt;br /&gt;
&lt;br /&gt;
== Standard-Ringpuffer ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Simpel&lt;br /&gt;
* Leicht verständlich&lt;br /&gt;
* Daten können auch vom Typ struct sein&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Schnell aber nicht optimal&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Ringpuffer.png|300px]]&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Circular buffer.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Inhalte des Puffers werden in einem schlichten Array gespeichert und der Zugriff erfolgt über einen Integer-Index. Erreicht ein Index die Obergrenze springt dieser auf Null zurück. Topologisch ist dies äquivalent zu einem Ringschluss, bei dem man ja auch nach dem letzten Element wieder beim ersten angelangt ist. Man denke zb an das Zifferblatt einer Uhr, bei der nach der 59 wieder die 0 kommt. Ein größer-gleich statt nur eines ist-gleich Vergleichs ist sicherer gegenüber Programmfehlern, bei denen der Index verstellt wurde.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
write = write + 1;&lt;br /&gt;
if (write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  write = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Haben Lese- und Schreib-Index den gleichen Wert wird der Puffer als leer angesehen und es kann kein Element aus dem Puffer entnommen werden. Wenn write+1 und read identisch sind wird der Puffer als voll angesehen und es kann kein weiteres Element mehr im Puffer gespeichert werden. Nun fehlt noch der Sonderfall bei dem read gleich Null ist, hier funktioniert der write+1-Vergleich nicht mehr (wegen dem &#039;Überlauf&#039; am Ende des Arrays, der den Ringschluss bewirkt) und die Abfrage einer zusätzlichen Bedingung ist zur Voll-Abfrage erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == write =&amp;gt; leer&lt;br /&gt;
&lt;br /&gt;
write + 1 == read || read == 0 &amp;amp;&amp;amp; write+1 == BUFFER_SIZE =&amp;gt; voll&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigem Zugriff zwischen dem Befüller (zb. einer UART-ISR) und dem Entnahmecode (zb. der Code in der Hauptschleife) muss ausgeschlossen werden, dass der Entnahmecode beim Lesen aus dem Puffer nicht durch den Interrupt unterbrochen werden kann. Denn während diese Funktion läuft ist zwischenzeitlich der read-Index in einem inkonsistenten Zustand. Erst nach Beendigung der Funktion spiegelt der read-Index wieder die Realität des Füllgrades wieder. Als Abhilfe dienen [[Interrupt#Atomarer_Datenzugriff|Atomare Abschnitte]], solche können durch keinen Interrupt unterbrochen werden. Die Radialkur deaktiviert alle Interrupts, besser ist die Deaktivierung eines einzelnen Interrupts. Es geht aber meistens auch so, da die FIFO Funktionen sehr kurz sind und daher auch die Interrupts nur kurz abgeschaltet werden. Im Allgemeinen werden versäumte Interrupts nach erneuter Aktivierung nachgeholt und gehen nicht verloren, so dass bereits anstehende Interrupts nur kurz verzögert werden. Solange die Interruptrate größer als die Ausführungsgeschwindigkeit der FIFO Funktionen ist, stellt das Abschalten der Interrupts daher auch in der Praxis meist kein allzugroßes Problem dar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
cli()&lt;br /&gt;
ret = BufferOut(&amp;amp;var);&lt;br /&gt;
sei()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist die Initialisierung der Indizes (read, write) mit -1, denn damit kann man einen leere Ringpuffer von einem vollen unterscheiden. Initialisiert man nur mit 0, kann man einen leeren nicht von einem vollen mit read=write=0 unterscheiden. &lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit der Vereinfachung entsteht durch die Einführung einer &amp;quot;Counter&amp;quot; Variable, die den Füllstand des Buffers wiedergibt. Bei Einschreiben wird diese um +1 erhöht, beim Auslesen um 1 erniedrigt. Ist sie 0 ist der Buffer leer. Damit lässt sich die Codeeffizienz steigern, da die obigen Vergleiche kürzer ausfallen und sich auf nur diese eine Variable beschränken. Nachteilig ist allerdings, dass man eine weitere Variable im System hat und natürlich benötigen die entsprechenden Operationen zum Erhöhen bzw. Erniedrigen dieser Variable ja auch Ausführungszeit. Benötigt man im restlichen Code den Füllgrad der FIFO nicht als explizite Zahl, dann lohnt daher eine derartige Counter-Variable meistens nicht.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_SIZE 23&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  //if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  //  buffer.write = 0; // erhöht sicherheit&lt;br /&gt;
&lt;br /&gt;
  if ( ( buffer.write + 1 == buffer.read ) ||&lt;br /&gt;
       ( buffer.read == 0 &amp;amp;&amp;amp; buffer.write + 1 == BUFFER_SIZE ) )&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
&lt;br /&gt;
  buffer.write++;&lt;br /&gt;
  if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.write = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//    &lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read++;&lt;br /&gt;
  if (buffer.read &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.read = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-Ringpuffer - die schnellste Lösung ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Durch Bitmaske auf den Feld-Index &#039;&#039;write&#039;&#039; kann der nicht &amp;quot;Amoklaufen&amp;quot; und beliebige Inhalte im Speicher überschreiben&lt;br /&gt;
* Sehr einfach, sehr schnell&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Nur Größenfaktoren von 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; möglich&lt;br /&gt;
* Nur in speziellen Fällen Pointerreferenz möglich&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Index am oberen Ende angekommen ist springt er an den Anfang zurück. Die Definition der Obergrenze wird nicht durch einen Vergleich realisiert, sondern durch Modulo-Arithmetik. Dies kann sehr einfach und schnell durch Maskieren des Index-Werts umgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
( write + 1 ) &amp;amp; MASK&lt;br /&gt;
&lt;br /&gt;
15 = 0b00001111&lt;br /&gt;
15 + 1 = 16 = 0b000010000&lt;br /&gt;
16 &amp;amp; MASK = 0b000010000 &amp;amp; 0b000001111 = 0b000000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt das &#039;&#039;write&#039;&#039; nur einen Wert zwischen 0 und 15 einnehmen kann. Das ganze funktioniert nur mit Zahlen der Reihe 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da der Puffer nur eine endliche Größe aufweist, muss eine voll/leer Indikation verfügbar sein. read == write bedeutet Puffer leer, entsprechend bedeutet write + 1 == read, dass keine weiteren Elemente mehr hinzugefügt werden können, denn sonst würde das Hinzufügen weiterer Daten einen Pufferüberlauf hervorrufen. Für die Behandlung des Pufferüberlaufs gibt es zwei Methoden, nichts tun und Fehler zurück geben oder ältesten Puffer-Inhalt verwerfen (read+1).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == ((write + 1) &amp;amp; MASK)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verhindert die Bitmaske einen möglichen Überlauf. Weiter zu beachten ist, dass ein Byte durch die voll/leer Indikation verloren geht, der Puffer hat hier nur eine größe von 15 statt 16 Byte&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleiner Trick. Bei einer Zahl der Reihe 2^n lässt sich auf einfache Weise immer eine passende Bitmaske erzeugen und man sieht gleichzeitig klar die Anzahl der genutzten Bytes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# define SIZE 16&lt;br /&gt;
# define MASK (SIZE-1)&lt;br /&gt;
&lt;br /&gt;
16 = 0b010000&lt;br /&gt;
16 - 1 = 0b001111&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Normalbetrieb sollte der Puffer nie voll ausgereizt sein und wenn der Puffer bereits überläuft ist es für die Systemstabilität und Fehleranlyse oft schon zu spät, denn von einem nicht mehr funktionierenden Gerät lässt sich nur wenig Information herauskitzeln. Da kann ein Frühwarnsystem mehr bieten, wobei zwei Ausprägungen typisch sind, Höchstwert merken und eine Meldeschwelle einführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 freeMemory =  (read - write - 1 ) &amp;amp; MASK);&lt;br /&gt;
 if (freeMemory &amp;lt; floodmark)&lt;br /&gt;
   floodmark = freeMemory;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine entsprechende Auswertung kann z.B. bei einer UART zur Steuerung des Handshakes benutzt werden, bei dem die Gegenstelle aufgefordert wird, das Senden kurzfristig einzustellen, damit man nicht Gefahr läuft, übertragene Zeichen zu verlieren. Fällt der Füllgrad einer darartigen UART-FIFO wieder unter eine gewisse Schwelle, dann wird der Gegenstelle signalisiert, dass sie mit dem Senden fortfahren kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 if ( (read - write - 1 ) &amp;amp; MASK) &amp;lt;= FLOODMARK;&lt;br /&gt;
   FloodmarkExcess();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
 &lt;br /&gt;
#define BUFFER_SIZE 16 // muss 2^n betragen (8, 16, 32, 64 ...)&lt;br /&gt;
#define BUFFER_MASK (BUFFER_SIZE-1) // Klammern auf keinen Fall vergessen&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t next = ((buffer.write + 1) &amp;amp; BUFFER_MASK);&lt;br /&gt;
&lt;br /&gt;
  if (buffer.read == next)&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
  // buffer.data[buffer.write &amp;amp; BUFFER_MASK] = byte; // absolut Sicher&lt;br /&gt;
  buffer.write = next;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read = (buffer.read+1) &amp;amp; BUFFER_MASK;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FIFO mit C-Präprozessor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef FIFO_H_&lt;br /&gt;
#define FIFO_H_&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[64];&lt;br /&gt;
} FIFO64_t;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[128];&lt;br /&gt;
} FIFO128_t;&lt;br /&gt;
&lt;br /&gt;
#define FIFO_init(fifo)         { fifo._read = 0; fifo._write = 0; }&lt;br /&gt;
&lt;br /&gt;
#define FIFO_available(fifo)    ( fifo._read != fifo._write )&lt;br /&gt;
&lt;br /&gt;
#define FIFO_read(fifo, size) (                                         \&lt;br /&gt;
        (FIFO_available(fifo)) ?                                        \&lt;br /&gt;
        fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0      \&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define FIFO_write(fifo, data, size) {                                                          \&lt;br /&gt;
        uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */    \&lt;br /&gt;
        if(tmphead != fifo._read) {                             /* if buffer is not full */     \&lt;br /&gt;
                fifo._write = tmphead;                          /* store new index */           \&lt;br /&gt;
                fifo._buffer[tmphead] = data;                   /* store data in buffer */      \&lt;br /&gt;
        }                                                                                       \&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define FIFO64_read(fifo)			FIFO_read(fifo, 64)&lt;br /&gt;
#define FIFO64_write(fifo, data)		FIFO_write(fifo, data, 64)&lt;br /&gt;
&lt;br /&gt;
#define FIFO128_read(fifo)			FIFO_read(fifo, 128)&lt;br /&gt;
#define FIFO128_write(fifo, data)		FIFO_write(fifo, data, 128)&lt;br /&gt;
&lt;br /&gt;
#endif /*FIFO_H_*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hinweis: viele C-Compiler übersetzen automatisch eine Modulo-Division mit einem Divisor einer 2-er Potenz in Form einer entsprechenden Bitmaskierung. Zum einen erspart man sich dadurch das Bestimmen der entsprechenden Maske, zum anderen hat man dann den &#039;Bonus&#039;, dass der Code immer noch korrekt funktioniert (wenn auch langsamer), wenn der Anwender einen Fehler macht und die Array-Länge nicht als 2-er Potenz definiert. Die Operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
kann dann durch die in diesem Sinne gleichwertige Operation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) % size] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ausgedrückt werden, bzw.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) % size);          /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Circular_buffer Circular buffer], englische Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87545</id>
		<title>FIFO</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87545"/>
		<updated>2015-02-24T08:52:30Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: Code kommentiert, fehlende Makros ergänzt.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein FIFO (&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;n-&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;O&amp;lt;/b&amp;gt;ut) ist ein Pufferspeicher nach dem &amp;quot;Warteschlangen-Prinzip&amp;quot;. Pufferspeicher dienen dazu, für einen Prozessor Daten aufzufangen, die er noch nicht verarbeiten kann. Ein FIFO funktioniert definitionsgemäß so, dass das erste Element (First In), welches &amp;quot;hinten&amp;quot; in die Warteschlange eingefügt wird, später auch als erstes &amp;quot;vorne&amp;quot; herausgeholt wird (First Out). Das Gegenstück zum FIFO ist [[LIFO]].&lt;br /&gt;
&lt;br /&gt;
Ein bekanntes Beispiel für einen FIFO-Speicher ist der des [[UART]]s im PC. Dieser sammelt ankommende Daten solange, bis er fast voll ist (meist 14 Bytes). Dann wird ein [[Interrupt]] ausgelöst und der Prozessor kann &amp;quot;auf einen Rutsch&amp;quot; alle Daten auf einmal auslesen. Ganz am Anfang hatten UARTs keinen FIFO und erzeugten für jedes empfangene Byte einen Interrupt, so wie es heute noch die meisten Mikrocontroller machen. Damit ist aber die CPU-Belastung wesentlich höher, was bei höheren Datenraten zu Problemen führen kann.&lt;br /&gt;
&lt;br /&gt;
Die Umsetzung eines FIFOs in Software heißt Ringpuffer und weist auch eine feste Puffergröße auf. Wesentlicher Vorteil gegenüber der verketteten Liste ist die schnellere Ausführungszeit und der geringere Aufwand.&lt;br /&gt;
&lt;br /&gt;
== Standard-Ringpuffer ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Simpel&lt;br /&gt;
* Leicht verständlich&lt;br /&gt;
* Daten können auch vom Typ struct sein&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Schnell aber nicht optimal&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Ringpuffer.png|300px]]&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Circular buffer.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Inhalte des Puffers werden in einem schlichten Array gespeichert und der Zugriff erfolgt über einen Integer-Index. Erreicht ein Index die Obergrenze springt dieser auf Null zurück. Topologisch ist dies äquivalent zu einem Ringschluss, bei dem man ja auch nach dem letzten Element wieder beim ersten angelangt ist. Man denke zb an das Zifferblatt einer Uhr, bei der nach der 59 wieder die 0 kommt. Ein größer-gleich statt nur eines ist-gleich Vergleichs ist sicherer gegenüber Programmfehlern, bei denen der Index verstellt wurde.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
write = write + 1;&lt;br /&gt;
if (write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  write = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Haben Lese- und Schreib-Index den gleichen Wert wird der Puffer als leer angesehen und es kann kein Element aus dem Puffer entnommen werden. Wenn write+1 und read identisch sind wird der Puffer als voll angesehen und es kann kein weiteres Element mehr im Puffer gespeichert werden. Nun fehlt noch der Sonderfall bei dem read gleich Null ist, hier funktioniert der write+1-Vergleich nicht mehr (wegen dem &#039;Überlauf&#039; am Ende des Arrays, der den Ringschluss bewirkt) und die Abfrage einer zusätzlichen Bedingung ist zur Voll-Abfrage erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == write =&amp;gt; leer&lt;br /&gt;
&lt;br /&gt;
write + 1 == read || read == 0 &amp;amp;&amp;amp; write+1 == BUFFER_SIZE =&amp;gt; voll&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigem Zugriff zwischen dem Befüller (zb. einer UART-ISR) und dem Entnahmecode (zb. der Code in der Hauptschleife) muss ausgeschlossen werden, dass der Entnahmecode beim Lesen aus dem Puffer nicht durch den Interrupt unterbrochen werden kann. Denn während diese Funktion läuft ist zwischenzeitlich der read-Index in einem inkonsistenten Zustand. Erst nach Beendigung der Funktion spiegelt der read-Index wieder die Realität des Füllgrades wieder. Als Abhilfe dienen [[Interrupt#Atomarer_Datenzugriff|Atomare Abschnitte]], solche können durch keinen Interrupt unterbrochen werden. Die Radialkur deaktiviert alle Interrupts, besser ist die Deaktivierung eines einzelnen Interrupts. Es geht aber meistens auch so, da die FIFO Funktionen sehr kurz sind und daher auch die Interrupts nur kurz abgeschaltet werden. Im Allgemeinen werden versäumte Interrupts nach erneuter Aktivierung nachgeholt und gehen nicht verloren, so dass bereits anstehende Interrupts nur kurz verzögert werden. Solange die Interruptrate größer als die Ausführungsgeschwindigkeit der FIFO Funktionen ist, stellt das Abschalten der Interrupts daher auch in der Praxis meist kein allzugroßes Problem dar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
cli()&lt;br /&gt;
ret = BufferOut(&amp;amp;var);&lt;br /&gt;
sei()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist die Initialisierung der Indizes (read, write) mit -1, denn damit kann man einen leere Ringpuffer von einem vollen unterscheiden. Initialisiert man nur mit 0, kann man einen leeren nicht von einem vollen mit read=write=0 unterscheiden. &lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit der Vereinfachung entsteht durch die Einführung einer &amp;quot;Counter&amp;quot; Variable, die den Füllstand des Buffers wiedergibt. Bei Einschreiben wird diese um +1 erhöht, beim Auslesen um 1 erniedrigt. Ist sie 0 ist der Buffer leer. Damit lässt sich die Codeeffizienz steigern, da die obigen Vergleiche kürzer ausfallen und sich auf nur diese eine Variable beschränken. Nachteilig ist allerdings, dass man eine weitere Variable im System hat und natürlich benötigen die entsprechenden Operationen zum Erhöhen bzw. Erniedrigen dieser Variable ja auch Ausführungszeit. Benötigt man im restlichen Code den Füllgrad der FIFO nicht als explizite Zahl, dann lohnt daher eine derartige Counter-Variable meistens nicht.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_SIZE 23&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  //if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  //  buffer.write = 0; // erhöht sicherheit&lt;br /&gt;
&lt;br /&gt;
  if ( ( buffer.write + 1 == buffer.read ) ||&lt;br /&gt;
       ( buffer.read == 0 &amp;amp;&amp;amp; buffer.write + 1 == BUFFER_SIZE ) )&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
&lt;br /&gt;
  buffer.write++;&lt;br /&gt;
  if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.write = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//    &lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read++;&lt;br /&gt;
  if (buffer.read &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.read = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-Ringpuffer - die schnellste Lösung ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Durch Bitmaske auf den Feld-Index &#039;&#039;write&#039;&#039; kann der nicht &amp;quot;Amoklaufen&amp;quot; und beliebige Inhalte im Speicher überschreiben&lt;br /&gt;
* Sehr einfach, sehr schnell&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Nur Größenfaktoren von 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; möglich&lt;br /&gt;
* Nur in speziellen Fällen Pointerreferenz möglich&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Index am oberen Ende angekommen ist springt er an den Anfang zurück. Die Definition der Obergrenze wird nicht durch einen Vergleich realisiert, sondern durch Modulo-Arithmetik. Dies kann sehr einfach und schnell durch Maskieren des Index-Werts umgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
( write + 1 ) &amp;amp; MASK&lt;br /&gt;
&lt;br /&gt;
15 = 0b00001111&lt;br /&gt;
15 + 1 = 16 = 0b000010000&lt;br /&gt;
16 &amp;amp; MASK = 0b000010000 &amp;amp; 0b000001111 = 0b000000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt das &#039;&#039;write&#039;&#039; nur einen Wert zwischen 0 und 15 einnehmen kann. Das ganze funktioniert nur mit Zahlen der Reihe 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da der Puffer nur eine endliche Größe aufweist, muss eine voll/leer Indikation verfügbar sein. read == write bedeutet Puffer leer, entsprechend bedeutet write + 1 == read, dass keine weiteren Elemente mehr hinzugefügt werden können, denn sonst würde das Hinzufügen weiterer Daten einen Pufferüberlauf hervorrufen. Für die Behandlung des Pufferüberlaufs gibt es zwei Methoden, nichts tun und Fehler zurück geben oder ältesten Puffer-Inhalt verwerfen (read+1).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == ((write + 1) &amp;amp; MASK)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verhindert die Bitmaske einen möglichen Überlauf. Weiter zu beachten ist, dass ein Byte durch die voll/leer Indikation verloren geht, der Puffer hat hier nur eine größe von 15 statt 16 Byte&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleiner Trick. Bei einer Zahl der Reihe 2^n lässt sich auf einfache Weise immer eine passende Bitmaske erzeugen und man sieht gleichzeitig klar die Anzahl der genutzten Bytes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# define SIZE 16&lt;br /&gt;
# define MASK (SIZE-1)&lt;br /&gt;
&lt;br /&gt;
16 = 0b010000&lt;br /&gt;
16 - 1 = 0b001111&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Normalbetrieb sollte der Puffer nie voll ausgereizt sein und wenn der Puffer bereits überläuft ist es für die Systemstabilität und Fehleranlyse oft schon zu spät, denn von einem nicht mehr funktionierenden Gerät lässt sich nur wenig Information herauskitzeln. Da kann ein Frühwarnsystem mehr bieten, wobei zwei Ausprägungen typisch sind, Höchstwert merken und eine Meldeschwelle einführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 freeMemory =  (read - write - 1 ) &amp;amp; MASK);&lt;br /&gt;
 if (freeMemory &amp;lt; floodmark)&lt;br /&gt;
   floodmark = freeMemory;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 if ( (read - write - 1 ) &amp;amp; MASK) &amp;lt;= FLOODMARK;&lt;br /&gt;
   FloodmarkExcess();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
 &lt;br /&gt;
#define BUFFER_SIZE 16 // muss 2^n betragen (8, 16, 32, 64 ...)&lt;br /&gt;
#define BUFFER_MASK (BUFFER_SIZE-1) // Klammern auf keinen Fall vergessen&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t next = ((buffer.write + 1) &amp;amp; BUFFER_MASK);&lt;br /&gt;
&lt;br /&gt;
  if (buffer.read == next)&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
  // buffer.data[buffer.write &amp;amp; BUFFER_MASK] = byte; // absolut Sicher&lt;br /&gt;
  buffer.write = next;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read = (buffer.read+1) &amp;amp; BUFFER_MASK;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FIFO mit C-Präprozessor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef FIFO_H_&lt;br /&gt;
#define FIFO_H_&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[64];&lt;br /&gt;
} FIFO64_t;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
        uint8_t _read;&lt;br /&gt;
        uint8_t _write;&lt;br /&gt;
        uint8_t _buffer[128];&lt;br /&gt;
} FIFO128_t;&lt;br /&gt;
&lt;br /&gt;
#define FIFO_init(fifo)         { fifo._read = 0; fifo._write = 0; }&lt;br /&gt;
&lt;br /&gt;
#define FIFO_available(fifo)    ( fifo._read != fifo._write )&lt;br /&gt;
&lt;br /&gt;
#define FIFO_read(fifo, size) (                                         \&lt;br /&gt;
        (FIFO_available(fifo)) ?                                        \&lt;br /&gt;
        fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0      \&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define FIFO_write(fifo, data, size) {                                                          \&lt;br /&gt;
        uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */    \&lt;br /&gt;
        if(tmphead != fifo._read) {                             /* if buffer is not full */     \&lt;br /&gt;
                fifo._write = tmphead;                          /* store new index */           \&lt;br /&gt;
                fifo._buffer[tmphead] = data;                   /* store data in buffer */      \&lt;br /&gt;
        }                                                                                       \&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define FIFO64_read(fifo)			FIFO_read(fifo, 64)&lt;br /&gt;
#define FIFO64_write(fifo, data)		FIFO_write(fifo, data, 64)&lt;br /&gt;
&lt;br /&gt;
#define FIFO128_read(fifo)			FIFO_read(fifo, 128)&lt;br /&gt;
#define FIFO128_write(fifo, data)		FIFO_write(fifo, data, 128)&lt;br /&gt;
&lt;br /&gt;
#endif /*FIFO_H_*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hinweis: viele C-Compiler übersetzen automatisch eine Modulo-Division mit einem Divisor einer 2-er Potenz in Form einer entsprechenden Bitmaskierung. Zum einen erspart man sich dadurch das Bestimmen der entsprechenden Maske, zum anderen hat man dann den &#039;Bonus&#039;, dass der Code immer noch korrekt funktioniert (wenn auch langsamer), wenn der Anwender einen Fehler macht und die Array-Länge nicht als 2-er Potenz definiert. Die Operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
kann dann durch die in diesem Sinne gleichwertige Operation&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) % size] : 0	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ausgedrückt werden, bzw.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1);       /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) % size);          /* calculate buffer index */	\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Circular_buffer Circular buffer], englische Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87544</id>
		<title>FIFO</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87544"/>
		<updated>2015-02-24T08:34:03Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: Kommentiert, fehlende Makros für die Return Werte eingefügt.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein FIFO (&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;n-&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;O&amp;lt;/b&amp;gt;ut) ist ein Pufferspeicher nach dem &amp;quot;Warteschlangen-Prinzip&amp;quot;. Pufferspeicher dienen dazu, für einen Prozessor Daten aufzufangen, die er noch nicht verarbeiten kann. Ein FIFO funktioniert definitionsgemäß so, dass das erste Element (First In), welches &amp;quot;hinten&amp;quot; in die Warteschlange eingefügt wird, später auch als erstes &amp;quot;vorne&amp;quot; herausgeholt wird (First Out). Das Gegenstück zum FIFO ist [[LIFO]].&lt;br /&gt;
&lt;br /&gt;
Ein bekanntes Beispiel für einen FIFO-Speicher ist der des [[UART]]s im PC. Dieser sammelt ankommende Daten solange, bis er fast voll ist (meist 14 Bytes). Dann wird ein [[Interrupt]] ausgelöst und der Prozessor kann &amp;quot;auf einen Rutsch&amp;quot; alle Daten auf einmal auslesen. Ganz am Anfang hatten UARTs keinen FIFO und erzeugten für jedes empfangene Byte einen Interrupt, so wie es heute noch die meisten Mikrocontroller machen. Damit ist aber die CPU-Belastung wesentlich höher, was bei höheren Datenraten zu Problemen führen kann.&lt;br /&gt;
&lt;br /&gt;
Die Umsetzung eines FIFOs in Software heißt Ringpuffer und weist auch eine feste Puffergröße auf. Wesentlicher Vorteil gegenüber der verketteten Liste ist die schnellere Ausführungszeit und der geringere Aufwand.&lt;br /&gt;
&lt;br /&gt;
== Standard-Ringpuffer ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Simpel&lt;br /&gt;
* Leicht verständlich&lt;br /&gt;
* Daten können auch vom Typ struct sein&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Schnell aber nicht optimal&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Ringpuffer.png|300px]]&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Circular buffer.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Inhalte des Puffers werden in einem schlichten Array gespeichert und der Zugriff erfolgt über einen Integer-Index. Erreicht ein Index die Obergrenze springt dieser auf Null zurück. Topologisch ist dies äquivalent zu einem Ringschluss, bei dem man ja auch nach dem letzten Element wieder beim ersten angelangt ist. Man denke zb an das Zifferblatt einer Uhr, bei der nach der 59 wieder die 0 kommt. Ein größer-gleich statt nur eines ist-gleich Vergleichs ist sicherer gegenüber Programmfehlern, bei denen der Index verstellt wurde.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
write = write + 1;&lt;br /&gt;
if (write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  write = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Haben Lese- und Schreib-Index den gleichen Wert wird der Puffer als leer angesehen und es kann kein Element aus dem Puffer entnommen werden. Wenn write+1 und read identisch sind wird der Puffer als voll angesehen und es kann kein weiteres Element mehr im Puffer gespeichert werden. Nun fehlt noch der Sonderfall bei dem read gleich Null ist, hier funktioniert der write+1-Vergleich nicht mehr (wegen dem &#039;Überlauf&#039; am Ende des Arrays, der den Ringschluss bewirkt) und die Abfrage einer zusätzlichen Bedingung ist zur Voll-Abfrage erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == write =&amp;gt; leer&lt;br /&gt;
&lt;br /&gt;
write + 1 == read || read == 0 &amp;amp;&amp;amp; write+1 == BUFFER_SIZE =&amp;gt; voll&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigem Zugriff zwischen dem Befüller (zb. einer UART-ISR) und dem Entnahmecode (zb. der Code in der Hauptschleife) muss ausgeschlossen werden, dass der Entnahmecode beim Lesen aus dem Puffer nicht durch den Interrupt unterbrochen werden kann. Denn während diese Funktion läuft ist zwischenzeitlich der read-Index in einem inkonsistenten Zustand. Erst nach Beendigung der Funktion spiegelt der read-Index wieder die Realität des Füllgrades wieder. Als Abhilfe dienen [[Interrupt#Atomarer_Datenzugriff|Atomare Abschnitte]], solche können durch keinen Interrupt unterbrochen werden. Die Radialkur deaktiviert alle Interrupts, besser ist die Deaktivierung eines einzelnen Interrupts. Es geht aber meistens auch so, da die FIFO Funktionen sehr kurz sind und daher auch die Interrupts nur kurz abgeschaltet werden. Im Allgemeinen werden versäumte Interrupts nach erneuter Aktivierung nachgeholt und gehen nicht verloren, so dass bereits anstehende Interrupts nur kurz verzögert werden. Solange die Interruptrate größer als die Ausführungsgeschwindigkeit der FIFO Funktionen ist, stellt das Abschalten der Interrupts daher auch in der Praxis meist kein allzugroßes Problem dar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
cli()&lt;br /&gt;
ret = BufferOut(&amp;amp;var);&lt;br /&gt;
sei()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist die Initialisierung der Indizes (read, write) mit -1, denn damit kann man einen leere Ringpuffer von einem vollen unterscheiden. Initialisiert man nur mit 0, kann man einen leeren nicht von einem vollen mit read=write=0 unterscheiden. &lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit der Vereinfachung entsteht durch die Einführung einer &amp;quot;Counter&amp;quot; Variable, die den Füllstand des Buffers wiedergibt. Bei Einschreiben wird diese um +1 erhöht, beim Auslesen um 1 erniedrigt. Ist sie 0 ist der Buffer leer. Damit lässt sich die Codeeffizienz steigern, da die obigen Vergleiche kürzer ausfallen und sich auf nur diese eine Variable beschränken. Nachteilig ist allerdings, dass man eine weitere Variable im System hat und natürlich benötigen die entsprechenden Operationen zum Erhöhen bzw. Erniedrigen dieser Variable ja auch Ausführungszeit. Benötigt man im restlichen Code den Füllgrad der FIFO nicht als explizite Zahl, dann lohnt daher eine derartige Counter-Variable meistens nicht.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_FAIL     0&lt;br /&gt;
#define BUFFER_SUCCESS  1&lt;br /&gt;
&lt;br /&gt;
#define BUFFER_SIZE 23&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Stellt 1 Byte in den Ringbuffer&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist voll. Es kann kein weiteres Byte gespeichert werden&lt;br /&gt;
//     BUFFER_SUCCESS    das Byte wurde gespeichert&lt;br /&gt;
//&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  //if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  //  buffer.write = 0; // erhöht sicherheit&lt;br /&gt;
&lt;br /&gt;
  if ( ( buffer.write + 1 == buffer.read ) ||&lt;br /&gt;
       ( buffer.read == 0 &amp;amp;&amp;amp; buffer.write + 1 == BUFFER_SIZE ) )&lt;br /&gt;
    return BUFFER_FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
&lt;br /&gt;
  buffer.write++;&lt;br /&gt;
  if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.write = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Holt 1 Byte aus dem Ringbuffer, sofern mindestens eines abholbereit ist&lt;br /&gt;
//&lt;br /&gt;
// Returns:&lt;br /&gt;
//     BUFFER_FAIL       der Ringbuffer ist leer. Es kann kein Byte geliefert werden.&lt;br /&gt;
//     BUFFER_SUCCESS    1 Byte wurde geliefert&lt;br /&gt;
//    &lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return BUFFER_FAIL;&lt;br /&gt;
&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read++;&lt;br /&gt;
  if (buffer.read &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.read = 0;&lt;br /&gt;
&lt;br /&gt;
  return BUFFER_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-Ringpuffer - die schnellste Lösung ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Durch Bitmaske auf den Feld-Index &#039;&#039;write&#039;&#039; kann der nicht &amp;quot;Amoklaufen&amp;quot; und beliebige Inhalte im Speicher überschreiben&lt;br /&gt;
* Sehr einfach, sehr schnell&lt;br /&gt;
* Daten können auch vom Typ &#039;&#039;struct&#039;&#039; sein&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Nur Größenfaktoren von 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; möglich&lt;br /&gt;
* Nur in speziellen Fällen Pointerreferenz möglich&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Index am oberen Ende angekommen ist springt er an den Anfang zurück. Die Definition der Obergrenze wird nicht durch einen Vergleich realisiert, sondern durch Modulo-Arithmetik. Dies kann sehr einfach und schnell durch Maskieren des Index-Werts umgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
( write + 1 ) &amp;amp; MASK&lt;br /&gt;
&lt;br /&gt;
15 = 0b00001111&lt;br /&gt;
15 + 1 = 16 = 0b000010000&lt;br /&gt;
16 &amp;amp; MASK = 0b000010000 &amp;amp; 0b000001111 = 0b000000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt das &#039;&#039;write&#039;&#039; nur einen Wert zwischen 0 und 15 einnehmen kann. Das ganze funktioniert nur mit Zahlen der Reihe 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da der Puffer nur eine endliche Größe aufweist, muss eine voll/leer Indikation verfügbar sein. read == write bedeutet Puffer leer, entsprechend bedeutet write + 1 == read, dass keine weiteren Elemente mehr hinzugefügt werden können, denn sonst würde das Hinzufügen weiterer Daten einen Pufferüberlauf hervorrufen. Für die Behandlung des Pufferüberlaufs gibt es zwei Methoden, nichts tun und Fehler zurück geben oder ältesten Puffer-Inhalt verwerfen (read+1).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == ((write + 1) &amp;amp; MASK)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verhindert die Bitmaske einen möglichen Überlauf. Weiter zu beachten ist, dass ein Byte durch die voll/leer Indikation verloren geht, der Puffer hat hier nur eine größe von 15 statt 16 Byte&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleiner Trick. Bei einer Zahl der Reihe 2^n lässt sich auf einfache Weise immer eine passende Bitmaske erzeugen und man sieht gleichzeitig klar die Anzahl der genutzten Bytes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# define SIZE 16&lt;br /&gt;
# define MASK (SIZE-1)&lt;br /&gt;
&lt;br /&gt;
16 = 0b010000&lt;br /&gt;
16 - 1 = 0b001111&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Normalbetrieb sollte der Puffer nie voll ausgereizt sein und wenn der Puffer bereits überläuft ist es für die Systemstabilität und Fehleranlyse oft schon zu spät, denn von einem nicht mehr funktionierenden Gerät lässt sich nur wenig Information herauskitzeln. Da kann ein Frühwarnsystem mehr bieten, wobei zwei Ausprägungen typisch sind, Höchstwert merken und eine Meldeschwelle einführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 freeMemory =  (read - write - 1 ) &amp;amp; MASK);&lt;br /&gt;
 if (freeMemory &amp;lt; floodmark)&lt;br /&gt;
   floodmark = freeMemory;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 if ( (read - write - 1 ) &amp;amp; MASK) &amp;lt;= FLOODMARK;&lt;br /&gt;
   FloodmarkExcess();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_SIZE 16 // muss 2^n betragen (8, 16, 32, 64 ...)&lt;br /&gt;
#define BUFFER_MASK (BUFFER_SIZE-1) // Klammern auf keinen Fall vergessen&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t next = ((buffer.write + 1) &amp;amp; BUFFER_MASK);&lt;br /&gt;
  if (buffer.read == next)&lt;br /&gt;
    return FAIL;&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
  // buffer.data[buffer.write &amp;amp; BUFFER_MASK] = byte; // absolut Sicher&lt;br /&gt;
  buffer.write = next;&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return FAIL;&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
  buffer.read = (buffer.read+1) &amp;amp; BUFFER_MASK;&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FIFO mit C-Präprozessor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef FIFO_H_&lt;br /&gt;
#define FIFO_H_&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
	uint8_t _read;&lt;br /&gt;
	uint8_t _write;&lt;br /&gt;
	uint8_t _buffer[64];&lt;br /&gt;
} FIFO64_t;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
	uint8_t _read;&lt;br /&gt;
	uint8_t _write;&lt;br /&gt;
	uint8_t _buffer[128];&lt;br /&gt;
} FIFO128_t;&lt;br /&gt;
&lt;br /&gt;
#define FIFO_init(fifo)		{ fifo._read = 0; fifo._write = 0; }&lt;br /&gt;
&lt;br /&gt;
#define FIFO_available(fifo)	( fifo._read != fifo._write )&lt;br /&gt;
&lt;br /&gt;
#define FIFO_read(fifo, size) (						\&lt;br /&gt;
	(FIFO_available(fifo)) ?					\&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0	\&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define FIFO_write(fifo, data, size) {								\&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1); 	/* calculate buffer index */	\&lt;br /&gt;
	if(tmphead != fifo._read) {				/* if buffer is not full */	\&lt;br /&gt;
		fifo._write = tmphead;				/* store new index */		\&lt;br /&gt;
		fifo._buffer[tmphead] = data;			/* store data in buffer */	\&lt;br /&gt;
	}											\&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define FIFO64_read(fifo)			FIFO_read(fifo, 64)&lt;br /&gt;
#define FIFO64_write(fifo, data)		FIFO_write(fifo, data, 64)&lt;br /&gt;
&lt;br /&gt;
#define FIFO128_read(fifo)			FIFO_read(fifo, 128)&lt;br /&gt;
#define FIFO128_write(fifo, data)		FIFO_write(fifo, data, 128)&lt;br /&gt;
&lt;br /&gt;
#endif /*FIFO_H_*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Circular_buffer Circular buffer], englische Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87543</id>
		<title>FIFO</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FIFO&amp;diff=87543"/>
		<updated>2015-02-24T08:25:42Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: Text ergänzt und allgemeiner formuliert.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein FIFO (&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;n-&amp;lt;b&amp;gt;F&amp;lt;/b&amp;gt;irst-&amp;lt;b&amp;gt;O&amp;lt;/b&amp;gt;ut) ist ein Pufferspeicher nach dem &amp;quot;Warteschlangen-Prinzip&amp;quot;. Pufferspeicher dienen dazu, für einen Prozessor Daten aufzufangen, die er noch nicht verarbeiten kann. Ein FIFO funktioniert definitionsgemäß so, dass das erste Element (First In), welches &amp;quot;hinten&amp;quot; in die Warteschlange eingefügt wird, später auch als erstes &amp;quot;vorne&amp;quot; herausgeholt wird (First Out). Das Gegenstück zum FIFO ist [[LIFO]].&lt;br /&gt;
&lt;br /&gt;
Ein bekanntes Beispiel für einen FIFO-Speicher ist der des [[UART]]s im PC. Dieser sammelt ankommende Daten solange, bis er fast voll ist (meist 14 Bytes). Dann wird ein [[Interrupt]] ausgelöst und der Prozessor kann &amp;quot;auf einen Rutsch&amp;quot; alle Daten auf einmal auslesen. Ganz am Anfang hatten UARTs keinen FIFO und erzeugten für jedes empfangene Byte einen Interrupt, so wie es heute noch die meisten Mikrocontroller machen. Damit ist aber die CPU-Belastung wesentlich höher, was bei höheren Datenraten zu Problemen führen kann.&lt;br /&gt;
&lt;br /&gt;
Die Umsetzung eines FIFOs in Software heißt Ringpuffer und weist auch eine feste Puffergröße auf. Wesentlicher Vorteil gegenüber der verketteten Liste ist die schnellere Ausführungszeit und der geringere Aufwand.&lt;br /&gt;
&lt;br /&gt;
== Standard-Ringpuffer ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Simpel&lt;br /&gt;
* Leicht verständlich&lt;br /&gt;
* Daten können auch vom Typ struct sein&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Schnell aber nicht optimal&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Ringpuffer.png|300px]]&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Circular buffer.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Inhalte des Puffers werden in einem schlichten Array gespeichert und der Zugriff erfolgt über einen Integer-Index. Erreicht ein Index die Obergrenze springt dieser auf Null zurück. Topologisch ist dies äquivalent zu einem Ringschluss, bei dem man ja auch nach dem letzten Element wieder beim ersten angelangt ist. Man denke zb an das Zifferblatt einer Uhr, bei der nach der 59 wieder die 0 kommt. Ein größer-gleich statt nur eines ist-gleich Vergleichs ist sicherer gegenüber Programmfehlern, bei denen der Index verstellt wurde.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
write = write + 1;&lt;br /&gt;
if (write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  write = 0;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Haben Lese- und Schreib-Index den gleichen Wert wird der Puffer als leer angesehen und es kann kein Element aus dem Puffer entnommen werden. Wenn write+1 und read identisch sind wird der Puffer als voll angesehen und es kann kein weiteres Element mehr im Puffer gespeichert werden. Nun fehlt noch der Sonderfall bei dem read gleich Null ist, hier funktioniert der write+1-Vergleich nicht mehr (wegen dem &#039;Überlauf&#039; am Ende des Arrays, der den Ringschluss bewirkt) und die Abfrage einer zusätzlichen Bedingung ist zur Voll-Abfrage erforderlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == write =&amp;gt; leer&lt;br /&gt;
&lt;br /&gt;
write + 1 == read || read == 0 &amp;amp;&amp;amp; write+1 == BUFFER_SIZE =&amp;gt; voll&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigem Zugriff zwischen dem Befüller (zb. einer UART-ISR) und dem Entnahmecode (zb. der Code in der Hauptschleife) muss ausgeschlossen werden, dass der Entnahmecode beim Lesen aus dem Puffer nicht durch den Interrupt unterbrochen werden kann. Denn während diese Funktion läuft ist zwischenzeitlich der read-Index in einem inkonsistenten Zustand. Erst nach Beendigung der Funktion spiegelt der read-Index wieder die Realität des Füllgrades wieder. Als Abhilfe dienen [[Interrupt#Atomarer_Datenzugriff|Atomare Abschnitte]], solche können durch keinen Interrupt unterbrochen werden. Die Radialkur deaktiviert alle Interrupts, besser ist die Deaktivierung eines einzelnen Interrupts. Es geht aber meistens auch so, da die FIFO Funktionen sehr kurz sind und daher auch die Interrupts nur kurz abgeschaltet werden. Im Allgemeinen werden versäumte Interrupts nach erneuter Aktivierung nachgeholt und gehen nicht verloren, so dass bereits anstehende Interrupts nur kurz verzögert werden. Solange die Interruptrate größer als die Ausführungsgeschwindigkeit der FIFO Funktionen ist, stellt das Abschalten der Interrupts daher auch in der Praxis meist kein allzugroßes Problem dar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
cli()&lt;br /&gt;
ret = BufferOut(&amp;amp;var);&lt;br /&gt;
sei()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist die Initialisierung der Indizes (read, write) mit -1, denn damit kann man einen leere Ringpuffer von einem vollen unterscheiden. Initialisiert man nur mit 0, kann man einen leeren nicht von einem vollen mit read=write=0 unterscheiden. &lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit der Vereinfachung entsteht durch die Einführung einer &amp;quot;Counter&amp;quot; Variable, die den Füllstand des Buffers wiedergibt. Bei Einschreiben wird diese um +1 erhöht, beim Auslesen um 1 erniedrigt. Ist sie 0 ist der Buffer leer. Damit lässt sich die Codeeffizienz steigern, da die obigen Vergleiche kürzer ausfallen und sich auf nur diese eine Variable beschränken. Nachteilig ist allerdings, dass man eine weitere Variable im System hat und natürlich benötigen die entsprechenden Operationen zum Erhöhen bzw. Erniedrigen dieser Variable ja auch Ausführungszeit. Benötigt man im restlichen Code den Füllgrad der FIFO nicht als explizite Zahl, dann lohnt daher eine derartige Counter-Variable meistens nicht.&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_SIZE 23&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  //if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
  //  buffer.write = 0; // erhöht sicherheit&lt;br /&gt;
&lt;br /&gt;
  if (buffer.write + 1 == buffer.read || buffer.read == 0 &amp;amp;&amp;amp; buffer.write + 1 == BUFFER_SIZE)&lt;br /&gt;
    return FAIL; // voll&lt;br /&gt;
&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
&lt;br /&gt;
  buffer.write = buffer.write + 1;&lt;br /&gt;
  if (buffer.write &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.write = 0;&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return FAIL;&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
&lt;br /&gt;
  buffer.read = buffer.read + 1;&lt;br /&gt;
  if (buffer.read &amp;gt;= BUFFER_SIZE)&lt;br /&gt;
    buffer.read = 0;&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;-Ringpuffer - die schnellste Lösung ==&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Durch Bitmaske auf den Feld-Index &#039;&#039;write&#039;&#039; kann der nicht &amp;quot;Amoklaufen&amp;quot; und beliebige Inhalte im Speicher überschreiben&lt;br /&gt;
* Sehr einfach, sehr schnell&lt;br /&gt;
* Daten können auch vom Typ &#039;&#039;struct&#039;&#039; sein&lt;br /&gt;
Nachteile:&lt;br /&gt;
* Nur Größenfaktoren von 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt; möglich&lt;br /&gt;
* Nur in speziellen Fällen Pointerreferenz möglich&lt;br /&gt;
&lt;br /&gt;
=== Beschreibung ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Index am oberen Ende angekommen ist springt er an den Anfang zurück. Die Definition der Obergrenze wird nicht durch einen Vergleich realisiert, sondern durch Modulo-Arithmetik. Dies kann sehr einfach und schnell durch Maskieren des Index-Werts umgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
( write + 1 ) &amp;amp; MASK&lt;br /&gt;
&lt;br /&gt;
15 = 0b00001111&lt;br /&gt;
15 + 1 = 16 = 0b000010000&lt;br /&gt;
16 &amp;amp; MASK = 0b000010000 &amp;amp; 0b000001111 = 0b000000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Daraus folgt das &#039;&#039;write&#039;&#039; nur einen Wert zwischen 0 und 15 einnehmen kann. Das ganze funktioniert nur mit Zahlen der Reihe 2&amp;lt;sup&amp;gt;n&amp;lt;/sup&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da der Puffer nur eine endliche Größe aufweist, muss eine voll/leer Indikation verfügbar sein. read == write bedeutet Puffer leer, entsprechend bedeutet write + 1 == read, dass keine weiteren Elemente mehr hinzugefügt werden können, denn sonst würde das Hinzufügen weiterer Daten einen Pufferüberlauf hervorrufen. Für die Behandlung des Pufferüberlaufs gibt es zwei Methoden, nichts tun und Fehler zurück geben oder ältesten Puffer-Inhalt verwerfen (read+1).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
read == ((write + 1) &amp;amp; MASK)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verhindert die Bitmaske einen möglichen Überlauf. Weiter zu beachten ist, dass ein Byte durch die voll/leer Indikation verloren geht, der Puffer hat hier nur eine größe von 15 statt 16 Byte&lt;br /&gt;
&lt;br /&gt;
Und noch ein kleiner Trick. Bei einer Zahl der Reihe 2^n lässt sich auf einfache Weise immer eine passende Bitmaske erzeugen und man sieht gleichzeitig klar die Anzahl der genutzten Bytes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# define SIZE 16&lt;br /&gt;
# define MASK (SIZE-1)&lt;br /&gt;
&lt;br /&gt;
16 = 0b010000&lt;br /&gt;
16 - 1 = 0b001111&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Normalbetrieb sollte der Puffer nie voll ausgereizt sein und wenn der Puffer bereits überläuft ist es für die Systemstabilität und Fehleranlyse oft schon zu spät, denn von einem nicht mehr funktionierenden Gerät lässt sich nur wenig Information herauskitzeln. Da kann ein Frühwarnsystem mehr bieten, wobei zwei Ausprägungen typisch sind, Höchstwert merken und eine Meldeschwelle einführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 freeMemory =  (read - write - 1 ) &amp;amp; MASK);&lt;br /&gt;
 if (freeMemory &amp;lt; floodmark)&lt;br /&gt;
   floodmark = freeMemory;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 if ( (read - write - 1 ) &amp;amp; MASK) &amp;lt;= FLOODMARK;&lt;br /&gt;
   FloodmarkExcess();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code-Beispiel ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_SIZE 16 // muss 2^n betragen (8, 16, 32, 64 ...)&lt;br /&gt;
#define BUFFER_MASK (BUFFER_SIZE-1) // Klammern auf keinen Fall vergessen&lt;br /&gt;
&lt;br /&gt;
struct Buffer {&lt;br /&gt;
  uint8_t data[BUFFER_SIZE];&lt;br /&gt;
  uint8_t read; // zeigt auf das Feld mit dem ältesten Inhalt&lt;br /&gt;
  uint8_t write; // zeigt immer auf leeres Feld&lt;br /&gt;
} buffer = {{}, 0, 0};&lt;br /&gt;
&lt;br /&gt;
uint8_t BufferIn(uint8_t byte)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t next = ((buffer.write + 1) &amp;amp; BUFFER_MASK);&lt;br /&gt;
  if (buffer.read == next)&lt;br /&gt;
    return FAIL;&lt;br /&gt;
  buffer.data[buffer.write] = byte;&lt;br /&gt;
  // buffer.data[buffer.write &amp;amp; BUFFER_MASK] = byte; // absolut Sicher&lt;br /&gt;
  buffer.write = next;&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BufferOut(uint8_t *pByte)&lt;br /&gt;
{&lt;br /&gt;
  if (buffer.read == buffer.write)&lt;br /&gt;
    return FAIL;&lt;br /&gt;
  *pByte = buffer.data[buffer.read];&lt;br /&gt;
  buffer.read = (buffer.read+1) &amp;amp; BUFFER_MASK;&lt;br /&gt;
  return SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FIFO mit C-Präprozessor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef FIFO_H_&lt;br /&gt;
#define FIFO_H_&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
	uint8_t _read;&lt;br /&gt;
	uint8_t _write;&lt;br /&gt;
	uint8_t _buffer[64];&lt;br /&gt;
} FIFO64_t;&lt;br /&gt;
&lt;br /&gt;
typedef struct {&lt;br /&gt;
	uint8_t _read;&lt;br /&gt;
	uint8_t _write;&lt;br /&gt;
	uint8_t _buffer[128];&lt;br /&gt;
} FIFO128_t;&lt;br /&gt;
&lt;br /&gt;
#define FIFO_init(fifo)		{ fifo._read = 0; fifo._write = 0; }&lt;br /&gt;
&lt;br /&gt;
#define FIFO_available(fifo)	( fifo._read != fifo._write )&lt;br /&gt;
&lt;br /&gt;
#define FIFO_read(fifo, size) (						\&lt;br /&gt;
	(FIFO_available(fifo)) ?					\&lt;br /&gt;
	fifo._buffer[fifo._read = (fifo._read + 1) &amp;amp; (size-1)] : 0	\&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
#define FIFO_write(fifo, data, size) {								\&lt;br /&gt;
	uint8_t tmphead = ( fifo._write + 1 ) &amp;amp; (size-1); 	/* calculate buffer index */	\&lt;br /&gt;
	if(tmphead != fifo._read) {				/* if buffer is not full */	\&lt;br /&gt;
		fifo._write = tmphead;				/* store new index */		\&lt;br /&gt;
		fifo._buffer[tmphead] = data;			/* store data in buffer */	\&lt;br /&gt;
	}											\&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define FIFO64_read(fifo)			FIFO_read(fifo, 64)&lt;br /&gt;
#define FIFO64_write(fifo, data)		FIFO_write(fifo, data, 64)&lt;br /&gt;
&lt;br /&gt;
#define FIFO128_read(fifo)			FIFO_read(fifo, 128)&lt;br /&gt;
#define FIFO128_write(fifo, data)		FIFO_write(fifo, data, 128)&lt;br /&gt;
&lt;br /&gt;
#endif /*FIFO_H_*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Circular_buffer Circular buffer], englische Wikipedia&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=86033</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=86033"/>
		<updated>2014-12-02T12:14:48Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Header File - wie geht das */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Library [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Wertebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit einen Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
Präprozessordefinitionen besitzen keine eigene Datentypen, da sie eine reine Textersetzungen durchführen. Deren Inhalte können aber ggf. zu Werten aufgelöst werden, die datentypbehaftet sind.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File - wie geht das =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen VarExtA bzw. VarExtB, die zu den jewiligen Funktionen A bzw. B gehören. Für dieses System soll eine Aufteilung erfolgen, so dass functionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll. Bei dem Makro A_DEFAULT kann es unterschiedliche Ansichten geben. Handelt es sich um einen Wert, den ein Aufrufer kennen soll, dann wird man das #define ins Header File verschieben (und aus dem C-File rausnehmen). Sieht man dieses #define aber als &#039;Privatsache&#039; des C-Files, dann bleibt der #define dort wo er jetzt ist.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=86032</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=86032"/>
		<updated>2014-12-02T12:11:18Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: Makro Syntax auf die uebliche Grossschreibung angepasst und so geaendert, dass es keinen Syntaxfehler gibt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Library [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Wertebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit einen Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
Präprozessordefinitionen besitzen keine eigene Datentypen, da sie eine reine Textersetzungen durchführen. Deren Inhalte können aber ggf. zu Werten aufgelöst werden, die datentypbehaftet sind.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File - wie geht das =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define A_DEFAULT  5&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = A_DEFAULT;&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SETTING_B 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SETTING_B;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Speicher&amp;diff=85314</id>
		<title>AVR-Tutorial: Speicher</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Speicher&amp;diff=85314"/>
		<updated>2014-10-19T21:05:27Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Flash-ROM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Speichertypen ==&lt;br /&gt;
&lt;br /&gt;
Die AVR-Mikrocontroller besitzen 3 verschiedene Arten von Speicher: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;has&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Flash&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;EEPROM&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;RAM&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Schreibzyklen&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;&amp;amp;gt;10.000&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;&amp;amp;gt;100.000&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Lesezyklen&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;flüchtig&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;nein&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;nein&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;ja&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Größe beim ATtiny2313&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;2 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;128 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;128 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Größe beim ATmega8&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;8 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;512 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Größe beim ATmega32&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;32 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;2 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash-ROM ===&lt;br /&gt;
&lt;br /&gt;
Das [[Speicher#Flash-ROM | &#039;&#039;&#039;Flash-ROM&#039;&#039;&#039;]] der AVRs dient als Programmspeicher. Über den Programmieradapter werden die kompilierten Programme vom PC an den Controller übertragen und im Flash-ROM abgelegt. Bei der Programmausführung wird das ROM [[Digitaltechnik#Word | Wort]] für Wort ausgelesen und ausgeführt. Es lässt sich aber auch zur Speicherung von Daten nutzen (z.&amp;amp;nbsp;B. Texte für ein [[LCD]]). Vom laufenden Programm aus kann man das ROM normalerweise nur lesen, nicht beschreiben. Es kann beliebig oft ausgelesen werden, aber theoretisch nur ~10.000 mal beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
=== EEPROM ===&lt;br /&gt;
&lt;br /&gt;
Das [[Speicher#EEPROM |&#039;&#039;&#039;EEPROM&#039;&#039;&#039;]] ist wie das Flash ein nichtflüchtiger Speicher, die Daten bleiben also auch nach dem Ausschalten der Betriebsspannung erhalten. Es kann beliebig oft gelesen und mindestens 100.000 mal beschrieben werden. Bei den AVRs kann man es z.&amp;amp;nbsp;B. als Speicher für Messwerte oder Einstellungen benutzen. &lt;br /&gt;
&lt;br /&gt;
=== RAM ===&lt;br /&gt;
&lt;br /&gt;
Das [[Speicher#RAM |&#039;&#039;&#039;RAM&#039;&#039;&#039;]] ist ein flüchtiger Speicher, d.h. die Daten gehen nach dem Ausschalten verloren. Es kann beliebig oft gelesen und beschrieben werden, weshalb es sich zur Speicherung von Variablen eignet für die die Register R0-R31 nicht ausreichen. Daneben dient es als Speicherort für den Stack, auf dem z.&amp;amp;nbsp;B. bei Unterprogrammaufrufen (rcall) die Rücksprungadresse gespeichert wird (siehe [[AVR-Tutorial:_SRAM | AVR-Tutorial: SRAM]]).&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Flash-ROM ===&lt;br /&gt;
&lt;br /&gt;
Die erste und wichtigste Anwendung des Flash-ROMs kennen wir bereits: Das Speichern von Programmen, die wir nach dem Assemblieren dort hineingeladen haben. Nun sollen aber auch vom laufenden Programm aus Daten ausgelesen werden. &lt;br /&gt;
&lt;br /&gt;
Um die Daten wieder auszulesen, muss man die Adresse, auf die zugegriffen werden soll, in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; laden. Der Z-Pointer besteht aus den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; (Low-Byte) und &#039;&#039;&#039;R31&#039;&#039;&#039; (High-Byte), daher kann man das Laden einer Konstante wie gewohnt mit dem Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039; durchführen. Statt R30 und R31 kann man übrigens einfach &#039;&#039;&#039;ZL&#039;&#039;&#039; und &#039;&#039;&#039;ZH&#039;&#039;&#039; schreiben, da diese Synonyme bereits in der include-Datei m8def.inc definiert sind. &lt;br /&gt;
&lt;br /&gt;
Wenn die richtige Adresse erstmal im Z-Pointer steht, geht das eigentliche Laden der Daten ganz einfach mit dem Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039;. Dieser Befehl, der im Gegensatz zu out, ldi usw. keine Operanden hat, veranlasst das Laden des durch den Z-Pointer addressierte Byte aus dem Programmspeicher in das Register &#039;&#039;&#039;R0&#039;&#039;&#039;, von wo aus man es weiterverarbeiten kann. &lt;br /&gt;
&lt;br /&gt;
Jetzt muss man nur noch wissen, wie man dem Assembler überhaupt beibringt, dass er die von uns festgelegten Daten im ROM platzieren soll und wie man dann an die Adresse kommt an der sich diese Daten befinden. Um den Programmspeicher mit Daten zu füllen, gibt es die Direktiven .db und .dw. In der Regel benötigt man nur .db, was folgendermaßen funktioniert: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
daten:&lt;br /&gt;
    .db 12, 20, 255, 0xFF, 0b10010000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direktiven wie .db sind Anweisungen an den Assembler, keine Prozessorbefehle. Von denen kann man sie durch den vorangestellten Punkt unterscheiden. In diesem Fall sagen wir dem Assembler, dass er die angegebenen Bytes nacheinander im Speicher platzieren soll; wenn man die Zeile also assembliert, erhält man eine Hex-Datei, die nur diese Daten enthält.&lt;br /&gt;
&lt;br /&gt;
Aber was soll das &#039;&#039;&#039;daten:&#039;&#039;&#039; am Anfang der Zeile? Bis jetzt haben wir Labels nur als Sprungmarken verwendet, um den Befehlen &#039;&#039;&#039;rcall&#039;&#039;&#039; und &#039;&#039;&#039;rjmp&#039;&#039;&#039; zu sagen, an welche Stelle im Programm gesprungen werden soll. Würden wir in diesem Fall &#039;&#039;&#039;rjmp daten&#039;&#039;&#039; im Programm stehen haben, dann würde die Programmausführung zur Stelle &#039;&#039;&#039;daten:&#039;&#039;&#039; springen, und versuchen die sinnlosen Daten als Befehle zu interpretieren - was mit Sicherheit dazu führt, dass der Controller Amok läuft. &lt;br /&gt;
&lt;br /&gt;
Statt nach &#039;&#039;&#039;daten:&#039;&#039;&#039; zu springen, sollten wir die Adresse besser in den Z-Pointer laden. Da der Z-Pointer aus zwei Bytes besteht, brauchen wir dazu zweimal den Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
    ldi ZL, LOW(daten*2)    ; Low-Byte der Adresse in Z-Pointer&lt;br /&gt;
    ldi ZH, HIGH(daten*2)   ; High-Byte der Adresse in Z-Pointer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie man sieht, ist das Ganze sehr einfach: Man kann die Labels im Assembler direkt wie Konstanten verwenden. Über die Multiplikation der Adresse mit zwei sollte man sich erst mal keine Gedanken machen: &amp;quot;Das ist einfach so.&amp;quot; Wer es genauer wissen will schaut [[AVR-Tutorial:_Mehrfachverzweigung#Z-Pointer_leicht_verst.C3.A4ndlich | hier]] nach.&lt;br /&gt;
&lt;br /&gt;
Um zu zeigen wie das alles konkret funktioniert, ist das folgende Beispiel nützlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    ldi     R16, 0xFF&lt;br /&gt;
    out     DDRB, R16               ; Port B: Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL, LOW(daten*2)        ; Low-Byte der Adresse in Z-Pointer&lt;br /&gt;
    ldi     ZH, HIGH(daten*2)       ; High-Byte der Adresse in Z-Pointer&lt;br /&gt;
&lt;br /&gt;
    lpm                             ; durch Z-Pointer adressiertes Byte&lt;br /&gt;
                                    ; in R0 laden&lt;br /&gt;
    out     PORTB, R0               ; an PORTB ausgeben&lt;br /&gt;
&lt;br /&gt;
ende:   &lt;br /&gt;
    rjmp ende                       ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
daten:&lt;br /&gt;
    .db 0b10101010&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man dieses Programm assembliert und in den Controller überträgt, dann kann man auf den an Port B angeschlossenen LEDs das mit &#039;&#039;&#039;.db 0b10101010&#039;&#039;&#039; im Programmspeicher abgelegte Bitmuster sehen. &lt;br /&gt;
&lt;br /&gt;
==== Fallbeispiel: Zeichenketten ====&lt;br /&gt;
Eine häufige Anwendung von &#039;&#039;&#039;lpm&#039;&#039;&#039; ist das Auslesen von Zeichenketten (&amp;quot;Strings&amp;quot;) aus dem Flash-ROM und die Ausgabe an den seriellen Port oder ein LCD. Das folgende Programm gibt in einer Endlosschleife den Text &amp;quot;AVR-Assembler ist ganz einfach&amp;quot;, gefolgt von einem Zeilenumbruch, an den UART aus. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
&lt;br /&gt;
.equ CLOCK = 4000000                ; Frequenz des Quarzes&lt;br /&gt;
.equ BAUD = 9600                    ; Baudrate&lt;br /&gt;
.equ UBRRVAL = CLOCK/(BAUD*16)-1    ; Baudratenteiler&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programmsegment los&lt;br /&gt;
&lt;br /&gt;
.CSEG &lt;br /&gt;
.org 0&lt;br /&gt;
    ldi     r16, low(RAMEND)            ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, r16                    &lt;br /&gt;
    ldi     r16, high(RAMEND)&lt;br /&gt;
    out     SPH, r16                    &lt;br /&gt;
&lt;br /&gt;
    ldi     temp, LOW(UBRRVAL)          ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
    ldi     temp, HIGH(UBRRVAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
                                    &lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0) ; Frame-Format: 8 Bit&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
    sbi     UCSRB, TXEN                 ; TX (Senden) aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    ldi     ZL, LOW(text*2)             ; Adresse des Strings in den&lt;br /&gt;
    ldi     ZH, HIGH(text*2)            ; Z-Pointer laden&lt;br /&gt;
    rcall   print                       ; Funktion print aufrufen&lt;br /&gt;
    rcall   wait                        ; kleine Pause&lt;br /&gt;
    rjmp    loop                        ; das Ganze wiederholen&lt;br /&gt;
&lt;br /&gt;
; kleine Pause&lt;br /&gt;
wait:&lt;br /&gt;
    ldi     temp,0&lt;br /&gt;
wait_1:&lt;br /&gt;
    ldi     temp1,0&lt;br /&gt;
wait_2:&lt;br /&gt;
    dec     temp1&lt;br /&gt;
    brne    wait_2&lt;br /&gt;
    dec     temp&lt;br /&gt;
    brne    wait_1&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; print: sendet die durch den Z-Pointer adressierte Zeichenkette&lt;br /&gt;
&lt;br /&gt;
print:&lt;br /&gt;
    lpm                                 ; Erstes Byte des Strings nach R0 lesen&lt;br /&gt;
    tst     R0                          ; R0 auf 0 testen&lt;br /&gt;
    breq    print_end                   ; wenn 0, dann zu print_end&lt;br /&gt;
    mov     r16, r0                     ; Inhalt von R0 nach R16 kopieren&lt;br /&gt;
    rcall   sendbyte                    ; UART-Sendefunktion aufrufen&lt;br /&gt;
    adiw    ZL, 1                       ; Adresse des Z-Pointers um 1 erhöhen&lt;br /&gt;
    rjmp    print                       ; wieder zum Anfang springen&lt;br /&gt;
print_end:&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; sendbyte: sendet das Byte aus R16 über das UART&lt;br /&gt;
&lt;br /&gt;
sendbyte:&lt;br /&gt;
    sbis    UCSRA, UDRE                 ; warten bis das UART bereit ist&lt;br /&gt;
    rjmp    sendbyte&lt;br /&gt;
    out     UDR, r16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; Konstanten werden hier im Flash abgelegt&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
    .db &amp;quot;AVR-Assembler ist ganz einfach&amp;quot;,10,13,0 &lt;br /&gt;
    ; Stringkonstante, durch eine 0 abgeschlossen&lt;br /&gt;
    ; die 10 bzw. 13 sind Steuerzeichen für Wagenrücklauf und neue Zeile&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVR-Controller besitzen einen erweiterten Befehlssatz. Darunter befindet sich auch der folgende Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
    lpm     r16, Z+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl liest ein Byte aus dem Flash und speichert es in einem beliebigen Register, hier r16. Danach wird der Zeiger Z um eins erhöht. Für die neuen Controller, wie ATmegas kann das Codebeispiel also so abgeändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
; print: sendet die durch den Z-Pointer adressierte Zeichenkette&lt;br /&gt;
print:&lt;br /&gt;
    lpm     r16, Z+         ; Erstes Byte des Strings nach r16 lesen&lt;br /&gt;
    tst     r16             ; r16 auf 0 testen&lt;br /&gt;
    breq    print_end       ; wenn 0, dann zu print_end&lt;br /&gt;
    rcall   sendbyte        ; UART-Sendefunktion aufrufen&lt;br /&gt;
    rjmp    print           ; wieder zum Anfang springen&lt;br /&gt;
print_end:&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man bei .db einen Text in doppelten Anführungszeichen angibt, werden die Zeichen automatisch in die entsprechenden ASCII-Codes umgerechnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
    .db     &amp;quot;Test&amp;quot;, 0               &lt;br /&gt;
    ; ist äquivalent zu&lt;br /&gt;
    .db     84, 101, 115, 116, 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit das Programm das Ende der Zeichenkette erkennen kann, wird eine 0 an den Text angehängt. &lt;br /&gt;
&lt;br /&gt;
Das ist doch schonmal sehr viel praktischer, als jeden Buchstaben einzeln in ein Register zu laden und abzuschicken. Und wenn man statt &#039;&#039;&#039;sendbyte&#039;&#039;&#039; einfach die Routine &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aus dem 4. Teil des Tutorials aufruft, dann funktioniert das gleiche sogar mit dem LCD!&lt;br /&gt;
&lt;br /&gt;
==== Neue Assemblerbefehle ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
    lpm                             ; Liest das durch den Z-Pointer&lt;br /&gt;
                                    ; addressierte Byte aus dem Flash-ROM&lt;br /&gt;
                                    ; in das Register R0 ein. &lt;br /&gt;
&lt;br /&gt;
    lpm     [Register], Z           ; Macht das gleiche wie lpm, jedoch in&lt;br /&gt;
                                    ; ein beliebiges Register&lt;br /&gt;
&lt;br /&gt;
    lpm     [Register], Z+          ; Erhöht zusätzlich den Z-Zeiger&lt;br /&gt;
&lt;br /&gt;
    tst     [Register]              ; Prüft, ob Inhalt eines Registers&lt;br /&gt;
                                    ; gleich 0 ist.&lt;br /&gt;
&lt;br /&gt;
    breq    [Label]                 ; Springt zu [Label], wenn der&lt;br /&gt;
                                    ; vorhergehende Vergleich wahr ist. &lt;br /&gt;
&lt;br /&gt;
    adiw    [Register], [Konstante] ; Addiert eine Konstante zu einem&lt;br /&gt;
                                    ; Registerpaar. [Register] bezeichnet das&lt;br /&gt;
                                    ; untere der beiden Register.&lt;br /&gt;
                                    ; Kann nur auf die Registerpaare&lt;br /&gt;
                                    ; R25:R24, R27:R26, R29:R28 und R31:R30&lt;br /&gt;
                                    ; angewendet werden. &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM ===&lt;br /&gt;
&lt;br /&gt;
Den Flash Speicher kann man also benutzen, um dort Daten abzulegen, die sich während eines Programmlaufs nicht verändern. Irgendwelche Tabellen oder konstante Texte. Aber des öfteren möchte man auch die Möglichkeit haben, Daten zu speichern und wieder zu lesen, die sich während des Programmlaufs ändern. Ganz besonders möchte man eine Speicherfläche zur Verfügung haben, die ihren Inhalt auch dann behält, wenn dem µC die Versorgungsspannung abgedreht wird. Man denke z.B. an irgendwelche Konfigurationsdaten oder an Informationen, wie weit der µC in der Bearbeitung von Daten vorangekommen ist oder irgendwelche Statistikdaten, die auch nach einem Stromausfall noch verfügbar sein sollen. In solchen Fällen kommt das EEPROM zum Einsatz&lt;br /&gt;
&lt;br /&gt;
==== Lesen ====&lt;br /&gt;
&lt;br /&gt;
Als erstes muss geprüft werden, ob ein vorheriger Schreibzugriff schon abgeschlossen ist. Danach wird die EEPROM-Adresse von der gelesen werden soll in das IO-Registerpaar &#039;&#039;&#039;EEARH/EEARL&#039;&#039;&#039; (EEPROM Address Register) geladen. Da der ATmega8 mehr als 256 Byte EEPROM hat, passt die Adresse nicht in ein einziges 8-Bit-Register, sondern muss in zwei Register aufgeteilt werden: EEARH bekommt das obere Byte der Adresse, EEARL das untere Byte. Dann löst man den Lesevorgang durch das Setzen des Bits &#039;&#039;&#039;EERE&#039;&#039;&#039; (EEPROM Read Enable) im IO-Register &#039;&#039;&#039;EECR&#039;&#039;&#039; (EEPROM Control Register) aus. Das gelesene Byte kann sofort aus dem IO-Register &#039;&#039;&#039;EEDR&#039;&#039;&#039; (EEPROM Data Register) in ein normales CPU-Register kopiert und dort weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Flash-ROM kann man das EEPROM über den ISP-Programmer programmieren. Die Daten, die im EEPROM abgelegt werden sollen, werden wie gewohnt mit .db angegeben; allerdings muss man dem Assembler natürlich sagen, dass es sich hier um Daten für das EEPROM handelt. Das macht man durch die Direktive &#039;&#039;&#039;.eseg&#039;&#039;&#039;, woran der Assembler erkennt, dass alle nun folgenden Daten für das EEPROM bestimmt sind. &lt;br /&gt;
&lt;br /&gt;
Damit man die Bytes nicht von Hand abzählen muss um die Adresse herauszufinden, kann man auch im EEPROM-Segment wieder Labels einsetzen und diese im Assemblerprogramm wie Konstanten verwenden. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
; hier geht die Programmsektion los&lt;br /&gt;
.cseg&lt;br /&gt;
&lt;br /&gt;
    ldi     r16, low(RAMEND)            ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, r16                    &lt;br /&gt;
    ldi     r16, high(RAMEND)&lt;br /&gt;
    out     SPH, r16                    &lt;br /&gt;
&lt;br /&gt;
    ldi     r16, 0xFF&lt;br /&gt;
    out     DDRB, r16                   ; Port B Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL,low(daten)               ; Z-Zeiger laden&lt;br /&gt;
    ldi     ZH,high(daten)&lt;br /&gt;
    rcall   EEPROM_read                 ; Daten aus EEPROM lesen&lt;br /&gt;
    out     PORTB, r16&lt;br /&gt;
&lt;br /&gt;
loop:   &lt;br /&gt;
    rjmp loop&lt;br /&gt;
&lt;br /&gt;
EEPROM_read:&lt;br /&gt;
    sbic    EECR,EEWE                   ; prüfe ob der vorherige Schreibzugriff&lt;br /&gt;
                                        ; beendet ist&lt;br /&gt;
    rjmp    EEPROM_read                 ; nein, nochmal prüfen&lt;br /&gt;
&lt;br /&gt;
    out     EEARH, ZH                   ; Adresse laden&lt;br /&gt;
    out     EEARL, ZL    &lt;br /&gt;
    sbi     EECR, EERE                  ; Lesevorgang aktivieren&lt;br /&gt;
    in      r16, EEDR                   ; Daten in CPU Register kopieren&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; Daten im EEPROM definieren&lt;br /&gt;
.eseg&lt;br /&gt;
daten:&lt;br /&gt;
    .db     0b10101010&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man dieses Programm assembliert, erhält man außer der .hex-Datei noch eine Datei mit der Endung &#039;&#039;&#039;.eep&#039;&#039;&#039;. Diese Datei enthält die Daten aus dem EEPROM-Segment (.eseg), und muss zusätzlich zu der hex-Datei in den Controller programmiert werden. &lt;br /&gt;
&lt;br /&gt;
Das Programm gibt die Binärzahl 0b10101010 an den Port B aus, das heißt jetzt sollte jede zweite LED leuchten.&lt;br /&gt;
&lt;br /&gt;
Natürlich kann man auch aus dem EEPROM Strings lesen und an den UART senden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.equ CLOCK = 4000000                ; Frequenz des Quarzes&lt;br /&gt;
&lt;br /&gt;
.equ BAUD = 9600                    ; Baudrate&lt;br /&gt;
.equ UBRRVAL = CLOCK/(BAUD*16)-1    ; Baudratenteiler&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programmsegment los&lt;br /&gt;
&lt;br /&gt;
.CSEG &lt;br /&gt;
 &lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
main:&lt;br /&gt;
    ldi     temp, LOW(RAMEND)           ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
                          &lt;br /&gt;
    ldi     temp, LOW(UBRRVAL)          ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
    ldi     temp, HIGH(UBRRVAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
                                    &lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0) ; Frame-Format: 8 Bit&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
    sbi     UCSRB, TXEN                 ; TX (Senden) aktivieren&lt;br /&gt;
 &lt;br /&gt;
    ldi     ZL, low(text1)              ; ersten String senden&lt;br /&gt;
    ldi     ZH, high(text1)             ; Z-Pointer laden&lt;br /&gt;
    rcall   EEPROM_print&lt;br /&gt;
    &lt;br /&gt;
    ldi     ZL, low(text2)              ; zweiten String senden&lt;br /&gt;
    ldi     ZH, high(text2)             ; Z-Pointer laden&lt;br /&gt;
    rcall   EEPROM_print&lt;br /&gt;
&lt;br /&gt;
loop:   &lt;br /&gt;
    rjmp    loop                        ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; EEPROM Lesezugriff auf Strings + UART Ausgabe&lt;br /&gt;
&lt;br /&gt;
EEPROM_print:&lt;br /&gt;
    sbic    EECR,EEWE           ; prüf ob der vorherige Schreibzugriff&lt;br /&gt;
                                ; beendet ist&lt;br /&gt;
    rjmp    EEPROM_print        ; nein, nochmal prüfen&lt;br /&gt;
&lt;br /&gt;
    out     EEARH, ZH           ; Adresse laden&lt;br /&gt;
    out     EEARL, ZL&lt;br /&gt;
    &lt;br /&gt;
    sbi     EECR, EERE          ; Lesevorgang aktivieren&lt;br /&gt;
    in      temp, EEDR          ; Daten in CPU Register kopieren&lt;br /&gt;
    tst     temp                ; auf 0 testen (=Stringende)&lt;br /&gt;
    breq    eep_print_end       ; falls 0, Funktion beenden&lt;br /&gt;
    rcall   sendbyte            ; ansonsten Byte senden...&lt;br /&gt;
    adiw    ZL,1                ; Adresse um 1 erhöhen...&lt;br /&gt;
    rjmp    EEPROM_print        ; und zum Anfang der Funktion&lt;br /&gt;
eep_print_end:&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; sendbyte: sendet das Byte aus &amp;quot;data&amp;quot; über den UART&lt;br /&gt;
&lt;br /&gt;
sendbyte:&lt;br /&gt;
    sbis    UCSRA, UDRE         ; warten bis das UART bereit ist&lt;br /&gt;
    rjmp    sendbyte&lt;br /&gt;
    out     UDR, temp&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; hier wird der EEPROM-Inhalt definiert&lt;br /&gt;
&lt;br /&gt;
.ESEG&lt;br /&gt;
&lt;br /&gt;
text1:&lt;br /&gt;
    .db     &amp;quot;Strings funktionieren auch &amp;quot;, 0&lt;br /&gt;
text2:&lt;br /&gt;
    .db     &amp;quot;im EEPROM&amp;quot;,10,13, 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schreiben ====&lt;br /&gt;
&lt;br /&gt;
Als erstes muss geprüft werden, ob ein vorheriger Schreibzugriff schon abgeschlossen ist. Danach wird die EEPROM-Adresse, auf die geschrieben wird, in das IO-Register &#039;&#039;&#039;EEAR&#039;&#039;&#039; (&#039;&#039;&#039;EE&#039;&#039;&#039;PROM &#039;&#039;&#039;A&#039;&#039;&#039;ddress &#039;&#039;&#039;R&#039;&#039;&#039;egister) geladen. Dann schreibt man die Daten, welche man auf der im Adressregister abgespeicherten Position ablegen will ins Register &#039;&#039;&#039;EEDR&#039;&#039;&#039; (&#039;&#039;&#039;EE&#039;&#039;&#039;PROM &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister). Als nächstes setzt man das &#039;&#039;&#039;EEMWE&#039;&#039;&#039; Bit im EEPROM-Kontrollregister &#039;&#039;&#039;EECR&#039;&#039;&#039; (&#039;&#039;&#039;EE&#039;&#039;&#039;PROM &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister) um den Schreibvorgang vorzubereiten. Nun wird es zeitkritisch - es darf nun keinesfalls ein Interrupt dazwischenfahren - denn man muss innerhalb von 4 Taktzyklen das &#039;&#039;&#039;EEWE&#039;&#039;&#039; Bit setzen um den Schreibvorgang auszulösen. Um das unter allen Bedingungen sicherzustellen werden die Interrupts kurz gesperrt. Danach startet der Schreibvorgang und läuft automatisch ab. Wenn er beendet ist, wird von der Hardware das &#039;&#039;&#039;EEWE&#039;&#039;&#039; Bit im Register &#039;&#039;&#039;EECR&#039;&#039;&#039; wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel werden Zeichen per UART und Interrupt empfangen und nacheinander im EEPROM gespeichert. Per Terminalprogramm kann man nun bis zu 512 Zeichen in den EEPROM schreiben. Per Programmieradapter kann man denn EEPROM wieder auslesen und seine gespeicherten Daten anschauen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp      = r16&lt;br /&gt;
.def sreg_save = r17&lt;br /&gt;
&lt;br /&gt;
.equ CLOCK = 4000000&lt;br /&gt;
&lt;br /&gt;
.equ BAUD = 9600&lt;br /&gt;
.equ UBRRVAL = CLOCK/(BAUD*16)-1&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programmsegment los&lt;br /&gt;
&lt;br /&gt;
.CSEG&lt;br /&gt;
.org 0x00&lt;br /&gt;
    rjmp    main&lt;br /&gt;
 &lt;br /&gt;
.org URXCaddr&lt;br /&gt;
    rjmp    int_rxc&lt;br /&gt;
 &lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
main:&lt;br /&gt;
    ldi     temp, LOW(RAMEND)           ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
                          &lt;br /&gt;
    ldi     temp, LOW(UBRRVAL)          ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
    ldi     temp, HIGH(UBRRVAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
 &lt;br /&gt;
                                   &lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0) ; Frame-Format: 8 Bit&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
 &lt;br /&gt;
    sbi     UCSRB, RXCIE                ; Interrupt bei Empfang&lt;br /&gt;
    sbi     UCSRB, RXEN                 ; RX (Empfang) aktivieren&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL,low(daten)               ; der Z-Zeiger wird hier exclusiv&lt;br /&gt;
    ldi     ZH,high(daten)              ; für die Datenadressierung verwendet&lt;br /&gt;
    &lt;br /&gt;
    sei                                 ; Interrupts global aktivieren&lt;br /&gt;
    &lt;br /&gt;
loop:   &lt;br /&gt;
    rjmp    loop                        ; Endlosschleife (ABER Interrupts!)&lt;br /&gt;
        &lt;br /&gt;
; Interruptroutine wird ausgeführt,&lt;br /&gt;
; sobald ein Byte über den UART empfangen wurde&lt;br /&gt;
&lt;br /&gt;
int_rxc:&lt;br /&gt;
    push    temp                        ; temp auf dem Stack sichern&lt;br /&gt;
    in      temp,sreg                   ; SREG sicher, muss praktisch in jeder&lt;br /&gt;
                                        ; Interruptroutine gemacht werden&lt;br /&gt;
    push    temp&lt;br /&gt;
    &lt;br /&gt;
    in      temp, UDR                   ; empfangenes Byte lesen&lt;br /&gt;
    rcall   EEPROM_write                ; Byte im EEPROM speichern&lt;br /&gt;
    adiw    ZL,1                        ; Zeiger erhöhen&lt;br /&gt;
    cpi     ZL,low(EEPROMEND+1)         ; Vergleiche den Z Zeiger&lt;br /&gt;
    ldi     temp,high(EEPROMEND+1)      ; mit der maximalen EEPROM Adresse +1&lt;br /&gt;
    cpc     ZH,temp&lt;br /&gt;
    brne    int_rxc_1                   ; wenn ungleich, springen&lt;br /&gt;
    ldi     ZL,low(Daten)               ; wenn gleich, Zeiger zurücksetzen&lt;br /&gt;
    ldi     ZH,high(Daten)&lt;br /&gt;
int_rxc_1:&lt;br /&gt;
&lt;br /&gt;
    pop     temp&lt;br /&gt;
    out     sreg,temp&lt;br /&gt;
    pop     temp                        ; temp wiederherstellen&lt;br /&gt;
    reti&lt;br /&gt;
&lt;br /&gt;
; der eigentliche EEPROM Schreibzugriff&lt;br /&gt;
; Adresse in ZL/ZH&lt;br /&gt;
; Daten in temp&lt;br /&gt;
&lt;br /&gt;
EEPROM_write:&lt;br /&gt;
    sbic    EECR, EEWE                  ; prüfe ob der letzte Schreibvorgang beendet ist&lt;br /&gt;
    rjmp    EEPROM_write                ; wenn nein, nochmal prüfen&lt;br /&gt;
&lt;br /&gt;
    out     EEARH, ZH                   ; Adresse schreiben&lt;br /&gt;
    out     EEARL, ZL                   ; &lt;br /&gt;
    out     EEDR,temp                   ; Daten  schreiben&lt;br /&gt;
    in      sreg_save,sreg              ; SREG sichern&lt;br /&gt;
    cli                                 ; Interrupts sperren, die nächsten&lt;br /&gt;
                                        ; zwei Befehle dürfen NICHT&lt;br /&gt;
                                        ; unterbrochen werden&lt;br /&gt;
    sbi     EECR,EEMWE                  ; Schreiben vorbereiten&lt;br /&gt;
    sbi     EECR,EEWE                   ; Und los !&lt;br /&gt;
    out     sreg, sreg_save             ; SREG wieder herstellen&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; hier wird der EEPROM-Inhalt definiert&lt;br /&gt;
.ESEG&lt;br /&gt;
&lt;br /&gt;
Daten:  &lt;br /&gt;
    .db     0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SRAM ===&lt;br /&gt;
&lt;br /&gt;
Die Verwendung des SRAM wird in einem anderen Kapitel erklärt: [[AVR-Tutorial: SRAM]]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=UART|&lt;br /&gt;
zurücklink=AVR-Tutorial: UART|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Timer|&lt;br /&gt;
vorlink=AVR-Tutorial: Timer}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Speicher]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Operationsverst%C3%A4rker-Grundschaltungen&amp;diff=85053</id>
		<title>Operationsverstärker-Grundschaltungen</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Operationsverst%C3%A4rker-Grundschaltungen&amp;diff=85053"/>
		<updated>2014-09-29T13:10:26Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Idealisiertes Modell eines OPV==&lt;br /&gt;
&lt;br /&gt;
=== Anschlüsse ===&lt;br /&gt;
Ein Operationsverstärker hat zwei Eingänge (+) und (-) und einen Ausgang&lt;br /&gt;
(UA). Außerdem verfügt er über eine positive und eine negative&lt;br /&gt;
Spannungsversorgung (V+) und (V-).&lt;br /&gt;
&lt;br /&gt;
=== Spannungsversorgungen ===&lt;br /&gt;
Die Spannungsversorgungen sollen zunächst nicht interessieren. Sie&lt;br /&gt;
werden in Schaltungen oft nicht eingezeichnet. &lt;br /&gt;
In der Praxis ist es jedoch wichtig zu wissen, dass die Ausgangsspannung immer zwischen (V+) und (V-) liegt. Die Ausgangsspannung des OPV kommt schließlich  dadurch zustande, dass der Ausgang über einen Transistor mehr oder weniger hochohmig mit den beiden Versorgungsspannungen verbunden wird.&lt;br /&gt;
&lt;br /&gt;
Wenn man einen OPV also mit +5V versorgt, so kann der OPV im besten Fall am Ausgang +5V erzeugen. Man würde in diesem Fall von einem &amp;quot;Rail-to-Rail&amp;quot; Operationsverstärker sprechen.&lt;br /&gt;
Bei vielen Operationsverstärkern ist die maximal mögliche Ausgangsspannung geringer als die Versorgungsspannung. Ein mit +5V Spannungsversorgung beschalteter OPV kann dann beispielsweise nur +4V Ausgangsspannung erzeugen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    | \&lt;br /&gt;
    |  \&lt;br /&gt;
 -- | - \&lt;br /&gt;
    |    \_______&lt;br /&gt;
    |    /&lt;br /&gt;
 -- | + /&lt;br /&gt;
    |  /&lt;br /&gt;
    | /&lt;br /&gt;
&lt;br /&gt;
=== Ausgang ===&lt;br /&gt;
Der Ausgang des OPV ist eine ideale Spannungsquelle. Das bedeutet, dass die&lt;br /&gt;
Ausgangsspannung unabhängig davon ist, was ausgangsseitig an den OPV&lt;br /&gt;
angeschlossen wird.&lt;br /&gt;
In der Praxis gilt dieses Modell häufig nur bei &amp;quot;sinnvollen Anwendungen&amp;quot;. So ist beispielsweise der Ausgangsstrom des OPV nach oben begrenzt (typischerweise im mA-Bereich), und manche OPV schwingen sehr leicht, wenn man sie kapazitiv belastet.&lt;br /&gt;
&lt;br /&gt;
=== Eingänge ===&lt;br /&gt;
Die Eingänge eines OPV sind hochohmig, d. h., es handelt sich nur um &amp;quot;Messfühler&amp;quot;, die keinen Strom führen.&lt;br /&gt;
Achtung: Die Eingangsschutzbeschaltung (Dioden von GND und gegen VCC) bei manchen OPVs kann jedoch dazu führen, dass Strom in den Eingang fliesst, wenn dessen Betriebsspannung z.B. abgeschaltet ist.&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
Der OPV mißt zu jeder Zeit die Differenz &amp;lt;math&amp;gt;U_D = U(+) - U(-)&amp;lt;/math&amp;gt; der&lt;br /&gt;
Eingangsspannungen.&lt;br /&gt;
&lt;br /&gt;
Ist die Spannung an (+) größer als an (-), so erhöht der OPV die&lt;br /&gt;
Ausgangsspannung.&lt;br /&gt;
Ist die Spannung an (+) niedriger als an (-), so vermindert der OPV die&lt;br /&gt;
Ausgangsspannung.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis dieses Vorgangs wird häufig über die Gleichung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_a = v \cdot U_D&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
beschrieben, wobei &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; eine sehr große Zahl (10^4...10^6) ist. &lt;br /&gt;
&lt;br /&gt;
Mit Hilfe der beschriebenen Funktionsweise lassen sich alle grundlegenden Schaltungen herleiten.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Betrachtet wird die invertierende Grundschaltung nach Abbildung a) im Abschnitt [[Operationsverstärker-Grundschaltungen#Verstärkergrundschaltungen|Verstärkergrundschaltungen]].&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-verstaerker-a.png]]&lt;br /&gt;
&lt;br /&gt;
Für die Pfeilrichtungen der Spannungen und Ströme gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt;: von oben nach unten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{R3}&amp;lt;/math&amp;gt;: von links nach rechts&lt;br /&gt;
&lt;br /&gt;
Die Spannung am (+)Eingang ist gleich Null. Die Spannung am (-)Eingang wird durch die Spannungsquelle &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; und durch die im OPV befindliche und mit dem Ausgang verbundene Spannungsquelle des OPV manipuliert.&lt;br /&gt;
&lt;br /&gt;
* Ist die Spannung am (-)Eingang negativ, so erhöht der Operationsverstärker die Ausgangsspannung. Dadurch wird durch die Rückführung über den Widerstand auch die Spannung am (-)Eingang positiver. Und zwar so lange, bis die Spannung am (-)Eingang gleich groß ist, wie die Spannung am (+)Eingang, also U(-)=0V.&lt;br /&gt;
* Ist die Spannung am (-)Eingang positiv, so vermindert der Operationsverstärker die Ausgangsspannung. Dadurch wird durch die Rückführung über den Widerstand auch die Spannung am (-)Eingang negativer. Und zwar so lange, bis die Spannung am (-)Eingang gleich groß ist, wie die Spannung am (+)Eingang, also U(-)=0V.&lt;br /&gt;
&lt;br /&gt;
Der Operationsverstärker wird also die Spannungen an (+) und (-) angleichen. Das passiert immer dann, wenn der Ausgang mit dem (-)Eingang verbunden ist. Der Trick in dieser Schaltung besteht darin, dass von der Ausgangsspannung nur ein Teil wieder rückgeführt wird. Die Spannung U_e ist daher höher, als der Teil der benötigt wird, um die Spannungen an den Eingängen aneinander anzugleichen.&lt;br /&gt;
Man nennt das Prinzip &amp;quot;Gegenkopplung&amp;quot;. Auf diese Art und Weise funktionieren alle analogen OPV-Schaltungen.&lt;br /&gt;
&lt;br /&gt;
Da an (+) Massepotential anliegt, wird somit auch (-) daran angeglichen, und so liegt an &amp;lt;math&amp;gt;R_3&amp;lt;/math&amp;gt; die Spannung &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; an. Daher gilt: &lt;br /&gt;
&amp;lt;math&amp;gt;I_{R3}=\frac{U_e}{R_3}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da der (-)Eingang hochohmig ist, fließt &amp;lt;math&amp;gt;I_{R3}&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;R_4&amp;lt;/math&amp;gt; weiter zum OPV-Ausgang.&lt;br /&gt;
&lt;br /&gt;
Ua ist die Spannung vom Ausgang zur (virtuellen) Masse am (-)Eingang.&lt;br /&gt;
(--&amp;gt; Pfeil einzeichnen und klarmachen, daß es egal ist, ob der Pfeil vom&lt;br /&gt;
Ausgang zur Masse geht oder vom Ausgang &amp;quot;entgegen der Stromrichtung&amp;quot; zur&lt;br /&gt;
virtuellen Masse an (-)!)&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe von &amp;lt;math&amp;gt;I_{R3}=\frac{U_e}{R_3}&amp;lt;/math&amp;gt; ergibt sich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_a = -R_4 \cdot I_{R3} = -{{R_4} \over {R_3}} \cdot U_e.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Energie für den Stromtransport über &amp;lt;math&amp;gt;R_4&amp;lt;/math&amp;gt; stammt vom OPV! Sobald die Ladungen ausgehend von der Spannungsquelle &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; die virtuelle Masse an (-) erreicht haben, hat &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; seine gesamte Energie abgegeben.&lt;br /&gt;
&lt;br /&gt;
== Reale OPs / Kennwerte ==&lt;br /&gt;
Abweichend vom idealen OP besitzen reale OPs diverse Einschränkungen und Kennwerte, die sie für verschiedene Einsätze mehr oder weniger prädestinieren.&lt;br /&gt;
&lt;br /&gt;
=== Leerlaufverstärkung ===&lt;br /&gt;
Die Leerlaufverstärkung gibt an, wie stark sich das Ausgangssignal i.A. der Änderung eines Eingangsignals statisch ändert, bzw nach dem Einschwingen erreichen könnte, wenn es nicht durch die Betriebsgrenzen limitiert wäre.&lt;br /&gt;
&lt;br /&gt;
=== Verstärkungs-Bandbreiteprodukt ===&lt;br /&gt;
Das Verstärkungs-Bandbreiteprodukt gibt an, bei welcher Verstärkung welche Bandbreite erreicht werden kann. Durch Rückkopplung kann die Verstärkung eingestellt werden. Bei kleinerer Verstärkung ergibt sich somit eine höhere Bandbreite, wenn das Produkt aus beiden konstant ist. Die Bandbreite bei der Verstärkung eins heißt Transitfrequenz. Das Verstärkungs-Bandbreiteprodukt ist entscheidend für das Kleinsignalverhalten.&lt;br /&gt;
&lt;br /&gt;
=== Anstiegszeit ===&lt;br /&gt;
Bestimmend für das Großsignalverhalten ist neben dem Verstärkungs-Bandbreiteprodukt die Anstiegszeit (slew rate), da bei hohen Ausgangsamplituden die Ausgangskurve eventuell zu steil wird, um richtig wiedergegeben zu werden.&lt;br /&gt;
&lt;br /&gt;
=== Gleichtaktverstärkung ===&lt;br /&gt;
Infolge des inhomogenen Aufbaus der internen Verstärkerstufen werden die beiden Eingangssignale nicht exakt gleich verstärkt, was in einen Gleichanteil und einen Differenzanteil aufgeteilt werden kann. Der nicht erwünschte Gleichtakt (engl. &#039;&#039;common mode&#039;&#039;) ist dabei ein Maß für die Qualität des OP.&lt;br /&gt;
&lt;br /&gt;
== Verstärkergrundschaltungen ==&lt;br /&gt;
=== Grundbeschaltung mit Berechnung ===&lt;br /&gt;
[[Bild:Op-verstaerker-a.png]] [[Bild:Op-verstaerker-b.png]]&lt;br /&gt;
&lt;br /&gt;
In a) und b) verwenden wir den OP als Verstärker und nutzen hier die Möglichkeit der Gegenkopplung, um definierte Verstärkungen zu erhalten. Wir gehen wieder davon aus, dass der OP ein ideales Bauteil ist und daher seine Leerlaufverstärkung unendlich ist. Ebenso betrachten wir den Eingangswiderstand als unendlich.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;a)&#039;&#039;&#039; ist ein invertierender Verstärker mit einem OP dargestellt. Durch die Widerstände R3 und R4 wird die Verstärkung bestimmt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = \frac{U_a}{U_e} = -\frac{R_4}{R_3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Verhältnis der beiden Widerstände bestimmt also die Verstärkung und somit die Ausgangsspannung:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -\frac{R_4}{R_3} \cdot U_e &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder auch&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = V \cdot U_e&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das negative Vorzeichen drückt aus, dass es sich um einen invertierenden Verstärker handelt.&lt;br /&gt;
&lt;br /&gt;
Beim nichtinvertierenden Verstärker &#039;&#039;&#039;b)&#039;&#039;&#039; finden wir auch eine Rückkopplung über R6 zum invertierenden Eingang des OP. Die Verstärkung wird durch das Gegenkopplungsnetzwerk R6 und R7 bestimmt. Hier ist:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = 1 + \frac{R6}{R7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Verstärkung von 1 ist sinnvoll, wenn eingangsseitig eine Spannungsquelle mit hohem Innenwiderstand verwendet wird. Für &amp;lt;math&amp;gt;\frac{R6}{R7} \to 0&amp;lt;/math&amp;gt; heißt die Schaltung &amp;quot;Spannungsfolger&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Kleinere Werte als 1 lassen sich nicht realisieren. Die Ausgangsspannung errechnet sich also so:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_e \cdot \left (1 + \frac{R_6}{R_7}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Eine Eingangsspannung von 0,5 V soll auf den Wert 5 V verstärkt werden, es ist also eine Verstärkung V von 10 benötigt. R7 ist mit 10 k&amp;amp;Omega; vorgegeben. Also ist das Verhältnis&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{R_6}{R_7} = V - 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einem Wert von 10 k&amp;amp;Omega; für R7 errechnet sich R6 zu&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
R6 = (V - 1) \cdot R7&lt;br /&gt;
   = (10 - 1) \cdot 10\,\mathrm{k\Omega}&lt;br /&gt;
   = 90\,\mathrm{k\Omega}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgangsspannung Ua wird also:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_a =   U_e \cdot \left (1 + \frac{R_6}{R_7}\right )&lt;br /&gt;
   = 0,5\,\mathrm{V} \cdot \left (1 + \frac{90\,\mathrm{k\Omega}}{10\,\mathrm{k\Omega}}\right)&lt;br /&gt;
   = 5\,\mathrm{V}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nichtinvertierender Verstärker mit Offset ===&lt;br /&gt;
Eine Abwandlung des nichtinvertierenden Verstärkers erlaubt es, einen konstanten Offset vorzugeben. D.h. von der zu verstärkenden Eingangsspannung U(e) wird eine konstante Spannung U(o) abgezogen und die Differenz verstärkt. Auf der Ausgangsspannung U(a) findet sich die Offsetspannung U(o) allerdings wieder.&lt;br /&gt;
[[Bild:Op-verstaerker-offset.png]]&lt;br /&gt;
&lt;br /&gt;
Es gilt:&lt;br /&gt;
&lt;br /&gt;
Offsetspannung: &lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_o =  U_V \cdot \frac{R2}{R1 + R2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
mit U(V) = Versorgungsspannung an R1&lt;br /&gt;
&lt;br /&gt;
Verstärkung:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
V = 1 + \frac{R3}{\frac{R1 \cdot R2}{R1 + R2}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausgangsspannung:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_a = (U_e - U_o) \cdot V + U_o&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung ist, daß nur die Differenz verstärkt wird. Damit kann eine größere Verstärkung gewählt werden. Zu berücksichtigen ist dabei, daß die Ausgangsspannung U(a) um die Offsetspannung U(o) überlagert ist.&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfolger (Impedanzwandler) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger1.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Abart des nichtinvertierenden Verstärkers stellt der Spannungsfolger dar. Beim nichtinvertierenden Verstärker errechnet sich die Ausgangsspannung aus:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_e \cdot \left (1 + \frac{R_2}{R_1}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir R2 auf 0Ω (mit R1 &amp;gt; 0) oder R1 auf unendlich (mit R2 &amp;lt; ∞) ändern, erhalten wir daher:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = 1 + \frac{R_2}{R_1} = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Spannungsfolger hat also eine Verstärkung V von 1.&lt;br /&gt;
&lt;br /&gt;
Umgezeichnet sieht die Schaltung so aus: &lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger2.png]]&lt;br /&gt;
&lt;br /&gt;
Was soll das nun? Wir nutzen die Eigenschaft, dass ein idealer OP einen unendlichen Eingangswiderstand und einen Ausgangswiderstand von 0Ω hat. Real sieht das natürlich anders aus: so liegt der Eingangswiderstand Re bei normalen OPs in der Größenordnung von 1MOhm bis &amp;lt;math&amp;gt;10^{15} \Omega&amp;lt;/math&amp;gt;, der Ausgangswiderstand Ra im Bereich 20Ω bis 1kOhm. Deshalb spricht man bei dieser Schaltung von einem Impedanzwandler. Eine solche Schaltung kann also aus einer relativ hochohmigen Spannungsquelle eine niederohmige, durch Folgeschaltungen belastbare Spannungsquelle machen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger3.png|left]]&lt;br /&gt;
&lt;br /&gt;
In dem nebenstehenden Beispiel ist eine einfache Möglichkeit zur Erzeugung einer Referenzspannung gezeigt. Es kommt eine normale Stabilisierungsschaltung mit einer Zenerdiode zur Anwendung, die aber nicht mehr die schlechten Eigenschaften der Standardbeschaltung mit lediglich Widerstand und Zenerdiode  hat. Bei einer Zenerdiode hängt die genaue Spannung davon ab, welcher Strom durch sie fliesst. Dieser Strom (und damit auch die Höhe der Zenerspannung) würde sich aber ändern, wenn ein Verbraucher die Zenerdiode direkt mit seinem Stromfluss belasten würde. Als Folge davon würde die Spannungslage der Zenerdiode je nach Verbraucher leicht schwanken. Durch den Spannungsfolger wird das verhindert, weil der jetzt den vom Verbraucher gezogenen Strom bereitstellt.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger4.png|left]]&lt;br /&gt;
&lt;br /&gt;
Eine weitere Anwendungsmöglichkeit wäre das hochohmige Auskoppeln einer Brückenspannung. Die Brückenschaltung selbst wird durch Folgeschaltungen nicht mehr belastet, alle anderen Eigenschaften bleiben erhalten.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
=== Der Komparator ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-komp-a.png]] [[Bild:Op-komp-b.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der einfachsten Beschaltung des Operationsverstärkers erhält man einen Komparator. Es fällt auf, dass kein Gegenkopplungsnetzwerk vorhanden ist. Der OP arbeitet daher mit seiner vollen Leerlaufverstärkung Vo. Dies bedeutet, dass bereits eine kleine Eingangsspannung genügt, um den OP in die Begrenzung zu treiben. Das heißt, die Ausgangsspannung Ua wird annähernd die Betriebsspannung erreichen.&lt;br /&gt;
&lt;br /&gt;
Beim Komparator gibt es zwei Möglichkeiten der Beschaltung: die invertierende nach a) und die nichtinvertierende Beschaltung nach b). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Berechnungsbeispiel für Schaltung b)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Angenommen die Leerlaufverstärkung Vo von 40000 und eine Eingangsspannung von 0,1 Volt. Die Betriebsspannungen Vcc und Vee legen wir auf +/- 24 V fest. Damit ergibt sich theoretisch für Ua:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a =  V_0 \cdot U_e = 40000 \cdot 0,1\,\mathrm{V} = 4000\,\mathrm{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist natürlich ein unrealistischer Wert, da Ua nicht höher sein kann als die Betriebsspannung. Also anders ausgedrückt: Bei welcher Spannung Ue erreicht der OP seine Aussteuerungsgrenze?&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_e = V_{cc} / V_0 = 24\,\mathrm{V} / 40000 = 0,6\,\mathrm{mV}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, dass eine Spannung von 0,6 mV ausreicht um den Komparator in die Begrenzung zu treiben.&lt;br /&gt;
&lt;br /&gt;
Das gleiche gilt auch für den invertierenden Komparator, allerdings wird hier der OP in die negative Begrenzung gebracht.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = V_0 \cdot U_e&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So hat der Komparator nun einen einstellbaren Schaltpunkt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-komp-c.PNG]]&lt;br /&gt;
&lt;br /&gt;
=== Der Addierer (Summierverstärker) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-addierer.png]]&lt;br /&gt;
&lt;br /&gt;
Ein als invertierender Verstärker beschalteter OP lässt sich so beschalten, dass ein Summensignal aus den Eingangsspannungen gebildet wird. Um die Funktion deutlich zu machen, ist eine Betrachtung der einzelnen Ströme nötig.&lt;br /&gt;
&lt;br /&gt;
In einem invertierenden Verstärker wird sich die Ausgangsspannung immer so einstellen, dass der invertierende Eingang Massepotential hat. Die virtuelle Masse (VM) unterscheidet sich von einer &amp;quot;normalen&amp;quot; Masse dadurch, dass das Potential durch einen Regelungsvorgang zustande kommt. &lt;br /&gt;
An der virtuellen Masse (VM) gilt die Knotenpunktregel, wonach die Summe der zufließenden Ströme gleich der Summe der abfließenden Ströme ist.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_1 + I_2 = -I_3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sofern &amp;lt;math&amp;gt;U_{e1}&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_{e2}&amp;lt;/math&amp;gt; bekannt sind, lässt sich die Gleichung umformen in:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{e1}}{R_1} + \frac{U_{e2}}{R_2} = -\frac{U_a}{R_3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach Ua aufgelöst ergibt sich:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = \left (U_{e1} \cdot \frac{R_3}{R_1}\right ) + \left (U_{e2} \cdot \frac{R_3}{R_2}\right ) + ... + \left (U_{en} \cdot \frac{R_3}{R_n}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Sonderfall gibt es, wenn die Widerstände R1 und R2 gleich sind. Dann gilt&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;R_1 = R_2 = R_x&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
und damit&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = \frac{R_3}{R_x} \cdot (U_{e1} + U_{e2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Der Subtrahierer (Differenzverstärker) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-subtrahierer.png]]&lt;br /&gt;
&lt;br /&gt;
Ein Subtrahierer ist die Zusammenschaltung eines invertierenden und eines nichtinvertierenden Verstärkers. Schliessen wir Punkt Ue1 nach Masse kurz und steuern Ue2 an, arbeitet die Schaltung als nichtinvertierender Verstärker. Wird Ue2 nach Masse verbunden und Ue1 angesteuert, verhält sich die Schaltung als invertierender Verstärker (R7 vorerst nicht beachten).&lt;br /&gt;
&lt;br /&gt;
Für den 1. Fall (nichtinvertierender Verstärker) gilt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} \cdot \left (1 + \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den 2. Fall (invertierender Verstärker) gilt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -U_{e1} \cdot \frac{R_6}{R_4}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der dritte Fall ist die Ansteuerung beider Eingänge:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -U_{e1} \cdot \frac{R_6}{R_4} + U_{e_2} \cdot \left (1 + \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Schaltung ist gut für eine Erklärung, praktisch aber taugt sie nichts. Denn liegen an den Eingängen gleiche Spannungen an, ist die Ausgangsspannung nicht 0, wie eigentlich zu vermuten wäre. Deshalb ändern wir die Schaltung und fügen R7 ein. Jetzt stellt sich am Punkt + des OPs die Spannung&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_{e2+} = U_{e2} \cdot \frac{R_7}{R_5 + R_7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein. Wenn wir das berücksichtigen, erhalten wir endlich einen richtigen Subtrahierer:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} \cdot \left (1 + \frac{R_6}{R_4}\right ) \cdot \frac{R_7}{R_5 + R_7} - \frac{R_6}{R_4} \cdot U_{e1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies gilt für alle Subtrahierer, obwohl es natürlich auch hier wieder zwei Sonderfälle gibt; nämlich a) wenn alle Gegenkopplungswiderstände gleich sind:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;R_6 = R_7 = R_4 = R_5&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dann ist &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} - U_{e1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder b) wenn die Widerstandsverhältnisse gleich sind  :&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{R_6}{R_4} = \frac{R_7}{R_5}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann ergibt sich für Ua:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = \left (U_{e2} \cdot \frac{R_7}{R_5}\right ) - \left (U_{e1} \cdot \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder noch einfacher:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = (U_{e2} - U_{e1}) \cdot \frac{R_6}{R_4}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Addierer/Subtrahierer mit unterschiedlichen Faktoren ===&lt;br /&gt;
Legt man nicht den + sondern den - Eingang des Operationsverstärkers als Bezugspunkt zur Masse mit einem Widerstand fest, übernimmt der Vorwiderstand vom - Eingang, R4 die Aufgabe von R5.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-addsub.png]]&lt;br /&gt;
&lt;br /&gt;
Hier die Schaltung die addieren und subtrahieren kann, mit unterschiedlichen Faktoren.&lt;br /&gt;
Sie kann verwendet werden für Aufgaben wie: Gesucht ist eine Schaltung, die aus 0...2.56 V eine Spannung von -10V...10V macht. Für dieses Beispiel wird hier die Dimensionierung durchgeführt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align: left&amp;quot; &lt;br /&gt;
|&#039;&#039;&#039;Ue1 = 5V&#039;&#039;&#039;&lt;br /&gt;
|Einfach festgelegt, muss nur ein positiver Wert sein&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;R6 = 200k&#039;&#039;&#039;&lt;br /&gt;
|Einfach festgelegt, könnte auch 100k oder 500k sein&lt;br /&gt;
|-&lt;br /&gt;
|Ue2i = 0V, &#039;&#039;&#039;Uai = -10V&#039;&#039;&#039;&lt;br /&gt;
|gewählter momentaner &#039;&#039;Zustand 1&#039;&#039;, Ue2 = 0V ist günstig für Berechnung, Ua ist die dazupassende Ausgangsspannung&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Ue2ii = 2.56V&#039;&#039;&#039;, &#039;&#039;&#039;Uaii = 10V&#039;&#039;&#039;&lt;br /&gt;
|gewählter beliebiger &#039;&#039;Zustand 2&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Um = Ue2&lt;br /&gt;
|Gleichgewicht am Eingang&lt;br /&gt;
|-&lt;br /&gt;
|Ia + Ib = Ic&lt;br /&gt;
|In den Eingang fließt &amp;quot;kein&amp;quot; Strom&lt;br /&gt;
|-&lt;br /&gt;
|(Ua-Ue2)/R6 + (Ue1-Ue2)/R4 = Ue2/Rc&lt;br /&gt;
|Gleichung mit den Unbekannten R4 und Rc&lt;br /&gt;
|-&lt;br /&gt;
|(Uai-Ue2i)/R6 + (Ue1-Ue2i)/R4 = Ue2i/Rc&lt;br /&gt;
|Variablen für &#039;&#039;Zustand 1&#039;&#039; eingesetzt, bildet 1. Gleichung&lt;br /&gt;
|-&lt;br /&gt;
|(Uaii-Ue2ii)/R6 + (Ue1-Ue2ii)/R4 = Ue2ii/Rc&lt;br /&gt;
|Variablen für &#039;&#039;Zustand 2&#039;&#039; eingesetzt, bildet 2. Gleichung&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;R4=-\frac{R6*U_{e1}}{U_{ai}}&amp;lt;/math&amp;gt;&lt;br /&gt;
|2 Gleichungen mit 2 Unbekannten, Lösung durch Umformen der 1. Gleichung nach R4 und einsetzen von Ue2i=0 (freundlicherweise fällt die 2. Unbekannte dabei raus)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;R4 = 100k&#039;&#039;&#039;&lt;br /&gt;
|restliche Werte eingesetzt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;Rc=\frac{R6*U_{e1}*U_{e2ii}}{U_{aii}*U_{e1}-U_{ai}*(U_{e1}-U_{e2ii})-U_{e1}*U_{e2ii}}&amp;lt;/math&amp;gt;&lt;br /&gt;
|Ergebnis für R4 in die 2. Gleichung einsetzen und Umformen nach Rc&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Rc = 41.6k&#039;&#039;&#039;&lt;br /&gt;
|Werte eingesetzt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Der Instrumenten-Verstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Instrument.png]]&lt;br /&gt;
&lt;br /&gt;
Ein Nachteil des Subtrahierer ist sein geringer Eingangswiderstand. Um den nahezu unendlichen Eingangswiderstand des verwendeten Operationsverstärkers zu erreichen, kann man einfach vor beide Eingänge je einen Impedanzwandler vorschalten.&lt;br /&gt;
Die hier beschriebene Schaltung ist um drei Widerstände erweitert und ermöglicht die Einstellung der Differenz-Verstärkung über nur einen Widerstand, nämlich R2.&lt;br /&gt;
&lt;br /&gt;
Am invertierenden Eingang von IC1A gilt (Knotenregel):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{a1}-U_{e1}}{R_1}-\frac{U_{e1}-U_{e2}}{R_2}=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am invertierenden Eingang von IC1C gilt (Knotenregel):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{a2}-U_{e2}}{R_1}+\frac{U_{e1}-U_{e2}}{R_2}=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Subtrahiert man die beiden Gleichungen voneinander, erhält man:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_{a2}-U_{a1}=(U_{e2}-U_{e1})\cdot\left (1+\frac{2\cdot R_1}{R_2}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Letztere Differenz ist die Eingangsspannung eines normalen Subtrahierers mit der Verstärkung 1.&lt;br /&gt;
&lt;br /&gt;
Also ergibt sich als Ausgangsspannung:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a=(U_{e2}-U_{e1}) \cdot \left (1+\frac{2\cdot R_1}{R_2}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Differenzverstärkung beträgt demnach:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V=\left (1+\frac{2\cdot R_1}{R_2} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anwendung: Auswertung von Brückenschaltungen, wie Drucksensoren oder Dehnungsmessstreifen, die durch den Eingangswiderstand der Messschaltung nicht belastet werden dürfen.&amp;lt;br&amp;gt;&lt;br /&gt;
Instrumenten-Verstärker kann man auch fertig kaufen. Im INA102 ist die komplette Schaltung integriert. Für R2 sind 3 verschiedene Werte eingebaut, die bei passender Verschaltung eine Verstärkung von 1, 10, 100 oder 1000 ermöglichen.&lt;br /&gt;
&lt;br /&gt;
=== Der Multiplizierer (Mischer) ===&lt;br /&gt;
&lt;br /&gt;
=== Der Potentialdifferenzverstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:Potentialdifferenzverstärker.png|400px]]&lt;br /&gt;
&lt;br /&gt;
Der Potentialdifferenzverstärker ist eine OPV-Schaltung zum gewichteten Addieren und Subtrahieren beliebiger Spannungen.&lt;br /&gt;
&lt;br /&gt;
Falls die Bedingung &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\sum\frac{R_{0}}{R_{i}}=\sum\frac{R_{0}^{&#039;}}{R_{i}^{&#039;}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
erfüllt ist, vereinfacht sich der Term für die Ausgangsspannung zu folgendem Term:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
U_{a}=\sum\frac{R_{0}^{&#039;}}{R_{i}^{&#039;}}U_{i}^{&#039;}-\sum\frac{R_{0}}{R_{i}}U_{i}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Der Logarithmierer ===&lt;br /&gt;
Logarithmierer werden mit der Kennlinie einer Diode konstruiert, die einen eingeprägten Strom in eine Spannung übersetzt.&lt;br /&gt;
&lt;br /&gt;
= Spannungsversorgung und Beschaltung =&lt;br /&gt;
&lt;br /&gt;
== Betrieb mit einfacher Versorgungsspannung ==&lt;br /&gt;
&lt;br /&gt;
Häufig möchte man Wechselspannung (z. B. Audiosignale) die auch negative Spannungen enthält mit einem Opamp verstärken, hat aber nur eine einfache Versorgungsspannung, eine positive in Bezug zu Masse, zur Verfügung. Dafür bieten sich folgende Schaltungen an, die in der Literatur leider häufig vernachlässigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Nichtinvertierender Verstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Bild: Ss_opamp1.png]]&lt;br /&gt;
&lt;br /&gt;
Der positive Eingang wird mit einem Spannungsteiler (R3 und R5) auf die halbe Betriebsspannung gelegt. Dieser Spannung wird dann die zu verstärkende Eingangswechselspannung überlagert. Mit den Kondensatoren am Eingang (C1) und am Ausgang (C2) wird der Gleichspannungsanteil abgekoppelt.&lt;br /&gt;
&lt;br /&gt;
Die Verstärkung ist in diesem Beispiel für Wechselspannung 11 (Formel wie oben), für Gleichspannung aber 1, da C4 für Gleichspannung einen unendlichen Widerstand darstellt. C3 sollte dorthin gehen, wo das Eingangssignal seinen Bezugspunkt hat, also die Abschirmung der Cinch-Buchse, während R5 dorthin geht, wo der Operationsverstärker seine negative Versorgungsspannung her bekommt, falls das nicht die gleichen Potentiale, hier GND, sein sollten.&lt;br /&gt;
&lt;br /&gt;
=== Invertierender Verstärker ===&lt;br /&gt;
&lt;br /&gt;
Das Prinzip funktioniert analog auch für die invertierende Beschaltung:&lt;br /&gt;
&lt;br /&gt;
[[Bild: Ss_opamp2.png]]&lt;br /&gt;
&lt;br /&gt;
= Kaufempfehlung =&lt;br /&gt;
LM 158/258/358 2 OPs in einem Gehäuse Preis ca. 0,30€.&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[Standardbauelemente#Operationsverst.C3.A4rker|Standardbauelemente - Operationsverstärker]].&lt;br /&gt;
&lt;br /&gt;
Wer Audio OpAmps sucht - tangentsoft.net hat mal welche unter die Lupe genommen: [http://www.tangentsoft.net/audio/opamps.html Notes on Audio OpAmps]&lt;br /&gt;
&lt;br /&gt;
= Weblinks =&lt;br /&gt;
*[http://www.eetkorea.com/ARTICLES/2003SEP/A/2003SEP19_AMD_AN07.PDF Op Amp Circuit Collection] - National Semiconductor Application Note 31 mit vielen weiteren OP-Schaltungen&lt;br /&gt;
*[http://www-s.ti.com/sc/psheets/slod006b/slod006b.pdf Op Amps for Everyone] - englischsprachiges, sehr umfangreiches Dokument zu OPV und deren Anwendung&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0209092.htm Operationsverstärker im ElKo]&lt;br /&gt;
*[http://www.inf.fu-berlin.de/lehre/WS00/peg/folien/Peg_v7a.pdf OP Teil 1], [http://www.inf.fu-berlin.de/lehre/WS00/peg/folien/Peg_v7b.pdf OP Teil 2] - OP-Schaltungen (deutsch)&lt;br /&gt;
*[http://www.blecken.de/download/opverst.zip Schaltungstechnik mit Operationsverstärkern] - Prof. K. Blecken, Skript zur Vorlesung (deutsch, *.doc-Format)&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Operationsverst%C3%A4rker RN-Wissen Operationsverstärker]&lt;br /&gt;
* [http://www.national.com/AU/design/0,4706,268_0_,00.html Online Seminar] von National Semiconductor&lt;br /&gt;
* [http://www.franzis.de/elo-das-magazin/grundlagen-und-ausbildung/operationsverstaerker/der-operationsverstaerker ELO-Online-Magazin, Franzis-Verlag], [http://www.franzis.de/online-shop/elektronik/lernpakete-elektronik/lernpaket-elektronik-mit-ics Lernpaket Elektronik mit ICs] &amp;quot;Elektronische Experimente mit integriertem Schaltkreis&amp;quot;, Kasten mit Steckbrett/Bauelementen (ca. 40EUR), &lt;br /&gt;
* [[Schmitt-Trigger]]&lt;br /&gt;
* [[Aktiver RC-Bandpass|Aktiver RC-Bandpass auf Operationsverstärker-Basis]]&lt;br /&gt;
* [http://sound.westhost.com/appnotes/an001.htm Präzisionsgleichrichter], engl.&lt;br /&gt;
* [http://www.elektronikwissen.net/opamp/9-opamp-wissen.html OpAmp Praxis], Praktikertipps + schwingende Operationsverstärker in den Griff bekommen&lt;br /&gt;
&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Operationsverst%C3%A4rker-Grundschaltungen&amp;diff=85052</id>
		<title>Operationsverstärker-Grundschaltungen</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Operationsverst%C3%A4rker-Grundschaltungen&amp;diff=85052"/>
		<updated>2014-09-29T13:02:33Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Idealisiertes Modell eines OPV==&lt;br /&gt;
&lt;br /&gt;
=== Anschlüsse ===&lt;br /&gt;
Ein Operationsverstärker hat zwei Eingänge (+) und (-) und einen Ausgang&lt;br /&gt;
(UA). Außerdem verfügt er über eine positive und eine negative&lt;br /&gt;
Spannungsversorgung (V+) und (V-).&lt;br /&gt;
&lt;br /&gt;
=== Spannungsversorgungen ===&lt;br /&gt;
Die Spannungsversorgungen sollen zunächst nicht interessieren. Sie&lt;br /&gt;
werden in Schaltungen oft nicht eingezeichnet. &lt;br /&gt;
In der Praxis ist es jedoch wichtig zu wissen, dass die Ausgangsspannung immer zwischen (V+) und (V-) liegt. Die Ausgangsspannung des OPV kommt schließlich  dadurch zustande, dass der Ausgang über einen Transistor mehr oder weniger hochohmig mit den beiden Versorgungsspannungen verbunden wird.&lt;br /&gt;
&lt;br /&gt;
Wenn man einen OPV also mit +5V versorgt, so kann der OPV im besten Fall am Ausgang +5V erzeugen. Man würde in diesem Fall von einem &amp;quot;Rail-to-Rail&amp;quot; Operationsverstärker sprechen.&lt;br /&gt;
Bei vielen Operationsverstärkern ist die maximal mögliche Ausgangsspannung geringer als die Versorgungsspannung. Ein mit +5V Spannungsversorgung beschalteter OPV kann dann beispielsweise nur +4V Ausgangsspannung erzeugen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    | \&lt;br /&gt;
    |  \&lt;br /&gt;
 -- | - \&lt;br /&gt;
    |    \_______&lt;br /&gt;
    |    /&lt;br /&gt;
 -- | + /&lt;br /&gt;
    |  /&lt;br /&gt;
    | /&lt;br /&gt;
&lt;br /&gt;
=== Ausgang ===&lt;br /&gt;
Der Ausgang des OPV ist eine ideale Spannungsquelle. Das bedeutet, dass die&lt;br /&gt;
Ausgangsspannung unabhängig davon ist, was ausgangsseitig an den OPV&lt;br /&gt;
angeschlossen wird.&lt;br /&gt;
In der Praxis gilt dieses Modell häufig nur bei &amp;quot;sinnvollen Anwendungen&amp;quot;. So ist beispielsweise der Ausgangsstrom des OPV nach oben begrenzt (typischerweise im mA-Bereich), und manche OPV schwingen sehr leicht, wenn man sie kapazitiv belastet.&lt;br /&gt;
&lt;br /&gt;
=== Eingänge ===&lt;br /&gt;
Die Eingänge eines OPV sind hochohmig, d. h., es handelt sich nur um &amp;quot;Messfühler&amp;quot;, die keinen Strom führen.&lt;br /&gt;
Achtung: Die Eingangsschutzbeschaltung (Dioden von GND und gegen VCC) bei manchen OPVs kann jedoch dazu führen, dass Strom in den Eingang fliesst, wenn dessen Betriebsspannung z.B. abgeschaltet ist.&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
Der OPV mißt zu jeder Zeit die Differenz &amp;lt;math&amp;gt;U_D = U(+) - U(-)&amp;lt;/math&amp;gt; der&lt;br /&gt;
Eingangsspannungen.&lt;br /&gt;
&lt;br /&gt;
Ist die Spannung an (+) größer als an (-), so erhöht der OPV die&lt;br /&gt;
Ausgangsspannung.&lt;br /&gt;
Ist die Spannung an (+) niedriger als an (-), so vermindert der OPV die&lt;br /&gt;
Ausgangsspannung.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis dieses Vorgangs wird häufig über die Gleichung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_a = v \cdot U_D&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
beschrieben, wobei &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; eine sehr große Zahl (10^4...10^6) ist. &lt;br /&gt;
&lt;br /&gt;
Mit Hilfe der beschriebenen Funktionsweise lassen sich alle grundlegenden Schaltungen herleiten.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Betrachtet wird die invertierende Grundschaltung nach Abbildung a) im Abschnitt [[Operationsverstärker-Grundschaltungen#Verstärkergrundschaltungen|Verstärkergrundschaltungen]].&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-verstaerker-a.png]]&lt;br /&gt;
&lt;br /&gt;
Für die Pfeilrichtungen der Spannungen und Ströme gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt;: von oben nach unten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{R3}&amp;lt;/math&amp;gt;: von links nach rechts&lt;br /&gt;
&lt;br /&gt;
Die Spannung am (+)Eingang ist gleich Null. Die Spannung am (-)Eingang wird durch die Spannungsquelle &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; und durch die im OPV befindliche und mit dem Ausgang verbundene Spannungsquelle des OPV manipuliert.&lt;br /&gt;
&lt;br /&gt;
* Ist die Spannung am (-)Eingang negativ, so erhöht der Operationsverstärker die Ausgangsspannung. Dadurch wird auch die Spannung am (-)Eingang positiver. Und zwar so lange, bis die Spannung am (-)Eingang gleich groß ist, wie die Spannung am (+)Eingang, also U(-)=0V.&lt;br /&gt;
* Ist die Spannung am (-)Eingang positiv, so vermindert der Operationsverstärker die Ausgangsspannung. Dadurch wird auch die Spannung am (-)Eingang negativer. Und zwar so lange, bis die Spannung am (-)Eingang gleich groß ist, wie die Spannung am (+)Eingang, also U(-)=0V.&lt;br /&gt;
&lt;br /&gt;
Der Operationsverstärker wird also die Spannungen an (+) und (-)&lt;br /&gt;
angleichen. Das passiert immer dann, wenn der Ausgang mit dem (-)Eingang&lt;br /&gt;
verbunden ist. Man nennt das Prinzip &amp;quot;Gegenkopplung&amp;quot;. Auf diese Art und&lt;br /&gt;
Weise funktionieren alle analogen OPV-Schaltungen.&lt;br /&gt;
&lt;br /&gt;
Da an (+) Massepotential anliegt, wird somit auch (-) daran angeglichen, und so liegt an &amp;lt;math&amp;gt;R_3&amp;lt;/math&amp;gt; die Spannung &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; an. Daher gilt: &lt;br /&gt;
&amp;lt;math&amp;gt;I_{R3}=\frac{U_e}{R_3}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da der (-)Eingang hochohmig ist, fließt &amp;lt;math&amp;gt;I_{R3}&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;R_4&amp;lt;/math&amp;gt; weiter zum OPV-Ausgang.&lt;br /&gt;
&lt;br /&gt;
Ua ist die Spannung vom Ausgang zur (virtuellen) Masse am (-)Eingang.&lt;br /&gt;
(--&amp;gt; Pfeil einzeichnen und klarmachen, daß es egal ist, ob der Pfeil vom&lt;br /&gt;
Ausgang zur Masse geht oder vom Ausgang &amp;quot;entgegen der Stromrichtung&amp;quot; zur&lt;br /&gt;
virtuellen Masse an (-)!)&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe von &amp;lt;math&amp;gt;I_{R3}=\frac{U_e}{R_3}&amp;lt;/math&amp;gt; ergibt sich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_a = -R_4 \cdot I_{R3} = -{{R_4} \over {R_3}} \cdot U_e.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Energie für den Stromtransport über &amp;lt;math&amp;gt;R_4&amp;lt;/math&amp;gt; stammt vom OPV! Sobald die Ladungen ausgehend von der Spannungsquelle &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; die virtuelle Masse an (-) erreicht haben, hat &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; seine gesamte Energie abgegeben.&lt;br /&gt;
&lt;br /&gt;
== Reale OPs / Kennwerte ==&lt;br /&gt;
Abweichend vom idealen OP besitzen reale OPs diverse Einschränkungen und Kennwerte, die sie für verschiedene Einsätze mehr oder weniger prädestinieren.&lt;br /&gt;
&lt;br /&gt;
=== Leerlaufverstärkung ===&lt;br /&gt;
Die Leerlaufverstärkung gibt an, wie stark sich das Ausgangssignal i.A. der Änderung eines Eingangsignals statisch ändert, bzw nach dem Einschwingen erreichen könnte, wenn es nicht durch die Betriebsgrenzen limitiert wäre.&lt;br /&gt;
&lt;br /&gt;
=== Verstärkungs-Bandbreiteprodukt ===&lt;br /&gt;
Das Verstärkungs-Bandbreiteprodukt gibt an, bei welcher Verstärkung welche Bandbreite erreicht werden kann. Durch Rückkopplung kann die Verstärkung eingestellt werden. Bei kleinerer Verstärkung ergibt sich somit eine höhere Bandbreite, wenn das Produkt aus beiden konstant ist. Die Bandbreite bei der Verstärkung eins heißt Transitfrequenz. Das Verstärkungs-Bandbreiteprodukt ist entscheidend für das Kleinsignalverhalten.&lt;br /&gt;
&lt;br /&gt;
=== Anstiegszeit ===&lt;br /&gt;
Bestimmend für das Großsignalverhalten ist neben dem Verstärkungs-Bandbreiteprodukt die Anstiegszeit (slew rate), da bei hohen Ausgangsamplituden die Ausgangskurve eventuell zu steil wird, um richtig wiedergegeben zu werden.&lt;br /&gt;
&lt;br /&gt;
=== Gleichtaktverstärkung ===&lt;br /&gt;
Infolge des inhomogenen Aufbaus der internen Verstärkerstufen werden die beiden Eingangssignale nicht exakt gleich verstärkt, was in einen Gleichanteil und einen Differenzanteil aufgeteilt werden kann. Der nicht erwünschte Gleichtakt (engl. &#039;&#039;common mode&#039;&#039;) ist dabei ein Maß für die Qualität des OP.&lt;br /&gt;
&lt;br /&gt;
== Verstärkergrundschaltungen ==&lt;br /&gt;
=== Grundbeschaltung mit Berechnung ===&lt;br /&gt;
[[Bild:Op-verstaerker-a.png]] [[Bild:Op-verstaerker-b.png]]&lt;br /&gt;
&lt;br /&gt;
In a) und b) verwenden wir den OP als Verstärker und nutzen hier die Möglichkeit der Gegenkopplung, um definierte Verstärkungen zu erhalten. Wir gehen wieder davon aus, dass der OP ein ideales Bauteil ist und daher seine Leerlaufverstärkung unendlich ist. Ebenso betrachten wir den Eingangswiderstand als unendlich.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;a)&#039;&#039;&#039; ist ein invertierender Verstärker mit einem OP dargestellt. Durch die Widerstände R3 und R4 wird die Verstärkung bestimmt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = \frac{U_a}{U_e} = -\frac{R_4}{R_3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Verhältnis der beiden Widerstände bestimmt also die Verstärkung und somit die Ausgangsspannung:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -\frac{R_4}{R_3} \cdot U_e &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder auch&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = V \cdot U_e&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das negative Vorzeichen drückt aus, dass es sich um einen invertierenden Verstärker handelt.&lt;br /&gt;
&lt;br /&gt;
Beim nichtinvertierenden Verstärker &#039;&#039;&#039;b)&#039;&#039;&#039; finden wir auch eine Rückkopplung über R6 zum invertierenden Eingang des OP. Die Verstärkung wird durch das Gegenkopplungsnetzwerk R6 und R7 bestimmt. Hier ist:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = 1 + \frac{R6}{R7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Verstärkung von 1 ist sinnvoll, wenn eingangsseitig eine Spannungsquelle mit hohem Innenwiderstand verwendet wird. Für &amp;lt;math&amp;gt;\frac{R6}{R7} \to 0&amp;lt;/math&amp;gt; heißt die Schaltung &amp;quot;Spannungsfolger&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Kleinere Werte als 1 lassen sich nicht realisieren. Die Ausgangsspannung errechnet sich also so:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_e \cdot \left (1 + \frac{R_6}{R_7}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Eine Eingangsspannung von 0,5 V soll auf den Wert 5 V verstärkt werden, es ist also eine Verstärkung V von 10 benötigt. R7 ist mit 10 k&amp;amp;Omega; vorgegeben. Also ist das Verhältnis&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{R_6}{R_7} = V - 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einem Wert von 10 k&amp;amp;Omega; für R7 errechnet sich R6 zu&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
R6 = (V - 1) \cdot R7&lt;br /&gt;
   = (10 - 1) \cdot 10\,\mathrm{k\Omega}&lt;br /&gt;
   = 90\,\mathrm{k\Omega}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgangsspannung Ua wird also:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_a =   U_e \cdot \left (1 + \frac{R_6}{R_7}\right )&lt;br /&gt;
   = 0,5\,\mathrm{V} \cdot \left (1 + \frac{90\,\mathrm{k\Omega}}{10\,\mathrm{k\Omega}}\right)&lt;br /&gt;
   = 5\,\mathrm{V}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nichtinvertierender Verstärker mit Offset ===&lt;br /&gt;
Eine Abwandlung des nichtinvertierenden Verstärkers erlaubt es, einen konstanten Offset vorzugeben. D.h. von der zu verstärkenden Eingangsspannung U(e) wird eine konstante Spannung U(o) abgezogen und die Differenz verstärkt. Auf der Ausgangsspannung U(a) findet sich die Offsetspannung U(o) allerdings wieder.&lt;br /&gt;
[[Bild:Op-verstaerker-offset.png]]&lt;br /&gt;
&lt;br /&gt;
Es gilt:&lt;br /&gt;
&lt;br /&gt;
Offsetspannung: &lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_o =  U_V \cdot \frac{R2}{R1 + R2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
mit U(V) = Versorgungsspannung an R1&lt;br /&gt;
&lt;br /&gt;
Verstärkung:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
V = 1 + \frac{R3}{\frac{R1 \cdot R2}{R1 + R2}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausgangsspannung:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_a = (U_e - U_o) \cdot V + U_o&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Vorteil dieser Schaltung ist, daß nur die Differenz verstärkt wird. Damit kann eine größere Verstärkung gewählt werden. Zu berücksichtigen ist dabei, daß die Ausgangsspannung U(a) um die Offsetspannung U(o) überlagert ist.&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfolger (Impedanzwandler) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger1.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Abart des nichtinvertierenden Verstärkers stellt der Spannungsfolger dar. Beim nichtinvertierenden Verstärker errechnet sich die Ausgangsspannung aus:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_e \cdot \left (1 + \frac{R_2}{R_1}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir R2 auf 0Ω (mit R1 &amp;gt; 0) oder R1 auf unendlich (mit R2 &amp;lt; ∞) ändern, erhalten wir daher:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = 1 + \frac{R_2}{R_1} = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Spannungsfolger hat also eine Verstärkung V von 1.&lt;br /&gt;
&lt;br /&gt;
Umgezeichnet sieht die Schaltung so aus: &lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger2.png]]&lt;br /&gt;
&lt;br /&gt;
Was soll das nun? Wir nutzen die Eigenschaft, dass ein idealer OP einen unendlichen Eingangswiderstand und einen Ausgangswiderstand von 0Ω hat. Real sieht das natürlich anders aus: so liegt der Eingangswiderstand Re bei normalen OPs in der Größenordnung von 1MOhm bis &amp;lt;math&amp;gt;10^{15} \Omega&amp;lt;/math&amp;gt;, der Ausgangswiderstand Ra im Bereich 20Ω bis 1kOhm. Deshalb spricht man bei dieser Schaltung von einem Impedanzwandler. Eine solche Schaltung kann also aus einer relativ hochohmigen Spannungsquelle eine niederohmige, durch Folgeschaltungen belastbare Spannungsquelle machen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger3.png|left]]&lt;br /&gt;
&lt;br /&gt;
In dem nebenstehenden Beispiel ist eine einfache Möglichkeit zur Erzeugung einer Referenzspannung gezeigt. Es kommt eine normale Stabilisierungsschaltung mit einer Zenerdiode zur Anwendung, die aber nicht mehr die schlechten Eigenschaften der Standardbeschaltung mit lediglich Widerstand und Zenerdiode  hat. Bei einer Zenerdiode hängt die genaue Spannung davon ab, welcher Strom durch sie fliesst. Dieser Strom (und damit auch die Höhe der Zenerspannung) würde sich aber ändern, wenn ein Verbraucher die Zenerdiode direkt mit seinem Stromfluss belasten würde. Als Folge davon würde die Spannungslage der Zenerdiode je nach Verbraucher leicht schwanken. Durch den Spannungsfolger wird das verhindert, weil der jetzt den vom Verbraucher gezogenen Strom bereitstellt.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger4.png|left]]&lt;br /&gt;
&lt;br /&gt;
Eine weitere Anwendungsmöglichkeit wäre das hochohmige Auskoppeln einer Brückenspannung. Die Brückenschaltung selbst wird durch Folgeschaltungen nicht mehr belastet, alle anderen Eigenschaften bleiben erhalten.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
=== Der Komparator ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-komp-a.png]] [[Bild:Op-komp-b.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der einfachsten Beschaltung des Operationsverstärkers erhält man einen Komparator. Es fällt auf, dass kein Gegenkopplungsnetzwerk vorhanden ist. Der OP arbeitet daher mit seiner vollen Leerlaufverstärkung Vo. Dies bedeutet, dass bereits eine kleine Eingangsspannung genügt, um den OP in die Begrenzung zu treiben. Das heißt, die Ausgangsspannung Ua wird annähernd die Betriebsspannung erreichen.&lt;br /&gt;
&lt;br /&gt;
Beim Komparator gibt es zwei Möglichkeiten der Beschaltung: die invertierende nach a) und die nichtinvertierende Beschaltung nach b). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Berechnungsbeispiel für Schaltung b)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Angenommen die Leerlaufverstärkung Vo von 40000 und eine Eingangsspannung von 0,1 Volt. Die Betriebsspannungen Vcc und Vee legen wir auf +/- 24 V fest. Damit ergibt sich theoretisch für Ua:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a =  V_0 \cdot U_e = 40000 \cdot 0,1\,\mathrm{V} = 4000\,\mathrm{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist natürlich ein unrealistischer Wert, da Ua nicht höher sein kann als die Betriebsspannung. Also anders ausgedrückt: Bei welcher Spannung Ue erreicht der OP seine Aussteuerungsgrenze?&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_e = V_{cc} / V_0 = 24\,\mathrm{V} / 40000 = 0,6\,\mathrm{mV}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, dass eine Spannung von 0,6 mV ausreicht um den Komparator in die Begrenzung zu treiben.&lt;br /&gt;
&lt;br /&gt;
Das gleiche gilt auch für den invertierenden Komparator, allerdings wird hier der OP in die negative Begrenzung gebracht.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = V_0 \cdot U_e&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So hat der Komparator nun einen einstellbaren Schaltpunkt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-komp-c.PNG]]&lt;br /&gt;
&lt;br /&gt;
=== Der Addierer (Summierverstärker) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-addierer.png]]&lt;br /&gt;
&lt;br /&gt;
Ein als invertierender Verstärker beschalteter OP lässt sich so beschalten, dass ein Summensignal aus den Eingangsspannungen gebildet wird. Um die Funktion deutlich zu machen, ist eine Betrachtung der einzelnen Ströme nötig.&lt;br /&gt;
&lt;br /&gt;
In einem invertierenden Verstärker wird sich die Ausgangsspannung immer so einstellen, dass der invertierende Eingang Massepotential hat. Die virtuelle Masse (VM) unterscheidet sich von einer &amp;quot;normalen&amp;quot; Masse dadurch, dass das Potential durch einen Regelungsvorgang zustande kommt. &lt;br /&gt;
An der virtuellen Masse (VM) gilt die Knotenpunktregel, wonach die Summe der zufließenden Ströme gleich der Summe der abfließenden Ströme ist.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_1 + I_2 = -I_3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sofern &amp;lt;math&amp;gt;U_{e1}&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_{e2}&amp;lt;/math&amp;gt; bekannt sind, lässt sich die Gleichung umformen in:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{e1}}{R_1} + \frac{U_{e2}}{R_2} = -\frac{U_a}{R_3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach Ua aufgelöst ergibt sich:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = \left (U_{e1} \cdot \frac{R_3}{R_1}\right ) + \left (U_{e2} \cdot \frac{R_3}{R_2}\right ) + ... + \left (U_{en} \cdot \frac{R_3}{R_n}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Sonderfall gibt es, wenn die Widerstände R1 und R2 gleich sind. Dann gilt&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;R_1 = R_2 = R_x&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
und damit&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = \frac{R_3}{R_x} \cdot (U_{e1} + U_{e2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Der Subtrahierer (Differenzverstärker) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-subtrahierer.png]]&lt;br /&gt;
&lt;br /&gt;
Ein Subtrahierer ist die Zusammenschaltung eines invertierenden und eines nichtinvertierenden Verstärkers. Schliessen wir Punkt Ue1 nach Masse kurz und steuern Ue2 an, arbeitet die Schaltung als nichtinvertierender Verstärker. Wird Ue2 nach Masse verbunden und Ue1 angesteuert, verhält sich die Schaltung als invertierender Verstärker (R7 vorerst nicht beachten).&lt;br /&gt;
&lt;br /&gt;
Für den 1. Fall (nichtinvertierender Verstärker) gilt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} \cdot \left (1 + \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den 2. Fall (invertierender Verstärker) gilt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -U_{e1} \cdot \frac{R_6}{R_4}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der dritte Fall ist die Ansteuerung beider Eingänge:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -U_{e1} \cdot \frac{R_6}{R_4} + U_{e_2} \cdot \left (1 + \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Schaltung ist gut für eine Erklärung, praktisch aber taugt sie nichts. Denn liegen an den Eingängen gleiche Spannungen an, ist die Ausgangsspannung nicht 0, wie eigentlich zu vermuten wäre. Deshalb ändern wir die Schaltung und fügen R7 ein. Jetzt stellt sich am Punkt + des OPs die Spannung&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_{e2+} = U_{e2} \cdot \frac{R_7}{R_5 + R_7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein. Wenn wir das berücksichtigen, erhalten wir endlich einen richtigen Subtrahierer:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} \cdot \left (1 + \frac{R_6}{R_4}\right ) \cdot \frac{R_7}{R_5 + R_7} - \frac{R_6}{R_4} \cdot U_{e1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies gilt für alle Subtrahierer, obwohl es natürlich auch hier wieder zwei Sonderfälle gibt; nämlich a) wenn alle Gegenkopplungswiderstände gleich sind:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;R_6 = R_7 = R_4 = R_5&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dann ist &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} - U_{e1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder b) wenn die Widerstandsverhältnisse gleich sind  :&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{R_6}{R_4} = \frac{R_7}{R_5}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann ergibt sich für Ua:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = \left (U_{e2} \cdot \frac{R_7}{R_5}\right ) - \left (U_{e1} \cdot \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder noch einfacher:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = (U_{e2} - U_{e1}) \cdot \frac{R_6}{R_4}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Addierer/Subtrahierer mit unterschiedlichen Faktoren ===&lt;br /&gt;
Legt man nicht den + sondern den - Eingang des Operationsverstärkers als Bezugspunkt zur Masse mit einem Widerstand fest, übernimmt der Vorwiderstand vom - Eingang, R4 die Aufgabe von R5.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-addsub.png]]&lt;br /&gt;
&lt;br /&gt;
Hier die Schaltung die addieren und subtrahieren kann, mit unterschiedlichen Faktoren.&lt;br /&gt;
Sie kann verwendet werden für Aufgaben wie: Gesucht ist eine Schaltung, die aus 0...2.56 V eine Spannung von -10V...10V macht. Für dieses Beispiel wird hier die Dimensionierung durchgeführt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align: left&amp;quot; &lt;br /&gt;
|&#039;&#039;&#039;Ue1 = 5V&#039;&#039;&#039;&lt;br /&gt;
|Einfach festgelegt, muss nur ein positiver Wert sein&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;R6 = 200k&#039;&#039;&#039;&lt;br /&gt;
|Einfach festgelegt, könnte auch 100k oder 500k sein&lt;br /&gt;
|-&lt;br /&gt;
|Ue2i = 0V, &#039;&#039;&#039;Uai = -10V&#039;&#039;&#039;&lt;br /&gt;
|gewählter momentaner &#039;&#039;Zustand 1&#039;&#039;, Ue2 = 0V ist günstig für Berechnung, Ua ist die dazupassende Ausgangsspannung&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Ue2ii = 2.56V&#039;&#039;&#039;, &#039;&#039;&#039;Uaii = 10V&#039;&#039;&#039;&lt;br /&gt;
|gewählter beliebiger &#039;&#039;Zustand 2&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Um = Ue2&lt;br /&gt;
|Gleichgewicht am Eingang&lt;br /&gt;
|-&lt;br /&gt;
|Ia + Ib = Ic&lt;br /&gt;
|In den Eingang fließt &amp;quot;kein&amp;quot; Strom&lt;br /&gt;
|-&lt;br /&gt;
|(Ua-Ue2)/R6 + (Ue1-Ue2)/R4 = Ue2/Rc&lt;br /&gt;
|Gleichung mit den Unbekannten R4 und Rc&lt;br /&gt;
|-&lt;br /&gt;
|(Uai-Ue2i)/R6 + (Ue1-Ue2i)/R4 = Ue2i/Rc&lt;br /&gt;
|Variablen für &#039;&#039;Zustand 1&#039;&#039; eingesetzt, bildet 1. Gleichung&lt;br /&gt;
|-&lt;br /&gt;
|(Uaii-Ue2ii)/R6 + (Ue1-Ue2ii)/R4 = Ue2ii/Rc&lt;br /&gt;
|Variablen für &#039;&#039;Zustand 2&#039;&#039; eingesetzt, bildet 2. Gleichung&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;R4=-\frac{R6*U_{e1}}{U_{ai}}&amp;lt;/math&amp;gt;&lt;br /&gt;
|2 Gleichungen mit 2 Unbekannten, Lösung durch Umformen der 1. Gleichung nach R4 und einsetzen von Ue2i=0 (freundlicherweise fällt die 2. Unbekannte dabei raus)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;R4 = 100k&#039;&#039;&#039;&lt;br /&gt;
|restliche Werte eingesetzt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;Rc=\frac{R6*U_{e1}*U_{e2ii}}{U_{aii}*U_{e1}-U_{ai}*(U_{e1}-U_{e2ii})-U_{e1}*U_{e2ii}}&amp;lt;/math&amp;gt;&lt;br /&gt;
|Ergebnis für R4 in die 2. Gleichung einsetzen und Umformen nach Rc&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Rc = 41.6k&#039;&#039;&#039;&lt;br /&gt;
|Werte eingesetzt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Der Instrumenten-Verstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Instrument.png]]&lt;br /&gt;
&lt;br /&gt;
Ein Nachteil des Subtrahierer ist sein geringer Eingangswiderstand. Um den nahezu unendlichen Eingangswiderstand des verwendeten Operationsverstärkers zu erreichen, kann man einfach vor beide Eingänge je einen Impedanzwandler vorschalten.&lt;br /&gt;
Die hier beschriebene Schaltung ist um drei Widerstände erweitert und ermöglicht die Einstellung der Differenz-Verstärkung über nur einen Widerstand, nämlich R2.&lt;br /&gt;
&lt;br /&gt;
Am invertierenden Eingang von IC1A gilt (Knotenregel):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{a1}-U_{e1}}{R_1}-\frac{U_{e1}-U_{e2}}{R_2}=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am invertierenden Eingang von IC1C gilt (Knotenregel):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{a2}-U_{e2}}{R_1}+\frac{U_{e1}-U_{e2}}{R_2}=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Subtrahiert man die beiden Gleichungen voneinander, erhält man:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_{a2}-U_{a1}=(U_{e2}-U_{e1})\cdot\left (1+\frac{2\cdot R_1}{R_2}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Letztere Differenz ist die Eingangsspannung eines normalen Subtrahierers mit der Verstärkung 1.&lt;br /&gt;
&lt;br /&gt;
Also ergibt sich als Ausgangsspannung:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a=(U_{e2}-U_{e1}) \cdot \left (1+\frac{2\cdot R_1}{R_2}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Differenzverstärkung beträgt demnach:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V=\left (1+\frac{2\cdot R_1}{R_2} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anwendung: Auswertung von Brückenschaltungen, wie Drucksensoren oder Dehnungsmessstreifen, die durch den Eingangswiderstand der Messschaltung nicht belastet werden dürfen.&amp;lt;br&amp;gt;&lt;br /&gt;
Instrumenten-Verstärker kann man auch fertig kaufen. Im INA102 ist die komplette Schaltung integriert. Für R2 sind 3 verschiedene Werte eingebaut, die bei passender Verschaltung eine Verstärkung von 1, 10, 100 oder 1000 ermöglichen.&lt;br /&gt;
&lt;br /&gt;
=== Der Multiplizierer (Mischer) ===&lt;br /&gt;
&lt;br /&gt;
=== Der Potentialdifferenzverstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:Potentialdifferenzverstärker.png|400px]]&lt;br /&gt;
&lt;br /&gt;
Der Potentialdifferenzverstärker ist eine OPV-Schaltung zum gewichteten Addieren und Subtrahieren beliebiger Spannungen.&lt;br /&gt;
&lt;br /&gt;
Falls die Bedingung &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\sum\frac{R_{0}}{R_{i}}=\sum\frac{R_{0}^{&#039;}}{R_{i}^{&#039;}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
erfüllt ist, vereinfacht sich der Term für die Ausgangsspannung zu folgendem Term:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
U_{a}=\sum\frac{R_{0}^{&#039;}}{R_{i}^{&#039;}}U_{i}^{&#039;}-\sum\frac{R_{0}}{R_{i}}U_{i}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Der Logarithmierer ===&lt;br /&gt;
Logarithmierer werden mit der Kennlinie einer Diode konstruiert, die einen eingeprägten Strom in eine Spannung übersetzt.&lt;br /&gt;
&lt;br /&gt;
= Spannungsversorgung und Beschaltung =&lt;br /&gt;
&lt;br /&gt;
== Betrieb mit einfacher Versorgungsspannung ==&lt;br /&gt;
&lt;br /&gt;
Häufig möchte man Wechselspannung (z. B. Audiosignale) die auch negative Spannungen enthält mit einem Opamp verstärken, hat aber nur eine einfache Versorgungsspannung, eine positive in Bezug zu Masse, zur Verfügung. Dafür bieten sich folgende Schaltungen an, die in der Literatur leider häufig vernachlässigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Nichtinvertierender Verstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Bild: Ss_opamp1.png]]&lt;br /&gt;
&lt;br /&gt;
Der positive Eingang wird mit einem Spannungsteiler (R3 und R5) auf die halbe Betriebsspannung gelegt. Dieser Spannung wird dann die zu verstärkende Eingangswechselspannung überlagert. Mit den Kondensatoren am Eingang (C1) und am Ausgang (C2) wird der Gleichspannungsanteil abgekoppelt.&lt;br /&gt;
&lt;br /&gt;
Die Verstärkung ist in diesem Beispiel für Wechselspannung 11 (Formel wie oben), für Gleichspannung aber 1, da C4 für Gleichspannung einen unendlichen Widerstand darstellt. C3 sollte dorthin gehen, wo das Eingangssignal seinen Bezugspunkt hat, also die Abschirmung der Cinch-Buchse, während R5 dorthin geht, wo der Operationsverstärker seine negative Versorgungsspannung her bekommt, falls das nicht die gleichen Potentiale, hier GND, sein sollten.&lt;br /&gt;
&lt;br /&gt;
=== Invertierender Verstärker ===&lt;br /&gt;
&lt;br /&gt;
Das Prinzip funktioniert analog auch für die invertierende Beschaltung:&lt;br /&gt;
&lt;br /&gt;
[[Bild: Ss_opamp2.png]]&lt;br /&gt;
&lt;br /&gt;
= Kaufempfehlung =&lt;br /&gt;
LM 158/258/358 2 OPs in einem Gehäuse Preis ca. 0,30€.&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[Standardbauelemente#Operationsverst.C3.A4rker|Standardbauelemente - Operationsverstärker]].&lt;br /&gt;
&lt;br /&gt;
Wer Audio OpAmps sucht - tangentsoft.net hat mal welche unter die Lupe genommen: [http://www.tangentsoft.net/audio/opamps.html Notes on Audio OpAmps]&lt;br /&gt;
&lt;br /&gt;
= Weblinks =&lt;br /&gt;
*[http://www.eetkorea.com/ARTICLES/2003SEP/A/2003SEP19_AMD_AN07.PDF Op Amp Circuit Collection] - National Semiconductor Application Note 31 mit vielen weiteren OP-Schaltungen&lt;br /&gt;
*[http://www-s.ti.com/sc/psheets/slod006b/slod006b.pdf Op Amps for Everyone] - englischsprachiges, sehr umfangreiches Dokument zu OPV und deren Anwendung&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0209092.htm Operationsverstärker im ElKo]&lt;br /&gt;
*[http://www.inf.fu-berlin.de/lehre/WS00/peg/folien/Peg_v7a.pdf OP Teil 1], [http://www.inf.fu-berlin.de/lehre/WS00/peg/folien/Peg_v7b.pdf OP Teil 2] - OP-Schaltungen (deutsch)&lt;br /&gt;
*[http://www.blecken.de/download/opverst.zip Schaltungstechnik mit Operationsverstärkern] - Prof. K. Blecken, Skript zur Vorlesung (deutsch, *.doc-Format)&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Operationsverst%C3%A4rker RN-Wissen Operationsverstärker]&lt;br /&gt;
* [http://www.national.com/AU/design/0,4706,268_0_,00.html Online Seminar] von National Semiconductor&lt;br /&gt;
* [http://www.franzis.de/elo-das-magazin/grundlagen-und-ausbildung/operationsverstaerker/der-operationsverstaerker ELO-Online-Magazin, Franzis-Verlag], [http://www.franzis.de/online-shop/elektronik/lernpakete-elektronik/lernpaket-elektronik-mit-ics Lernpaket Elektronik mit ICs] &amp;quot;Elektronische Experimente mit integriertem Schaltkreis&amp;quot;, Kasten mit Steckbrett/Bauelementen (ca. 40EUR), &lt;br /&gt;
* [[Schmitt-Trigger]]&lt;br /&gt;
* [[Aktiver RC-Bandpass|Aktiver RC-Bandpass auf Operationsverstärker-Basis]]&lt;br /&gt;
* [http://sound.westhost.com/appnotes/an001.htm Präzisionsgleichrichter], engl.&lt;br /&gt;
* [http://www.elektronikwissen.net/opamp/9-opamp-wissen.html OpAmp Praxis], Praktikertipps + schwingende Operationsverstärker in den Griff bekommen&lt;br /&gt;
&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Snippets&amp;diff=83633</id>
		<title>Snippets</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Snippets&amp;diff=83633"/>
		<updated>2014-07-15T16:52:14Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* AVR: ADC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Auf dieser Seite können kleine, oft verwendbare Quelltextchen und Designideen (Snippets) abgelegt und abgerufen werden.&lt;br /&gt;
&lt;br /&gt;
Bitte die Snippets universell verwendbar halten und für Dritte ausführlich und verständlich kommentieren.&lt;br /&gt;
&lt;br /&gt;
== Software-Snippets ==&lt;br /&gt;
&lt;br /&gt;
=== AVR: ADC ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Routinen sind einfachste C-Funktionen zum Auslesen des [[AD-Wandler]]s eines AVR. Die Registernamen bitte an den konkret verwendeten AVR anpassen (Datenblatt).&lt;br /&gt;
&lt;br /&gt;
Anm.: Umfangreichere Routinen mit Mittelwertbildung usw. finden sich im [[AVR-GCC-Tutorial#Analoge_Ein-_und_Ausgabe|AVR-GCC-Tutorial: Analoge Ein- und Ausgabe]]. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auslesen eines Analogports&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
 // Führt eine AD Konversion durch und&lt;br /&gt;
 // liefert den Wert (0-1023) von PORT ADC(src) zurück&lt;br /&gt;
&lt;br /&gt;
 uint16_t ReadValue(uint8_t src)&lt;br /&gt;
 {&lt;br /&gt;
   ADMUX = src;&lt;br /&gt;
   ADCSR = _BV(ADEN)  | _BV(ADSC) | _BV(ADPS2);&lt;br /&gt;
   while (bit_is_set (ADCSR, ADSC))&lt;br /&gt;
     ;  &lt;br /&gt;
   return ADC;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
* ADMUX Parameter siehe Datenblatt (nicht gleich &amp;quot;Pinnummer&amp;quot;)&lt;br /&gt;
* ADSC/ADEN/ADPS2 Parameter siehe Datenblatt&lt;br /&gt;
* Einstellung der Referenzspannung beachten (Intern/Extern)&lt;br /&gt;
* Wandlerfrequenz an Genauigkeitsansprüche anpassen und nach Empfehlungen des Datenblatts in abh. vom &amp;quot;Haupttakt&amp;quot; einstellen&lt;br /&gt;
* ADEN nach erstmaligem Einschalten redundant&lt;br /&gt;
* evtl. Dummy Readout durchfuehren&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Umrechnung Wandlungsergebnis in gemessene Spannung&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Liefert die am ADC anliegende Spannung in mV zurück&lt;br /&gt;
#define ADC_CONV_CONST 5000/1024 // Spannung pro LSB (10 bit Wandler)&lt;br /&gt;
#define ADC_CORR_VAL (ADC*8)/9   // bei 5V Ref-Spannung erhält man mit dieser korrektur 5001mV bei einem ADC-Wert von 1023!&lt;br /&gt;
                                 // Für eine fehlerfreie Umwandlung ist eine Referenz von z.b 4.096V oder 2.56V notwendig...&lt;br /&gt;
uint16_t ReadValue(uint8_t src) {&lt;br /&gt;
  ADMUX = src;&lt;br /&gt;
  ADCSR = _BV(ADEN)  | _BV(ADSC) | _BV(ADPS2);&lt;br /&gt;
&lt;br /&gt;
  while (bit_is_set (ADCSR, ADSC))&lt;br /&gt;
    ;&lt;br /&gt;
&lt;br /&gt;
  return ADC*ADC_CONV_CONST+ADC_CORR_VAL;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hardware-Snippets ==&lt;br /&gt;
&lt;br /&gt;
=== Wie schließe ich ein &amp;quot;LC-Display&amp;quot; an einen Mikrocontroller an? ===&lt;br /&gt;
&lt;br /&gt;
Siehe [[AVR-GCC-Tutorial#Ansteuerung_eines_LCD|AVR-GCC-Tutorial: Ansteuerung eines LCD]] und [[LCD]].&lt;br /&gt;
&lt;br /&gt;
===Wie schließe ich einen MOSFET an einen Mikrocontroller an?===&lt;br /&gt;
&lt;br /&gt;
In folgenden sind keine konkreten Typen für die MOS[[FET]]s genannt. Typen findet man in der [[Mosfet-Übersicht]] und in den parametrischen Suchmaschinen der Hersteller. Die genauen Daten und Belastungsgrenzen sind dann aus den Datenblättern zu entnehmen und für die geplante Schaltung zu prüfen. &lt;br /&gt;
&lt;br /&gt;
==&amp;gt; * [[FET]]&lt;br /&gt;
&lt;br /&gt;
==&amp;gt; * [[Treiber]]&lt;br /&gt;
&lt;br /&gt;
====Direkte Methode: N-Kanal MOSFET (NFET)====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                (VCC)&lt;br /&gt;
                  |&lt;br /&gt;
                (Last)&lt;br /&gt;
                  |&lt;br /&gt;
                  |&lt;br /&gt;
               (Drain )&lt;br /&gt;
(Port µC) -----(Gate  )&lt;br /&gt;
               (Source)&lt;br /&gt;
                  |&lt;br /&gt;
                  |&lt;br /&gt;
                (GND)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschlaggebend für die Steuerung ist die Gate-Source-Spannung. In dieser einfachen Schaltung ist das also die Spannung am Port des µC minus die Spannung an Source (= GND = 0V). Diese Spannung ist positiv!&lt;br /&gt;
Wenn diese einen Schwellwert überschreitet, dann sinkt der Drain-Source-Widerstand (RDSon)schlagartig auf ein Minimum. Übliche Werte liegen bei 0.1Ω, dieser Widerstand ist aber von dem Drain-Source-Strom und der Gate-Source-Spannung abhängig.&lt;br /&gt;
Der Umschaltpunkt (Spannung und Widerstand) kann im Datenblatt in den &amp;quot;RDSon vs. VGS&amp;quot; Diagramm abgelesen werden.&lt;br /&gt;
&lt;br /&gt;
====Direkte Methode: P-Kanal MOSFETs (PFET)====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                (VCC)&lt;br /&gt;
                  |&lt;br /&gt;
                  |&lt;br /&gt;
               (Source)&lt;br /&gt;
(Port µC) -----(Gate  )&lt;br /&gt;
               (Drain )&lt;br /&gt;
                  |&lt;br /&gt;
                  |&lt;br /&gt;
                (Last)&lt;br /&gt;
                  |&lt;br /&gt;
                (GND)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ausschlaggebend für die Steuerung ist die Gate-Source-Spannung. In dieser einfachen Schaltung ist das also die Spannung am Port des µC minus die Spannung an Source (= VCC). Diese Spannung ist negativ!&lt;br /&gt;
Wenn diese einen Schwellwert unterschreitet, dann sinkt der Drain-Source-Widerstand (RDSon)schlagartig auf ein Minimum. Übliche Werte liegen bei 0.1Ω, dieser Widerstand ist aber von dem Drain-Source-Strom und der Gate-Source-Spannung abhängig.&lt;br /&gt;
Der Umschaltpunkt (Spannung und Widerstand) kann im Datenblatt in den &amp;quot;RDSon vs. VGS&amp;quot; Diagramm abgelesen werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkungen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass zwischen Gate und Source und zwischen Gate und Drain eine Kapazität existiert, welche bei jedem Umschalt-Vorgang umgeladen werden muss. Besonders bei höheren Frequenzen (&amp;gt; 10kHz) ist zur Strombegrenzung zwischen Gate und Port-Pin ein Widerstand sinnvoll. Übliche Werte dafür sind 50 - 100Ω. Je höher der Widerstand ist, desto länger dauert das Umladen des Kondensators und desto langsamer schaltet der Transistor um. Daraus ergibt sich eine höhere Verlustleistung im Transistor, was unter Umständen zur Zerstörung des Transistors führen kann.&lt;br /&gt;
&lt;br /&gt;
TODO: Pinbelegung eines typischen MOSFETS&lt;br /&gt;
&lt;br /&gt;
====Indirekte Methode: Ansteuerungen mit Push-Pull-Transistoren====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 12V------------------------o------o---------------o-------------&lt;br /&gt;
                            |      |               |&lt;br /&gt;
                            |      |               |&lt;br /&gt;
                           .-.     |               |&lt;br /&gt;
                           | |2k2  |               |&lt;br /&gt;
                           | |R2   |               |&lt;br /&gt;
                           &#039;-&#039;     |T2             |&lt;br /&gt;
                            |      |BC338          |&lt;br /&gt;
                            |    |/                |&lt;br /&gt;
                            o----|                 |&lt;br /&gt;
                            |    |&amp;gt;         P-FET  |&lt;br /&gt;
                            |      |    ___ IRF4905|&lt;br /&gt;
                            o---|&amp;lt;-o---|___|----||-+&lt;br /&gt;
  .---------.               |  1N4148  R3 10R   ||-&amp;gt;&lt;br /&gt;
  |         |               |                   ||-+&lt;br /&gt;
  |         |    ___      |/                       |&lt;br /&gt;
  |         |---|___|-----|   T1                   o---------&lt;br /&gt;
  |         |   R1 2k2    |&amp;gt;  BC338                |         |&lt;br /&gt;
  |   µC    |               |                     .-.        |&lt;br /&gt;
  |         |               |                     | |        |&lt;br /&gt;
  |         |               |                     | | Last   - 1N5819&lt;br /&gt;
  |         |               |                     &#039;-&#039;        ^&lt;br /&gt;
  &#039;---------&#039;               |                      |         |&lt;br /&gt;
                            |                      |         |&lt;br /&gt;
 GND------------------------o----------------------o---------o---&lt;br /&gt;
(created by AACircuit v1.28.6 beta 04/19/05 www.tech-chat.de)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch die beiden NPN Treibertransistoren T1 &amp;amp; T2 (http://www.mikrocontroller.net/part/BC338) wird das Gate des P-FET auf HIGH oder LOW gezogen.&lt;br /&gt;
Wenn die Basis von T1 auf LOW ist dann wird die Basis von T2 auf HIGH gezogen.&lt;br /&gt;
Der P-FET kann natürlich auch einfach gegen einen N-FET Ausgetauscht werden, wenn man die Last zwischen +12V und Drain Schaltet.&lt;br /&gt;
Mit dieser Schaltung kann man einen FET mit 20kHz und mehr (laut Simulation sind auch 50kHz kein Problem) ansteuern.&lt;br /&gt;
Der Strom, welcher in das Gate rein bzw. raus fliesst, kann man mit R3 begrenzt werden.&lt;br /&gt;
Die geschaltete Betriebsspannung darf die zulässige Gate-Source Spannung (meist 20V) bei dieser Schaltung nicht überschreiten.&lt;br /&gt;
&lt;br /&gt;
Im µC Bereich können desweiteren folgende Hinweise  gegeben werden:&lt;br /&gt;
&lt;br /&gt;
* Es sollten sog. &amp;quot;Logik-Level&amp;quot; MOSFETS verwendet werden (Vth &amp;lt; 2V)&lt;br /&gt;
* Die Schaltleistung des FETs muss beachtet werden (Ids, VBrss)&lt;br /&gt;
* Beim Schalten von induktiven Lasten wie Relais, Motoren, oder auch Lasten über eine längere Zuleitung, sollten [[Relais_mit_Logik_ansteuern#Freilaufdiode|Freilaufdioden]] verwendet werden (im Schaltplan die Schottkydiode 1N5819 in Sperrrichtung über der Last.) Normale 1N400x Dioden sind zu langsam.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Algorithmen und Arithmetik]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=79989</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=79989"/>
		<updated>2013-12-13T11:07:50Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: Fragezeichen entfernt, um einfache Verlinkung aus Threads zu ermöglichen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Library [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Wertebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit einen Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
Präprozessordefinitionen besitzen keine eigene Datentypen, da sie eine reine Textersetzungen durchführen. Deren Inhalte können aber ggf. zu Werten aufgelöst werden, die datentypbehaftet sind.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File - wie geht das =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=C-Pr%C3%A4prozessor&amp;diff=79699</id>
		<title>C-Präprozessor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=C-Pr%C3%A4prozessor&amp;diff=79699"/>
		<updated>2013-11-25T16:07:00Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* #define */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der erste Verarbeitungsschritt bei der Kompilierung eines [[C]]/[[C++]]-Programmes erfolgt durch den &#039;&#039;&#039;Präprozessor&#039;&#039;&#039;. Dieser verändert den Quelltext, den die späteren Verarbeitungsphasen erhalten, in folgender Hinsicht:&lt;br /&gt;
* Einbeziehen zusätzlicher Dateien ([[Include-Files]])&lt;br /&gt;
* Ersetzen von (parametrisierbaren) [[Makro]]s&lt;br /&gt;
* Entfernen einzelner Abschnitte (= bedingte Kompilierung)&lt;br /&gt;
&lt;br /&gt;
Im Grunde kann man sich den Präprozessor als eine Art Texteditor vorstellen, der die Anweisungen, was er zu tun hat, dem Text entnimmt, den er bearbeitet. In jedem Texteditor gibt es zb. die Funktion &#039;Suchen und Ersetzen&#039;. Auch im Präprozessor gibt es sie, nur heißt sie dort #define. Alle Anweisungen an den Präprozessor beginnen grundsätzlich damit, daß das Zeichen &#039;#&#039; das erste Zeichen in einer Textzeile darstellt. Und umgekehrt: Ist das erste Zeichen in einer Textzeile ein &#039;#&#039;, so handelt es sich um eine Präprozessor-Anweisung.&lt;br /&gt;
&lt;br /&gt;
==#include==&lt;br /&gt;
Die #include weist den Präprozessor an, den Inhalt der angegebenen Datei anstelle der #include Anweisung einzusetzen. Weiter passiert nichts. Bei der Angabe des Dateinamens der einzusetzenden Datei gibt es 2 Formen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Datei1.xyz&amp;quot;&lt;br /&gt;
#include &amp;lt;Datei2.abc&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der Unterschied zwischen beiden Formen besteht rein im Aufsuchpfad, den der Präprozessor benutzt, um die Datei zu finden. Per Konvention wird die &amp;lt; &amp;gt;-Form benutzt, um systemweite Includes durchzuführen. Alle mit dem Compiler mitgelieferten Header Files sind zb. solche systemweite-Includes. Bei der Installation des Compilers wurde im System hinterlassen, auf welchem Pfad sie gefunden werden können. Durch Verwendung der &amp;lt; &amp;gt;-Form wird dem Präprozessor mitgeteilt, dass diese damals vereinbarten Pfadangaben zur Aufsuche dieser Datei benutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==#define==&lt;br /&gt;
Mittels #define wird eine Textersetzung vereinbart.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define ABC xyz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
weist den Präprozessor an, im weiteren Quelltext alle Vorkommen von &#039;ABC&#039; durch den Text &#039;xyz&#039; zu ersetzen. Der Präprozessor macht dies überall, solange&lt;br /&gt;
* es sich an der zu ersetzenden Stelle um keinen String handelt. Mit obigem #define würde also in &amp;quot;Dies ist ABC&amp;quot; keine Textersetzung stattfinden.&lt;br /&gt;
* er den Ursprungstext als &#039;Wort&#039; im Sinne eines C-Wortes handelt. Mit obigem #define würde also in cdABCef = 5; keine Textersetzung stattfinden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist: Der Präprozessor führt eine reine Textersetzung durch! Ob sich durch diese Ersetzung eine Logikänderung im Programm ergibt, interessiert den Präprozessor nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define NR 5&lt;br /&gt;
&lt;br /&gt;
int Werte[NR];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; NR;   i )&lt;br /&gt;
    printf( &amp;quot;%d&amp;quot;, Werte[i] );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bevor der eigentliche Compiler den Quelltext zu Gesicht bekommt, wird er zunächst vom Präprozessor bearbeitet. Dieser führt die Textersetzung durch, indem er alle Vorkommen von NR durch den Text 5 ersetzt. Erst dieses Ergebnis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Werte[5];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; 5;   i )&lt;br /&gt;
    printf( &amp;quot;%d&amp;quot;, Werte[i] );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird dann dem eigentlichen Compiler zur Übersetzung vorgelegt. In diesem Beispiel hat man durch den Einsatz des Präprozessors erreicht, dass die Anzahl der Arrayelemente immer mit dem Maximalwert in der for-Schleife übereinstimmt. Ein Fehler, dass beispielweise die Arraygröße verändert wird, ohne das die for-Schleife angepasst würde, ist durch den Einsatz des Präprozessors wirkungsvoll verhindert worden.&lt;br /&gt;
&lt;br /&gt;
Aber auch hier wieder: Der Präprozessors macht nur eine Textersetzung! Für den Präprozessors ist es völlig unerheblich, ob sich dadurch die Logik des Programms aus Sicht des Programmierers verändert&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define PART    3+5&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  y = 4*PART;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Beispiel mag es schon so sein, dass der Programmierer im Sinn hatte, den Ausdruck 4 mal 8 berechnen zu lassen, wobei sich die 8 durch eine Addition von 3 und 5 ergeben. Das interessiert aber den Präprozessor nicht. Der macht eine reine textuelle Ersetzung, indem er den Text &#039;PART&#039; durch den Text &#039;3+5&#039; austauscht, wodurch dieses Ergebnis entsteht&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define PART    3+5&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  y = 4*3+5;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies ist aber etwas anderes, nämlich &#039;(4*3)+5&#039;, also ursprünglich beabsichtigt war, nämlich &#039;4*(3+5)&#039;.&lt;br /&gt;
&lt;br /&gt;
Schlussfolgerung: Es ist bei komplexeren Makros nicht ungewöhnlich, dass sich in Makros relativ viele Klammern wiederfinden, deren Zweck gerade für einen Neuling nicht auf den ersten Blick zu durchschauen ist.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define PART    (3+5)&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  x = PART;&lt;br /&gt;
  y = 4*PART;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Während die Klammern im Makro bei der Zuweisung an x keine Funktion erfüllen (aber auch nicht störend sind), sind sie bei der Zuweisung an y lebenswichtig um der ganzen Anweisung nach der Textersetzung die beabsichtigte Bedeutung zu geben.&lt;br /&gt;
&lt;br /&gt;
==#if, #ifdef==&lt;br /&gt;
&lt;br /&gt;
==mögliche Probleme beim Einsatz des Präprozessors==&lt;br /&gt;
Eine am C-Präprozessor häufig geäußerte Kritik ist, dass er (nahezu) ohne Berücksichtigung der eigentlichen Sprachsyntax arbeitet (&amp;quot;The C-Preprocessor doesn&#039;t know about C&amp;quot;). Die Tatsache, dass Makros beispielsweise auf der Basis von Textersatz arbeiten, kann zu Überaschungen führen. So wird in&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define cub(a) a*a*a&lt;br /&gt;
...&lt;br /&gt;
int x, y;&lt;br /&gt;
y = 4;&lt;br /&gt;
x = cub(y+1);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
in x nicht etwa der Wert 125 (5 hoch 3) stehen, sondern der Wert 13, da nach Ersetzen des Makros der folgende Quelltext kompiliert wird ...&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
x = y+1*y+1*y+1; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
... und durch die arithmetischen Vorrangregeln, wird dieser Ausdruck so ausgewertet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
x = y + (1*y) + (1*y) + 1; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb sollte man jeden Parameter eines Makros bei jeder Verwendung klammern. Damit werden viele Probleme mit Makros gelöst und man erhält für obiges Beispiel folgende Form und damit auch eine korrekte Berechnung:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define cub(a) ((a)*(a)*(a))&lt;br /&gt;
...&lt;br /&gt;
int x, y;&lt;br /&gt;
y = 4;&lt;br /&gt;
x = cub(y+1);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ein weiteres Problem besteht jedoch, wenn ein Makro-Parameter im Ersatztext doppelt verwendet wird:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define max(a, b) ((a&amp;gt;b) ? (a) : (b))&lt;br /&gt;
...&lt;br /&gt;
int x, y;&lt;br /&gt;
...&lt;br /&gt;
x = max(y, 10);   /* OK */&lt;br /&gt;
x = max(++y, 10); /* ?? */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Im zweiten Fall wird die Variable &#039;&#039;y&#039;&#039; u.U. &#039;&#039;zweimal&#039;&#039; inkrementiert - was ohne Kenntnis der Makro-Definition keineswegs offensichtlich ist (&#039;&#039;max&#039;&#039; könnte auch eine echte Funktion sein).&lt;br /&gt;
&lt;br /&gt;
Die Tatsache, dass der C-Präprozessor die Syntax von C/C++ nicht wirklich berücksichtigt, ist allerdings auch nützlich. So lassen sich mit dem C-Präprozessor Datentypen parametrisieren, um systematische Programmteile zu vereinfachen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* GSAWP:&lt;br /&gt;
   generiert Funktionsdefinition zum Vertauschen des Inhalts von zwei Variablen&lt;br /&gt;
*/&lt;br /&gt;
#define GSWAP(n, T)s\&lt;br /&gt;
        void n(T *xp, T *yp) { T tmp = *xp; *xp = *yp; *yp = tmp; }&lt;br /&gt;
...&lt;br /&gt;
GSWAP(iswap, int)&lt;br /&gt;
GSWAP(dswap, double)&lt;br /&gt;
GSWAP(swap_s, struct s)&lt;br /&gt;
...&lt;br /&gt;
int a, b;&lt;br /&gt;
double c, d;&lt;br /&gt;
struct s e, f;&lt;br /&gt;
...&lt;br /&gt;
iswap(&amp;amp;a, &amp;amp;b);&lt;br /&gt;
dswap(&amp;amp;c, &amp;amp;d);&lt;br /&gt;
swap_s(&amp;amp;e. &amp;amp;f);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Viele typische Anwendungsfälle des Präprozessors lassen sich allerdings bereits mit Standard-C-Bordmitteln erfolgreich erschlagen:&lt;br /&gt;
* Statt &#039;&#039;#define&#039;&#039; besser mit &#039;&#039;const&#039;&#039; vereinbarte Variablen benutzen. Bei jedem besseren Compiler ergeben sich dank Optimierung keinerlei Nachteile.&lt;br /&gt;
* &#039;&#039;enum&#039;&#039; für Konstantenfelder benutzen. Dabei sollte man allerdings sauber casten, da &#039;&#039;enum&#039;&#039;-Werte in der Regel als &#039;&#039;int&#039;&#039; interpretiert werden.&lt;br /&gt;
* Neuere Versionen des GCC unterstützen &#039;&#039;inline&#039;&#039;, wodurch Makros für Einzeiler überflüssig werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In [[C++]] wurden ergänzend zum C-Präprozessor weitere Mechanismen eingeführt. So erlauben [[Templates (C++)|Templates]] generische Programmierung, womit viele der &amp;quot;Makro-Tricks&amp;quot; wie die obigen überflüssig werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=C-Pr%C3%A4prozessor&amp;diff=79698</id>
		<title>C-Präprozessor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=C-Pr%C3%A4prozessor&amp;diff=79698"/>
		<updated>2013-11-25T16:05:41Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* #define */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der erste Verarbeitungsschritt bei der Kompilierung eines [[C]]/[[C++]]-Programmes erfolgt durch den &#039;&#039;&#039;Präprozessor&#039;&#039;&#039;. Dieser verändert den Quelltext, den die späteren Verarbeitungsphasen erhalten, in folgender Hinsicht:&lt;br /&gt;
* Einbeziehen zusätzlicher Dateien ([[Include-Files]])&lt;br /&gt;
* Ersetzen von (parametrisierbaren) [[Makro]]s&lt;br /&gt;
* Entfernen einzelner Abschnitte (= bedingte Kompilierung)&lt;br /&gt;
&lt;br /&gt;
Im Grunde kann man sich den Präprozessor als eine Art Texteditor vorstellen, der die Anweisungen, was er zu tun hat, dem Text entnimmt, den er bearbeitet. In jedem Texteditor gibt es zb. die Funktion &#039;Suchen und Ersetzen&#039;. Auch im Präprozessor gibt es sie, nur heißt sie dort #define. Alle Anweisungen an den Präprozessor beginnen grundsätzlich damit, daß das Zeichen &#039;#&#039; das erste Zeichen in einer Textzeile darstellt. Und umgekehrt: Ist das erste Zeichen in einer Textzeile ein &#039;#&#039;, so handelt es sich um eine Präprozessor-Anweisung.&lt;br /&gt;
&lt;br /&gt;
==#include==&lt;br /&gt;
Die #include weist den Präprozessor an, den Inhalt der angegebenen Datei anstelle der #include Anweisung einzusetzen. Weiter passiert nichts. Bei der Angabe des Dateinamens der einzusetzenden Datei gibt es 2 Formen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Datei1.xyz&amp;quot;&lt;br /&gt;
#include &amp;lt;Datei2.abc&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der Unterschied zwischen beiden Formen besteht rein im Aufsuchpfad, den der Präprozessor benutzt, um die Datei zu finden. Per Konvention wird die &amp;lt; &amp;gt;-Form benutzt, um systemweite Includes durchzuführen. Alle mit dem Compiler mitgelieferten Header Files sind zb. solche systemweite-Includes. Bei der Installation des Compilers wurde im System hinterlassen, auf welchem Pfad sie gefunden werden können. Durch Verwendung der &amp;lt; &amp;gt;-Form wird dem Präprozessor mitgeteilt, dass diese damals vereinbarten Pfadangaben zur Aufsuche dieser Datei benutzt werden soll.&lt;br /&gt;
&lt;br /&gt;
==#define==&lt;br /&gt;
Mittels #define wird eine Textersetzung vereinbart.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define ABC xyz&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
weist den Präprozessor an, im weiteren Quelltext alle Vorkommen von &#039;ABC&#039; durch den Text &#039;xyz&#039; zu ersetzen. Der Präprozessor macht dies überall, solange&lt;br /&gt;
* es sich an der zu ersetzenden Stelle um keinen String handelt. Mit obigem #define würde also in &amp;quot;Dies ist ABC&amp;quot; keine Textersetzung stattfinden.&lt;br /&gt;
* er den Ursprungstext als &#039;Wort&#039; im Sinne eines C-Wortes handelt. Mit obigem #define würde also in cdABCef = 5; keine Textersetzung stattfinden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist: Der Präprozessor führt eine reine Textersetzung durch! Ob sich durch diese Ersetzung eine Logikänderung im Programm ergibt, interessiert den Präprozessor nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define NR 5&lt;br /&gt;
&lt;br /&gt;
int Werte[NR];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; NR;   i )&lt;br /&gt;
    printf( &amp;quot;%d&amp;quot;, Werte[i] );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bevor der eigentliche Compiler den Quelltext zu Gesicht bekommt, wird er zunächst vom Präprozessor bearbeitet. Dieser führt die Textersetzung durch, indem er alle Vorkommen von NR durch den Text 5 ersetzt. Erst dieses Ergebnis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Werte[5];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; 5;   i )&lt;br /&gt;
    printf( &amp;quot;%d&amp;quot;, Werte[i] );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird dann dem eigentlichen Compiler zur Übersetzung vorgelegt. In diesem Beispiel hat man durch den Einsatz des Präprozessors erreicht, dass die Anzahl der Arrayelemente immer mit dem Maximalwert in der for-Schleife übereinstimmt. Ein Fehler, dass beispielweise die Arraygröße verändert wird, ohne das die for-Schleife angepasst würde, ist durch den Einsatz des Präprozessors wirkungsvoll verhindert worden.&lt;br /&gt;
&lt;br /&gt;
Aber auch hier wieder: Der Präprozessors macht nur eine Textersetzung! Für den Präprozessors ist es völlig unerheblich, ob sich dadurch die Logik des Programms aus Sicht des Programmierers verändert&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define PART    3+5&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  y = 8*PART;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Beispiel mag es schon so sein, dass der Programmierer im Sinn hatte, den Ausdruck 3 mal 8 berechnen zu lassen, wobei sich die 8 durch eine Addition von 3 und 5 ergeben. Das interessiert aber den Präprozessor nicht. Der macht eine reine textuelle Ersetzung, indem er den Text &#039;PART&#039; durch den Text &#039;3+5&#039; austauscht, wodurch dieses Ergebnis entsteht&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define PART    3+5&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  y = 8*3+5;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies ist aber etwas anderes, nämlich &#039;(8*3)+5&#039;, also urspünglich beabsichtigt war, nämlich &#039;8*(3+5)&#039;.&lt;br /&gt;
&lt;br /&gt;
Schlussfolgerung: Es ist bei komplexeren Makros nicht ungewöhnlich, dass sich in Makros relativ viele Klammern wiederfinden, deren Zweck gerade für einen Neuling nicht auf den ersten Blick zu durchschauen ist.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define PART    (3+5)&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  x = PART;&lt;br /&gt;
  y = 8*PART;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Während die Klammern im Makro bei der Zuweisung an x keine Funktion erfüllen (aber auch nicht störend sind), sind sie bei der Zuweisung an y lebenswichtig um der ganzen Anweisung nach der Textersetzung die beabsichtigte Bedeutung zu geben.&lt;br /&gt;
&lt;br /&gt;
==#if, #ifdef==&lt;br /&gt;
&lt;br /&gt;
==mögliche Probleme beim Einsatz des Präprozessors==&lt;br /&gt;
Eine am C-Präprozessor häufig geäußerte Kritik ist, dass er (nahezu) ohne Berücksichtigung der eigentlichen Sprachsyntax arbeitet (&amp;quot;The C-Preprocessor doesn&#039;t know about C&amp;quot;). Die Tatsache, dass Makros beispielsweise auf der Basis von Textersatz arbeiten, kann zu Überaschungen führen. So wird in&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define cub(a) a*a*a&lt;br /&gt;
...&lt;br /&gt;
int x, y;&lt;br /&gt;
y = 4;&lt;br /&gt;
x = cub(y+1);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
in x nicht etwa der Wert 125 (5 hoch 3) stehen, sondern der Wert 13, da nach Ersetzen des Makros der folgende Quelltext kompiliert wird ...&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
x = y+1*y+1*y+1; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
... und durch die arithmetischen Vorrangregeln, wird dieser Ausdruck so ausgewertet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
x = y + (1*y) + (1*y) + 1; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb sollte man jeden Parameter eines Makros bei jeder Verwendung klammern. Damit werden viele Probleme mit Makros gelöst und man erhält für obiges Beispiel folgende Form und damit auch eine korrekte Berechnung:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define cub(a) ((a)*(a)*(a))&lt;br /&gt;
...&lt;br /&gt;
int x, y;&lt;br /&gt;
y = 4;&lt;br /&gt;
x = cub(y+1);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ein weiteres Problem besteht jedoch, wenn ein Makro-Parameter im Ersatztext doppelt verwendet wird:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define max(a, b) ((a&amp;gt;b) ? (a) : (b))&lt;br /&gt;
...&lt;br /&gt;
int x, y;&lt;br /&gt;
...&lt;br /&gt;
x = max(y, 10);   /* OK */&lt;br /&gt;
x = max(++y, 10); /* ?? */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Im zweiten Fall wird die Variable &#039;&#039;y&#039;&#039; u.U. &#039;&#039;zweimal&#039;&#039; inkrementiert - was ohne Kenntnis der Makro-Definition keineswegs offensichtlich ist (&#039;&#039;max&#039;&#039; könnte auch eine echte Funktion sein).&lt;br /&gt;
&lt;br /&gt;
Die Tatsache, dass der C-Präprozessor die Syntax von C/C++ nicht wirklich berücksichtigt, ist allerdings auch nützlich. So lassen sich mit dem C-Präprozessor Datentypen parametrisieren, um systematische Programmteile zu vereinfachen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* GSAWP:&lt;br /&gt;
   generiert Funktionsdefinition zum Vertauschen des Inhalts von zwei Variablen&lt;br /&gt;
*/&lt;br /&gt;
#define GSWAP(n, T)s\&lt;br /&gt;
        void n(T *xp, T *yp) { T tmp = *xp; *xp = *yp; *yp = tmp; }&lt;br /&gt;
...&lt;br /&gt;
GSWAP(iswap, int)&lt;br /&gt;
GSWAP(dswap, double)&lt;br /&gt;
GSWAP(swap_s, struct s)&lt;br /&gt;
...&lt;br /&gt;
int a, b;&lt;br /&gt;
double c, d;&lt;br /&gt;
struct s e, f;&lt;br /&gt;
...&lt;br /&gt;
iswap(&amp;amp;a, &amp;amp;b);&lt;br /&gt;
dswap(&amp;amp;c, &amp;amp;d);&lt;br /&gt;
swap_s(&amp;amp;e. &amp;amp;f);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Viele typische Anwendungsfälle des Präprozessors lassen sich allerdings bereits mit Standard-C-Bordmitteln erfolgreich erschlagen:&lt;br /&gt;
* Statt &#039;&#039;#define&#039;&#039; besser mit &#039;&#039;const&#039;&#039; vereinbarte Variablen benutzen. Bei jedem besseren Compiler ergeben sich dank Optimierung keinerlei Nachteile.&lt;br /&gt;
* &#039;&#039;enum&#039;&#039; für Konstantenfelder benutzen. Dabei sollte man allerdings sauber casten, da &#039;&#039;enum&#039;&#039;-Werte in der Regel als &#039;&#039;int&#039;&#039; interpretiert werden.&lt;br /&gt;
* Neuere Versionen des GCC unterstützen &#039;&#039;inline&#039;&#039;, wodurch Makros für Einzeiler überflüssig werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In [[C++]] wurden ergänzend zum C-Präprozessor weitere Mechanismen eingeführt. So erlauben [[Templates (C++)|Templates]] generische Programmierung, womit viele der &amp;quot;Makro-Tricks&amp;quot; wie die obigen überflüssig werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Strukturen_in_C&amp;diff=79671</id>
		<title>Strukturen in C</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Strukturen_in_C&amp;diff=79671"/>
		<updated>2013-11-22T06:53:40Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Motivation hinter Strukturen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Motivation hinter Strukturen ==&lt;br /&gt;
&lt;br /&gt;
Ein struct ermöglicht es, einen logischen Zusammenhalt mehrerer Werte explizit auszudrücken und den Dingen einen Namen zu geben.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise besteht ein Datum nun mal aus Tag/Monat/Jahr&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct date_&lt;br /&gt;
{&lt;br /&gt;
  uint8_t  Tag;&lt;br /&gt;
  uint8_t  Monat;&lt;br /&gt;
  uint16_t Jahr;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der komplette Name einer Person besteht aus Vorname und Familienname&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct name_&lt;br /&gt;
{&lt;br /&gt;
  char Vorname[20];&lt;br /&gt;
  char Familienname[30];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Eine Ortsangabe besteht aus Postleitzahl, Ortsname, Strasse und Hausnummer&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ort_&lt;br /&gt;
{&lt;br /&gt;
  char PLZ[6];&lt;br /&gt;
  char Name[30];&lt;br /&gt;
  char Strasse[30];&lt;br /&gt;
  char Nummer[5];&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und eine komplette Personenbeschreibung besteht aus dem kompletten &lt;br /&gt;
Namen, einem Wohnort, seinem Geburtstag, seinem Hochzeitstag und der Anzahl der Kinder&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct person&lt;br /&gt;
{&lt;br /&gt;
  struct name_ Name;&lt;br /&gt;
  struct date_ Geburtstag;&lt;br /&gt;
  struct date_ Hochzeitstag;&lt;br /&gt;
  struct ort_  Wohnort;&lt;br /&gt;
  uint8_t      NrKinder;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Man sieht sehr schön, wie sich die Beschreibung einer Person mit den vorhergehenden Strukturen ganz automatisch strukturiert. Eine Person Franz&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   struct person_ Franz;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
hat damit automatisch einen Monat in dem er geboren wurde. Er &amp;quot;erbt&amp;quot; dies, weil er einen Geburtstag hat und dieser als struct date_ klarerweise über ein Monat verfügt. Der Monat in dem Franz geboren wurde ist daher&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
     Franz.Geburtstag.Monat&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Liest man diesen C-Ausdruck von links nach rechts ist das Ganze weitgehend &lt;br /&gt;
selbsterklärend. Vom Allgemeineren zum immer Spezifischeren. &#039;Franz&#039; ist die komplette Person. &#039;Franz.Geburtstag&#039; ist der Geburtstag der Person Franz. &#039;Franz.Geburtstag.Monat&#039; ist der Monat des Geburtstages der Person Franz.&lt;br /&gt;
&lt;br /&gt;
Würde man zum struct ort_ noch das Land mit dazunehmen,&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ort_&lt;br /&gt;
{&lt;br /&gt;
  char PLZ[6];&lt;br /&gt;
  char Name[30];&lt;br /&gt;
  char Strasse[30];&lt;br /&gt;
  char Nummer[5];&lt;br /&gt;
  char Land[30];&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann &#039;erben&#039; alle weiteren Strukturen oder Funktionen, die mit einem struct ort_ &lt;br /&gt;
arbeiten ganz automatisch, dass ein struct ort_ jetzt auch noch das Land oder den Staat mit beinhaltet.&lt;br /&gt;
&lt;br /&gt;
Strukturen sind Datentypen wie alle anderen eingebauten Datentypen auch (int, long, char, double, ...). Will man also beispielsweise die Belegschaft einer Firma speichern, die aus maximal 300 Personen besteht, dann kann man selbstverständlich ein derartiges Array von Personen erzeugen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct person_ Belegschaft[300];&lt;br /&gt;
int AnzahlPersonen;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Damit hat man entsprechenden Speicher bereitgestellt um 300 Personen &lt;br /&gt;
speichern zu können. Mit allem drum und dran. Inklusive Wohnort, inklusive Geburtstag, inklusive eben allem was eine Person ausmacht. Jede einzelne Person der Belegschaft hat einen Geburtsmonat. Denn im Array sind ja lauter Personen gespeichert und jede ist vom Typ struct person_ und hat als solches einen Geburtstag.&lt;br /&gt;
Schreibt man sich daher eine Funktion, die einen beliebiges struct date_ &lt;br /&gt;
ausgeben kann&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void printDate( struct date_ * Datum )&lt;br /&gt;
{&lt;br /&gt;
  printf( &amp;quot;%d/%d/%04d&amp;quot;, Datum-&amp;gt;Tag, Datum-&amp;gt;Monat, Datum-&amp;gt;Jahr );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann kann man, weil ja ein Geburtstag einer Person so ein Datum ist, die Geburtstage der kompletten Belegschaft ausgeben&lt;br /&gt;
  printf( &amp;quot;Geburtstage\n&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; AnzahlPersonen; i++ ) {&lt;br /&gt;
    printf( &amp;quot;%s %s: &amp;quot;, Belegschaft[i].Name, Belegschaft[i].Vorname );&lt;br /&gt;
    printfDate( &amp;amp; Belegschaft[i].Geburtstag );&lt;br /&gt;
    printf( &amp;quot;\n&amp;quot; );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Das ist aber nicht die einzige mögliche Verwendung so einer Funktion. Würde man für eine Person in der Belegschaft zb auch noch das Datum mitspeichern, an dem sie in die Firma gekommen ist.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct person&lt;br /&gt;
{&lt;br /&gt;
  struct name_ Name;&lt;br /&gt;
  struct date_ Geburtstag;&lt;br /&gt;
  struct date_ Hochzeitstag;&lt;br /&gt;
  struct date_ Firmeneintritt;&lt;br /&gt;
  struct ort_  Wohnort;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann kann man selbstverständlich die Funktion printDate auch dazu benutzen, dieses Datum auszugeben. Denn auch ein &#039;Firmeneintritt&#039; ist ja nur ein struct date_ und damit ist die Funktion dafür benutzbar&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  printf( &amp;quot;Firmeneintritte\n&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; 300; i++ ) {&lt;br /&gt;
    printf( &amp;quot;%s %s: &amp;quot;, Belegschaft[i].Name, Belegschaft[i].Vorname );&lt;br /&gt;
    printfDate( &amp;amp; Belegschaft[i].Firmeneintritt);&lt;br /&gt;
    printf( &amp;quot;\n&amp;quot; );&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ein anderes Beispiel wären Basisfunktionen, die zb mit einem struct date_ diverse Vergleiche oder Berechnungen anstellen, dann kann man dann zb. wiederrum Funktionen schreiben, die alle Mitarbeiter rausfiltern, die heuer 30 Jahre in der Firma sind, oder die gerade ihren 25. Geburtstag feiern etc, etc.&lt;br /&gt;
&lt;br /&gt;
Aus einer &#039;logischen Gruppierung&#039;, in der man Dinge die zusammengehören auch in Form einer Datenstruktur zusammenfasst und strukturiert, folgt also wesentlich mehr als man jemals mit mehrspaltigen Arrays ausdrücken könnte. Man verpasst damit den beteiligten Daten eine Struktur die diesem logischen Zusammenhalt entspricht und versetzt sich damit in die Lage, die reale Welt besser und übersichtlicher und vor allen Dingen in &#039;der dem Programmierer gewohnten Sprache&#039; in einem Programm abbilden zu können. Wieviel aussagekräftiger ist&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   Belegschaft[i].Geburtstag.Monat&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
als im Vergleich dazu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   Belegschaft[i][12]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
nur weil der Programmierer zufällig weißt, dass in der Spalte 12 eines 2-dimensionalen Arrays das Geburtsmonat gespeichert ist?&lt;br /&gt;
&lt;br /&gt;
Zudem ermöglichst es Strukturen, dass man sich bei der Programmierung auf &lt;br /&gt;
Teilbereiche konzentrieren kann. Erstellt sich der Programmierer Funktionen, die &lt;br /&gt;
mit einem struct date_ arbeiten (egal ob Ausgeben, Vergleichen, Tage zwischen Daten berechnen, etc...), zu diesem Zeitpunkt konzentriert man sich nur und ausschliesslich auf das, was mit einem struct date_ möglich sein soll und wie diese Funktionalität zu implementieren ist.&lt;br /&gt;
 &lt;br /&gt;
Das dieses struct date_ dann in Form von Geburtstagen, Firmeneintritten, &lt;br /&gt;
Jubiläen etc. benutzt wird bzw. werden kann, ist beim Schreiben der Datumsfunktionen herzlich egal - und das soll es auch sein. Natürlich &lt;br /&gt;
wird die komplette Entwicklung dieser Funktionen durch das eigentliche Projekt getrieben werden, weil man ja weiß, was an Funktionalität in etwa gebraucht werden wird. Der springende Punkt ist aber, dass die entstehenden Funktionen erst mal damit nichts zu tun haben. Sie wären auch abseits dieses Projektes nützlich.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=79542</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=79542"/>
		<updated>2013-11-11T21:24:01Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Header File wie geht das */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File - wie geht das =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=79541</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=79541"/>
		<updated>2013-11-11T21:23:20Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Header File, wie geht das? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf bei WinAVR bzw AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File, wie geht das =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78930</id>
		<title>AVR Net-IO Bausatz von Pollin</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78930"/>
		<updated>2013-10-13T19:57:07Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Funktionsumfang der Originalsoftware */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier steht eine Beschreibung des Pollin Bausatzes [http://www.pollin.de/shop/shop.php?cf=detail.php&amp;amp;pg=NQ==&amp;amp;a=MTQ5OTgxOTk= AVR-NET-IO. Best.Nr. 810 058], oder als aufgebautes Fertigmodul, Best.Nr. 810 073. &lt;br /&gt;
&lt;br /&gt;
Einige Features: Ethernet-Platine mit ATmega32 und Netzwerkcontroller ENC28J60. Die Platine verfügt über 8 digitale Ausgänge, 4 digitale und 4 ADC-Eingänge, welche alle über einen Netzwerkanschluss (TCP/IP) abgerufen bzw. geschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AVR-NET-IO ADD-ON.JPG|thumb|right|400px|AVR-NET-IO (links) mit zusätzlicher SUB-D Anschlussplatine (rechts, nicht im Lieferumfang)und Add-On-Board (oben, mit aufgelötetem RFM12-433-Modul, beides nicht im Lieferumfang). Ebenso ist zusätzlich ein nicht im Lieferumfang enthaltener kleiner Kühlkörper auf einem der Spannungsregler montiert und die Schraubklemmen zur Stromversorgung wurden durch Buchsen ersetzt.]]&lt;br /&gt;
&lt;br /&gt;
== Technische Daten ==&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 9 V AC/DC&lt;br /&gt;
* Stromaufnahme ca. 190 mA&lt;br /&gt;
* 8 digitale Ausgänge (0/5 V) [PC0-PC7 an J3]&lt;br /&gt;
* 4 digitale Eingänge (0/5 V) [PA0-PA3 an J3]&lt;br /&gt;
* 4 ADC-Eingänge (10 Bit) [PA4-PA7 an Schraubklemmen]&lt;br /&gt;
* LCD-Anschluss (HD44780 komp. Controller nötig) [PD2-7,PB0,PB3 an EXT]&lt;br /&gt;
* [[ENC28J60]]&lt;br /&gt;
* [http://www.atmel.com/dyn/Products/Product_card.asp?part_id=2014 ATmega32] Mikrocontroller&lt;br /&gt;
&lt;br /&gt;
Maße (L×B×H): 108×76×22 mm.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Die Schaltung des AVR-NET-IO ist recht einfach:&lt;br /&gt;
* Ein ATmega32 Mikrocontroller enthält die gesamte Software&lt;br /&gt;
* Ein ENC28J60 Ethernet-Controller für das Senden und Empfangen von Ethernet Frames (MAC und PHY Ethernet Layer) ist über [[SPI]] mit dem ATmega32 verbunden&lt;br /&gt;
* Ein Ethernet RJ-45 MagJack TRJ 0011 BA NL von [http://www.trxcom.com/ Trxcom] mit eingebautem Übertrager und Anzeige-LEDs am ENC28J60.&lt;br /&gt;
* Ein MAX232 für die serielle Schnittstelle&lt;br /&gt;
* Zwei Spannungsregler, 5 V und 3,3 V&lt;br /&gt;
* &amp;quot;Hühnerfutter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Fast alle I/O Pins des ATmega32 sind irgendwo auf Anschlüssen herausgeführt. Entweder auf dem SUB-D Stecker, dem EXT oder ISP Wannensteckern oder den blauen Anschlussklemmen. Eine Schutzbeschaltung gibt es nicht.&lt;br /&gt;
&lt;br /&gt;
Die blauen Anschlussklemmen haben eine Nut und eine Feder mit denen man&lt;br /&gt;
sie zusammenstecken kann, dadurch ist das Anlöten wesentlich leichter&lt;br /&gt;
und sie stehen auch sauber in der Reihe.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
Seit Januar 2010 gibt es auch eine Erweiterungsplatine &lt;br /&gt;
&lt;br /&gt;
[http://www.pollin.de/shop/dt/Nzg4OTgxOTk-/Bausaetze/Diverse/Bausatz_Add_on_fuer_AVR_NET_IO.html Add-on für AVR-NET-IO-Board Best.Nr. 810 112]&lt;br /&gt;
&lt;br /&gt;
Diese Platine erweitert das NET-IO um:&lt;br /&gt;
&lt;br /&gt;
* SD-Karten-Slot über SPI&lt;br /&gt;
* Display über PCF 8574&lt;br /&gt;
* Infrarot-Empfang&lt;br /&gt;
* [[RFM12]] Funkmodul (nicht im Lieferumfang enthalten)&lt;br /&gt;
&lt;br /&gt;
Eine Aufstellung bekannter Fehler findet sich weiter unten [[#Bekannte Fehler|Bekannte Fehler - Erweiterungplatine]] &lt;br /&gt;
&lt;br /&gt;
Erste Erfahrungsberichte im Forum http://www.mikrocontroller.net/topic/161354&lt;br /&gt;
&lt;br /&gt;
=== Hardware-Umbauten &amp;amp; -Verbesserungen ===&lt;br /&gt;
&lt;br /&gt;
* Kühlkörper auf dem 7805 - (Vorsicht: Der LM317T ist rückseitig spannungsführend. Den Kühlkörper also keinesfalls an beide Spannungsregler anschließen!)&lt;br /&gt;
* MAX232 nach anfänglicher Konfiguration nicht bestücken um Strom zu sparen oder um zwei weitere I/O-Pins zu gewinnen&lt;br /&gt;
* 10µF-Elkos für MAX232N (C14-C17) durch 1µF ersetzen. Eine 10µF-Version für den MAX232 gibt es nicht. Die 10µF-Elkos können auch Ursache einer nicht funktionierenden RS232 sein.&lt;br /&gt;
** Laut Spezifikation sind auch mehr als 1µF erlaubt. Selbst Atmel verwendet beim STK500 10µF. Dies führt keinesfalls dazu, dass die RS232 nicht mehr funktioniert.&lt;br /&gt;
* Die IC-Fassungen aus &amp;quot;Pollins Resterampe&amp;quot; durch Fassungen mit gedrehten Kontakten ersetzen. &lt;br /&gt;
* &#039;&#039;Netz&#039;&#039; LED nicht bestücken oder größere Widerstände einlöten um Strom zu sparen (R3)&lt;br /&gt;
* Vorwiderstände der Ethernet-LEDs größer machen (z.&amp;amp;nbsp;B. verdoppeln) um Strom zu sparen (R6,R7)&lt;br /&gt;
* Linear-Spannungsregler ersetzen&lt;br /&gt;
* Kondensator an AREF-Pin des ATmega32 (ATmega32 Datenblatt) (100nF gegen Masse)&lt;br /&gt;
* Kondensator an den RESET-Pin des ATmega32 ([http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Atmel Application Note AVR042: AVR Hardware Design Considerations]) Wenn man diese Quelle genauer liest, ist das aber eher unnötig. - Kondensator bei selbstbau-ISP empfehlenswert.&lt;br /&gt;
* Umbau auf 3,3 V:&lt;br /&gt;
** Ersatz der Spannungsregler durch einen einzigen 3,3 V Regler&lt;br /&gt;
** Anpassen (verkleinern) des LED-Vorwiderstands R3 für 3,3 Volt Betrieb&lt;br /&gt;
** Reduktion der Taktfrequenz (Austausch von Q2) auf den bei 3,3V erlaubten Bereich des ATmega32 ( ATmega32(L)  3.3V /8.0 Mhz Takt )&lt;br /&gt;
** Ersatz des MAX232 durch einen MAX3232&lt;br /&gt;
[[Bild:POWER.JPG|thumb|400px|5V Stromversorgung über USB Kabel, ohne 5 V Spannungsregler und Gleichrichterdioden, Vorsicht: kein Verpolungsschutz!  ]]&lt;br /&gt;
* ATmega32 vom ENC28J60 takten (OSC2)&lt;br /&gt;
* Betrieb mit Gleichspannung:&lt;br /&gt;
** Dioden D2 und D5 durch Drahtbrücken ersetzen, D1 und D4 nicht bestücken (komplette Entfernung des Brückengleichrichters, beinhaltet Verlust des Verpolungsschutzes)&lt;br /&gt;
** Diode D2 bestücken, D5 durch Drahtbrücke ersetzen, D1 und D4 nicht bestücken (Brückengleichrichter durch Verpolungsschutze ersetzen)&lt;br /&gt;
*** ??? Ist dies nicht kontraproduktiv? Bei mir wurde durch D2-Bestückung die Eingangsspannung von ca. 5,2 V am LM317T auf ca. 4,6 V gedrückt, so dass am ENC28J60 nur ca. 2,6 V ankamen (außerhalb der lt. Datenblatt &amp;quot;Operating voltage range of 3.14V to 3.45V&amp;quot;). Man müsste also ein geregeltes Netzteil mit ca. 5,5 V anschließen um 5 und 3,3 V zu erzielen. Dann lieber den Verpolungsschutz durch andere Maßnahmen sicherstellen.&lt;br /&gt;
** Beim Betrieb von USB beachten, dass USB-Spezifikation keinesfalls 5V garantiert, sondern Spannung bis runter 4.4V erlaubt und dann u.U. durch den LM317 nicht mehr genügend Spannung am ENC anliegt. Das äußert sich so, dass zwar der Atmega einwandfrei funktioniert, die Ethernet-Kommunikation aber nicht oder nur sehr sporadisch.&lt;br /&gt;
* Ersatz des ATmega32 durch einen ATmega644 oder ATmega1284p mit mehr FLASH-Speicher.&lt;br /&gt;
** Der ATmega644 und auch der ATMega1284p sind nicht mit der Pollin-Firmware kompatibel&lt;br /&gt;
* 100nF über alle drei IC Störunterdrückung zusätzlich bestücken&lt;br /&gt;
&lt;br /&gt;
== Inbetriebnahme der Originalsoftware ==&lt;br /&gt;
=== Einleitung ===&lt;br /&gt;
&lt;br /&gt;
Die bei Auslieferung (Stand September 2008) in den ATmega32 gebrannte Firmware stellt sich manchmal recht zickig an. Es scheint dann die Netzwerk-Schnittstelle, ggf. auch  die serielle Schnittstelle, nicht zu funktionieren. Falls es Probleme geben sollte, sollte man erst einmal ein Firmwareupdate versuchen. Dies geschieht über die serielle Schnittstelle mittels des Programmes NetServer (aktuelle Version 1.03, Februar 2010), die dem Bausatz beiliegt. &lt;br /&gt;
&lt;br /&gt;
Falls die serielle Schnittstelle ebenfalls nicht zugänglich ist, kann mit den im folgenden beschriebenen Schritten die Inbetriebnahme der Software möglich sein. Dazu benötigt man:&lt;br /&gt;
&lt;br /&gt;
* Einen Windows-PC mit Ethernet-Schnittstelle und RS232-Schnittstelle (ein Prolific RS232-USB Konverter funktioniert)&lt;br /&gt;
* Entweder&lt;br /&gt;
**zwei normale (&#039;&#039;straight through&#039;&#039;) Ethernet-Kabel und einen Ethernet Switch/Hub, oder&lt;br /&gt;
**ein gekreuztes(&#039;&#039;cross over&#039;&#039;) Ethernet-Kabel&lt;br /&gt;
* Einen AVR Programmer (Hardware und Software). Zum Beispiel einen [[AVR Dragon]] oder [[STK500]] mit [[AVR Studio]] oder das [[Pollin ATMEL Evaluations-Board]] und [[avrdude]].&lt;br /&gt;
* Die [http://www.pollin.de/shop/ds/MTQ5OTgxOTk-.html Pollin NetServer Software], Version 1.03 (oder neuer)&lt;br /&gt;
&lt;br /&gt;
=== Gelieferten ATmega32 richtig einstellen ===&lt;br /&gt;
[[image:fuse_bits_avr_studio.jpg|thumb|right|250px|Einstellungen der Fuse-Bits mittels AVR Studio 4]]&lt;br /&gt;
Die Fuses der gelieferten ATmega32s scheinen nicht immer mit den im Handbuch auf Seite 12 als erforderlich angegebenen Fuse-Einstellungen übereinzustimmen.&lt;br /&gt;
&lt;br /&gt;
Dies kann man mittels eines Programmers ändern. LFuse = 0xBF, HFuse = 0xD2. Das genaue Vorgehen hängt dabei vom verwendeten Programmer ab. Bei der Gelegenheit kann man ebenfalls eine Sicherheitskopie des ursprünglichen Flash-Inhalts und des EEPROMs anfertigen. Das EEPROM scheint die MAC-Adresse des Ethernet-Ports zu enthalten.&lt;br /&gt;
&lt;br /&gt;
Entgegen der Spezifikation im Handbuch von Pollin sollten die &#039;&#039;&#039;HFuses auf 0xC2&#039;&#039;&#039; gesetzt werden, d. h. CKOPT-Fuse programmiert (dies ist in der Software Version 1.03 bereits vollzogen). Das sorgt für einen stabilen Betrieb des AVR-Oszillators im &amp;quot;full rail-to-rail swing&amp;quot;-Mode bei 16 MHz. Atmel garantiert ansonsten nur stabilen Betrieb bis 8 MHz. Siehe ATmega32-Datenblatt, Kapitel 8.4, Crystal Oscillator.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Funktionsfähige Konfiguration - AVR-Prog ====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avrprog.png|thumb|right|250px]]&lt;br /&gt;
Benutzer von AVR-Prog können die nachfolgenden Einstellungen für die Lock- und Fuse-Bits verwenden. Hierbei handelt es sich um die ausgelesenen Einstellungen eines funktionsfähigen Controllers. Allerdings sollte, laut Handbuch des AVR-NET-IO-Boards, das Fuse-Bit EESAVE eigentlich gesetzt sein. &lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch per avrdude die Einstellung getroffen werden:&lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U lfuse:w:0xBF:m&lt;br /&gt;
und &lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U hfuse:w:0xC2:m&lt;br /&gt;
&lt;br /&gt;
Anschließend muß noch der Bootloader und die Firmware aktualisiert werden (siehe Handbuch AVR-NET-IO-Board Seite 12 Punkt 3).&lt;br /&gt;
&lt;br /&gt;
=== PC Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
==== PC normalerweise nicht im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
Betreibt man den PC nicht im 192.168.0.0/24 Subnetz, muss er wie folgt umkonfiguriert werden, oder die IP Adresse des Boards wird entsprechend angepasst. ( Siehe Handbuch Seite 14ff. Das ist meist sinnvoller und auch einfacher. ) &lt;br /&gt;
&lt;br /&gt;
Den PC vom normalen Netzwerk abstecken[1]. Zur Umkonfiguration dazu bei Windows XP in der Systemsteuerung &#039;&#039;Netzwerkverbindungen&#039;&#039; aufrufen und die lokale &#039;&#039;LAN-Verbindung&#039;&#039; markieren. Dann in der rechten Leiste &#039;&#039;Einstellungen dieser Verbindung ändern&#039;&#039; aufrufen. &lt;br /&gt;
&lt;br /&gt;
Es erscheint der Dialog &#039;&#039;Eigenschaften von &amp;lt;Verbindungsname&amp;gt;&#039;&#039;. In der Liste im Dialog zu &#039;&#039;Internetprotokoll (TCP/IP)&#039;&#039; gehen. Ein Doppelklick auf den Eintrag öffnet den &#039;&#039;Eigenschaften von Internetprotokoll (TCP/IP)&#039;&#039; Dialog.&lt;br /&gt;
&lt;br /&gt;
In diesem Dialog &#039;&#039;Folgende IP-Adresse verwenden:&#039;&#039; auswählen und zum Beispiel&lt;br /&gt;
&lt;br /&gt;
IP-Adresse: &#039;&#039;&#039;192.168.0.100&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Subnetzmaske: &#039;&#039;&#039;255.255.255.0&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Standardgateway: &#039;&#039;&#039;192.168.0.1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
eingeben. &lt;br /&gt;
&lt;br /&gt;
Anmerkung von bitman:&lt;br /&gt;
[1] Dies ist spätestens ab Windows XP nicht mehr notwendig, wenn das Netz 192.168.0.0/24 noch frei ist. Dann kann man einfach den Client &#039;&#039;zusätzlich&#039;&#039; in diesem Netzwerk zusätzlich einbinden über Einstellungen/Netzwerkverbindungen/Lanverbindung/Eigenschaften/TCP-IP/Eigenschaften/Erweitert/IP-Adresse hinzufügen. Es werden dann eben mehrere IP-Adressen an den NIC gebunden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle geöffneten Dialoge nacheinander mit OK schließen.&lt;br /&gt;
&lt;br /&gt;
Alternativ bietet sich das Umprogrammieren des Boards über die serielle Schnittstelle an. Die Werte für IP-Adresse, Netzmaske und Standard-Gateway werden mit den dokumentierten SETxx-Befehlen geändert, das Board neu gestartet und ans vorhandene Netzwerk gesteckt.&lt;br /&gt;
&lt;br /&gt;
Im EEPROM sind folgende Werte vorprogrammiert:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3EE - 3F3 MAC-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
3F4 - 3F7 GATEWAY&amp;lt;br&amp;gt;&lt;br /&gt;
3F8 - 3FB NETMASK&amp;lt;br&amp;gt;&lt;br /&gt;
3FC - 3FF IP-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PC bereits im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
In diesem Fall muss man prüfen, ob die IP-Adresse 192.168.0.90 bereits im Subnetz verwendet wird. Ist dies der Fall, muss das verwendete Gerät mit dieser IP vorübergehend aus dem Subnetz entfernt werden. Es sei denn, dabei handelt es sich um den PC. In diesem Fall muss er wie zuvor umkonfiguriert werden. Ansonsten kann der unverändert im Netz verbleiben.&lt;br /&gt;
&lt;br /&gt;
Dem AVR-NET-IO gibt man eine neue, zuvor unbenutzte Adresse (siehe unten). Dann kann das abgekoppelte Gerät wieder angeschlossen werden, beziehungsweise der PC zurückkonfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
=== AVR-NET-IO anschließen ===&lt;br /&gt;
&lt;br /&gt;
Musste man den PC umkonfigurieren, so werden jetzt nur der PC und der AVR-NET-IO über Ethernet miteinander verbunden. Je nach Ethernet-Kabel benötigt man dazu einen Switch/Hub oder nicht.&lt;br /&gt;
&lt;br /&gt;
Musste man den PC nicht umkonfigurieren, so kann man den AVR-NET-IO wie einen normalen Rechner an das vorhandenen Netz anschließen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich schließt man die serielle Schnittstelle des AVR-NET-IO an den PC an.&lt;br /&gt;
&lt;br /&gt;
=== Firmware 1.03 einspielen ===&lt;br /&gt;
&lt;br /&gt;
Laut Handbuch sollte der AVR-NET-IO jetzt über Ethernet funktionieren. Ebenso sollte er über die serielle Schnittstelle und ein Terminalprogramm konfigurierbar sein. Beides ist offensichtlich im Auslieferungszustand selten der Fall.&lt;br /&gt;
&lt;br /&gt;
Auch wenn sich Pollins NetServer Software nicht mit dem AVR-NET-IO verbinden lässt, so ist sie jedoch in der Lage eine neue Firmware 1.03 einzuspielen. Das Vorgehen ist im Handbuch auf Seite 12 beschrieben. NetServer präsentiert dabei ein paar einfache Anweisungen denen man folgen sollte.&lt;br /&gt;
&lt;br /&gt;
Wenn sich nach dem scheinbar erfolgreichem Einspielen der Firmware, später nichts tut, so sollte die Firmware mit gesetztem &amp;quot;FailSafe&amp;quot; in der mitgelieferten NetServer-Software nochmals Eingespielt werden.&lt;br /&gt;
&lt;br /&gt;
=== Abschluss ===&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte sich die NetServer Software mit dem AVR-NET-IO über Ethernet verbinden lassen. Dies macht es wiederum möglich, den AVR-NET-IO mit einer anderen IP-Adresse zu versehen. Will man den AVR-NET-IO in einem anderen Subnetz betreiben kann man dies jetzt einstellen.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse neu eingestellt hat, muss man den PC zurückkonfigurieren und kann dann sowohl den AVR-NET-IO und den PC zusammen betreiben.&lt;br /&gt;
&lt;br /&gt;
== Funktionsumfang der Originalsoftware ==&lt;br /&gt;
Die Originalsoftware ist sehr einfach gestrickt. Im AVR Net-IO läuft ein Programm, welches am Port 50290 ein Textschnittstelle bereit stellt. Diese Schnittstelle kann sowohl über die RS232 als auch über Ethernet angesprochen werden kann. Mit diesem Programm ist es möglich, alle Eingänge sowie alle Ausgänge anzusprechen, als auch die Konfiguration der Netzwerkeinstellungen zu verändern. Die Karte versteht dann folgende Befehle:&lt;br /&gt;
* SETPORT&lt;br /&gt;
Einen digitalen Ausgang auf 0 oder 1 schalten&lt;br /&gt;
* GETPORT&lt;br /&gt;
Den Status eines digital Ausgangs/Eingangs abfragen&lt;br /&gt;
* GETSTATUS&lt;br /&gt;
Den Status aller digital Ausgänge abfragen&lt;br /&gt;
* GETADC&lt;br /&gt;
Den Messwert eines analogen Eingangs ermitteln&lt;br /&gt;
* SETIP&lt;br /&gt;
Die IP-Adresse des Boards ändern&lt;br /&gt;
* GETIP&lt;br /&gt;
Die eingestellte IP-Adresse abfragen&lt;br /&gt;
* SETMASK&lt;br /&gt;
Die Netzwerkmaske verändern&lt;br /&gt;
* GETMASK&lt;br /&gt;
Die eingestelle Netzwerkmaske abfragen&lt;br /&gt;
* SETGW&lt;br /&gt;
Das Netzwerk-Gateway verändern &lt;br /&gt;
* GETGW&lt;br /&gt;
Das eingestellte Netzwerk-Gateway abfragen&lt;br /&gt;
* INITLCD&lt;br /&gt;
Ein angeschlossenes LCD initialisieren&lt;br /&gt;
* WRITELCD&lt;br /&gt;
Einen Text am LCD ausgeben&lt;br /&gt;
* CLEARLCD&lt;br /&gt;
Ein angeschlossenes LCD löschen&lt;br /&gt;
* VERSION&lt;br /&gt;
Die Versionsnummer des Programms ausgeben&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es ein auf Java basierendes Programm, welches alternativ zu einem Terminalprogramm benutzt werden kann. Es zeigt den Status der Portpins an und diese lassen sich auch mit der Maus verändern. Die Messwerte der 4 analogen Eingänge werden ständig angezeigt, wobei es möglich ist, durch einstellbare Faktoren die internen Werte gleich in sinnvolle Einheiten umrechnen zu lassen. Diese Messwerte können auch in eine Datei geloggt werden.&lt;br /&gt;
Neben der Möglichkeit, alle Kommandos auch händisch einzugeben, wird das Java-Programm auch benötigt, um einen Firmware-Update der Net-IO Platine durchzuführen.&lt;br /&gt;
Alles in allem ist dieses Frontend aber mehr als Testumgebung anzusehen, um damit die Hardware in Betrieb zu nehmen. Für einen sinnvollen Dauereinsatz ist das Programm deutlich zu einfach und unflexibel gestrickt. Auch kann die grafische Aufbereitung der Anzeige- und Kontrollelemente nicht befriedigen.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Fehler ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[#Hardware-Umbauten_.26_-Verbesserungen|Hardware-Umbauten und Verbesserungen]]&amp;lt;br&amp;gt;&lt;br /&gt;
Käufer berichten von fehlenden Bauteilen im Bausatz (Wannenstecker, Widerstände, Kondensatoren, Induktivitäten). Für Reklamationen: [https://www.pollin.de/shop/kontakt_service/reklamation.html]&lt;br /&gt;
&lt;br /&gt;
* Die Stückliste auf Seite 4 in den Anleitung mit den Versionsangaben&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
:ist falsch. Pollin legt dem Bausatz irgendwann ab September 2008 einen gedruckten Korrekturzettel bei. Die Online-Version der Anleitung ist korrigiert.&lt;br /&gt;
* Im Schaltplan auf Seite 7 in den Anleitungen mit den Versionen&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
** &#039;&#039;Stand 03.09.2008, online, #all, wpe&#039;&#039; (Online)&lt;br /&gt;
:ist eine 25-polige SUB-D Buchse gezeichnet. Geliefert wird und in der Stückliste verzeichnet ist ein Stecker.&lt;br /&gt;
&lt;br /&gt;
* Die September 2008 ausgelieferte Firmware im ATmega32  funktioniert bei vielen nicht und muss erst upgedatet werden (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Im Flash der gelieferten AVR ist anders als beschrieben nur der Bootloader enthalten, die eigentliche Firmware muss erst mit Hilfe der Updatefunktion geladen werden. Wenn zusätzlich auch die Fuses falsch gebrannt sind, dann funktioniert das Update nicht, auch wenn das PC Programm was anderes behauptet.&lt;br /&gt;
&lt;br /&gt;
* Die Fuse-Einstellungen des ausgelieferten ATmega32 entspricht nicht der Anleitung (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Bausatz, gekauft am 27.10.08, Anleitungsversion 19.09.08, ohne Probleme oder erkennbare Fehler zusammengebaut und in Betrieb genommen.&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft 29.09.2008, Pinbelegung des 25 poligen D-Sub &amp;quot;Anschlusses&amp;quot; stimmt nicht mit der Anleitung überein. Der Aufdruck auf der Platine ist falsch. Pin1 &amp;lt;-&amp;gt; Pin13, Pin2 &amp;lt;-&amp;gt; Pin12 usw. Setzt man den D-Sub Stecker ein, so sind dessen Pinnummern korrekt. Bei einem Bausatz gekauft 05/2013 ist dies ebenfalls noch der Fall.&lt;br /&gt;
&lt;br /&gt;
* 3 Bausätze Anf. Oktober 2008 gekauft, bei einem waren 2 LM317 dabei, dafür fehlte der 7805 - aus der Bastelkiste ersetzt. Alle haben jedoch auf Anhieb funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft Ende Januar 2009. Die Lock-Bits (u.a. für PonyProg2000) werden falsch beschrieben. Die in Klammern aufgeführten Werte stimmen bei einem Bit nicht. Die Texte &amp;quot;Programmiert/Unprogrammiert&amp;quot; hingegen schon. Bei den Bauteilen gab es 4 Kondensatoren mit der Aufschrift &amp;quot;220&amp;quot;, ich habe diese durch welche mit 22p ersetzt, da ich nicht sicher war ob wirklich 22p geliefert wurden. Dafür wurden statt einem zwei 7805 und statt einem mindestens vier LM317 mitgeliefert.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 22.4.2009. Alles vollständig, zusammengebaut, läuft. Software-Version 1.03. Für den oben schon genannten Steckverbinder wurde eine Buchse geliefert. Allerdings stimmen die PIN-Nummern im Schaltplan nicht mit den PIN-Nummern auf der Buchse überein (sie sind gespiegelt), daher liefen die Test-LEDs zunächst nicht.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 11.7.2009. Spannungsregler LM317T fehlt, grüne statt roter LED. Ein Kondensator 22pF zu viel. LM317T wurde auf Anfrage kostenlos nachgeliefert (27.7.). Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 24.7.2009. Ein Quarz 16MHz zu viel, ebenfalls grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 20.08.2009. Ein Kondensator 22pF zuviel und grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli &#039;09 gekauft, grüne statt rote LED&lt;br /&gt;
&lt;br /&gt;
* Bausatz 25.09.09 geliefert, grüne Betriebs-LED, ein ELKO zuviel, Fehler 1µF am MAX232 statt 100nF behoben, richtiger C wird mitgeliefert, Aufbau komplett nach Pollin Anleitung durchgeführt, auf Anhieb fehlerfrei!&lt;br /&gt;
&lt;br /&gt;
* Bausatz 17.10.09 geliefert, grüne Betriebs-LED, zwei 100nF Kondensatoren zu wenig. Aufbau und Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz 21.10.09 gekauft, grüne Betriebs-LED. Aufbau problemlos, RS232 läuft nicht. LAN läuft&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, ENC28J60, MAX232 und ATmega32 fehlen, Nachlieferung nach einer Woche&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft,Bauteile komplett.Verbindungsaufbau Seriell klappt erst nach mehreren Versuchen.Problem gelöst:Spannung an MAX und Mega zu niedrig&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dez. 09 gekauft, grüne LED, 100µF Kondensator fehlt, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 09 gekauft, alle teile da nach Einstellen der fusebits lief alles perfekt&lt;br /&gt;
&lt;br /&gt;
* Bausatz Okt. 09 gekauft, ein 100nF Kondensator und 25MHz Quarz fehlten ... hab beim lokalen Elektronikhändler keinen 25Mhz Grundton Quarz sondern nur im 3. Oberton bekommen aber mit R2.2k parallel zum Quarz schwingt er in der Schaltung schön bei 25Mhz. Mit 1µF am MAX232 funktioniert jetzt auch die RS232.&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Feb. 10 gekauft, bei beiden fehlten 7805, L1+L2 je 100µH sowie 4x falscher Wert Kondensator an Max232 vorhanden. Fehlende Bauteile nachgelötet und Funktion getestet. Hat alles einwandfrei funktioniert!!!&lt;br /&gt;
&lt;br /&gt;
* Bausatz März. 10 gekauft, RS232 Printbuchse fehlt, dafür 1x 10pol Wannenstecker zuviel. Grüne LED statt Rot. Funktioniert ansonsten einwandfrei.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Jan. 10 gekauft, gelbe LED statt rot, C14...C17: 10µF, weder seriell noch via Ethernet Konnektivität. Nach Austausch von C14-C17 gegen 1µF, wenigstens serielle Kontaktaufnahme möglich, kein Ethernet auch nach Flash von 1.03 mit NetServer.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Feb. 10 gekauft, Spannungsregler LM317T fehlte&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft, gelbe statt rote LED geliefert, aber Aufbau und inbetriebnahme lt. Handbuch ohne Probleme&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft und gelbe statt rote LED geliefert, funzt wunderbar gemäß Anleitung&lt;br /&gt;
&lt;br /&gt;
* Fertig gelötete Platine gekauft. µC war falsch im Sockel.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - Funktion erst nach Ersetzung des ADM durch nen MAX&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - funktionierte sofort auch mit dem ADM232LJN.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft wurde mit grüner statt roter LED Ausgeliefert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juni 10 gekauft: wurde mit grüner statt roter Netz-LED ausgeliefert, 2x 22pF Kerko zuviel&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli 10 gekauft: 2 Quarze mit 16 MHz geliefert, statt 1x 16MHz und   1x25MHz.&lt;br /&gt;
&lt;br /&gt;
* Bausatz September 10 gekauft: hat sofort funktioniert. 1x 3,3k und 1x 10k Widerstand zuviel. Statt 100nF Kondensatoren wurden 1µF geliefert -&amp;gt; Platzprobleme auf der Platine durch grössere Bauform. LED grün.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober  6 gekauft: alles funktioniert. LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Fertigmodul Oktober 10 gekauft: Auf Anhieb alles funktioniert!&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft: komplett und sofort funktioniert (sogar mit der neusten Pollin Firmware 1.03 schon drauf) LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft. Nach Bezug neuer Feinst-Lötspitzen konnte ich ihn dann auch zusammenlöten. Es hat sofort alles funktioniert, obwohl ich nur 12V bzw. 9V Gleichspannung zur Verfügung hatte, und nicht sicher war, wieviel die Komponenten wirklich benötigen. Der Regler wird auch bei 9V Gleichspannungsversorgung noch sehr warm. Da muss auf jeden Fall ein Kühlkörper dran! Ich habe auch eine grüne LED bekommen, ist mir aber wurscht :-)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dezember 10 gekauft: komplett und funktionierte sofort. Firmware 1.03, grüne LED (Ein Quarz und ein IC-Sockel zu viel)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Januar 2011 gekauft: nur genau die richtige Anzahl Teile dabei, Firmware 1.03, grüne LED, ging auf Anhieb&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Januar 2011 gekauft: beide grüne LED, und 1x doppelter Satz Jumper/Stiftleiste, 22PF und Anschlussklemmen. Rest vollständig und beide haben sofort nach zusammenbau funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: die Diode D5 fehlt, 10 µF gegen 1 µF für MAX232 getauscht, Flash im ATmega32 war programmiert, passende IP-Adr über serielle Schnittstelle eingestellt; ADD-ON: für R1 war 22Ω statt 0,2Ω beigelegt, durch richtigen ersetzt, Beschreibung der LED Bestückung mangelhaft / oe1smc&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 1 Diode zuviel, 2 Spulen fehlen, LED grün. Die fehlenden Spulen wurden durch welche aus der Bastelkiste ersetzt - funktioniert. Der 7805 bekam einen kleinen Kühlkörper spendiert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 2x 10k Widerstände fehlen. Dafür eine Diode zu viel.&lt;br /&gt;
&lt;br /&gt;
* Ende Februar 2011: Zwei Bausaetze an jeweils zwei Adressen, alles in Ordnung.&lt;br /&gt;
&lt;br /&gt;
* Ende März 2011: 2x 25 Mhz Quarz statt 1x16 u. 1x25 Mhz. LED fehlt. Bausatz funktioniert nach Tausch des Quarz den mir mein Freund oe9rsv aus seinem Fundus spendiert hat. Danke auch für die Hilfe beim Fehler suchen.&lt;br /&gt;
&lt;br /&gt;
* Mitte 2010 gekauft: 1x 100nF fehlt&lt;br /&gt;
&lt;br /&gt;
* Mitte Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, Pollin wollte, dass ich das ganze Paket zurückschicke für einen Austausch. Ein 51Ω zu viel. 16Mhz im Handel und 25Mhz vom alten Mobo ausgelötet. Läuft wunderbar.&lt;br /&gt;
&lt;br /&gt;
* Anfang Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, nach kurzer Mail an Pollin (leider ohne Antwort) wurden diese nach ca. 1 Woche in einem Brief nachgeliefert.&lt;br /&gt;
&lt;br /&gt;
* August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: Platine fehlte -&amp;gt; in Nachlieferung&lt;br /&gt;
&lt;br /&gt;
* 30 August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* 06 Sep. 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: 6 Stück bestellt, bei einem haben die 100nF Kondensatoren gefehlt, bei einem zwei LM317 statt 7805 und LM317. Angerufen, 3 Tage später Nachlieferung erhalten.&lt;br /&gt;
&lt;br /&gt;
* Nov. 2011: &#039;&#039;&#039;Net_IO&#039;&#039;&#039; vollständig. Einspielen der Firmware 1.03 war erforderlich. Bei &#039;&#039;&#039;Add-On&#039;&#039;&#039; immer noch falscher Q1 BC548 (NPN) in Stückliste und Lieferung. BC327-40 oder BC328-40 (PNP) nachgefordert. R11 und R24 mitgeliefert, entsprechend Beschreibung V1.1 von Pollin&#039;s Download. Beiliegende Beschreibung war älter, ohne diese Widerstände.&lt;br /&gt;
&lt;br /&gt;
* Ende Nov.2011, alle Teile dabei, Firmware war drauf, sofort funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Anfang Dez.2011, komplett bestückte Platine gekauft. Auf 7805 Kühlkörper gebaut, da er nach 1 Minute schon ausgestiegen ist (LED hat das Pumpen angefangen). Firmware 1.03 musste noch aufgespielt werden danach funktioniert alles einwandfrei. In Betrieb mit 10V DC&lt;br /&gt;
&lt;br /&gt;
* Ende Dez.2011 2xNET-IO und 2xADD bestellt, 4 verschieden volle Kisten bekommen... WSL16 ist mit Verriegelung, geht nicht aufs Board, Jumper fehlen, Spannungsregler doppelt, Poti Löcher zu klein, lsb3 fehlt, SD-Slot hab ich jetzt 3, 100nF hab ich jetzt 4 übrig ... also immer noch lustig. HW-Stand immer noch 1.0 ( gab es überhaupt eine 1.1?)&lt;br /&gt;
&lt;br /&gt;
* Mitte Jan. 2012, 10pol. beide Wannenstecker nicht dabei, Firmware war drauf, sofort funktioniert. 1 LED zuviel. Unproblematische Nachlieferung bei Reklamation (wegen der beiden Wannenstecker kam ein PAKET!).&lt;br /&gt;
&lt;br /&gt;
* Ende Feb. 2012, Um die PHP-Scripte über öffentlichen Webserver zu betreiben muss mit SETGW die Gateway-Adresse des lokalen Routers eingetragen werden. Bei der NAT im Router sollte z.B. Port 8080 auf den internen Port 50290 umgeleitet werden, da manche Provider diesen Port für die Socket-Kommunikation nicht zulassen.&lt;br /&gt;
&lt;br /&gt;
* Juli 2012, Bausatz vollständig, Nach Aufbau sofort den 7805 mit einem kleinen Kühlkörper versehen, da er sonst thermisch überlastet wird! hat weder auf LAN noch RS232 reagiert, musste erst Update einspielen (Download Pollin), danach funktionierte alles einwandfrei!&lt;br /&gt;
&lt;br /&gt;
* Januar 2013, Bausatz vollständig, 7805 mit Kühlkörper versehen, nach Aufbau ohne Probleme funktioniert&lt;br /&gt;
&lt;br /&gt;
* Februar 2013, Bausatz komplett, Aufbau ohne Probleme, 7805 nur leicht warm, LAN funktioniert, RS232 noch ohne Funktion.&lt;br /&gt;
&lt;br /&gt;
* Oktober 2013, Bausatz komplett laut Stückliste, problemloser Aufbau, 7805 leicht warm am 10V DC. RS232 und LAN funktionierten auf Anhieb.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die nachfolgend genannten Änderungen sind in der Bauanleitung V1.1 mit Stand 07.12.2011 (Wichtig! Es gibt mehr als eine V1.1 ...) bereits berücksichtigt, R3 wird allerdings mit 3,6kΩ angegeben.&lt;br /&gt;
&lt;br /&gt;
Um bei einem Neuaufbau parallele Widerstände zu vermeiden, sollten folgende Änderungen auf dem Addon-Board gemacht werden:&lt;br /&gt;
*R2 1,5kΩ ersetzen mit 2kΩ&lt;br /&gt;
*R3 1,8K ersetzen mit 3,3kΩ&lt;br /&gt;
*R19 470kΩ ersetzen zu 470Ω&lt;br /&gt;
*Q1 BC548 ersetzen durch BC327 oder BC328 (Hauptsache PNP! und mehr als 100mA)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039; beim Anschluß eines LC-Displays an J5 (über I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C, PCF 8574): Vcc/5V liegt an Pin 1, GND an Pin 2 von J5. Es gibt etliche Displays mit abweichender Belegung Pin 1 GND und Pin 2 Vcc!&lt;br /&gt;
&lt;br /&gt;
Auch beim Add-On gibt es fehlende oder falsche Bauteile im Bausatz und Fehler im Schaltplan und auf der Platine:&lt;br /&gt;
&lt;br /&gt;
*Stand Feb. 2011: R2 wird mit 2,2kΩ und R3 wird mit 3,6kΩ ausgeliefert. Somit werden die 3,3 V richtig erzeugt. R19 hat 470Ω. Der ISP-Anschluß ist nicht vollständig durchgeschleift, es besteht keine Verbindung der RESET-Leitung zwischen ISP und ISP1 (Abhilfe: Drahtbrücke einlöten, [http://www.mikrocontroller.net/topic/161354#1600385 Quelle]). &lt;br /&gt;
&lt;br /&gt;
*Stand Nov. 2011: bei mir ist die RESET-Leitung korrekt zw. ISP und ISP1 verbunden. Es gibt jetzt auch einen R24 (470Ohm) und R11 (1KOhm), der in der bei mir mitgelieferten Bauanleitung fehlt, in der zum Download (V1.1) angebotenen  aber drin steht. Es wird immer noch der falsche Q1 BC548C (NPN) mitgeliefert. Das Schaltsymbol für einen PNP ist richtig im Schaltplan gezeichnet.&lt;br /&gt;
&lt;br /&gt;
*Stand Dez. 2011: R24 (470Ω) kann mit 0Ω ersetzt und R11 (1kΩ) völlig weggelassen werden. Diese Widerstände bilden einen (eigentlich überflüssigen) Spannungsteiler in der MISO Leitung. Der Spannungsteiler hat allerdings auch eine Schutzfunktion, falls weitere Slaves am SPI-Bus hängen, die 5V-Pegel nutzen, z. B. ISP-Programmierer. (siehe [http://son.ffdf-clan.de/include.php?path=forumsthread&amp;amp;threadid=1167&amp;amp;postid=9203 1284p: Board, ich oder beide verwirrt?])&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Bausatz mit Stecker anstelle einer Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s dafür aber 3 Poti zuviel.&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Ebenfalls Bausatz mit Sub-D-Stecker statt Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s und 3 Potis zuviel.&lt;br /&gt;
&lt;br /&gt;
*Stand März 2013: Die Belegung der 25 pol. Sub-D-Buchse im Schaltplan der Anleitung V1.1 (und früher) ist falsch dargestellt. SCL und SDA des I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C-Busses liegen wie beim AVR-NET-IO Board auf Pin 2 bzw. 3 und nicht auf Pin 11 bzw. 12 der Buchse. Ein 1:1 verdrahtetes Kabel funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Andere Software für den Client-PC ==&lt;br /&gt;
=== NetIOLib ===&lt;br /&gt;
&lt;br /&gt;
In C# geschriebene Bibliothek zur Ansteuerung der Platine im Orginalzustand. Inkl. Beispielsoftware und Quellcode (GNU GPL) &lt;br /&gt;
&lt;br /&gt;
Links gehen nicht:&lt;br /&gt;
DLL: [http://www.tware.org/downloads/NetIOLib_dll.zip Download-Link]&lt;br /&gt;
Source: [http://www.tware.org/downloads/NetIOLib_src.zip Download-Link]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Multi-Control ===&lt;br /&gt;
Mti dem E2000-NET-IO-Multi-Control ist es möglich, die vom dem E2000-NET-IO-Designer erstellten Projekte zu öffnen und die AVR-NET-IO&#039;s zu steuern. &lt;br /&gt;
&lt;br /&gt;
Die Anwendung liegt dem [http://www.mikrocontroller.net/articles/AVR_Net-IO_Bausatz_von_Pollin#E2000-NET-IO-Designer_.28Windows.5BXP.2F7.5D_.2F_Android.29 E2000-NET-IO-Designer] bei.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== ControlIO ===&lt;br /&gt;
Einfache Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://www.mikrocontroller.net/topic/149695&lt;br /&gt;
&lt;br /&gt;
=== JAVA Lib ===&lt;br /&gt;
Einfache Java-Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://son.ffdf-clan.de/?path=forumsthread&amp;amp;threadid=611&lt;br /&gt;
&lt;br /&gt;
=== PHP ===&lt;br /&gt;
PHP Klasse zur Ansteuerung mit der Originalfirmware. (Opensource Lizenz)&lt;br /&gt;
http://blog.coldtobi.de/1_coldtobis_blog/archive/298_pollin_net-io_php_library.html&lt;br /&gt;
&lt;br /&gt;
PHP Funktionen zum Ansteuern der Originalfirmware. (Free for All Lizenz)&lt;br /&gt;
http://defcon-cc.dyndns.org/wiki/index.php/Pollin_AVR-NetIO_PHP_Wrapper&lt;br /&gt;
&lt;br /&gt;
== Clients für Smartphones ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== AVR NET IO Control (Windows Phone 7.5,7.8,8.0) ===&lt;br /&gt;
&lt;br /&gt;
Freie App zur steuerung des AVR NET IO.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;App im Market: [http://www.windowsphone.com/de-de/store/app/avr-net-io-controll/030d107f-9a27-4a62-ac8f-cb74e79c0500 AVR NET IO Control]&#039;&#039;&#039;&lt;br /&gt;
=== App NetIO (Windows Mobile 6.5) ===&lt;br /&gt;
Frei verfügbare App für Windows Mobile zur Ansteuerung mit der Orginalsoftware. Das HTC HD2 wird damit zur Fernsteuerung für das AVR Net-IO Board.&lt;br /&gt;
http://www.heesch.net/netio.aspx&lt;br /&gt;
&lt;br /&gt;
=== NET-IO Control (Android) ===&lt;br /&gt;
Eine Application für das Android Betriebssystem zur Steuerung des AVR Net-IO Boards. Es ist möglich, alle Ausgänge zu steuern und alle Eingänge anzuzeigen. Die Analogen Eingänge können mit einem Berechnungsfaktor versehen werden. &#039;&#039;Geplant ist noch ein Offsetwert.&#039;&#039; Außerdem kann jedem analogen Wert eine Einheit zugeordnet werden. Die Ausgänge können in der neusten Version in einen Tastermodus gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:NET-IO-Control.png|200px]] [[Datei:NET-IO-Control2.png|200px]] [[Datei:NET-IO-Control3.png|200px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Trial-Version: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControlTRAIL Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(In dieser Probe Version können nur 1 Output und 2 Inputs gesteuert werden)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vollversion: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControl Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(Kostet im Google-Play 3,00 €)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Designer (Windows[XP/7] / Android) ===&lt;br /&gt;
[[Datei:E2000-NET-IO-Designer.png|400px|right]]&lt;br /&gt;
Mit dem E2000-NET-IO-Designer ist es möglich, eine grafische Oberfläche für die NET-IOs von Pollin zu erstellen. Dafür werden Knöpfe, Texte und Anzeigebilder zur Verfügung gestellt. Jedes dieser Element kann &amp;quot;Aufgaben&amp;quot; übernehmen um die NET-IOs zu steuern oder einen Status des NET-IO anzuzeigen. Weitere Icons können von dem Benutzer selbst in den entsprechenden Ordner gelegt werden und in die Oberfläche eingebunden werden. Es können mehrere Seiten designt werden die durch einen selbst positionierten Knopf erreichbar sind. Die Android Application arbeitet somit im Fullscreen-Modus. Des weiteren ist es möglich, mehrere NET-IO&#039;s in einem Projekt zu benutzen. Nach dem erstellen der grafischen Oberfläche mit dem E2000-NET-IO-Designer, kann mit der Android APP das Projekt einfach gedownloaded werden. Dafür baut die APP eine Verbindung zum E2000-NET-IO-Designer auf und läd alle benötigten Dateien herunter (Achtung: Firewall-Einstellungen beachten). Die designten Oberflächen können außerdem noch mit der E2000-NET-IO-Multi-Control.exe auf dem Computer ausgeführt werden. Dadurch ist es Möglich, seine NET-IOs vom PC aus zu steuern über eine selbst designte Oberfläche. &lt;br /&gt;
&lt;br /&gt;
Zum Ausführen des E2000-NET-IO-Designer muss .NET Framework 4.0 installiert sein und es muss eine Internetverbindung exisitieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Features:&#039;&#039;&#039;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Grafische Oberfläche am PC erstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verwendung von eigenen Button- und Hintergrundbildern&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designbare Anzeige von Digital- und Analogwerten&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Steuern von mehreren AVR-NET-IO Boards&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Unterstützung der E2000-NET-IO Firmware (mehere Boards gleichzeitig)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designte Steuerungen können auf dem PC oder dem Handy ausgeführt werden (Android)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Schnelle Verbindung&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Einfache übertragung auf das Handy durch Projekt Download&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/downloads.php?id=44 Download E2000-NET-IO-Designer]&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/hilfe/E2000Netdesigneruebersicht.html Online Hilfe]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== NetIO ( iPhone &amp;amp; Android ) ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;float: left; margin-right: 20px&amp;quot;&amp;gt;[[Datei:Netio_logo.png|70px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schöne universelle Fernbedienung für das Board (iOS / Android). Konfigurierbar über einen Browserbasierten Editor. &amp;lt;br&amp;gt;&lt;br /&gt;
Unterstützt TCP socket Verbindungen, kann http request absetzen und kann auch Webseiten einbinden (z.B. IP-Kameras). Kann mehrere &lt;br /&gt;
Boards gleichzeitig steuern! Einfach super schnell ins Projekt eingebaut... Es gibt Buttons (die auch als Taster konfiguriert werden können), Slider, Switches und einfache Labels. Dinge können geschaltet oder Daten ausgelesen und mit regex dargstellt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;float: right&#039;&amp;gt;&lt;br /&gt;
[[Datei:Netio_TV.png|190px]]&lt;br /&gt;
[[Datei:Netio_iphone5.png|190px]]&lt;br /&gt;
[[Datei:NetIO_appflow.jpg|190px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
AppStore Link: http://itunes.apple.com/app/netio/id464924297?mt=8 &amp;lt;br/&amp;gt;&lt;br /&gt;
Google Play Link: https://play.google.com/store/apps/details?id=com.luvago.netio &amp;lt;br/&amp;gt;&lt;br /&gt;
Die gleiche Konfiguration kann auch mit einem [https://github.com/davideickhoff/NetIO-OSX-Dashboard-Widget OSX Widget] benutzt werden. &amp;lt;br/&amp;gt;&lt;br /&gt;
Webseite: http://netio.davideickhoff.de &lt;br /&gt;
&lt;br /&gt;
Hinweis: &amp;lt;br&amp;gt; Man findet es unter &amp;quot;NetIO&amp;quot; im Store, im Icon selbst und in iTunes wird es als &amp;quot;Controller&amp;quot; angezeigt. &amp;lt;br&amp;gt;&lt;br /&gt;
Die eigene Konfiguration kann man mit dem [http://netio.davideickhoff.de/editor Online Editor] vorher am PC erstellen.&lt;br /&gt;
Eine fertige [http://netio.davideickhoff.de/editor/?config=pollin AVR-NET-IO Konfiguration] gibt es als funktionierendes Beispiel schon als Preset.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;div style=&#039;clear: both&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AVR Net IO (iPhone) ===&lt;br /&gt;
[[Datei:AVRNetIO-Screenshot1.png|160px|rechts]]&lt;br /&gt;
Update 15.12.2011: Die Neue Version 1.3 ist seit gestern im AppStore. Fehlerkorrekturen und ein robusteres Handling machen die App nun zur universellen AVR-Net-IO-Steuerung.&lt;br /&gt;
&lt;br /&gt;
Mit der [http://itunes.apple.com/de/app/avr-net-io/id460991760?mt=8 iPhone App AVR-Net-IO] kann das Board ferngesteuert werden. Die Fernsteuerung umfasst in der Version 1.1 folgende Funktionen:&amp;lt;br&amp;gt;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ADC-Werte zyklisch auslesen und darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Inputs zyklisch darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Outputs können über Buttons geschaltet werden. Die Werte der Digital-Outputs werden zuerst ausgelesen und zeigen den zuletzt gesetzten Wert an.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Terminal-Modus: Hier können beliebige AVR-Net-IO-Befehle eingegeben werden. Das Ergebnis wird 1:1 angezeigt, wie es vom Board kommt. Hilfreich für Tests bei Eigenentwicklungen und Konfigurationen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Mehr Infos gibt&#039;s dazu direkt von den Entwicklern auf [http://www.facebook.com/pages/AVR-Net-IO/187799687958255?ref=nf AVR-net-IO Facebook-Page] oder direkt auf deren Homepage [http://www.ondics.de/apps/1001/ Homepage].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Hinweis: MIt der aktuellen Version 1.1 gibt es noch bei manchen Boards Probleme beim auslesen und anzeigen der ADC- und Digital-Werte. Das Terminal funzt problemlos. Die Entwickler kümmern sich gerade drum und haben einen baldigen Update versprochen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Andere Software statt der Originalsoftware von Pollin ==&lt;br /&gt;
&lt;br /&gt;
Die Umrüstung auf einen Webserver durch Austausch der Software (und ev. des ATmega32) bietet sich an. Kleiner Hinweis dabei: wenn zum Flashen ein ISP-Adapter verwendet wird, diesen unbedingt vor dem Start der neuen Software abziehen! Der ISP arbeitet nämlich über dieselbe SPI-Schnittstelle über die auch der ENC28J60 angesteuert wird. Ein eventuell noch angeschlossener, wenn auch passiver ISP-Adapter stört diese Kommunikation, d.h. das Programm an sich scheint zu laufen, aber die Ethernet-Schnittstelle funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
=== E2000 - Logik ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:E2000-Logik-Bedienoberflaeche.jpg|500px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Anwenderfreundliche Logik-Software von Elektronik2000.de zur Steuerung des AVR-NET-IO. Der ATMEGA32 wird durch einen ATMEGA644 ersetzt und mit der E2000-Firmware beschrieben. Nun ist es möglich in der E2000-Logik Software eine Logikschaltung zu erstellen und diese auf das NET-IO-Board zu übertragen. Dort wird diese Schaltung simuliert. Dies funktioniert komplett ohne einen Computer.&lt;br /&gt;
&lt;br /&gt;
Durch ein Erweiterungsboard von Elektronik2000.de ist es auch möglich, das Board ohne Internet laufen zu lassen eine RTC (RealTimeClock) übernimmt dabei die Uhrzeitgesteuerten Funktionen. Auf dieser Erweiterung ist auch ein EEPROM integriert, um Firmware-Updates über Netzwerk einzuspielen (in Zukunft). Diese Erweiterung bietet außerdem die Anbindung an das E2000-Bus-System, durch das es möglich ist das AVR-NET-IO-Board durch weitere Ein- und Ausgänge zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Das Designen von Schaltaufgaben wird in diesem Programm grafisch dargestellt, durch das ein einfaches Anpassen seiner Logikschaltungen möglich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Steuerung des E2000-NET-IO ist möglich durch den Intregrieten Webserver, die PC-Software (E2000-NET-IO Control) oder der Androidsoftware. All diese Können gleichzeitig auf das E2000-NET-IO zugreifen und Funktionen ausführen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es auf der Entwicklerseite: [http://www.elektronik2000.de Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== Bascom Version von Hütti ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://bascom-forum.de/index.php/topic,1781.45.html )&lt;br /&gt;
dort am Ende der Seite.&lt;br /&gt;
&lt;br /&gt;
=== Ben&#039;s Bascom Quellcode ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://members.home.nl/bzijlstra/software/examples/enc28j60.htm )&lt;br /&gt;
&lt;br /&gt;
Muss aber für Bascom 1.11.9.3 angepasst werden, siehe Code von Hütti !&lt;br /&gt;
&lt;br /&gt;
=== U. Radigs Webserver ===&lt;br /&gt;
&lt;br /&gt;
Angepasster Sourcecode von U.Radig: http://www.mikrocontroller.net/attachment/40027/Webserver_MEGA32.hex&lt;br /&gt;
oder selbst anpassen: &lt;br /&gt;
Ändere in der Datei ENC28J60.H:&lt;br /&gt;
 #define ENC28J60_PIN_CS    4&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#988386)&lt;br /&gt;
&lt;br /&gt;
Temporären Dateien (*.d, *,lst,*.o) vorher im Verzeichnis löschen &#039;&#039;make clean&#039;&#039;, damit neu compiliert wird.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.0.99&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: uli1&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den orginal SourceCode gibt&#039;s übrigens hier:http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex&lt;br /&gt;
&lt;br /&gt;
Bei den Fuses BOOTRST ausschalten, da die Software keinen Bootloader enthält.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.1.90&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: tim&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://beitz-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://pieper-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterentwicklung des Radig-Codes von RoBue: &amp;lt;br&amp;gt;&lt;br /&gt;
- 1-Wire-Unterstützung (Anschlus an PORTA7) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA0-3 digitaler Eingang (ein/aus) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA4-6 analoger Eingang (0 - 1023) &amp;lt;br&amp;gt;&lt;br /&gt;
- LCD an PORTC &amp;lt;br&amp;gt;&lt;br /&gt;
- Schalten in Abhängigkeit von Temperatur und analogem Wert &amp;lt;br&amp;gt;&lt;br /&gt;
- (Teilweise) Administration über Weboberfläche &amp;lt;br&amp;gt;&lt;br /&gt;
- Erweiterung des cmd-Befehlsatzes für telnet/rs232 &amp;lt;br&amp;gt;&lt;br /&gt;
Gedacht ist der Einsatz des AVR-NET-IO-Bausatzes für Heizungs- oder Haussteuerung) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test: http://avrboard.eluhost.de/&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Quelle:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/43307/AVR-NET-IO_RoBue_V1.3.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/44569/AVR-NET-IO_RoBue_V1.4.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/46720/AVR-NET-IO_RoBue_1.5-final_hoffentlich_.zip)&lt;br /&gt;
&lt;br /&gt;
Bei der Ver 1.5 sind die Ports PD2+3 fürs 4bit LCD (Ext.) vertauscht ! Gruß B.P&lt;br /&gt;
&lt;br /&gt;
=== Simon Ks Webserver (uip-Stack) ===&lt;br /&gt;
Angepasster Sourcecode von Simon K: http://www.mikrocontroller.net/attachment/39939/uWebSrv.zip&lt;br /&gt;
IP: 192.168.0.93:8080&amp;lt;br&amp;gt;&lt;br /&gt;
Um diesen Code mit einem Atmega1284P verwenden zu können, muss in der main.c in Zeile 38, &amp;quot;TIMSK&amp;quot; durch &amp;quot;TIMSK1&amp;quot; ersetzt werden.&lt;br /&gt;
Die Fusebits für den Atmega1284p ohne Bootloader sind:&lt;br /&gt;
lfuse=0xFF, hfuse=0xD9, efuse=0xFF&lt;br /&gt;
&lt;br /&gt;
=== Ethersex Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.ethersex.de - Einfach für atmega32 compilieren und funktioniert.&lt;br /&gt;
&lt;br /&gt;
=== Etherrape Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.lochraster.org/etherrape/ &lt;br /&gt;
&lt;br /&gt;
ist in jedem Fall hier auch zu erwähnen zumal es sich beim etherrape um das Ursprungsprojekt von ethersex handelt.&lt;br /&gt;
Es scheint aber bei der Weiterentwicklung wenig zu passieren.&lt;br /&gt;
Ausführliche Dokumentation findet sich unter http://wiki.lochraster.org/wiki/Etherrape&lt;br /&gt;
&lt;br /&gt;
=== Mini SRCP Server (kommerziell, Closed-Source)===&lt;br /&gt;
&lt;br /&gt;
Damit wird die Platine zu einer Modellbahnsteuerung, die&lt;br /&gt;
über das Netzwerkprotokoll SRCP mit verschiedenen Programmen&lt;br /&gt;
gesteuert werden kann.&lt;br /&gt;
&lt;br /&gt;
[http://www.7soft.de/de/mini_srcp_server/index.html Infoseite] zur Hardware&lt;br /&gt;
und das zugrundeliegende [http://www.der-moba.de/index.php/Digitalprojekt Digitalprojekt].&lt;br /&gt;
&lt;br /&gt;
=== Avr ArtNET-Node ===&lt;br /&gt;
&lt;br /&gt;
Hiermit kann die Platine zu einem Art-Net Node werden, mit dem sich ein DMX-Universe über Ethernet übertragen lässt. Basiert auf den Quellen von Ulrich Radig.&lt;br /&gt;
&lt;br /&gt;
Dokumentation: [http://www.dmxcontrol.de/wiki/Art-Net-Node_f%C3%BCr_25_Euro Art-Net-Node für 25 Euro]&lt;br /&gt;
&lt;br /&gt;
=== Webserver von G. Menke ===&lt;br /&gt;
&lt;br /&gt;
Ein Webserver (basierend auf den Sourcen von U. Radig), der so angepasst ist, dass alle Ein- und Ausgänge wie bei der originalen Pollin-Software genutzt werden können (8xDIGOUT, 4xDIGIN, 4xADIN). Der Webserver kann daher direkt auf das Net-IO geladen werden. Im ZIP-File sind ein ReadMe und alle C-Sourcen enthalten.&amp;lt;br&amp;gt;&amp;lt;i&amp;gt;Download&amp;lt;/i&amp;gt;:&lt;br /&gt;
[http://gm.stream-center.de/webserver/ Webserver mit passender IO]&lt;br /&gt;
&lt;br /&gt;
=== OpenMCP ===&lt;br /&gt;
&lt;br /&gt;
Tolles Projekt, welches viele Features bietet und stabil läuft. Hervorzuheben ist die Übersichtlichkeit der Programmteile/Module und die vielleicht nicht ganz komplette Dokumentation. Man merkt, dass viel Arbeit und Liebe in diesem Projekt steckt. Herausgekommen ist dabei eine einfach zu handhabende Entwicklungsumgebung. Anfänger können, dank des gut durchdachten CGI-Systems, welches sich um alle wichtigen Sachen kümmert, leicht eigene CGI implementieren. Alle Ausgaben erfolgen nur mit printf über die Standardausgabe und werden automatisch richtig per Netzwerk übertragen, dadurch ist es auch für den Anfänger recht gut geeignet, da man sich nicht mit der Netzwerkprogrammierung auseinander setzen muss.&lt;br /&gt;
&lt;br /&gt;
Die Software belegt im Moment (Stand Juli 2010) ca. 55 Kb im Flash, so dass man das Board mit einem grösseren µC (z.B. ATMega644) aufrüsten muss.&lt;br /&gt;
&lt;br /&gt;
[http://wiki.neo-guerillaz.de Projekt und Doku]&lt;br /&gt;
&lt;br /&gt;
Der Autor stellt zwei über das Internet erreichbare Testboards bereit unter http://www.neo-guerillaz.de:81 und http://www.neo-guerillaz.de:82 die beide unter OpenMCP laufen, je auf einen AVR-NETIO mit einem ATmega644 und dem eigentlichen Board mit einem ATmega2561. Zusätzlich ist gerade eine Version für das myAVR in Arbeit die schon ordentlich Fortschritte macht.&lt;br /&gt;
&lt;br /&gt;
=== OpenMLP ===&lt;br /&gt;
Auf openMCP basierender Port nach [http://avr.myluna.de LunaAVR] (GPL). Umfangreiche Funktionalität und direkte Anpassung an die Sprache. Abgespeckte Version auch auf Atmega32 lauffähig. Die Original-Dokumentation kann zum Großteil hergenommen werden. Einige Zusatzfeatures. Leichte Konfiguration und guter Einstieg für Anfänger und zum Verständnis der Serverfunktionalität.&lt;br /&gt;
&lt;br /&gt;
[http://avr.myluna.de/doku.php?id=de:openmlp Artikelseite]&lt;br /&gt;
[http://forum.myluna.de/viewtopic.php?f=8&amp;amp;t=13 Forum]&lt;br /&gt;
&lt;br /&gt;
=== ENC28J60 I/O-Webserver von Thomas Heldt ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul-Webserver (Softwarekompatibel zum Pollin Webserver), der durch div. Module erweitert werden kann, Software in Bascom basierend auf dem Code von Ben Zijlsta wurde erweitert und angepasst:&lt;br /&gt;
&lt;br /&gt;
[http://mikrocontroller.heldt.eu/index.php?page=enc28j60-io-webserver Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
=== AVR-Netino ===&lt;br /&gt;
[http://code.google.com/p/avr-netino/ Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
Arduino fürs Net-IO&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/topic/109988&lt;br /&gt;
* [http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en022889 ENC28J60 Produktseite]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/DeviceDoc/39662c.pdf ENC28J60 Datenblatt(pdf)]&lt;br /&gt;
* [http://son.ffdf-clan.de Forum für AVR-Net-IO]&lt;br /&gt;
* [http://avr.myluna.de/doku.php?id=de:openmlp LunaAVR openMLP ]&lt;br /&gt;
* [http://bascom-forum.de/index.php/topic,1781.0.html Bascom Forum ]&lt;br /&gt;
* [http://hobbyelektronik.org/w/index.php/AVR-NET-IO-Shield Shield für den NET-IO]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Category:Ethernet|P]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78929</id>
		<title>AVR Net-IO Bausatz von Pollin</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78929"/>
		<updated>2013-10-13T19:55:56Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier steht eine Beschreibung des Pollin Bausatzes [http://www.pollin.de/shop/shop.php?cf=detail.php&amp;amp;pg=NQ==&amp;amp;a=MTQ5OTgxOTk= AVR-NET-IO. Best.Nr. 810 058], oder als aufgebautes Fertigmodul, Best.Nr. 810 073. &lt;br /&gt;
&lt;br /&gt;
Einige Features: Ethernet-Platine mit ATmega32 und Netzwerkcontroller ENC28J60. Die Platine verfügt über 8 digitale Ausgänge, 4 digitale und 4 ADC-Eingänge, welche alle über einen Netzwerkanschluss (TCP/IP) abgerufen bzw. geschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AVR-NET-IO ADD-ON.JPG|thumb|right|400px|AVR-NET-IO (links) mit zusätzlicher SUB-D Anschlussplatine (rechts, nicht im Lieferumfang)und Add-On-Board (oben, mit aufgelötetem RFM12-433-Modul, beides nicht im Lieferumfang). Ebenso ist zusätzlich ein nicht im Lieferumfang enthaltener kleiner Kühlkörper auf einem der Spannungsregler montiert und die Schraubklemmen zur Stromversorgung wurden durch Buchsen ersetzt.]]&lt;br /&gt;
&lt;br /&gt;
== Technische Daten ==&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 9 V AC/DC&lt;br /&gt;
* Stromaufnahme ca. 190 mA&lt;br /&gt;
* 8 digitale Ausgänge (0/5 V) [PC0-PC7 an J3]&lt;br /&gt;
* 4 digitale Eingänge (0/5 V) [PA0-PA3 an J3]&lt;br /&gt;
* 4 ADC-Eingänge (10 Bit) [PA4-PA7 an Schraubklemmen]&lt;br /&gt;
* LCD-Anschluss (HD44780 komp. Controller nötig) [PD2-7,PB0,PB3 an EXT]&lt;br /&gt;
* [[ENC28J60]]&lt;br /&gt;
* [http://www.atmel.com/dyn/Products/Product_card.asp?part_id=2014 ATmega32] Mikrocontroller&lt;br /&gt;
&lt;br /&gt;
Maße (L×B×H): 108×76×22 mm.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Die Schaltung des AVR-NET-IO ist recht einfach:&lt;br /&gt;
* Ein ATmega32 Mikrocontroller enthält die gesamte Software&lt;br /&gt;
* Ein ENC28J60 Ethernet-Controller für das Senden und Empfangen von Ethernet Frames (MAC und PHY Ethernet Layer) ist über [[SPI]] mit dem ATmega32 verbunden&lt;br /&gt;
* Ein Ethernet RJ-45 MagJack TRJ 0011 BA NL von [http://www.trxcom.com/ Trxcom] mit eingebautem Übertrager und Anzeige-LEDs am ENC28J60.&lt;br /&gt;
* Ein MAX232 für die serielle Schnittstelle&lt;br /&gt;
* Zwei Spannungsregler, 5 V und 3,3 V&lt;br /&gt;
* &amp;quot;Hühnerfutter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Fast alle I/O Pins des ATmega32 sind irgendwo auf Anschlüssen herausgeführt. Entweder auf dem SUB-D Stecker, dem EXT oder ISP Wannensteckern oder den blauen Anschlussklemmen. Eine Schutzbeschaltung gibt es nicht.&lt;br /&gt;
&lt;br /&gt;
Die blauen Anschlussklemmen haben eine Nut und eine Feder mit denen man&lt;br /&gt;
sie zusammenstecken kann, dadurch ist das Anlöten wesentlich leichter&lt;br /&gt;
und sie stehen auch sauber in der Reihe.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
Seit Januar 2010 gibt es auch eine Erweiterungsplatine &lt;br /&gt;
&lt;br /&gt;
[http://www.pollin.de/shop/dt/Nzg4OTgxOTk-/Bausaetze/Diverse/Bausatz_Add_on_fuer_AVR_NET_IO.html Add-on für AVR-NET-IO-Board Best.Nr. 810 112]&lt;br /&gt;
&lt;br /&gt;
Diese Platine erweitert das NET-IO um:&lt;br /&gt;
&lt;br /&gt;
* SD-Karten-Slot über SPI&lt;br /&gt;
* Display über PCF 8574&lt;br /&gt;
* Infrarot-Empfang&lt;br /&gt;
* [[RFM12]] Funkmodul (nicht im Lieferumfang enthalten)&lt;br /&gt;
&lt;br /&gt;
Eine Aufstellung bekannter Fehler findet sich weiter unten [[#Bekannte Fehler|Bekannte Fehler - Erweiterungplatine]] &lt;br /&gt;
&lt;br /&gt;
Erste Erfahrungsberichte im Forum http://www.mikrocontroller.net/topic/161354&lt;br /&gt;
&lt;br /&gt;
=== Hardware-Umbauten &amp;amp; -Verbesserungen ===&lt;br /&gt;
&lt;br /&gt;
* Kühlkörper auf dem 7805 - (Vorsicht: Der LM317T ist rückseitig spannungsführend. Den Kühlkörper also keinesfalls an beide Spannungsregler anschließen!)&lt;br /&gt;
* MAX232 nach anfänglicher Konfiguration nicht bestücken um Strom zu sparen oder um zwei weitere I/O-Pins zu gewinnen&lt;br /&gt;
* 10µF-Elkos für MAX232N (C14-C17) durch 1µF ersetzen. Eine 10µF-Version für den MAX232 gibt es nicht. Die 10µF-Elkos können auch Ursache einer nicht funktionierenden RS232 sein.&lt;br /&gt;
** Laut Spezifikation sind auch mehr als 1µF erlaubt. Selbst Atmel verwendet beim STK500 10µF. Dies führt keinesfalls dazu, dass die RS232 nicht mehr funktioniert.&lt;br /&gt;
* Die IC-Fassungen aus &amp;quot;Pollins Resterampe&amp;quot; durch Fassungen mit gedrehten Kontakten ersetzen. &lt;br /&gt;
* &#039;&#039;Netz&#039;&#039; LED nicht bestücken oder größere Widerstände einlöten um Strom zu sparen (R3)&lt;br /&gt;
* Vorwiderstände der Ethernet-LEDs größer machen (z.&amp;amp;nbsp;B. verdoppeln) um Strom zu sparen (R6,R7)&lt;br /&gt;
* Linear-Spannungsregler ersetzen&lt;br /&gt;
* Kondensator an AREF-Pin des ATmega32 (ATmega32 Datenblatt) (100nF gegen Masse)&lt;br /&gt;
* Kondensator an den RESET-Pin des ATmega32 ([http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Atmel Application Note AVR042: AVR Hardware Design Considerations]) Wenn man diese Quelle genauer liest, ist das aber eher unnötig. - Kondensator bei selbstbau-ISP empfehlenswert.&lt;br /&gt;
* Umbau auf 3,3 V:&lt;br /&gt;
** Ersatz der Spannungsregler durch einen einzigen 3,3 V Regler&lt;br /&gt;
** Anpassen (verkleinern) des LED-Vorwiderstands R3 für 3,3 Volt Betrieb&lt;br /&gt;
** Reduktion der Taktfrequenz (Austausch von Q2) auf den bei 3,3V erlaubten Bereich des ATmega32 ( ATmega32(L)  3.3V /8.0 Mhz Takt )&lt;br /&gt;
** Ersatz des MAX232 durch einen MAX3232&lt;br /&gt;
[[Bild:POWER.JPG|thumb|400px|5V Stromversorgung über USB Kabel, ohne 5 V Spannungsregler und Gleichrichterdioden, Vorsicht: kein Verpolungsschutz!  ]]&lt;br /&gt;
* ATmega32 vom ENC28J60 takten (OSC2)&lt;br /&gt;
* Betrieb mit Gleichspannung:&lt;br /&gt;
** Dioden D2 und D5 durch Drahtbrücken ersetzen, D1 und D4 nicht bestücken (komplette Entfernung des Brückengleichrichters, beinhaltet Verlust des Verpolungsschutzes)&lt;br /&gt;
** Diode D2 bestücken, D5 durch Drahtbrücke ersetzen, D1 und D4 nicht bestücken (Brückengleichrichter durch Verpolungsschutze ersetzen)&lt;br /&gt;
*** ??? Ist dies nicht kontraproduktiv? Bei mir wurde durch D2-Bestückung die Eingangsspannung von ca. 5,2 V am LM317T auf ca. 4,6 V gedrückt, so dass am ENC28J60 nur ca. 2,6 V ankamen (außerhalb der lt. Datenblatt &amp;quot;Operating voltage range of 3.14V to 3.45V&amp;quot;). Man müsste also ein geregeltes Netzteil mit ca. 5,5 V anschließen um 5 und 3,3 V zu erzielen. Dann lieber den Verpolungsschutz durch andere Maßnahmen sicherstellen.&lt;br /&gt;
** Beim Betrieb von USB beachten, dass USB-Spezifikation keinesfalls 5V garantiert, sondern Spannung bis runter 4.4V erlaubt und dann u.U. durch den LM317 nicht mehr genügend Spannung am ENC anliegt. Das äußert sich so, dass zwar der Atmega einwandfrei funktioniert, die Ethernet-Kommunikation aber nicht oder nur sehr sporadisch.&lt;br /&gt;
* Ersatz des ATmega32 durch einen ATmega644 oder ATmega1284p mit mehr FLASH-Speicher.&lt;br /&gt;
** Der ATmega644 und auch der ATMega1284p sind nicht mit der Pollin-Firmware kompatibel&lt;br /&gt;
* 100nF über alle drei IC Störunterdrückung zusätzlich bestücken&lt;br /&gt;
&lt;br /&gt;
== Inbetriebnahme der Originalsoftware ==&lt;br /&gt;
=== Einleitung ===&lt;br /&gt;
&lt;br /&gt;
Die bei Auslieferung (Stand September 2008) in den ATmega32 gebrannte Firmware stellt sich manchmal recht zickig an. Es scheint dann die Netzwerk-Schnittstelle, ggf. auch  die serielle Schnittstelle, nicht zu funktionieren. Falls es Probleme geben sollte, sollte man erst einmal ein Firmwareupdate versuchen. Dies geschieht über die serielle Schnittstelle mittels des Programmes NetServer (aktuelle Version 1.03, Februar 2010), die dem Bausatz beiliegt. &lt;br /&gt;
&lt;br /&gt;
Falls die serielle Schnittstelle ebenfalls nicht zugänglich ist, kann mit den im folgenden beschriebenen Schritten die Inbetriebnahme der Software möglich sein. Dazu benötigt man:&lt;br /&gt;
&lt;br /&gt;
* Einen Windows-PC mit Ethernet-Schnittstelle und RS232-Schnittstelle (ein Prolific RS232-USB Konverter funktioniert)&lt;br /&gt;
* Entweder&lt;br /&gt;
**zwei normale (&#039;&#039;straight through&#039;&#039;) Ethernet-Kabel und einen Ethernet Switch/Hub, oder&lt;br /&gt;
**ein gekreuztes(&#039;&#039;cross over&#039;&#039;) Ethernet-Kabel&lt;br /&gt;
* Einen AVR Programmer (Hardware und Software). Zum Beispiel einen [[AVR Dragon]] oder [[STK500]] mit [[AVR Studio]] oder das [[Pollin ATMEL Evaluations-Board]] und [[avrdude]].&lt;br /&gt;
* Die [http://www.pollin.de/shop/ds/MTQ5OTgxOTk-.html Pollin NetServer Software], Version 1.03 (oder neuer)&lt;br /&gt;
&lt;br /&gt;
=== Gelieferten ATmega32 richtig einstellen ===&lt;br /&gt;
[[image:fuse_bits_avr_studio.jpg|thumb|right|250px|Einstellungen der Fuse-Bits mittels AVR Studio 4]]&lt;br /&gt;
Die Fuses der gelieferten ATmega32s scheinen nicht immer mit den im Handbuch auf Seite 12 als erforderlich angegebenen Fuse-Einstellungen übereinzustimmen.&lt;br /&gt;
&lt;br /&gt;
Dies kann man mittels eines Programmers ändern. LFuse = 0xBF, HFuse = 0xD2. Das genaue Vorgehen hängt dabei vom verwendeten Programmer ab. Bei der Gelegenheit kann man ebenfalls eine Sicherheitskopie des ursprünglichen Flash-Inhalts und des EEPROMs anfertigen. Das EEPROM scheint die MAC-Adresse des Ethernet-Ports zu enthalten.&lt;br /&gt;
&lt;br /&gt;
Entgegen der Spezifikation im Handbuch von Pollin sollten die &#039;&#039;&#039;HFuses auf 0xC2&#039;&#039;&#039; gesetzt werden, d. h. CKOPT-Fuse programmiert (dies ist in der Software Version 1.03 bereits vollzogen). Das sorgt für einen stabilen Betrieb des AVR-Oszillators im &amp;quot;full rail-to-rail swing&amp;quot;-Mode bei 16 MHz. Atmel garantiert ansonsten nur stabilen Betrieb bis 8 MHz. Siehe ATmega32-Datenblatt, Kapitel 8.4, Crystal Oscillator.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Funktionsfähige Konfiguration - AVR-Prog ====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avrprog.png|thumb|right|250px]]&lt;br /&gt;
Benutzer von AVR-Prog können die nachfolgenden Einstellungen für die Lock- und Fuse-Bits verwenden. Hierbei handelt es sich um die ausgelesenen Einstellungen eines funktionsfähigen Controllers. Allerdings sollte, laut Handbuch des AVR-NET-IO-Boards, das Fuse-Bit EESAVE eigentlich gesetzt sein. &lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch per avrdude die Einstellung getroffen werden:&lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U lfuse:w:0xBF:m&lt;br /&gt;
und &lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U hfuse:w:0xC2:m&lt;br /&gt;
&lt;br /&gt;
Anschließend muß noch der Bootloader und die Firmware aktualisiert werden (siehe Handbuch AVR-NET-IO-Board Seite 12 Punkt 3).&lt;br /&gt;
&lt;br /&gt;
=== PC Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
==== PC normalerweise nicht im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
Betreibt man den PC nicht im 192.168.0.0/24 Subnetz, muss er wie folgt umkonfiguriert werden, oder die IP Adresse des Boards wird entsprechend angepasst. ( Siehe Handbuch Seite 14ff. Das ist meist sinnvoller und auch einfacher. ) &lt;br /&gt;
&lt;br /&gt;
Den PC vom normalen Netzwerk abstecken[1]. Zur Umkonfiguration dazu bei Windows XP in der Systemsteuerung &#039;&#039;Netzwerkverbindungen&#039;&#039; aufrufen und die lokale &#039;&#039;LAN-Verbindung&#039;&#039; markieren. Dann in der rechten Leiste &#039;&#039;Einstellungen dieser Verbindung ändern&#039;&#039; aufrufen. &lt;br /&gt;
&lt;br /&gt;
Es erscheint der Dialog &#039;&#039;Eigenschaften von &amp;lt;Verbindungsname&amp;gt;&#039;&#039;. In der Liste im Dialog zu &#039;&#039;Internetprotokoll (TCP/IP)&#039;&#039; gehen. Ein Doppelklick auf den Eintrag öffnet den &#039;&#039;Eigenschaften von Internetprotokoll (TCP/IP)&#039;&#039; Dialog.&lt;br /&gt;
&lt;br /&gt;
In diesem Dialog &#039;&#039;Folgende IP-Adresse verwenden:&#039;&#039; auswählen und zum Beispiel&lt;br /&gt;
&lt;br /&gt;
IP-Adresse: &#039;&#039;&#039;192.168.0.100&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Subnetzmaske: &#039;&#039;&#039;255.255.255.0&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Standardgateway: &#039;&#039;&#039;192.168.0.1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
eingeben. &lt;br /&gt;
&lt;br /&gt;
Anmerkung von bitman:&lt;br /&gt;
[1] Dies ist spätestens ab Windows XP nicht mehr notwendig, wenn das Netz 192.168.0.0/24 noch frei ist. Dann kann man einfach den Client &#039;&#039;zusätzlich&#039;&#039; in diesem Netzwerk zusätzlich einbinden über Einstellungen/Netzwerkverbindungen/Lanverbindung/Eigenschaften/TCP-IP/Eigenschaften/Erweitert/IP-Adresse hinzufügen. Es werden dann eben mehrere IP-Adressen an den NIC gebunden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle geöffneten Dialoge nacheinander mit OK schließen.&lt;br /&gt;
&lt;br /&gt;
Alternativ bietet sich das Umprogrammieren des Boards über die serielle Schnittstelle an. Die Werte für IP-Adresse, Netzmaske und Standard-Gateway werden mit den dokumentierten SETxx-Befehlen geändert, das Board neu gestartet und ans vorhandene Netzwerk gesteckt.&lt;br /&gt;
&lt;br /&gt;
Im EEPROM sind folgende Werte vorprogrammiert:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3EE - 3F3 MAC-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
3F4 - 3F7 GATEWAY&amp;lt;br&amp;gt;&lt;br /&gt;
3F8 - 3FB NETMASK&amp;lt;br&amp;gt;&lt;br /&gt;
3FC - 3FF IP-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PC bereits im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
In diesem Fall muss man prüfen, ob die IP-Adresse 192.168.0.90 bereits im Subnetz verwendet wird. Ist dies der Fall, muss das verwendete Gerät mit dieser IP vorübergehend aus dem Subnetz entfernt werden. Es sei denn, dabei handelt es sich um den PC. In diesem Fall muss er wie zuvor umkonfiguriert werden. Ansonsten kann der unverändert im Netz verbleiben.&lt;br /&gt;
&lt;br /&gt;
Dem AVR-NET-IO gibt man eine neue, zuvor unbenutzte Adresse (siehe unten). Dann kann das abgekoppelte Gerät wieder angeschlossen werden, beziehungsweise der PC zurückkonfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
=== AVR-NET-IO anschließen ===&lt;br /&gt;
&lt;br /&gt;
Musste man den PC umkonfigurieren, so werden jetzt nur der PC und der AVR-NET-IO über Ethernet miteinander verbunden. Je nach Ethernet-Kabel benötigt man dazu einen Switch/Hub oder nicht.&lt;br /&gt;
&lt;br /&gt;
Musste man den PC nicht umkonfigurieren, so kann man den AVR-NET-IO wie einen normalen Rechner an das vorhandenen Netz anschließen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich schließt man die serielle Schnittstelle des AVR-NET-IO an den PC an.&lt;br /&gt;
&lt;br /&gt;
=== Firmware 1.03 einspielen ===&lt;br /&gt;
&lt;br /&gt;
Laut Handbuch sollte der AVR-NET-IO jetzt über Ethernet funktionieren. Ebenso sollte er über die serielle Schnittstelle und ein Terminalprogramm konfigurierbar sein. Beides ist offensichtlich im Auslieferungszustand selten der Fall.&lt;br /&gt;
&lt;br /&gt;
Auch wenn sich Pollins NetServer Software nicht mit dem AVR-NET-IO verbinden lässt, so ist sie jedoch in der Lage eine neue Firmware 1.03 einzuspielen. Das Vorgehen ist im Handbuch auf Seite 12 beschrieben. NetServer präsentiert dabei ein paar einfache Anweisungen denen man folgen sollte.&lt;br /&gt;
&lt;br /&gt;
Wenn sich nach dem scheinbar erfolgreichem Einspielen der Firmware, später nichts tut, so sollte die Firmware mit gesetztem &amp;quot;FailSafe&amp;quot; in der mitgelieferten NetServer-Software nochmals Eingespielt werden.&lt;br /&gt;
&lt;br /&gt;
=== Abschluss ===&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte sich die NetServer Software mit dem AVR-NET-IO über Ethernet verbinden lassen. Dies macht es wiederum möglich, den AVR-NET-IO mit einer anderen IP-Adresse zu versehen. Will man den AVR-NET-IO in einem anderen Subnetz betreiben kann man dies jetzt einstellen.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse neu eingestellt hat, muss man den PC zurückkonfigurieren und kann dann sowohl den AVR-NET-IO und den PC zusammen betreiben.&lt;br /&gt;
&lt;br /&gt;
== Funktionsumfang der Originalsoftware ==&lt;br /&gt;
Die Originalsoftware ist sehr einfach gestrickt. Im AVR Net-IO läuft ein Programm, welches am Port 50290 ein Textschnittstelle bereit stellt. Diese Schnittstelle kann sowohl über die RS232 als auch über Ethernet angesprochen werden kann. Mit diesem Programm ist es möglich, alle Eingänge sowie alle Ausgänge anzusprechen, als auch die Konfiguration der Netzwerkeinstellungen zu verändern. Die Karte versteht dann folgende Befehle:&lt;br /&gt;
* SETPORT&lt;br /&gt;
Einen digitalen Ausgang auf 0 oder 1 schalten&lt;br /&gt;
* GETPORT&lt;br /&gt;
Den Status eines digital Ausgangs/Eingangs abfragen&lt;br /&gt;
* GETSTATUS&lt;br /&gt;
Den Status aller digital Ausgänge abfragen&lt;br /&gt;
* GETADC&lt;br /&gt;
Den Messwert eines analogen Eingangs ermitteln&lt;br /&gt;
* SETIP&lt;br /&gt;
Die IP-Adresse des Boards ändern&lt;br /&gt;
* GETIP&lt;br /&gt;
Die eingestellte IP-Adresse abfragen&lt;br /&gt;
* SETMASK&lt;br /&gt;
Die Netzwerkmaske verändern&lt;br /&gt;
* GETMASK&lt;br /&gt;
Die eingestelle Netzwerkmaske abfragen&lt;br /&gt;
* SETGW&lt;br /&gt;
Das Netzwerk-Gateway verändern &lt;br /&gt;
* GETGW&lt;br /&gt;
Das eingestellte Netzwerk-Gateway abfragen&lt;br /&gt;
* INITLCD&lt;br /&gt;
Ein angeschlossenes LCD initialisieren&lt;br /&gt;
* WRITELCD&lt;br /&gt;
Einen Text am LCD ausgeben&lt;br /&gt;
* CLEARLCD&lt;br /&gt;
Ein angeschlossenes LCD löschen&lt;br /&gt;
* VERSION&lt;br /&gt;
Die Versionsnummer des Programms ausgeben&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es ein auf Java basierendes Programm, welches alternativ zu einem Terminalprogramm benutzt werden kann. Es zeigt den Status der Portpins an und diese lassen sich auch mit der Maus verändern. Die Messwerte der 4 analogen Eingänge werden ständig angezeigt, wobei es möglich ist, durch einstellbare Faktoren die internen Werte gleich in sinnvolle Einheiten umrechnen zu lassen. Diese Messwerte können auch in eine Datei geloggt werden.&lt;br /&gt;
Neben der Möglichkeit, alle Kommandos auch händisch einzugeben, wird das Windows Programm auch benötigt, um einen Firmware-Update der Net-IO Platine durchzuführen.&lt;br /&gt;
Alles in allem ist dieses Windows-Frontend aber mehr als Testumgebung anzusehen, um damit die Hardware in Betrieb zu nehmen. Für einen sinnvollen Dauereinsatz ist das Programm deutlich zu einfach und unflexibel gestrickt. Auch kann die grafische Aufbereitung der Anzeige- und Kontrollelemente nicht befriedigen.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Fehler ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[#Hardware-Umbauten_.26_-Verbesserungen|Hardware-Umbauten und Verbesserungen]]&amp;lt;br&amp;gt;&lt;br /&gt;
Käufer berichten von fehlenden Bauteilen im Bausatz (Wannenstecker, Widerstände, Kondensatoren, Induktivitäten). Für Reklamationen: [https://www.pollin.de/shop/kontakt_service/reklamation.html]&lt;br /&gt;
&lt;br /&gt;
* Die Stückliste auf Seite 4 in den Anleitung mit den Versionsangaben&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
:ist falsch. Pollin legt dem Bausatz irgendwann ab September 2008 einen gedruckten Korrekturzettel bei. Die Online-Version der Anleitung ist korrigiert.&lt;br /&gt;
* Im Schaltplan auf Seite 7 in den Anleitungen mit den Versionen&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
** &#039;&#039;Stand 03.09.2008, online, #all, wpe&#039;&#039; (Online)&lt;br /&gt;
:ist eine 25-polige SUB-D Buchse gezeichnet. Geliefert wird und in der Stückliste verzeichnet ist ein Stecker.&lt;br /&gt;
&lt;br /&gt;
* Die September 2008 ausgelieferte Firmware im ATmega32  funktioniert bei vielen nicht und muss erst upgedatet werden (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Im Flash der gelieferten AVR ist anders als beschrieben nur der Bootloader enthalten, die eigentliche Firmware muss erst mit Hilfe der Updatefunktion geladen werden. Wenn zusätzlich auch die Fuses falsch gebrannt sind, dann funktioniert das Update nicht, auch wenn das PC Programm was anderes behauptet.&lt;br /&gt;
&lt;br /&gt;
* Die Fuse-Einstellungen des ausgelieferten ATmega32 entspricht nicht der Anleitung (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Bausatz, gekauft am 27.10.08, Anleitungsversion 19.09.08, ohne Probleme oder erkennbare Fehler zusammengebaut und in Betrieb genommen.&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft 29.09.2008, Pinbelegung des 25 poligen D-Sub &amp;quot;Anschlusses&amp;quot; stimmt nicht mit der Anleitung überein. Der Aufdruck auf der Platine ist falsch. Pin1 &amp;lt;-&amp;gt; Pin13, Pin2 &amp;lt;-&amp;gt; Pin12 usw. Setzt man den D-Sub Stecker ein, so sind dessen Pinnummern korrekt. Bei einem Bausatz gekauft 05/2013 ist dies ebenfalls noch der Fall.&lt;br /&gt;
&lt;br /&gt;
* 3 Bausätze Anf. Oktober 2008 gekauft, bei einem waren 2 LM317 dabei, dafür fehlte der 7805 - aus der Bastelkiste ersetzt. Alle haben jedoch auf Anhieb funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft Ende Januar 2009. Die Lock-Bits (u.a. für PonyProg2000) werden falsch beschrieben. Die in Klammern aufgeführten Werte stimmen bei einem Bit nicht. Die Texte &amp;quot;Programmiert/Unprogrammiert&amp;quot; hingegen schon. Bei den Bauteilen gab es 4 Kondensatoren mit der Aufschrift &amp;quot;220&amp;quot;, ich habe diese durch welche mit 22p ersetzt, da ich nicht sicher war ob wirklich 22p geliefert wurden. Dafür wurden statt einem zwei 7805 und statt einem mindestens vier LM317 mitgeliefert.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 22.4.2009. Alles vollständig, zusammengebaut, läuft. Software-Version 1.03. Für den oben schon genannten Steckverbinder wurde eine Buchse geliefert. Allerdings stimmen die PIN-Nummern im Schaltplan nicht mit den PIN-Nummern auf der Buchse überein (sie sind gespiegelt), daher liefen die Test-LEDs zunächst nicht.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 11.7.2009. Spannungsregler LM317T fehlt, grüne statt roter LED. Ein Kondensator 22pF zu viel. LM317T wurde auf Anfrage kostenlos nachgeliefert (27.7.). Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 24.7.2009. Ein Quarz 16MHz zu viel, ebenfalls grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 20.08.2009. Ein Kondensator 22pF zuviel und grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli &#039;09 gekauft, grüne statt rote LED&lt;br /&gt;
&lt;br /&gt;
* Bausatz 25.09.09 geliefert, grüne Betriebs-LED, ein ELKO zuviel, Fehler 1µF am MAX232 statt 100nF behoben, richtiger C wird mitgeliefert, Aufbau komplett nach Pollin Anleitung durchgeführt, auf Anhieb fehlerfrei!&lt;br /&gt;
&lt;br /&gt;
* Bausatz 17.10.09 geliefert, grüne Betriebs-LED, zwei 100nF Kondensatoren zu wenig. Aufbau und Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz 21.10.09 gekauft, grüne Betriebs-LED. Aufbau problemlos, RS232 läuft nicht. LAN läuft&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, ENC28J60, MAX232 und ATmega32 fehlen, Nachlieferung nach einer Woche&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft,Bauteile komplett.Verbindungsaufbau Seriell klappt erst nach mehreren Versuchen.Problem gelöst:Spannung an MAX und Mega zu niedrig&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dez. 09 gekauft, grüne LED, 100µF Kondensator fehlt, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 09 gekauft, alle teile da nach Einstellen der fusebits lief alles perfekt&lt;br /&gt;
&lt;br /&gt;
* Bausatz Okt. 09 gekauft, ein 100nF Kondensator und 25MHz Quarz fehlten ... hab beim lokalen Elektronikhändler keinen 25Mhz Grundton Quarz sondern nur im 3. Oberton bekommen aber mit R2.2k parallel zum Quarz schwingt er in der Schaltung schön bei 25Mhz. Mit 1µF am MAX232 funktioniert jetzt auch die RS232.&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Feb. 10 gekauft, bei beiden fehlten 7805, L1+L2 je 100µH sowie 4x falscher Wert Kondensator an Max232 vorhanden. Fehlende Bauteile nachgelötet und Funktion getestet. Hat alles einwandfrei funktioniert!!!&lt;br /&gt;
&lt;br /&gt;
* Bausatz März. 10 gekauft, RS232 Printbuchse fehlt, dafür 1x 10pol Wannenstecker zuviel. Grüne LED statt Rot. Funktioniert ansonsten einwandfrei.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Jan. 10 gekauft, gelbe LED statt rot, C14...C17: 10µF, weder seriell noch via Ethernet Konnektivität. Nach Austausch von C14-C17 gegen 1µF, wenigstens serielle Kontaktaufnahme möglich, kein Ethernet auch nach Flash von 1.03 mit NetServer.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Feb. 10 gekauft, Spannungsregler LM317T fehlte&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft, gelbe statt rote LED geliefert, aber Aufbau und inbetriebnahme lt. Handbuch ohne Probleme&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft und gelbe statt rote LED geliefert, funzt wunderbar gemäß Anleitung&lt;br /&gt;
&lt;br /&gt;
* Fertig gelötete Platine gekauft. µC war falsch im Sockel.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - Funktion erst nach Ersetzung des ADM durch nen MAX&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - funktionierte sofort auch mit dem ADM232LJN.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft wurde mit grüner statt roter LED Ausgeliefert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juni 10 gekauft: wurde mit grüner statt roter Netz-LED ausgeliefert, 2x 22pF Kerko zuviel&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli 10 gekauft: 2 Quarze mit 16 MHz geliefert, statt 1x 16MHz und   1x25MHz.&lt;br /&gt;
&lt;br /&gt;
* Bausatz September 10 gekauft: hat sofort funktioniert. 1x 3,3k und 1x 10k Widerstand zuviel. Statt 100nF Kondensatoren wurden 1µF geliefert -&amp;gt; Platzprobleme auf der Platine durch grössere Bauform. LED grün.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober  6 gekauft: alles funktioniert. LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Fertigmodul Oktober 10 gekauft: Auf Anhieb alles funktioniert!&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft: komplett und sofort funktioniert (sogar mit der neusten Pollin Firmware 1.03 schon drauf) LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft. Nach Bezug neuer Feinst-Lötspitzen konnte ich ihn dann auch zusammenlöten. Es hat sofort alles funktioniert, obwohl ich nur 12V bzw. 9V Gleichspannung zur Verfügung hatte, und nicht sicher war, wieviel die Komponenten wirklich benötigen. Der Regler wird auch bei 9V Gleichspannungsversorgung noch sehr warm. Da muss auf jeden Fall ein Kühlkörper dran! Ich habe auch eine grüne LED bekommen, ist mir aber wurscht :-)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dezember 10 gekauft: komplett und funktionierte sofort. Firmware 1.03, grüne LED (Ein Quarz und ein IC-Sockel zu viel)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Januar 2011 gekauft: nur genau die richtige Anzahl Teile dabei, Firmware 1.03, grüne LED, ging auf Anhieb&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Januar 2011 gekauft: beide grüne LED, und 1x doppelter Satz Jumper/Stiftleiste, 22PF und Anschlussklemmen. Rest vollständig und beide haben sofort nach zusammenbau funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: die Diode D5 fehlt, 10 µF gegen 1 µF für MAX232 getauscht, Flash im ATmega32 war programmiert, passende IP-Adr über serielle Schnittstelle eingestellt; ADD-ON: für R1 war 22Ω statt 0,2Ω beigelegt, durch richtigen ersetzt, Beschreibung der LED Bestückung mangelhaft / oe1smc&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 1 Diode zuviel, 2 Spulen fehlen, LED grün. Die fehlenden Spulen wurden durch welche aus der Bastelkiste ersetzt - funktioniert. Der 7805 bekam einen kleinen Kühlkörper spendiert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 2x 10k Widerstände fehlen. Dafür eine Diode zu viel.&lt;br /&gt;
&lt;br /&gt;
* Ende Februar 2011: Zwei Bausaetze an jeweils zwei Adressen, alles in Ordnung.&lt;br /&gt;
&lt;br /&gt;
* Ende März 2011: 2x 25 Mhz Quarz statt 1x16 u. 1x25 Mhz. LED fehlt. Bausatz funktioniert nach Tausch des Quarz den mir mein Freund oe9rsv aus seinem Fundus spendiert hat. Danke auch für die Hilfe beim Fehler suchen.&lt;br /&gt;
&lt;br /&gt;
* Mitte 2010 gekauft: 1x 100nF fehlt&lt;br /&gt;
&lt;br /&gt;
* Mitte Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, Pollin wollte, dass ich das ganze Paket zurückschicke für einen Austausch. Ein 51Ω zu viel. 16Mhz im Handel und 25Mhz vom alten Mobo ausgelötet. Läuft wunderbar.&lt;br /&gt;
&lt;br /&gt;
* Anfang Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, nach kurzer Mail an Pollin (leider ohne Antwort) wurden diese nach ca. 1 Woche in einem Brief nachgeliefert.&lt;br /&gt;
&lt;br /&gt;
* August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: Platine fehlte -&amp;gt; in Nachlieferung&lt;br /&gt;
&lt;br /&gt;
* 30 August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* 06 Sep. 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: 6 Stück bestellt, bei einem haben die 100nF Kondensatoren gefehlt, bei einem zwei LM317 statt 7805 und LM317. Angerufen, 3 Tage später Nachlieferung erhalten.&lt;br /&gt;
&lt;br /&gt;
* Nov. 2011: &#039;&#039;&#039;Net_IO&#039;&#039;&#039; vollständig. Einspielen der Firmware 1.03 war erforderlich. Bei &#039;&#039;&#039;Add-On&#039;&#039;&#039; immer noch falscher Q1 BC548 (NPN) in Stückliste und Lieferung. BC327-40 oder BC328-40 (PNP) nachgefordert. R11 und R24 mitgeliefert, entsprechend Beschreibung V1.1 von Pollin&#039;s Download. Beiliegende Beschreibung war älter, ohne diese Widerstände.&lt;br /&gt;
&lt;br /&gt;
* Ende Nov.2011, alle Teile dabei, Firmware war drauf, sofort funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Anfang Dez.2011, komplett bestückte Platine gekauft. Auf 7805 Kühlkörper gebaut, da er nach 1 Minute schon ausgestiegen ist (LED hat das Pumpen angefangen). Firmware 1.03 musste noch aufgespielt werden danach funktioniert alles einwandfrei. In Betrieb mit 10V DC&lt;br /&gt;
&lt;br /&gt;
* Ende Dez.2011 2xNET-IO und 2xADD bestellt, 4 verschieden volle Kisten bekommen... WSL16 ist mit Verriegelung, geht nicht aufs Board, Jumper fehlen, Spannungsregler doppelt, Poti Löcher zu klein, lsb3 fehlt, SD-Slot hab ich jetzt 3, 100nF hab ich jetzt 4 übrig ... also immer noch lustig. HW-Stand immer noch 1.0 ( gab es überhaupt eine 1.1?)&lt;br /&gt;
&lt;br /&gt;
* Mitte Jan. 2012, 10pol. beide Wannenstecker nicht dabei, Firmware war drauf, sofort funktioniert. 1 LED zuviel. Unproblematische Nachlieferung bei Reklamation (wegen der beiden Wannenstecker kam ein PAKET!).&lt;br /&gt;
&lt;br /&gt;
* Ende Feb. 2012, Um die PHP-Scripte über öffentlichen Webserver zu betreiben muss mit SETGW die Gateway-Adresse des lokalen Routers eingetragen werden. Bei der NAT im Router sollte z.B. Port 8080 auf den internen Port 50290 umgeleitet werden, da manche Provider diesen Port für die Socket-Kommunikation nicht zulassen.&lt;br /&gt;
&lt;br /&gt;
* Juli 2012, Bausatz vollständig, Nach Aufbau sofort den 7805 mit einem kleinen Kühlkörper versehen, da er sonst thermisch überlastet wird! hat weder auf LAN noch RS232 reagiert, musste erst Update einspielen (Download Pollin), danach funktionierte alles einwandfrei!&lt;br /&gt;
&lt;br /&gt;
* Januar 2013, Bausatz vollständig, 7805 mit Kühlkörper versehen, nach Aufbau ohne Probleme funktioniert&lt;br /&gt;
&lt;br /&gt;
* Februar 2013, Bausatz komplett, Aufbau ohne Probleme, 7805 nur leicht warm, LAN funktioniert, RS232 noch ohne Funktion.&lt;br /&gt;
&lt;br /&gt;
* Oktober 2013, Bausatz komplett laut Stückliste, problemloser Aufbau, 7805 leicht warm am 10V DC. RS232 und LAN funktionierten auf Anhieb.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die nachfolgend genannten Änderungen sind in der Bauanleitung V1.1 mit Stand 07.12.2011 (Wichtig! Es gibt mehr als eine V1.1 ...) bereits berücksichtigt, R3 wird allerdings mit 3,6kΩ angegeben.&lt;br /&gt;
&lt;br /&gt;
Um bei einem Neuaufbau parallele Widerstände zu vermeiden, sollten folgende Änderungen auf dem Addon-Board gemacht werden:&lt;br /&gt;
*R2 1,5kΩ ersetzen mit 2kΩ&lt;br /&gt;
*R3 1,8K ersetzen mit 3,3kΩ&lt;br /&gt;
*R19 470kΩ ersetzen zu 470Ω&lt;br /&gt;
*Q1 BC548 ersetzen durch BC327 oder BC328 (Hauptsache PNP! und mehr als 100mA)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039; beim Anschluß eines LC-Displays an J5 (über I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C, PCF 8574): Vcc/5V liegt an Pin 1, GND an Pin 2 von J5. Es gibt etliche Displays mit abweichender Belegung Pin 1 GND und Pin 2 Vcc!&lt;br /&gt;
&lt;br /&gt;
Auch beim Add-On gibt es fehlende oder falsche Bauteile im Bausatz und Fehler im Schaltplan und auf der Platine:&lt;br /&gt;
&lt;br /&gt;
*Stand Feb. 2011: R2 wird mit 2,2kΩ und R3 wird mit 3,6kΩ ausgeliefert. Somit werden die 3,3 V richtig erzeugt. R19 hat 470Ω. Der ISP-Anschluß ist nicht vollständig durchgeschleift, es besteht keine Verbindung der RESET-Leitung zwischen ISP und ISP1 (Abhilfe: Drahtbrücke einlöten, [http://www.mikrocontroller.net/topic/161354#1600385 Quelle]). &lt;br /&gt;
&lt;br /&gt;
*Stand Nov. 2011: bei mir ist die RESET-Leitung korrekt zw. ISP und ISP1 verbunden. Es gibt jetzt auch einen R24 (470Ohm) und R11 (1KOhm), der in der bei mir mitgelieferten Bauanleitung fehlt, in der zum Download (V1.1) angebotenen  aber drin steht. Es wird immer noch der falsche Q1 BC548C (NPN) mitgeliefert. Das Schaltsymbol für einen PNP ist richtig im Schaltplan gezeichnet.&lt;br /&gt;
&lt;br /&gt;
*Stand Dez. 2011: R24 (470Ω) kann mit 0Ω ersetzt und R11 (1kΩ) völlig weggelassen werden. Diese Widerstände bilden einen (eigentlich überflüssigen) Spannungsteiler in der MISO Leitung. Der Spannungsteiler hat allerdings auch eine Schutzfunktion, falls weitere Slaves am SPI-Bus hängen, die 5V-Pegel nutzen, z. B. ISP-Programmierer. (siehe [http://son.ffdf-clan.de/include.php?path=forumsthread&amp;amp;threadid=1167&amp;amp;postid=9203 1284p: Board, ich oder beide verwirrt?])&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Bausatz mit Stecker anstelle einer Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s dafür aber 3 Poti zuviel.&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Ebenfalls Bausatz mit Sub-D-Stecker statt Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s und 3 Potis zuviel.&lt;br /&gt;
&lt;br /&gt;
*Stand März 2013: Die Belegung der 25 pol. Sub-D-Buchse im Schaltplan der Anleitung V1.1 (und früher) ist falsch dargestellt. SCL und SDA des I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C-Busses liegen wie beim AVR-NET-IO Board auf Pin 2 bzw. 3 und nicht auf Pin 11 bzw. 12 der Buchse. Ein 1:1 verdrahtetes Kabel funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Andere Software für den Client-PC ==&lt;br /&gt;
=== NetIOLib ===&lt;br /&gt;
&lt;br /&gt;
In C# geschriebene Bibliothek zur Ansteuerung der Platine im Orginalzustand. Inkl. Beispielsoftware und Quellcode (GNU GPL) &lt;br /&gt;
&lt;br /&gt;
Links gehen nicht:&lt;br /&gt;
DLL: [http://www.tware.org/downloads/NetIOLib_dll.zip Download-Link]&lt;br /&gt;
Source: [http://www.tware.org/downloads/NetIOLib_src.zip Download-Link]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Multi-Control ===&lt;br /&gt;
Mti dem E2000-NET-IO-Multi-Control ist es möglich, die vom dem E2000-NET-IO-Designer erstellten Projekte zu öffnen und die AVR-NET-IO&#039;s zu steuern. &lt;br /&gt;
&lt;br /&gt;
Die Anwendung liegt dem [http://www.mikrocontroller.net/articles/AVR_Net-IO_Bausatz_von_Pollin#E2000-NET-IO-Designer_.28Windows.5BXP.2F7.5D_.2F_Android.29 E2000-NET-IO-Designer] bei.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== ControlIO ===&lt;br /&gt;
Einfache Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://www.mikrocontroller.net/topic/149695&lt;br /&gt;
&lt;br /&gt;
=== JAVA Lib ===&lt;br /&gt;
Einfache Java-Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://son.ffdf-clan.de/?path=forumsthread&amp;amp;threadid=611&lt;br /&gt;
&lt;br /&gt;
=== PHP ===&lt;br /&gt;
PHP Klasse zur Ansteuerung mit der Originalfirmware. (Opensource Lizenz)&lt;br /&gt;
http://blog.coldtobi.de/1_coldtobis_blog/archive/298_pollin_net-io_php_library.html&lt;br /&gt;
&lt;br /&gt;
PHP Funktionen zum Ansteuern der Originalfirmware. (Free for All Lizenz)&lt;br /&gt;
http://defcon-cc.dyndns.org/wiki/index.php/Pollin_AVR-NetIO_PHP_Wrapper&lt;br /&gt;
&lt;br /&gt;
== Clients für Smartphones ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== AVR NET IO Control (Windows Phone 7.5,7.8,8.0) ===&lt;br /&gt;
&lt;br /&gt;
Freie App zur steuerung des AVR NET IO.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;App im Market: [http://www.windowsphone.com/de-de/store/app/avr-net-io-controll/030d107f-9a27-4a62-ac8f-cb74e79c0500 AVR NET IO Control]&#039;&#039;&#039;&lt;br /&gt;
=== App NetIO (Windows Mobile 6.5) ===&lt;br /&gt;
Frei verfügbare App für Windows Mobile zur Ansteuerung mit der Orginalsoftware. Das HTC HD2 wird damit zur Fernsteuerung für das AVR Net-IO Board.&lt;br /&gt;
http://www.heesch.net/netio.aspx&lt;br /&gt;
&lt;br /&gt;
=== NET-IO Control (Android) ===&lt;br /&gt;
Eine Application für das Android Betriebssystem zur Steuerung des AVR Net-IO Boards. Es ist möglich, alle Ausgänge zu steuern und alle Eingänge anzuzeigen. Die Analogen Eingänge können mit einem Berechnungsfaktor versehen werden. &#039;&#039;Geplant ist noch ein Offsetwert.&#039;&#039; Außerdem kann jedem analogen Wert eine Einheit zugeordnet werden. Die Ausgänge können in der neusten Version in einen Tastermodus gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:NET-IO-Control.png|200px]] [[Datei:NET-IO-Control2.png|200px]] [[Datei:NET-IO-Control3.png|200px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Trial-Version: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControlTRAIL Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(In dieser Probe Version können nur 1 Output und 2 Inputs gesteuert werden)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vollversion: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControl Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(Kostet im Google-Play 3,00 €)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Designer (Windows[XP/7] / Android) ===&lt;br /&gt;
[[Datei:E2000-NET-IO-Designer.png|400px|right]]&lt;br /&gt;
Mit dem E2000-NET-IO-Designer ist es möglich, eine grafische Oberfläche für die NET-IOs von Pollin zu erstellen. Dafür werden Knöpfe, Texte und Anzeigebilder zur Verfügung gestellt. Jedes dieser Element kann &amp;quot;Aufgaben&amp;quot; übernehmen um die NET-IOs zu steuern oder einen Status des NET-IO anzuzeigen. Weitere Icons können von dem Benutzer selbst in den entsprechenden Ordner gelegt werden und in die Oberfläche eingebunden werden. Es können mehrere Seiten designt werden die durch einen selbst positionierten Knopf erreichbar sind. Die Android Application arbeitet somit im Fullscreen-Modus. Des weiteren ist es möglich, mehrere NET-IO&#039;s in einem Projekt zu benutzen. Nach dem erstellen der grafischen Oberfläche mit dem E2000-NET-IO-Designer, kann mit der Android APP das Projekt einfach gedownloaded werden. Dafür baut die APP eine Verbindung zum E2000-NET-IO-Designer auf und läd alle benötigten Dateien herunter (Achtung: Firewall-Einstellungen beachten). Die designten Oberflächen können außerdem noch mit der E2000-NET-IO-Multi-Control.exe auf dem Computer ausgeführt werden. Dadurch ist es Möglich, seine NET-IOs vom PC aus zu steuern über eine selbst designte Oberfläche. &lt;br /&gt;
&lt;br /&gt;
Zum Ausführen des E2000-NET-IO-Designer muss .NET Framework 4.0 installiert sein und es muss eine Internetverbindung exisitieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Features:&#039;&#039;&#039;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Grafische Oberfläche am PC erstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verwendung von eigenen Button- und Hintergrundbildern&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designbare Anzeige von Digital- und Analogwerten&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Steuern von mehreren AVR-NET-IO Boards&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Unterstützung der E2000-NET-IO Firmware (mehere Boards gleichzeitig)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designte Steuerungen können auf dem PC oder dem Handy ausgeführt werden (Android)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Schnelle Verbindung&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Einfache übertragung auf das Handy durch Projekt Download&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/downloads.php?id=44 Download E2000-NET-IO-Designer]&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/hilfe/E2000Netdesigneruebersicht.html Online Hilfe]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== NetIO ( iPhone &amp;amp; Android ) ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;float: left; margin-right: 20px&amp;quot;&amp;gt;[[Datei:Netio_logo.png|70px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schöne universelle Fernbedienung für das Board (iOS / Android). Konfigurierbar über einen Browserbasierten Editor. &amp;lt;br&amp;gt;&lt;br /&gt;
Unterstützt TCP socket Verbindungen, kann http request absetzen und kann auch Webseiten einbinden (z.B. IP-Kameras). Kann mehrere &lt;br /&gt;
Boards gleichzeitig steuern! Einfach super schnell ins Projekt eingebaut... Es gibt Buttons (die auch als Taster konfiguriert werden können), Slider, Switches und einfache Labels. Dinge können geschaltet oder Daten ausgelesen und mit regex dargstellt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;float: right&#039;&amp;gt;&lt;br /&gt;
[[Datei:Netio_TV.png|190px]]&lt;br /&gt;
[[Datei:Netio_iphone5.png|190px]]&lt;br /&gt;
[[Datei:NetIO_appflow.jpg|190px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
AppStore Link: http://itunes.apple.com/app/netio/id464924297?mt=8 &amp;lt;br/&amp;gt;&lt;br /&gt;
Google Play Link: https://play.google.com/store/apps/details?id=com.luvago.netio &amp;lt;br/&amp;gt;&lt;br /&gt;
Die gleiche Konfiguration kann auch mit einem [https://github.com/davideickhoff/NetIO-OSX-Dashboard-Widget OSX Widget] benutzt werden. &amp;lt;br/&amp;gt;&lt;br /&gt;
Webseite: http://netio.davideickhoff.de &lt;br /&gt;
&lt;br /&gt;
Hinweis: &amp;lt;br&amp;gt; Man findet es unter &amp;quot;NetIO&amp;quot; im Store, im Icon selbst und in iTunes wird es als &amp;quot;Controller&amp;quot; angezeigt. &amp;lt;br&amp;gt;&lt;br /&gt;
Die eigene Konfiguration kann man mit dem [http://netio.davideickhoff.de/editor Online Editor] vorher am PC erstellen.&lt;br /&gt;
Eine fertige [http://netio.davideickhoff.de/editor/?config=pollin AVR-NET-IO Konfiguration] gibt es als funktionierendes Beispiel schon als Preset.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;div style=&#039;clear: both&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AVR Net IO (iPhone) ===&lt;br /&gt;
[[Datei:AVRNetIO-Screenshot1.png|160px|rechts]]&lt;br /&gt;
Update 15.12.2011: Die Neue Version 1.3 ist seit gestern im AppStore. Fehlerkorrekturen und ein robusteres Handling machen die App nun zur universellen AVR-Net-IO-Steuerung.&lt;br /&gt;
&lt;br /&gt;
Mit der [http://itunes.apple.com/de/app/avr-net-io/id460991760?mt=8 iPhone App AVR-Net-IO] kann das Board ferngesteuert werden. Die Fernsteuerung umfasst in der Version 1.1 folgende Funktionen:&amp;lt;br&amp;gt;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ADC-Werte zyklisch auslesen und darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Inputs zyklisch darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Outputs können über Buttons geschaltet werden. Die Werte der Digital-Outputs werden zuerst ausgelesen und zeigen den zuletzt gesetzten Wert an.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Terminal-Modus: Hier können beliebige AVR-Net-IO-Befehle eingegeben werden. Das Ergebnis wird 1:1 angezeigt, wie es vom Board kommt. Hilfreich für Tests bei Eigenentwicklungen und Konfigurationen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Mehr Infos gibt&#039;s dazu direkt von den Entwicklern auf [http://www.facebook.com/pages/AVR-Net-IO/187799687958255?ref=nf AVR-net-IO Facebook-Page] oder direkt auf deren Homepage [http://www.ondics.de/apps/1001/ Homepage].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Hinweis: MIt der aktuellen Version 1.1 gibt es noch bei manchen Boards Probleme beim auslesen und anzeigen der ADC- und Digital-Werte. Das Terminal funzt problemlos. Die Entwickler kümmern sich gerade drum und haben einen baldigen Update versprochen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Andere Software statt der Originalsoftware von Pollin ==&lt;br /&gt;
&lt;br /&gt;
Die Umrüstung auf einen Webserver durch Austausch der Software (und ev. des ATmega32) bietet sich an. Kleiner Hinweis dabei: wenn zum Flashen ein ISP-Adapter verwendet wird, diesen unbedingt vor dem Start der neuen Software abziehen! Der ISP arbeitet nämlich über dieselbe SPI-Schnittstelle über die auch der ENC28J60 angesteuert wird. Ein eventuell noch angeschlossener, wenn auch passiver ISP-Adapter stört diese Kommunikation, d.h. das Programm an sich scheint zu laufen, aber die Ethernet-Schnittstelle funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
=== E2000 - Logik ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:E2000-Logik-Bedienoberflaeche.jpg|500px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Anwenderfreundliche Logik-Software von Elektronik2000.de zur Steuerung des AVR-NET-IO. Der ATMEGA32 wird durch einen ATMEGA644 ersetzt und mit der E2000-Firmware beschrieben. Nun ist es möglich in der E2000-Logik Software eine Logikschaltung zu erstellen und diese auf das NET-IO-Board zu übertragen. Dort wird diese Schaltung simuliert. Dies funktioniert komplett ohne einen Computer.&lt;br /&gt;
&lt;br /&gt;
Durch ein Erweiterungsboard von Elektronik2000.de ist es auch möglich, das Board ohne Internet laufen zu lassen eine RTC (RealTimeClock) übernimmt dabei die Uhrzeitgesteuerten Funktionen. Auf dieser Erweiterung ist auch ein EEPROM integriert, um Firmware-Updates über Netzwerk einzuspielen (in Zukunft). Diese Erweiterung bietet außerdem die Anbindung an das E2000-Bus-System, durch das es möglich ist das AVR-NET-IO-Board durch weitere Ein- und Ausgänge zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Das Designen von Schaltaufgaben wird in diesem Programm grafisch dargestellt, durch das ein einfaches Anpassen seiner Logikschaltungen möglich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Steuerung des E2000-NET-IO ist möglich durch den Intregrieten Webserver, die PC-Software (E2000-NET-IO Control) oder der Androidsoftware. All diese Können gleichzeitig auf das E2000-NET-IO zugreifen und Funktionen ausführen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es auf der Entwicklerseite: [http://www.elektronik2000.de Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== Bascom Version von Hütti ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://bascom-forum.de/index.php/topic,1781.45.html )&lt;br /&gt;
dort am Ende der Seite.&lt;br /&gt;
&lt;br /&gt;
=== Ben&#039;s Bascom Quellcode ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://members.home.nl/bzijlstra/software/examples/enc28j60.htm )&lt;br /&gt;
&lt;br /&gt;
Muss aber für Bascom 1.11.9.3 angepasst werden, siehe Code von Hütti !&lt;br /&gt;
&lt;br /&gt;
=== U. Radigs Webserver ===&lt;br /&gt;
&lt;br /&gt;
Angepasster Sourcecode von U.Radig: http://www.mikrocontroller.net/attachment/40027/Webserver_MEGA32.hex&lt;br /&gt;
oder selbst anpassen: &lt;br /&gt;
Ändere in der Datei ENC28J60.H:&lt;br /&gt;
 #define ENC28J60_PIN_CS    4&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#988386)&lt;br /&gt;
&lt;br /&gt;
Temporären Dateien (*.d, *,lst,*.o) vorher im Verzeichnis löschen &#039;&#039;make clean&#039;&#039;, damit neu compiliert wird.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.0.99&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: uli1&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den orginal SourceCode gibt&#039;s übrigens hier:http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex&lt;br /&gt;
&lt;br /&gt;
Bei den Fuses BOOTRST ausschalten, da die Software keinen Bootloader enthält.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.1.90&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: tim&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://beitz-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://pieper-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterentwicklung des Radig-Codes von RoBue: &amp;lt;br&amp;gt;&lt;br /&gt;
- 1-Wire-Unterstützung (Anschlus an PORTA7) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA0-3 digitaler Eingang (ein/aus) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA4-6 analoger Eingang (0 - 1023) &amp;lt;br&amp;gt;&lt;br /&gt;
- LCD an PORTC &amp;lt;br&amp;gt;&lt;br /&gt;
- Schalten in Abhängigkeit von Temperatur und analogem Wert &amp;lt;br&amp;gt;&lt;br /&gt;
- (Teilweise) Administration über Weboberfläche &amp;lt;br&amp;gt;&lt;br /&gt;
- Erweiterung des cmd-Befehlsatzes für telnet/rs232 &amp;lt;br&amp;gt;&lt;br /&gt;
Gedacht ist der Einsatz des AVR-NET-IO-Bausatzes für Heizungs- oder Haussteuerung) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test: http://avrboard.eluhost.de/&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Quelle:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/43307/AVR-NET-IO_RoBue_V1.3.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/44569/AVR-NET-IO_RoBue_V1.4.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/46720/AVR-NET-IO_RoBue_1.5-final_hoffentlich_.zip)&lt;br /&gt;
&lt;br /&gt;
Bei der Ver 1.5 sind die Ports PD2+3 fürs 4bit LCD (Ext.) vertauscht ! Gruß B.P&lt;br /&gt;
&lt;br /&gt;
=== Simon Ks Webserver (uip-Stack) ===&lt;br /&gt;
Angepasster Sourcecode von Simon K: http://www.mikrocontroller.net/attachment/39939/uWebSrv.zip&lt;br /&gt;
IP: 192.168.0.93:8080&amp;lt;br&amp;gt;&lt;br /&gt;
Um diesen Code mit einem Atmega1284P verwenden zu können, muss in der main.c in Zeile 38, &amp;quot;TIMSK&amp;quot; durch &amp;quot;TIMSK1&amp;quot; ersetzt werden.&lt;br /&gt;
Die Fusebits für den Atmega1284p ohne Bootloader sind:&lt;br /&gt;
lfuse=0xFF, hfuse=0xD9, efuse=0xFF&lt;br /&gt;
&lt;br /&gt;
=== Ethersex Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.ethersex.de - Einfach für atmega32 compilieren und funktioniert.&lt;br /&gt;
&lt;br /&gt;
=== Etherrape Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.lochraster.org/etherrape/ &lt;br /&gt;
&lt;br /&gt;
ist in jedem Fall hier auch zu erwähnen zumal es sich beim etherrape um das Ursprungsprojekt von ethersex handelt.&lt;br /&gt;
Es scheint aber bei der Weiterentwicklung wenig zu passieren.&lt;br /&gt;
Ausführliche Dokumentation findet sich unter http://wiki.lochraster.org/wiki/Etherrape&lt;br /&gt;
&lt;br /&gt;
=== Mini SRCP Server (kommerziell, Closed-Source)===&lt;br /&gt;
&lt;br /&gt;
Damit wird die Platine zu einer Modellbahnsteuerung, die&lt;br /&gt;
über das Netzwerkprotokoll SRCP mit verschiedenen Programmen&lt;br /&gt;
gesteuert werden kann.&lt;br /&gt;
&lt;br /&gt;
[http://www.7soft.de/de/mini_srcp_server/index.html Infoseite] zur Hardware&lt;br /&gt;
und das zugrundeliegende [http://www.der-moba.de/index.php/Digitalprojekt Digitalprojekt].&lt;br /&gt;
&lt;br /&gt;
=== Avr ArtNET-Node ===&lt;br /&gt;
&lt;br /&gt;
Hiermit kann die Platine zu einem Art-Net Node werden, mit dem sich ein DMX-Universe über Ethernet übertragen lässt. Basiert auf den Quellen von Ulrich Radig.&lt;br /&gt;
&lt;br /&gt;
Dokumentation: [http://www.dmxcontrol.de/wiki/Art-Net-Node_f%C3%BCr_25_Euro Art-Net-Node für 25 Euro]&lt;br /&gt;
&lt;br /&gt;
=== Webserver von G. Menke ===&lt;br /&gt;
&lt;br /&gt;
Ein Webserver (basierend auf den Sourcen von U. Radig), der so angepasst ist, dass alle Ein- und Ausgänge wie bei der originalen Pollin-Software genutzt werden können (8xDIGOUT, 4xDIGIN, 4xADIN). Der Webserver kann daher direkt auf das Net-IO geladen werden. Im ZIP-File sind ein ReadMe und alle C-Sourcen enthalten.&amp;lt;br&amp;gt;&amp;lt;i&amp;gt;Download&amp;lt;/i&amp;gt;:&lt;br /&gt;
[http://gm.stream-center.de/webserver/ Webserver mit passender IO]&lt;br /&gt;
&lt;br /&gt;
=== OpenMCP ===&lt;br /&gt;
&lt;br /&gt;
Tolles Projekt, welches viele Features bietet und stabil läuft. Hervorzuheben ist die Übersichtlichkeit der Programmteile/Module und die vielleicht nicht ganz komplette Dokumentation. Man merkt, dass viel Arbeit und Liebe in diesem Projekt steckt. Herausgekommen ist dabei eine einfach zu handhabende Entwicklungsumgebung. Anfänger können, dank des gut durchdachten CGI-Systems, welches sich um alle wichtigen Sachen kümmert, leicht eigene CGI implementieren. Alle Ausgaben erfolgen nur mit printf über die Standardausgabe und werden automatisch richtig per Netzwerk übertragen, dadurch ist es auch für den Anfänger recht gut geeignet, da man sich nicht mit der Netzwerkprogrammierung auseinander setzen muss.&lt;br /&gt;
&lt;br /&gt;
Die Software belegt im Moment (Stand Juli 2010) ca. 55 Kb im Flash, so dass man das Board mit einem grösseren µC (z.B. ATMega644) aufrüsten muss.&lt;br /&gt;
&lt;br /&gt;
[http://wiki.neo-guerillaz.de Projekt und Doku]&lt;br /&gt;
&lt;br /&gt;
Der Autor stellt zwei über das Internet erreichbare Testboards bereit unter http://www.neo-guerillaz.de:81 und http://www.neo-guerillaz.de:82 die beide unter OpenMCP laufen, je auf einen AVR-NETIO mit einem ATmega644 und dem eigentlichen Board mit einem ATmega2561. Zusätzlich ist gerade eine Version für das myAVR in Arbeit die schon ordentlich Fortschritte macht.&lt;br /&gt;
&lt;br /&gt;
=== OpenMLP ===&lt;br /&gt;
Auf openMCP basierender Port nach [http://avr.myluna.de LunaAVR] (GPL). Umfangreiche Funktionalität und direkte Anpassung an die Sprache. Abgespeckte Version auch auf Atmega32 lauffähig. Die Original-Dokumentation kann zum Großteil hergenommen werden. Einige Zusatzfeatures. Leichte Konfiguration und guter Einstieg für Anfänger und zum Verständnis der Serverfunktionalität.&lt;br /&gt;
&lt;br /&gt;
[http://avr.myluna.de/doku.php?id=de:openmlp Artikelseite]&lt;br /&gt;
[http://forum.myluna.de/viewtopic.php?f=8&amp;amp;t=13 Forum]&lt;br /&gt;
&lt;br /&gt;
=== ENC28J60 I/O-Webserver von Thomas Heldt ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul-Webserver (Softwarekompatibel zum Pollin Webserver), der durch div. Module erweitert werden kann, Software in Bascom basierend auf dem Code von Ben Zijlsta wurde erweitert und angepasst:&lt;br /&gt;
&lt;br /&gt;
[http://mikrocontroller.heldt.eu/index.php?page=enc28j60-io-webserver Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
=== AVR-Netino ===&lt;br /&gt;
[http://code.google.com/p/avr-netino/ Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
Arduino fürs Net-IO&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/topic/109988&lt;br /&gt;
* [http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en022889 ENC28J60 Produktseite]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/DeviceDoc/39662c.pdf ENC28J60 Datenblatt(pdf)]&lt;br /&gt;
* [http://son.ffdf-clan.de Forum für AVR-Net-IO]&lt;br /&gt;
* [http://avr.myluna.de/doku.php?id=de:openmlp LunaAVR openMLP ]&lt;br /&gt;
* [http://bascom-forum.de/index.php/topic,1781.0.html Bascom Forum ]&lt;br /&gt;
* [http://hobbyelektronik.org/w/index.php/AVR-NET-IO-Shield Shield für den NET-IO]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Category:Ethernet|P]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78892</id>
		<title>AVR Net-IO Bausatz von Pollin</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78892"/>
		<updated>2013-10-11T00:50:40Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Funktionsumfang der Originalsoftware */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier steht eine Beschreibung des Pollin Bausatzes [http://www.pollin.de/shop/shop.php?cf=detail.php&amp;amp;pg=NQ==&amp;amp;a=MTQ5OTgxOTk= AVR-NET-IO. Best.Nr. 810 058], oder als aufgebautes Fertigmodul, Best.Nr. 810 073. &lt;br /&gt;
&lt;br /&gt;
Einige Features: Ethernet-Platine mit ATmega32 und Netzwerkcontroller ENC28J60. Die Platine verfügt über 8 digitale Ausgänge, 4 digitale und 4 ADC-Eingänge, welche alle über einen Netzwerkanschluss (TCP/IP) abgerufen bzw. geschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AVR-NET-IO ADD-ON.JPG|thumb|right|400px|AVR-NET-IO (links) mit zusätzlicher SUB-D Anschlussplatine (rechts, nicht im Lieferumfang)und Add-On-Board (oben, mit aufgelötetem RFM12-433-Modul, beides nicht im Lieferumfang). Ebenso ist zusätzlich ein nicht im Lieferumfang enthaltener kleiner Kühlkörper auf einem der Spannungsregler montiert und die Schraubklemmen zur Stromversorgung wurden durch Buchsen ersetzt.]]&lt;br /&gt;
&lt;br /&gt;
== Technische Daten ==&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 9 V AC/DC&lt;br /&gt;
* Stromaufnahme ca. 190 mA&lt;br /&gt;
* 8 digitale Ausgänge (0/5 V) [PC0-PC7 an J3]&lt;br /&gt;
* 4 digitale Eingänge (0/5 V) [PA0-PA3 an J3]&lt;br /&gt;
* 4 ADC-Eingänge (10 Bit) [PA4-PA7 an Schraubklemmen]&lt;br /&gt;
* LCD-Anschluss (HD44780 komp. Controller nötig) [PD2-7,PB0,PB3 an EXT]&lt;br /&gt;
* [[ENC28J60]]&lt;br /&gt;
* [http://www.atmel.com/dyn/Products/Product_card.asp?part_id=2014 ATmega32] Mikrocontroller&lt;br /&gt;
&lt;br /&gt;
Maße (L×B×H): 108×76×22 mm.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Die Schaltung des AVR-NET-IO ist recht einfach:&lt;br /&gt;
* Ein ATmega32 Mikrocontroller enthält die gesamte Software&lt;br /&gt;
* Ein ENC28J60 Ethernet-Controller für das Senden und Empfangen von Ethernet Frames (MAC und PHY Ethernet Layer) ist über [[SPI]] mit dem ATmega32 verbunden&lt;br /&gt;
* Ein Ethernet RJ-45 MagJack TRJ 0011 BA NL von [http://www.trxcom.com/ Trxcom] mit eingebautem Übertrager und Anzeige-LEDs am ENC28J60.&lt;br /&gt;
* Ein MAX232 für die serielle Schnittstelle&lt;br /&gt;
* Zwei Spannungsregler, 5 V und 3,3 V&lt;br /&gt;
* &amp;quot;Hühnerfutter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Fast alle I/O Pins des ATmega32 sind irgendwo auf Anschlüssen herausgeführt. Entweder auf dem SUB-D Stecker, dem EXT oder ISP Wannensteckern oder den blauen Anschlussklemmen. Eine Schutzbeschaltung gibt es nicht.&lt;br /&gt;
&lt;br /&gt;
Die blauen Anschlussklemmen haben eine Nut und eine Feder mit denen man&lt;br /&gt;
sie zusammenstecken kann, dadurch ist das Anlöten wesentlich leichter&lt;br /&gt;
und sie stehen auch sauber in der Reihe.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
Seit Januar 2010 gibt es auch eine Erweiterungsplatine &lt;br /&gt;
&lt;br /&gt;
[http://www.pollin.de/shop/dt/Nzg4OTgxOTk-/Bausaetze/Diverse/Bausatz_Add_on_fuer_AVR_NET_IO.html Add-on für AVR-NET-IO-Board Best.Nr. 810 112]&lt;br /&gt;
&lt;br /&gt;
Diese Platine erweitert das NET-IO um:&lt;br /&gt;
&lt;br /&gt;
* SD-Karten-Slot über SPI&lt;br /&gt;
* Display über PCF 8574&lt;br /&gt;
* Infrarot-Empfang&lt;br /&gt;
* [[RFM12]] Funkmodul (nicht im Lieferumfang enthalten)&lt;br /&gt;
&lt;br /&gt;
Eine Aufstellung bekannter Fehler findet sich weiter unten [[#Bekannte Fehler|Bekannte Fehler - Erweiterungplatine]] &lt;br /&gt;
&lt;br /&gt;
Erste Erfahrungsberichte im Forum http://www.mikrocontroller.net/topic/161354&lt;br /&gt;
&lt;br /&gt;
=== Hardware-Umbauten &amp;amp; -Verbesserungen ===&lt;br /&gt;
&lt;br /&gt;
* Kühlkörper auf dem 7805 - (Vorsicht: Der LM317T ist rückseitig spannungsführend. Den Kühlkörper also keinesfalls an beide Spannungsregler anschließen!)&lt;br /&gt;
* MAX232 nach anfänglicher Konfiguration nicht bestücken um Strom zu sparen oder um zwei weitere I/O-Pins zu gewinnen&lt;br /&gt;
* 10µF-Elkos für MAX232N (C14-C17) durch 1µF ersetzen. Eine 10µF-Version für den MAX232 gibt es nicht. Die 10µF-Elkos können auch Ursache einer nicht funktionierenden RS232 sein.&lt;br /&gt;
** Laut Spezifikation sind auch mehr als 1µF erlaubt. Selbst Atmel verwendet beim STK500 10µF. Dies führt keinesfalls dazu, dass die RS232 nicht mehr funktioniert.&lt;br /&gt;
* Die IC-Fassungen aus &amp;quot;Pollins Resterampe&amp;quot; durch Fassungen mit gedrehten Kontakten ersetzen. &lt;br /&gt;
* &#039;&#039;Netz&#039;&#039; LED nicht bestücken oder größere Widerstände einlöten um Strom zu sparen (R3)&lt;br /&gt;
* Vorwiderstände der Ethernet-LEDs größer machen (z.&amp;amp;nbsp;B. verdoppeln) um Strom zu sparen (R6,R7)&lt;br /&gt;
* Linear-Spannungsregler ersetzen&lt;br /&gt;
* Kondensator an AREF-Pin des ATmega32 (ATmega32 Datenblatt) (100nF gegen Masse)&lt;br /&gt;
* Kondensator an den RESET-Pin des ATmega32 ([http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Atmel Application Note AVR042: AVR Hardware Design Considerations]) Wenn man diese Quelle genauer liest, ist das aber eher unnötig. - Kondensator bei selbstbau-ISP empfehlenswert.&lt;br /&gt;
* Umbau auf 3,3 V:&lt;br /&gt;
** Ersatz der Spannungsregler durch einen einzigen 3,3 V Regler&lt;br /&gt;
** Anpassen (verkleinern) des LED-Vorwiderstands R3 für 3,3 Volt Betrieb&lt;br /&gt;
** Reduktion der Taktfrequenz (Austausch von Q2) auf den bei 3,3V erlaubten Bereich des ATmega32 ( ATmega32(L)  3.3V /8.0 Mhz Takt )&lt;br /&gt;
** Ersatz des MAX232 durch einen MAX3232&lt;br /&gt;
[[Bild:POWER.JPG|thumb|400px|5V Stromversorgung über USB Kabel, ohne 5 V Spannungsregler und Gleichrichterdioden, Vorsicht: kein Verpolungsschutz!  ]]&lt;br /&gt;
* ATmega32 vom ENC28J60 takten (OSC2)&lt;br /&gt;
* Betrieb mit Gleichspannung:&lt;br /&gt;
** Dioden D2 und D5 durch Drahtbrücken ersetzen, D1 und D4 nicht bestücken (komplette Entfernung des Brückengleichrichters, beinhaltet Verlust des Verpolungsschutzes)&lt;br /&gt;
** Diode D2 bestücken, D5 durch Drahtbrücke ersetzen, D1 und D4 nicht bestücken (Brückengleichrichter durch Verpolungsschutze ersetzen)&lt;br /&gt;
*** ??? Ist dies nicht kontraproduktiv? Bei mir wurde durch D2-Bestückung die Eingangsspannung von ca. 5,2 V am LM317T auf ca. 4,6 V gedrückt, so dass am ENC28J60 nur ca. 2,6 V ankamen (außerhalb der lt. Datenblatt &amp;quot;Operating voltage range of 3.14V to 3.45V&amp;quot;). Man müsste also ein geregeltes Netzteil mit ca. 5,5 V anschließen um 5 und 3,3 V zu erzielen. Dann lieber den Verpolungsschutz durch andere Maßnahmen sicherstellen.&lt;br /&gt;
** Beim Betrieb von USB beachten, dass USB-Spezifikation keinesfalls 5V garantiert, sondern Spannung bis runter 4.4V erlaubt und dann u.U. durch den LM317 nicht mehr genügend Spannung am ENC anliegt. Das äußert sich so, dass zwar der Atmega einwandfrei funktioniert, die Ethernet-Kommunikation aber nicht oder nur sehr sporadisch.&lt;br /&gt;
* Ersatz des ATmega32 durch einen ATmega644 oder ATmega1284p mit mehr FLASH-Speicher.&lt;br /&gt;
** Der ATmega644 und auch der ATMega1284p sind nicht mit der Pollin-Firmware kompatibel&lt;br /&gt;
* 100nF über alle drei IC Störunterdrückung zusätzlich bestücken&lt;br /&gt;
&lt;br /&gt;
== Inbetriebnahme der Originalsoftware ==&lt;br /&gt;
=== Einleitung ===&lt;br /&gt;
&lt;br /&gt;
Die bei Auslieferung (Stand September 2008) in den ATmega32 gebrannte Firmware stellt sich manchmal recht zickig an. Es scheint dann die Netzwerk-Schnittstelle, ggf. auch  die serielle Schnittstelle, nicht zu funktionieren. Falls es Probleme geben sollte, sollte man erst einmal ein Firmwareupdate versuchen. Dies geschieht über die serielle Schnittstelle mittels des Programmes NetServer (aktuelle Version 1.03, Februar 2010), die dem Bausatz beiliegt. &lt;br /&gt;
&lt;br /&gt;
Falls die serielle Schnittstelle ebenfalls nicht zugänglich ist, kann mit den im folgenden beschriebenen Schritten die Inbetriebnahme der Software möglich sein. Dazu benötigt man:&lt;br /&gt;
&lt;br /&gt;
* Einen Windows-PC mit Ethernet-Schnittstelle und RS232-Schnittstelle (ein Prolific RS232-USB Konverter funktioniert)&lt;br /&gt;
* Entweder&lt;br /&gt;
**zwei normale (&#039;&#039;straight through&#039;&#039;) Ethernet-Kabel und einen Ethernet Switch/Hub, oder&lt;br /&gt;
**ein gekreuztes(&#039;&#039;cross over&#039;&#039;) Ethernet-Kabel&lt;br /&gt;
* Einen AVR Programmer (Hardware und Software). Zum Beispiel einen [[AVR Dragon]] oder [[STK500]] mit [[AVR Studio]] oder das [[Pollin ATMEL Evaluations-Board]] und [[avrdude]].&lt;br /&gt;
* Die [http://www.pollin.de/shop/ds/MTQ5OTgxOTk-.html Pollin NetServer Software], Version 1.03 (oder neuer)&lt;br /&gt;
&lt;br /&gt;
=== Gelieferten ATmega32 richtig einstellen ===&lt;br /&gt;
[[image:fuse_bits_avr_studio.jpg|thumb|right|250px|Einstellungen der Fuse-Bits mittels AVR Studio 4]]&lt;br /&gt;
Die Fuses der gelieferten ATmega32s scheinen nicht immer mit den im Handbuch auf Seite 12 als erforderlich angegebenen Fuse-Einstellungen übereinzustimmen.&lt;br /&gt;
&lt;br /&gt;
Dies kann man mittels eines Programmers ändern. LFuse = 0xBF, HFuse = 0xD2. Das genaue Vorgehen hängt dabei vom verwendeten Programmer ab. Bei der Gelegenheit kann man ebenfalls eine Sicherheitskopie des ursprünglichen Flash-Inhalts und des EEPROMs anfertigen. Das EEPROM scheint die MAC-Adresse des Ethernet-Ports zu enthalten.&lt;br /&gt;
&lt;br /&gt;
Entgegen der Spezifikation im Handbuch von Pollin sollten die &#039;&#039;&#039;HFuses auf 0xC2&#039;&#039;&#039; gesetzt werden, d. h. CKOPT-Fuse programmiert (dies ist in der Software Version 1.03 bereits vollzogen). Das sorgt für einen stabilen Betrieb des AVR-Oszillators im &amp;quot;full rail-to-rail swing&amp;quot;-Mode bei 16 MHz. Atmel garantiert ansonsten nur stabilen Betrieb bis 8 MHz. Siehe ATmega32-Datenblatt, Kapitel 8.4, Crystal Oscillator.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Funktionsfähige Konfiguration - AVR-Prog ====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avrprog.png|thumb|right|250px]]&lt;br /&gt;
Benutzer von AVR-Prog können die nachfolgenden Einstellungen für die Lock- und Fuse-Bits verwenden. Hierbei handelt es sich um die ausgelesenen Einstellungen eines funktionsfähigen Controllers. Allerdings sollte, laut Handbuch des AVR-NET-IO-Boards, das Fuse-Bit EESAVE eigentlich gesetzt sein. &lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch per avrdude die Einstellung getroffen werden:&lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U lfuse:w:0xBF:m&lt;br /&gt;
und &lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U hfuse:w:0xC2:m&lt;br /&gt;
&lt;br /&gt;
Anschließend muß noch der Bootloader und die Firmware aktualisiert werden (siehe Handbuch AVR-NET-IO-Board Seite 12 Punkt 3).&lt;br /&gt;
&lt;br /&gt;
=== PC Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
==== PC normalerweise nicht im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
Betreibt man den PC nicht im 192.168.0.0/24 Subnetz, muss er wie folgt umkonfiguriert werden, oder die IP Adresse des Boards wird entsprechend angepasst. ( Siehe Handbuch Seite 14ff. Das ist meist sinnvoller und auch einfacher. ) &lt;br /&gt;
&lt;br /&gt;
Den PC vom normalen Netzwerk abstecken[1]. Zur Umkonfiguration dazu bei Windows XP in der Systemsteuerung &#039;&#039;Netzwerkverbindungen&#039;&#039; aufrufen und die lokale &#039;&#039;LAN-Verbindung&#039;&#039; markieren. Dann in der rechten Leiste &#039;&#039;Einstellungen dieser Verbindung ändern&#039;&#039; aufrufen. &lt;br /&gt;
&lt;br /&gt;
Es erscheint der Dialog &#039;&#039;Eigenschaften von &amp;lt;Verbindungsname&amp;gt;&#039;&#039;. In der Liste im Dialog zu &#039;&#039;Internetprotokoll (TCP/IP)&#039;&#039; gehen. Ein Doppelklick auf den Eintrag öffnet den &#039;&#039;Eigenschaften von Internetprotokoll (TCP/IP)&#039;&#039; Dialog.&lt;br /&gt;
&lt;br /&gt;
In diesem Dialog &#039;&#039;Folgende IP-Adresse verwenden:&#039;&#039; auswählen und zum Beispiel&lt;br /&gt;
&lt;br /&gt;
IP-Adresse: &#039;&#039;&#039;192.168.0.100&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Subnetzmaske: &#039;&#039;&#039;255.255.255.0&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Standardgateway: &#039;&#039;&#039;192.168.0.1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
eingeben. &lt;br /&gt;
&lt;br /&gt;
Anmerkung von bitman:&lt;br /&gt;
[1] Dies ist spätestens ab Windows XP nicht mehr notwendig, wenn das Netz 192.168.0.0/24 noch frei ist. Dann kann man einfach den Client &#039;&#039;zusätzlich&#039;&#039; in diesem Netzwerk zusätzlich einbinden über Einstellungen/Netzwerkverbindungen/Lanverbindung/Eigenschaften/TCP-IP/Eigenschaften/Erweitert/IP-Adresse hinzufügen. Es werden dann eben mehrere IP-Adressen an den NIC gebunden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle geöffneten Dialoge nacheinander mit OK schließen.&lt;br /&gt;
&lt;br /&gt;
Alternativ bietet sich das Umprogrammieren des Boards über die serielle Schnittstelle an. Die Werte für IP-Adresse, Netzmaske und Standard-Gateway werden mit den dokumentierten SETxx-Befehlen geändert, das Board neu gestartet und ans vorhandene Netzwerk gesteckt.&lt;br /&gt;
&lt;br /&gt;
Im EEPROM sind folgende Werte vorprogrammiert:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3EE - 3F3 MAC-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
3F4 - 3F7 GATEWAY&amp;lt;br&amp;gt;&lt;br /&gt;
3F8 - 3FB NETMASK&amp;lt;br&amp;gt;&lt;br /&gt;
3FC - 3FF IP-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PC bereits im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
In diesem Fall muss man prüfen, ob die IP-Adresse 192.168.0.90 bereits im Subnetz verwendet wird. Ist dies der Fall, muss das verwendete Gerät mit dieser IP vorübergehend aus dem Subnetz entfernt werden. Es sei denn, dabei handelt es sich um den PC. In diesem Fall muss er wie zuvor umkonfiguriert werden. Ansonsten kann der unverändert im Netz verbleiben.&lt;br /&gt;
&lt;br /&gt;
Dem AVR-NET-IO gibt man eine neue, zuvor unbenutzte Adresse (siehe unten). Dann kann das abgekoppelte Gerät wieder angeschlossen werden, beziehungsweise der PC zurückkonfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
=== AVR-NET-IO anschließen ===&lt;br /&gt;
&lt;br /&gt;
Musste man den PC umkonfigurieren, so werden jetzt nur der PC und der AVR-NET-IO über Ethernet miteinander verbunden. Je nach Ethernet-Kabel benötigt man dazu einen Switch/Hub oder nicht.&lt;br /&gt;
&lt;br /&gt;
Musste man den PC nicht umkonfigurieren, so kann man den AVR-NET-IO wie einen normalen Rechner an das vorhandenen Netz anschließen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich schließt man die serielle Schnittstelle des AVR-NET-IO an den PC an.&lt;br /&gt;
&lt;br /&gt;
=== Firmware 1.03 einspielen ===&lt;br /&gt;
&lt;br /&gt;
Laut Handbuch sollte der AVR-NET-IO jetzt über Ethernet funktionieren. Ebenso sollte er über die serielle Schnittstelle und ein Terminalprogramm konfigurierbar sein. Beides ist offensichtlich im Auslieferungszustand selten der Fall.&lt;br /&gt;
&lt;br /&gt;
Auch wenn sich Pollins NetServer Software nicht mit dem AVR-NET-IO verbinden lässt, so ist sie jedoch in der Lage eine neue Firmware 1.03 einzuspielen. Das Vorgehen ist im Handbuch auf Seite 12 beschrieben. NetServer präsentiert dabei ein paar einfache Anweisungen denen man folgen sollte.&lt;br /&gt;
&lt;br /&gt;
Wenn sich nach dem scheinbar erfolgreichem Einspielen der Firmware, später nichts tut, so sollte die Firmware mit gesetztem &amp;quot;FailSafe&amp;quot; in der mitgelieferten NetServer-Software nochmals Eingespielt werden.&lt;br /&gt;
&lt;br /&gt;
=== Abschluss ===&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte sich die NetServer Software mit dem AVR-NET-IO über Ethernet verbinden lassen. Dies macht es wiederum möglich, den AVR-NET-IO mit einer anderen IP-Adresse zu versehen. Will man den AVR-NET-IO in einem anderen Subnetz betreiben kann man dies jetzt einstellen.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse neu eingestellt hat, muss man den PC zurückkonfigurieren und kann dann sowohl den AVR-NET-IO und den PC zusammen betreiben.&lt;br /&gt;
&lt;br /&gt;
== Funktionsumfang der Originalsoftware ==&lt;br /&gt;
Die Originalsoftware ist sehr einfach gestrickt. Im AVR Net-IO läuft ein Programm, welches sowohl über die RS232 als auch über Ethernet angesprochen werden kann. Mit diesem Programm ist es möglich, alle Eingänge, alle Ausgänge anzusprechen, sowie die Konfiguration der Netzwerkeinstellungen zu verändern. Die Karte versteht dann folgende Befehle:&lt;br /&gt;
* SETPORT&lt;br /&gt;
* GETPORT&lt;br /&gt;
* SETIP&lt;br /&gt;
* GETIP&lt;br /&gt;
* SETMASK&lt;br /&gt;
* GETMASK&lt;br /&gt;
* SETGW&lt;br /&gt;
* GETGW&lt;br /&gt;
* GETSTATUS&lt;br /&gt;
* INITLCD&lt;br /&gt;
* WRITELCD&lt;br /&gt;
* CLEARLCD&lt;br /&gt;
* VERSION&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es ein auf Windows lauffähiges Programm, welches alternativ zu einem Terminalprogramm benutzt werden kann. Es zeigt den Status der Portpins an und diese lassen sich auch mit der Maus verändern. Die Messwerte der 4 analogen Eingänge werden ständig angezeigt, wobei es möglich ist, durch einstellbare Faktoren die internen Werte gleich in sinnvolle Einheiten umrechnen zu lassen. Diese Messwerte können auch in eine Datei geloggt werden.&lt;br /&gt;
Neben der Möglichkeit, alle Kommandos auch händisch einzugeben, wird das Windows Programm auch benötigt, um einen Firmware-Update der Net-IO Platine durchzuführen.&lt;br /&gt;
Alles in allem ist dieses Windows-Frontend aber mehr als Testumgebung anzusehen, um damit die Hardware in Betrieb zu nehmen. Für einen sinnvollen Dauereinsatz ist das Programm deutlich zu einfach und unflexibel gestrickt. Auch kann die grafische Aufbereitung der Anzeige- und Kontrollelemente nicht befriedigen.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Fehler ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[#Hardware-Umbauten_.26_-Verbesserungen|Hardware-Umbauten und Verbesserungen]]&amp;lt;br&amp;gt;&lt;br /&gt;
Käufer berichten von fehlenden Bauteilen im Bausatz (Wannenstecker, Widerstände, Kondensatoren, Induktivitäten). Für Reklamationen: [https://www.pollin.de/shop/kontakt_service/reklamation.html]&lt;br /&gt;
&lt;br /&gt;
* Die Stückliste auf Seite 4 in den Anleitung mit den Versionsangaben&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
:ist falsch. Pollin legt dem Bausatz irgendwann ab September 2008 einen gedruckten Korrekturzettel bei. Die Online-Version der Anleitung ist korrigiert.&lt;br /&gt;
* Im Schaltplan auf Seite 7 in den Anleitungen mit den Versionen&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
** &#039;&#039;Stand 03.09.2008, online, #all, wpe&#039;&#039; (Online)&lt;br /&gt;
:ist eine 25-polige SUB-D Buchse gezeichnet. Geliefert wird und in der Stückliste verzeichnet ist ein Stecker.&lt;br /&gt;
&lt;br /&gt;
* Die September 2008 ausgelieferte Firmware im ATmega32  funktioniert bei vielen nicht und muss erst upgedatet werden (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Im Flash der gelieferten AVR ist anders als beschrieben nur der Bootloader enthalten, die eigentliche Firmware muss erst mit Hilfe der Updatefunktion geladen werden. Wenn zusätzlich auch die Fuses falsch gebrannt sind, dann funktioniert das Update nicht, auch wenn das PC Programm was anderes behauptet.&lt;br /&gt;
&lt;br /&gt;
* Die Fuse-Einstellungen des ausgelieferten ATmega32 entspricht nicht der Anleitung (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Bausatz, gekauft am 27.10.08, Anleitungsversion 19.09.08, ohne Probleme oder erkennbare Fehler zusammengebaut und in Betrieb genommen.&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft 29.09.2008, Pinbelegung des 25 poligen D-Sub &amp;quot;Anschlusses&amp;quot; stimmt nicht mit der Anleitung überein. Der Aufdruck auf der Platine ist falsch. Pin1 &amp;lt;-&amp;gt; Pin13, Pin2 &amp;lt;-&amp;gt; Pin12 usw. Setzt man den D-Sub Stecker ein, so sind dessen Pinnummern korrekt. Bei einem Bausatz gekauft 05/2013 ist dies ebenfalls noch der Fall.&lt;br /&gt;
&lt;br /&gt;
* 3 Bausätze Anf. Oktober 2008 gekauft, bei einem waren 2 LM317 dabei, dafür fehlte der 7805 - aus der Bastelkiste ersetzt. Alle haben jedoch auf Anhieb funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft Ende Januar 2009. Die Lock-Bits (u.a. für PonyProg2000) werden falsch beschrieben. Die in Klammern aufgeführten Werte stimmen bei einem Bit nicht. Die Texte &amp;quot;Programmiert/Unprogrammiert&amp;quot; hingegen schon. Bei den Bauteilen gab es 4 Kondensatoren mit der Aufschrift &amp;quot;220&amp;quot;, ich habe diese durch welche mit 22p ersetzt, da ich nicht sicher war ob wirklich 22p geliefert wurden. Dafür wurden statt einem zwei 7805 und statt einem mindestens vier LM317 mitgeliefert.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 22.4.2009. Alles vollständig, zusammengebaut, läuft. Software-Version 1.03. Für den oben schon genannten Steckverbinder wurde eine Buchse geliefert. Allerdings stimmen die PIN-Nummern im Schaltplan nicht mit den PIN-Nummern auf der Buchse überein (sie sind gespiegelt), daher liefen die Test-LEDs zunächst nicht.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 11.7.2009. Spannungsregler LM317T fehlt, grüne statt roter LED. Ein Kondensator 22pF zu viel. LM317T wurde auf Anfrage kostenlos nachgeliefert (27.7.). Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 24.7.2009. Ein Quarz 16MHz zu viel, ebenfalls grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 20.08.2009. Ein Kondensator 22pF zuviel und grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli &#039;09 gekauft, grüne statt rote LED&lt;br /&gt;
&lt;br /&gt;
* Bausatz 25.09.09 geliefert, grüne Betriebs-LED, ein ELKO zuviel, Fehler 1µF am MAX232 statt 100nF behoben, richtiger C wird mitgeliefert, Aufbau komplett nach Pollin Anleitung durchgeführt, auf Anhieb fehlerfrei!&lt;br /&gt;
&lt;br /&gt;
* Bausatz 17.10.09 geliefert, grüne Betriebs-LED, zwei 100nF Kondensatoren zu wenig. Aufbau und Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz 21.10.09 gekauft, grüne Betriebs-LED. Aufbau problemlos, RS232 läuft nicht. LAN läuft&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, ENC28J60, MAX232 und ATmega32 fehlen, Nachlieferung nach einer Woche&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft,Bauteile komplett.Verbindungsaufbau Seriell klappt erst nach mehreren Versuchen.Problem gelöst:Spannung an MAX und Mega zu niedrig&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dez. 09 gekauft, grüne LED, 100µF Kondensator fehlt, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 09 gekauft, alle teile da nach Einstellen der fusebits lief alles perfekt&lt;br /&gt;
&lt;br /&gt;
* Bausatz Okt. 09 gekauft, ein 100nF Kondensator und 25MHz Quarz fehlten ... hab beim lokalen Elektronikhändler keinen 25Mhz Grundton Quarz sondern nur im 3. Oberton bekommen aber mit R2.2k parallel zum Quarz schwingt er in der Schaltung schön bei 25Mhz. Mit 1µF am MAX232 funktioniert jetzt auch die RS232.&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Feb. 10 gekauft, bei beiden fehlten 7805, L1+L2 je 100µH sowie 4x falscher Wert Kondensator an Max232 vorhanden. Fehlende Bauteile nachgelötet und Funktion getestet. Hat alles einwandfrei funktioniert!!!&lt;br /&gt;
&lt;br /&gt;
* Bausatz März. 10 gekauft, RS232 Printbuchse fehlt, dafür 1x 10pol Wannenstecker zuviel. Grüne LED statt Rot. Funktioniert ansonsten einwandfrei.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Jan. 10 gekauft, gelbe LED statt rot, C14...C17: 10µF, weder seriell noch via Ethernet Konnektivität. Nach Austausch von C14-C17 gegen 1µF, wenigstens serielle Kontaktaufnahme möglich, kein Ethernet auch nach Flash von 1.03 mit NetServer.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Feb. 10 gekauft, Spannungsregler LM317T fehlte&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft, gelbe statt rote LED geliefert, aber Aufbau und inbetriebnahme lt. Handbuch ohne Probleme&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft und gelbe statt rote LED geliefert, funzt wunderbar gemäß Anleitung&lt;br /&gt;
&lt;br /&gt;
* Fertig gelötete Platine gekauft. µC war falsch im Sockel.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - Funktion erst nach Ersetzung des ADM durch nen MAX&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - funktionierte sofort auch mit dem ADM232LJN.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft wurde mit grüner statt roter LED Ausgeliefert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juni 10 gekauft: wurde mit grüner statt roter Netz-LED ausgeliefert, 2x 22pF Kerko zuviel&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli 10 gekauft: 2 Quarze mit 16 MHz geliefert, statt 1x 16MHz und   1x25MHz.&lt;br /&gt;
&lt;br /&gt;
* Bausatz September 10 gekauft: hat sofort funktioniert. 1x 3,3k und 1x 10k Widerstand zuviel. Statt 100nF Kondensatoren wurden 1µF geliefert -&amp;gt; Platzprobleme auf der Platine durch grössere Bauform. LED grün.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober  6 gekauft: alles funktioniert. LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Fertigmodul Oktober 10 gekauft: Auf Anhieb alles funktioniert!&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft: komplett und sofort funktioniert (sogar mit der neusten Pollin Firmware 1.03 schon drauf) LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft. Nach Bezug neuer Feinst-Lötspitzen konnte ich ihn dann auch zusammenlöten. Es hat sofort alles funktioniert, obwohl ich nur 12V bzw. 9V Gleichspannung zur Verfügung hatte, und nicht sicher war, wieviel die Komponenten wirklich benötigen. Der Regler wird auch bei 9V Gleichspannungsversorgung noch sehr warm. Da muss auf jeden Fall ein Kühlkörper dran! Ich habe auch eine grüne LED bekommen, ist mir aber wurscht :-)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dezember 10 gekauft: komplett und funktionierte sofort. Firmware 1.03, grüne LED (Ein Quarz und ein IC-Sockel zu viel)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Januar 2011 gekauft: nur genau die richtige Anzahl Teile dabei, Firmware 1.03, grüne LED, ging auf Anhieb&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Januar 2011 gekauft: beide grüne LED, und 1x doppelter Satz Jumper/Stiftleiste, 22PF und Anschlussklemmen. Rest vollständig und beide haben sofort nach zusammenbau funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: die Diode D5 fehlt, 10 µF gegen 1 µF für MAX232 getauscht, Flash im ATmega32 war programmiert, passende IP-Adr über serielle Schnittstelle eingestellt; ADD-ON: für R1 war 22Ω statt 0,2Ω beigelegt, durch richtigen ersetzt, Beschreibung der LED Bestückung mangelhaft / oe1smc&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 1 Diode zuviel, 2 Spulen fehlen, LED grün. Die fehlenden Spulen wurden durch welche aus der Bastelkiste ersetzt - funktioniert. Der 7805 bekam einen kleinen Kühlkörper spendiert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 2x 10k Widerstände fehlen. Dafür eine Diode zu viel.&lt;br /&gt;
&lt;br /&gt;
* Ende Februar 2011: Zwei Bausaetze an jeweils zwei Adressen, alles in Ordnung.&lt;br /&gt;
&lt;br /&gt;
* Ende März 2011: 2x 25 Mhz Quarz statt 1x16 u. 1x25 Mhz. LED fehlt. Bausatz funktioniert nach Tausch des Quarz den mir mein Freund oe9rsv aus seinem Fundus spendiert hat. Danke auch für die Hilfe beim Fehler suchen.&lt;br /&gt;
&lt;br /&gt;
* Mitte 2010 gekauft: 1x 100nF fehlt&lt;br /&gt;
&lt;br /&gt;
* Mitte Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, Pollin wollte, dass ich das ganze Paket zurückschicke für einen Austausch. Ein 51Ω zu viel. 16Mhz im Handel und 25Mhz vom alten Mobo ausgelötet. Läuft wunderbar.&lt;br /&gt;
&lt;br /&gt;
* Anfang Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, nach kurzer Mail an Pollin (leider ohne Antwort) wurden diese nach ca. 1 Woche in einem Brief nachgeliefert.&lt;br /&gt;
&lt;br /&gt;
* August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: Platine fehlte -&amp;gt; in Nachlieferung&lt;br /&gt;
&lt;br /&gt;
* 30 August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* 06 Sep. 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: 6 Stück bestellt, bei einem haben die 100nF Kondensatoren gefehlt, bei einem zwei LM317 statt 7805 und LM317. Angerufen, 3 Tage später Nachlieferung erhalten.&lt;br /&gt;
&lt;br /&gt;
* Nov. 2011: &#039;&#039;&#039;Net_IO&#039;&#039;&#039; vollständig. Einspielen der Firmware 1.03 war erforderlich. Bei &#039;&#039;&#039;Add-On&#039;&#039;&#039; immer noch falscher Q1 BC548 (NPN) in Stückliste und Lieferung. BC327-40 oder BC328-40 (PNP) nachgefordert. R11 und R24 mitgeliefert, entsprechend Beschreibung V1.1 von Pollin&#039;s Download. Beiliegende Beschreibung war älter, ohne diese Widerstände.&lt;br /&gt;
&lt;br /&gt;
* Ende Nov.2011, alle Teile dabei, Firmware war drauf, sofort funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Anfang Dez.2011, komplett bestückte Platine gekauft. Auf 7805 Kühlkörper gebaut, da er nach 1 Minute schon ausgestiegen ist (LED hat das Pumpen angefangen). Firmware 1.03 musste noch aufgespielt werden danach funktioniert alles einwandfrei. In Betrieb mit 10V DC&lt;br /&gt;
&lt;br /&gt;
* Ende Dez.2011 2xNET-IO und 2xADD bestellt, 4 verschieden volle Kisten bekommen... WSL16 ist mit Verriegelung, geht nicht aufs Board, Jumper fehlen, Spannungsregler doppelt, Poti Löcher zu klein, lsb3 fehlt, SD-Slot hab ich jetzt 3, 100nF hab ich jetzt 4 übrig ... also immer noch lustig. HW-Stand immer noch 1.0 ( gab es überhaupt eine 1.1?)&lt;br /&gt;
&lt;br /&gt;
* Mitte Jan. 2012, 10pol. beide Wannenstecker nicht dabei, Firmware war drauf, sofort funktioniert. 1 LED zuviel. Unproblematische Nachlieferung bei Reklamation (wegen der beiden Wannenstecker kam ein PAKET!).&lt;br /&gt;
&lt;br /&gt;
* Ende Feb. 2012, Um die PHP-Scripte über öffentlichen Webserver zu betreiben muss mit SETGW die Gateway-Adresse des lokalen Routers eingetragen werden. Bei der NAT im Router sollte z.B. Port 8080 auf den internen Port 50290 umgeleitet werden, da manche Provider diesen Port für die Socket-Kommunikation nicht zulassen.&lt;br /&gt;
&lt;br /&gt;
* Juli 2012, Bausatz vollständig, Nach Aufbau sofort den 7805 mit einem kleinen Kühlkörper versehen, da er sonst thermisch überlastet wird! hat weder auf LAN noch RS232 reagiert, musste erst Update einspielen (Download Pollin), danach funktionierte alles einwandfrei!&lt;br /&gt;
&lt;br /&gt;
* Januar 2013, Bausatz vollständig, 7805 mit Kühlkörper versehen, nach Aufbau ohne Probleme funktioniert&lt;br /&gt;
&lt;br /&gt;
* Februar 2013, Bausatz komplett, Aufbau ohne Probleme, 7805 nur leicht warm, LAN funktioniert, RS232 noch ohne Funktion.&lt;br /&gt;
&lt;br /&gt;
* Oktober 2013, Bausatz komplett laut Stückliste, problemloser Aufbau, 7805 leicht warm am 10V DC. RS232 und LAN funktionierten auf Anhieb.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die nachfolgend genannten Änderungen sind in der Bauanleitung V1.1 mit Stand 07.12.2011 (Wichtig! Es gibt mehr als eine V1.1 ...) bereits berücksichtigt, R3 wird allerdings mit 3,6kΩ angegeben.&lt;br /&gt;
&lt;br /&gt;
Um bei einem Neuaufbau parallele Widerstände zu vermeiden, sollten folgende Änderungen auf dem Addon-Board gemacht werden:&lt;br /&gt;
*R2 1,5kΩ ersetzen mit 2kΩ&lt;br /&gt;
*R3 1,8K ersetzen mit 3,3kΩ&lt;br /&gt;
*R19 470kΩ ersetzen zu 470Ω&lt;br /&gt;
*Q1 BC548 ersetzen durch BC327 oder BC328 (Hauptsache PNP! und mehr als 100mA)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039; beim Anschluß eines LC-Displays an J5 (über I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C, PCF 8574): Vcc/5V liegt an Pin 1, GND an Pin 2 von J5. Es gibt etliche Displays mit abweichender Belegung Pin 1 GND und Pin 2 Vcc!&lt;br /&gt;
&lt;br /&gt;
Auch beim Add-On gibt es fehlende oder falsche Bauteile im Bausatz und Fehler im Schaltplan und auf der Platine:&lt;br /&gt;
&lt;br /&gt;
*Stand Feb. 2011: R2 wird mit 2,2kΩ und R3 wird mit 3,6kΩ ausgeliefert. Somit werden die 3,3 V richtig erzeugt. R19 hat 470Ω. Der ISP-Anschluß ist nicht vollständig durchgeschleift, es besteht keine Verbindung der RESET-Leitung zwischen ISP und ISP1 (Abhilfe: Drahtbrücke einlöten, [http://www.mikrocontroller.net/topic/161354#1600385 Quelle]). &lt;br /&gt;
&lt;br /&gt;
*Stand Nov. 2011: bei mir ist die RESET-Leitung korrekt zw. ISP und ISP1 verbunden. Es gibt jetzt auch einen R24 (470Ohm) und R11 (1KOhm), der in der bei mir mitgelieferten Bauanleitung fehlt, in der zum Download (V1.1) angebotenen  aber drin steht. Es wird immer noch der falsche Q1 BC548C (NPN) mitgeliefert. Das Schaltsymbol für einen PNP ist richtig im Schaltplan gezeichnet.&lt;br /&gt;
&lt;br /&gt;
*Stand Dez. 2011: R24 (470Ω) kann mit 0Ω ersetzt und R11 (1kΩ) völlig weggelassen werden. Diese Widerstände bilden einen (eigentlich überflüssigen) Spannungsteiler in der MISO Leitung. Der Spannungsteiler hat allerdings auch eine Schutzfunktion, falls weitere Slaves am SPI-Bus hängen, die 5V-Pegel nutzen, z. B. ISP-Programmierer. (siehe [http://son.ffdf-clan.de/include.php?path=forumsthread&amp;amp;threadid=1167&amp;amp;postid=9203 1284p: Board, ich oder beide verwirrt?])&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Bausatz mit Stecker anstelle einer Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s dafür aber 3 Poti zuviel.&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Ebenfalls Bausatz mit Sub-D-Stecker statt Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s und 3 Potis zuviel.&lt;br /&gt;
&lt;br /&gt;
*Stand März 2013: Die Belegung der 25 pol. Sub-D-Buchse im Schaltplan der Anleitung V1.1 (und früher) ist falsch dargestellt. SCL und SDA des I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C-Busses liegen wie beim AVR-NET-IO Board auf Pin 2 bzw. 3 und nicht auf Pin 11 bzw. 12 der Buchse. Ein 1:1 verdrahtetes Kabel funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Andere Software für den Client-PC ==&lt;br /&gt;
=== NetIOLib ===&lt;br /&gt;
&lt;br /&gt;
In C# geschriebene Bibliothek zur Ansteuerung der Platine im Orginalzustand. Inkl. Beispielsoftware und Quellcode (GNU GPL) &lt;br /&gt;
&lt;br /&gt;
Links gehen nicht:&lt;br /&gt;
DLL: [http://www.tware.org/downloads/NetIOLib_dll.zip Download-Link]&lt;br /&gt;
Source: [http://www.tware.org/downloads/NetIOLib_src.zip Download-Link]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Multi-Control ===&lt;br /&gt;
Mti dem E2000-NET-IO-Multi-Control ist es möglich, die vom dem E2000-NET-IO-Designer erstellten Projekte zu öffnen und die AVR-NET-IO&#039;s zu steuern. &lt;br /&gt;
&lt;br /&gt;
Die Anwendung liegt dem [http://www.mikrocontroller.net/articles/AVR_Net-IO_Bausatz_von_Pollin#E2000-NET-IO-Designer_.28Windows.5BXP.2F7.5D_.2F_Android.29 E2000-NET-IO-Designer] bei.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== ControlIO ===&lt;br /&gt;
Einfache Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://www.mikrocontroller.net/topic/149695&lt;br /&gt;
&lt;br /&gt;
=== JAVA Lib ===&lt;br /&gt;
Einfache Java-Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://son.ffdf-clan.de/?path=forumsthread&amp;amp;threadid=611&lt;br /&gt;
&lt;br /&gt;
=== PHP ===&lt;br /&gt;
PHP Klasse zur Ansteuerung mit der Originalfirmware. (Opensource Lizenz)&lt;br /&gt;
http://blog.coldtobi.de/1_coldtobis_blog/archive/298_pollin_net-io_php_library.html&lt;br /&gt;
&lt;br /&gt;
PHP Funktionen zum Ansteuern der Originalfirmware. (Free for All Lizenz)&lt;br /&gt;
http://defcon-cc.dyndns.org/wiki/index.php/Pollin_AVR-NetIO_PHP_Wrapper&lt;br /&gt;
&lt;br /&gt;
== Clients für Smartphones ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== AVR NET IO Control (Windows Phone 7.5,7.8,8.0) ===&lt;br /&gt;
&lt;br /&gt;
Freie App zur steuerung des AVR NET IO.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;App im Market: [http://www.windowsphone.com/de-de/store/app/avr-net-io-controll/030d107f-9a27-4a62-ac8f-cb74e79c0500 AVR NET IO Control]&#039;&#039;&#039;&lt;br /&gt;
=== App NetIO (Windows Mobile 6.5) ===&lt;br /&gt;
Frei verfügbare App für Windows Mobile zur Ansteuerung mit der Orginalsoftware. Das HTC HD2 wird damit zur Fernsteuerung für das AVR Net-IO Board.&lt;br /&gt;
http://www.heesch.net/netio.aspx&lt;br /&gt;
&lt;br /&gt;
=== NET-IO Control (Android) ===&lt;br /&gt;
Eine Application für das Android Betriebssystem zur Steuerung des AVR Net-IO Boards. Es ist möglich, alle Ausgänge zu steuern und alle Eingänge anzuzeigen. Die Analogen Eingänge können mit einem Berechnungsfaktor versehen werden. &#039;&#039;Geplant ist noch ein Offsetwert.&#039;&#039; Außerdem kann jedem analogen Wert eine Einheit zugeordnet werden. Die Ausgänge können in der neusten Version in einen Tastermodus gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:NET-IO-Control.png|200px]] [[Datei:NET-IO-Control2.png|200px]] [[Datei:NET-IO-Control3.png|200px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Trial-Version: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControlTRAIL Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(In dieser Probe Version können nur 1 Output und 2 Inputs gesteuert werden)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vollversion: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControl Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(Kostet im Google-Play 3,00 €)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Designer (Windows[XP/7] / Android) ===&lt;br /&gt;
[[Datei:E2000-NET-IO-Designer.png|400px|right]]&lt;br /&gt;
Mit dem E2000-NET-IO-Designer ist es möglich, eine grafische Oberfläche für die NET-IOs von Pollin zu erstellen. Dafür werden Knöpfe, Texte und Anzeigebilder zur Verfügung gestellt. Jedes dieser Element kann &amp;quot;Aufgaben&amp;quot; übernehmen um die NET-IOs zu steuern oder einen Status des NET-IO anzuzeigen. Weitere Icons können von dem Benutzer selbst in den entsprechenden Ordner gelegt werden und in die Oberfläche eingebunden werden. Es können mehrere Seiten designt werden die durch einen selbst positionierten Knopf erreichbar sind. Die Android Application arbeitet somit im Fullscreen-Modus. Des weiteren ist es möglich, mehrere NET-IO&#039;s in einem Projekt zu benutzen. Nach dem erstellen der grafischen Oberfläche mit dem E2000-NET-IO-Designer, kann mit der Android APP das Projekt einfach gedownloaded werden. Dafür baut die APP eine Verbindung zum E2000-NET-IO-Designer auf und läd alle benötigten Dateien herunter (Achtung: Firewall-Einstellungen beachten). Die designten Oberflächen können außerdem noch mit der E2000-NET-IO-Multi-Control.exe auf dem Computer ausgeführt werden. Dadurch ist es Möglich, seine NET-IOs vom PC aus zu steuern über eine selbst designte Oberfläche. &lt;br /&gt;
&lt;br /&gt;
Zum Ausführen des E2000-NET-IO-Designer muss .NET Framework 4.0 installiert sein und es muss eine Internetverbindung exisitieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Features:&#039;&#039;&#039;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Grafische Oberfläche am PC erstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verwendung von eigenen Button- und Hintergrundbildern&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designbare Anzeige von Digital- und Analogwerten&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Steuern von mehreren AVR-NET-IO Boards&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Unterstützung der E2000-NET-IO Firmware (mehere Boards gleichzeitig)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designte Steuerungen können auf dem PC oder dem Handy ausgeführt werden (Android)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Schnelle Verbindung&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Einfache übertragung auf das Handy durch Projekt Download&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/downloads.php?id=44 Download E2000-NET-IO-Designer]&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/hilfe/E2000Netdesigneruebersicht.html Online Hilfe]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== NetIO ( iPhone &amp;amp; Android ) ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;float: left; margin-right: 20px&amp;quot;&amp;gt;[[Datei:Netio_logo.png|70px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schöne universelle Fernbedienung für das Board (iOS / Android). Konfigurierbar über einen Browserbasierten Editor. &amp;lt;br&amp;gt;&lt;br /&gt;
Unterstützt TCP socket Verbindungen, kann http request absetzen und kann auch Webseiten einbinden (z.B. IP-Kameras). Kann mehrere &lt;br /&gt;
Boards gleichzeitig steuern! Einfach super schnell ins Projekt eingebaut... Es gibt Buttons (die auch als Taster konfiguriert werden können), Slider, Switches und einfache Labels. Dinge können geschaltet oder Daten ausgelesen und mit regex dargstellt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;float: right&#039;&amp;gt;&lt;br /&gt;
[[Datei:Netio_TV.png|190px]]&lt;br /&gt;
[[Datei:Netio_iphone5.png|190px]]&lt;br /&gt;
[[Datei:NetIO_appflow.jpg|190px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
AppStore Link: http://itunes.apple.com/app/netio/id464924297?mt=8 &amp;lt;br/&amp;gt;&lt;br /&gt;
Google Play Link: https://play.google.com/store/apps/details?id=com.luvago.netio &amp;lt;br/&amp;gt;&lt;br /&gt;
Die gleiche Konfiguration kann auch mit einem [https://github.com/davideickhoff/NetIO-OSX-Dashboard-Widget OSX Widget] benutzt werden. &amp;lt;br/&amp;gt;&lt;br /&gt;
Webseite: http://netio.davideickhoff.de &lt;br /&gt;
&lt;br /&gt;
Hinweis: &amp;lt;br&amp;gt; Man findet es unter &amp;quot;NetIO&amp;quot; im Store, im Icon selbst und in iTunes wird es als &amp;quot;Controller&amp;quot; angezeigt. &amp;lt;br&amp;gt;&lt;br /&gt;
Die eigene Konfiguration kann man mit dem [http://netio.davideickhoff.de/editor Online Editor] vorher am PC erstellen.&lt;br /&gt;
Eine fertige [http://netio.davideickhoff.de/editor/?config=pollin AVR-NET-IO Konfiguration] gibt es als funktionierendes Beispiel schon als Preset.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;div style=&#039;clear: both&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AVR Net IO (iPhone) ===&lt;br /&gt;
[[Datei:AVRNetIO-Screenshot1.png|160px|rechts]]&lt;br /&gt;
Update 15.12.2011: Die Neue Version 1.3 ist seit gestern im AppStore. Fehlerkorrekturen und ein robusteres Handling machen die App nun zur universellen AVR-Net-IO-Steuerung.&lt;br /&gt;
&lt;br /&gt;
Mit der [http://itunes.apple.com/de/app/avr-net-io/id460991760?mt=8 iPhone App AVR-Net-IO] kann das Board ferngesteuert werden. Die Fernsteuerung umfasst in der Version 1.1 folgende Funktionen:&amp;lt;br&amp;gt;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ADC-Werte zyklisch auslesen und darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Inputs zyklisch darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Outputs können über Buttons geschaltet werden. Die Werte der Digital-Outputs werden zuerst ausgelesen und zeigen den zuletzt gesetzten Wert an.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Terminal-Modus: Hier können beliebige AVR-Net-IO-Befehle eingegeben werden. Das Ergebnis wird 1:1 angezeigt, wie es vom Board kommt. Hilfreich für Tests bei Eigenentwicklungen und Konfigurationen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Mehr Infos gibt&#039;s dazu direkt von den Entwicklern auf [http://www.facebook.com/pages/AVR-Net-IO/187799687958255?ref=nf AVR-net-IO Facebook-Page] oder direkt auf deren Homepage [http://www.ondics.de/apps/1001/ Homepage].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Hinweis: MIt der aktuellen Version 1.1 gibt es noch bei manchen Boards Probleme beim auslesen und anzeigen der ADC- und Digital-Werte. Das Terminal funzt problemlos. Die Entwickler kümmern sich gerade drum und haben einen baldigen Update versprochen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Andere Software statt der Originalsoftware von Pollin ==&lt;br /&gt;
&lt;br /&gt;
Die Umrüstung auf einen Webserver durch Austausch der Software (und ev. des ATmega32) bietet sich an. Kleiner Hinweis dabei: wenn zum Flashen ein ISP-Adapter verwendet wird, diesen unbedingt vor dem Start der neuen Software abziehen! Der ISP arbeitet nämlich über dieselbe SPI-Schnittstelle über die auch der ENC28J60 angesteuert wird. Ein eventuell noch angeschlossener, wenn auch passiver ISP-Adapter stört diese Kommunikation, d.h. das Programm an sich scheint zu laufen, aber die Ethernet-Schnittstelle funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
=== E2000 - Logik ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:E2000-Logik-Bedienoberflaeche.jpg|500px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Anwenderfreundliche Logik-Software von Elektronik2000.de zur Steuerung des AVR-NET-IO. Der ATMEGA32 wird durch einen ATMEGA644 ersetzt und mit der E2000-Firmware beschrieben. Nun ist es möglich in der E2000-Logik Software eine Logikschaltung zu erstellen und diese auf das NET-IO-Board zu übertragen. Dort wird diese Schaltung simuliert. Dies funktioniert komplett ohne einen Computer.&lt;br /&gt;
&lt;br /&gt;
Durch ein Erweiterungsboard von Elektronik2000.de ist es auch möglich, das Board ohne Internet laufen zu lassen eine RTC (RealTimeClock) übernimmt dabei die Uhrzeitgesteuerten Funktionen. Auf dieser Erweiterung ist auch ein EEPROM integriert, um Firmware-Updates über Netzwerk einzuspielen (in Zukunft). Diese Erweiterung bietet außerdem die Anbindung an das E2000-Bus-System, durch das es möglich ist das AVR-NET-IO-Board durch weitere Ein- und Ausgänge zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Das Designen von Schaltaufgaben wird in diesem Programm grafisch dargestellt, durch das ein einfaches Anpassen seiner Logikschaltungen möglich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Steuerung des E2000-NET-IO ist möglich durch den Intregrieten Webserver, die PC-Software (E2000-NET-IO Control) oder der Androidsoftware. All diese Können gleichzeitig auf das E2000-NET-IO zugreifen und Funktionen ausführen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es auf der Entwicklerseite: [http://www.elektronik2000.de Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== Bascom Version von Hütti ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://bascom-forum.de/index.php/topic,1781.45.html )&lt;br /&gt;
dort am Ende der Seite.&lt;br /&gt;
&lt;br /&gt;
=== Ben&#039;s Bascom Quellcode ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://members.home.nl/bzijlstra/software/examples/enc28j60.htm )&lt;br /&gt;
&lt;br /&gt;
Muss aber für Bascom 1.11.9.3 angepasst werden, siehe Code von Hütti !&lt;br /&gt;
&lt;br /&gt;
=== U. Radigs Webserver ===&lt;br /&gt;
&lt;br /&gt;
Angepasster Sourcecode von U.Radig: http://www.mikrocontroller.net/attachment/40027/Webserver_MEGA32.hex&lt;br /&gt;
oder selbst anpassen: &lt;br /&gt;
Ändere in der Datei ENC28J60.H:&lt;br /&gt;
 #define ENC28J60_PIN_CS    4&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#988386)&lt;br /&gt;
&lt;br /&gt;
Temporären Dateien (*.d, *,lst,*.o) vorher im Verzeichnis löschen &#039;&#039;make clean&#039;&#039;, damit neu compiliert wird.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.0.99&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: uli1&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den orginal SourceCode gibt&#039;s übrigens hier:http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex&lt;br /&gt;
&lt;br /&gt;
Bei den Fuses BOOTRST ausschalten, da die Software keinen Bootloader enthält.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.1.90&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: tim&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://beitz-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://pieper-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterentwicklung des Radig-Codes von RoBue: &amp;lt;br&amp;gt;&lt;br /&gt;
- 1-Wire-Unterstützung (Anschlus an PORTA7) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA0-3 digitaler Eingang (ein/aus) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA4-6 analoger Eingang (0 - 1023) &amp;lt;br&amp;gt;&lt;br /&gt;
- LCD an PORTC &amp;lt;br&amp;gt;&lt;br /&gt;
- Schalten in Abhängigkeit von Temperatur und analogem Wert &amp;lt;br&amp;gt;&lt;br /&gt;
- (Teilweise) Administration über Weboberfläche &amp;lt;br&amp;gt;&lt;br /&gt;
- Erweiterung des cmd-Befehlsatzes für telnet/rs232 &amp;lt;br&amp;gt;&lt;br /&gt;
Gedacht ist der Einsatz des AVR-NET-IO-Bausatzes für Heizungs- oder Haussteuerung) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test: http://avrboard.eluhost.de/&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Quelle:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/43307/AVR-NET-IO_RoBue_V1.3.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/44569/AVR-NET-IO_RoBue_V1.4.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/46720/AVR-NET-IO_RoBue_1.5-final_hoffentlich_.zip)&lt;br /&gt;
&lt;br /&gt;
Bei der Ver 1.5 sind die Ports PD2+3 fürs 4bit LCD (Ext.) vertauscht ! Gruß B.P&lt;br /&gt;
&lt;br /&gt;
=== Simon Ks Webserver (uip-Stack) ===&lt;br /&gt;
Angepasster Sourcecode von Simon K: http://www.mikrocontroller.net/attachment/39939/uWebSrv.zip&lt;br /&gt;
IP: 192.168.0.93:8080&amp;lt;br&amp;gt;&lt;br /&gt;
Um diesen Code mit einem Atmega1284P verwenden zu können, muss in der main.c in Zeile 38, &amp;quot;TIMSK&amp;quot; durch &amp;quot;TIMSK1&amp;quot; ersetzt werden.&lt;br /&gt;
Die Fusebits für den Atmega1284p ohne Bootloader sind:&lt;br /&gt;
lfuse=0xFF, hfuse=0xD9, efuse=0xFF&lt;br /&gt;
&lt;br /&gt;
=== Ethersex Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.ethersex.de - Einfach für atmega32 compilieren und funktioniert.&lt;br /&gt;
&lt;br /&gt;
=== Etherrape Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.lochraster.org/etherrape/ &lt;br /&gt;
&lt;br /&gt;
ist in jedem Fall hier auch zu erwähnen zumal es sich beim etherrape um das Ursprungsprojekt von ethersex handelt.&lt;br /&gt;
Es scheint aber bei der Weiterentwicklung wenig zu passieren.&lt;br /&gt;
Ausführliche Dokumentation findet sich unter http://wiki.lochraster.org/wiki/Etherrape&lt;br /&gt;
&lt;br /&gt;
=== Mini SRCP Server (kommerziell, Closed-Source)===&lt;br /&gt;
&lt;br /&gt;
Damit wird die Platine zu einer Modellbahnsteuerung, die&lt;br /&gt;
über das Netzwerkprotokoll SRCP mit verschiedenen Programmen&lt;br /&gt;
gesteuert werden kann.&lt;br /&gt;
&lt;br /&gt;
[http://www.7soft.de/de/mini_srcp_server/index.html Infoseite] zur Hardware&lt;br /&gt;
und das zugrundeliegende [http://www.der-moba.de/index.php/Digitalprojekt Digitalprojekt].&lt;br /&gt;
&lt;br /&gt;
=== Avr ArtNET-Node ===&lt;br /&gt;
&lt;br /&gt;
Hiermit kann die Platine zu einem Art-Net Node werden, mit dem sich ein DMX-Universe über Ethernet übertragen lässt. Basiert auf den Quellen von Ulrich Radig.&lt;br /&gt;
&lt;br /&gt;
Dokumentation: [http://www.dmxcontrol.de/wiki/Art-Net-Node_f%C3%BCr_25_Euro Art-Net-Node für 25 Euro]&lt;br /&gt;
&lt;br /&gt;
=== Webserver von G. Menke ===&lt;br /&gt;
&lt;br /&gt;
Ein Webserver (basierend auf den Sourcen von U. Radig), der so angepasst ist, dass alle Ein- und Ausgänge wie bei der originalen Pollin-Software genutzt werden können (8xDIGOUT, 4xDIGIN, 4xADIN). Der Webserver kann daher direkt auf das Net-IO geladen werden. Im ZIP-File sind ein ReadMe und alle C-Sourcen enthalten.&amp;lt;br&amp;gt;&amp;lt;i&amp;gt;Download&amp;lt;/i&amp;gt;:&lt;br /&gt;
[http://gm.stream-center.de/webserver/ Webserver mit passender IO]&lt;br /&gt;
&lt;br /&gt;
=== OpenMCP ===&lt;br /&gt;
&lt;br /&gt;
Tolles Projekt, welches viele Features bietet und stabil läuft. Hervorzuheben ist die Übersichtlichkeit der Programmteile/Module und die vielleicht nicht ganz komplette Dokumentation. Man merkt, dass viel Arbeit und Liebe in diesem Projekt steckt. Herausgekommen ist dabei eine einfach zu handhabende Entwicklungsumgebung. Anfänger können, dank des gut durchdachten CGI-Systems, welches sich um alle wichtigen Sachen kümmert, leicht eigene CGI implementieren. Alle Ausgaben erfolgen nur mit printf über die Standardausgabe und werden automatisch richtig per Netzwerk übertragen, dadurch ist es auch für den Anfänger recht gut geeignet, da man sich nicht mit der Netzwerkprogrammierung auseinander setzen muss.&lt;br /&gt;
&lt;br /&gt;
Die Software belegt im Moment (Stand Juli 2010) ca. 55 Kb im Flash, so dass man das Board mit einem grösseren µC (z.B. ATMega644) aufrüsten muss.&lt;br /&gt;
&lt;br /&gt;
[http://wiki.neo-guerillaz.de Projekt und Doku]&lt;br /&gt;
&lt;br /&gt;
Der Autor stellt zwei über das Internet erreichbare Testboards bereit unter http://www.neo-guerillaz.de:81 und http://www.neo-guerillaz.de:82 die beide unter OpenMCP laufen, je auf einen AVR-NETIO mit einem ATmega644 und dem eigentlichen Board mit einem ATmega2561. Zusätzlich ist gerade eine Version für das myAVR in Arbeit die schon ordentlich Fortschritte macht.&lt;br /&gt;
&lt;br /&gt;
=== OpenMLP ===&lt;br /&gt;
Auf openMCP basierender Port nach [http://avr.myluna.de LunaAVR] (GPL). Umfangreiche Funktionalität und direkte Anpassung an die Sprache. Abgespeckte Version auch auf Atmega32 lauffähig. Die Original-Dokumentation kann zum Großteil hergenommen werden. Einige Zusatzfeatures. Leichte Konfiguration und guter Einstieg für Anfänger und zum Verständnis der Serverfunktionalität.&lt;br /&gt;
&lt;br /&gt;
[http://avr.myluna.de/doku.php?id=de:openmlp Artikelseite]&lt;br /&gt;
[http://forum.myluna.de/viewtopic.php?f=8&amp;amp;t=13 Forum]&lt;br /&gt;
&lt;br /&gt;
=== ENC28J60 I/O-Webserver von Thomas Heldt ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul-Webserver (Softwarekompatibel zum Pollin Webserver), der durch div. Module erweitert werden kann, Software in Bascom basierend auf dem Code von Ben Zijlsta wurde erweitert und angepasst:&lt;br /&gt;
&lt;br /&gt;
[http://mikrocontroller.heldt.eu/index.php?page=enc28j60-io-webserver Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
=== AVR-Netino ===&lt;br /&gt;
[http://code.google.com/p/avr-netino/ Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
Arduino fürs Net-IO&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/topic/109988&lt;br /&gt;
* [http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en022889 ENC28J60 Produktseite]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/DeviceDoc/39662c.pdf ENC28J60 Datenblatt(pdf)]&lt;br /&gt;
* [http://son.ffdf-clan.de Forum für AVR-Net-IO]&lt;br /&gt;
* [http://avr.myluna.de/doku.php?id=de:openmlp LunaAVR openMLP ]&lt;br /&gt;
* [http://bascom-forum.de/index.php/topic,1781.0.html Bascom Forum ]&lt;br /&gt;
* [http://hobbyelektronik.org/w/index.php/AVR-NET-IO-Shield Shield für den NET-IO]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Category:Ethernet|P]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78891</id>
		<title>AVR Net-IO Bausatz von Pollin</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78891"/>
		<updated>2013-10-11T00:49:40Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Inbetriebnahme der Originalsoftware */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier steht eine Beschreibung des Pollin Bausatzes [http://www.pollin.de/shop/shop.php?cf=detail.php&amp;amp;pg=NQ==&amp;amp;a=MTQ5OTgxOTk= AVR-NET-IO. Best.Nr. 810 058], oder als aufgebautes Fertigmodul, Best.Nr. 810 073. &lt;br /&gt;
&lt;br /&gt;
Einige Features: Ethernet-Platine mit ATmega32 und Netzwerkcontroller ENC28J60. Die Platine verfügt über 8 digitale Ausgänge, 4 digitale und 4 ADC-Eingänge, welche alle über einen Netzwerkanschluss (TCP/IP) abgerufen bzw. geschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AVR-NET-IO ADD-ON.JPG|thumb|right|400px|AVR-NET-IO (links) mit zusätzlicher SUB-D Anschlussplatine (rechts, nicht im Lieferumfang)und Add-On-Board (oben, mit aufgelötetem RFM12-433-Modul, beides nicht im Lieferumfang). Ebenso ist zusätzlich ein nicht im Lieferumfang enthaltener kleiner Kühlkörper auf einem der Spannungsregler montiert und die Schraubklemmen zur Stromversorgung wurden durch Buchsen ersetzt.]]&lt;br /&gt;
&lt;br /&gt;
== Technische Daten ==&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 9 V AC/DC&lt;br /&gt;
* Stromaufnahme ca. 190 mA&lt;br /&gt;
* 8 digitale Ausgänge (0/5 V) [PC0-PC7 an J3]&lt;br /&gt;
* 4 digitale Eingänge (0/5 V) [PA0-PA3 an J3]&lt;br /&gt;
* 4 ADC-Eingänge (10 Bit) [PA4-PA7 an Schraubklemmen]&lt;br /&gt;
* LCD-Anschluss (HD44780 komp. Controller nötig) [PD2-7,PB0,PB3 an EXT]&lt;br /&gt;
* [[ENC28J60]]&lt;br /&gt;
* [http://www.atmel.com/dyn/Products/Product_card.asp?part_id=2014 ATmega32] Mikrocontroller&lt;br /&gt;
&lt;br /&gt;
Maße (L×B×H): 108×76×22 mm.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Die Schaltung des AVR-NET-IO ist recht einfach:&lt;br /&gt;
* Ein ATmega32 Mikrocontroller enthält die gesamte Software&lt;br /&gt;
* Ein ENC28J60 Ethernet-Controller für das Senden und Empfangen von Ethernet Frames (MAC und PHY Ethernet Layer) ist über [[SPI]] mit dem ATmega32 verbunden&lt;br /&gt;
* Ein Ethernet RJ-45 MagJack TRJ 0011 BA NL von [http://www.trxcom.com/ Trxcom] mit eingebautem Übertrager und Anzeige-LEDs am ENC28J60.&lt;br /&gt;
* Ein MAX232 für die serielle Schnittstelle&lt;br /&gt;
* Zwei Spannungsregler, 5 V und 3,3 V&lt;br /&gt;
* &amp;quot;Hühnerfutter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Fast alle I/O Pins des ATmega32 sind irgendwo auf Anschlüssen herausgeführt. Entweder auf dem SUB-D Stecker, dem EXT oder ISP Wannensteckern oder den blauen Anschlussklemmen. Eine Schutzbeschaltung gibt es nicht.&lt;br /&gt;
&lt;br /&gt;
Die blauen Anschlussklemmen haben eine Nut und eine Feder mit denen man&lt;br /&gt;
sie zusammenstecken kann, dadurch ist das Anlöten wesentlich leichter&lt;br /&gt;
und sie stehen auch sauber in der Reihe.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
Seit Januar 2010 gibt es auch eine Erweiterungsplatine &lt;br /&gt;
&lt;br /&gt;
[http://www.pollin.de/shop/dt/Nzg4OTgxOTk-/Bausaetze/Diverse/Bausatz_Add_on_fuer_AVR_NET_IO.html Add-on für AVR-NET-IO-Board Best.Nr. 810 112]&lt;br /&gt;
&lt;br /&gt;
Diese Platine erweitert das NET-IO um:&lt;br /&gt;
&lt;br /&gt;
* SD-Karten-Slot über SPI&lt;br /&gt;
* Display über PCF 8574&lt;br /&gt;
* Infrarot-Empfang&lt;br /&gt;
* [[RFM12]] Funkmodul (nicht im Lieferumfang enthalten)&lt;br /&gt;
&lt;br /&gt;
Eine Aufstellung bekannter Fehler findet sich weiter unten [[#Bekannte Fehler|Bekannte Fehler - Erweiterungplatine]] &lt;br /&gt;
&lt;br /&gt;
Erste Erfahrungsberichte im Forum http://www.mikrocontroller.net/topic/161354&lt;br /&gt;
&lt;br /&gt;
=== Hardware-Umbauten &amp;amp; -Verbesserungen ===&lt;br /&gt;
&lt;br /&gt;
* Kühlkörper auf dem 7805 - (Vorsicht: Der LM317T ist rückseitig spannungsführend. Den Kühlkörper also keinesfalls an beide Spannungsregler anschließen!)&lt;br /&gt;
* MAX232 nach anfänglicher Konfiguration nicht bestücken um Strom zu sparen oder um zwei weitere I/O-Pins zu gewinnen&lt;br /&gt;
* 10µF-Elkos für MAX232N (C14-C17) durch 1µF ersetzen. Eine 10µF-Version für den MAX232 gibt es nicht. Die 10µF-Elkos können auch Ursache einer nicht funktionierenden RS232 sein.&lt;br /&gt;
** Laut Spezifikation sind auch mehr als 1µF erlaubt. Selbst Atmel verwendet beim STK500 10µF. Dies führt keinesfalls dazu, dass die RS232 nicht mehr funktioniert.&lt;br /&gt;
* Die IC-Fassungen aus &amp;quot;Pollins Resterampe&amp;quot; durch Fassungen mit gedrehten Kontakten ersetzen. &lt;br /&gt;
* &#039;&#039;Netz&#039;&#039; LED nicht bestücken oder größere Widerstände einlöten um Strom zu sparen (R3)&lt;br /&gt;
* Vorwiderstände der Ethernet-LEDs größer machen (z.&amp;amp;nbsp;B. verdoppeln) um Strom zu sparen (R6,R7)&lt;br /&gt;
* Linear-Spannungsregler ersetzen&lt;br /&gt;
* Kondensator an AREF-Pin des ATmega32 (ATmega32 Datenblatt) (100nF gegen Masse)&lt;br /&gt;
* Kondensator an den RESET-Pin des ATmega32 ([http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Atmel Application Note AVR042: AVR Hardware Design Considerations]) Wenn man diese Quelle genauer liest, ist das aber eher unnötig. - Kondensator bei selbstbau-ISP empfehlenswert.&lt;br /&gt;
* Umbau auf 3,3 V:&lt;br /&gt;
** Ersatz der Spannungsregler durch einen einzigen 3,3 V Regler&lt;br /&gt;
** Anpassen (verkleinern) des LED-Vorwiderstands R3 für 3,3 Volt Betrieb&lt;br /&gt;
** Reduktion der Taktfrequenz (Austausch von Q2) auf den bei 3,3V erlaubten Bereich des ATmega32 ( ATmega32(L)  3.3V /8.0 Mhz Takt )&lt;br /&gt;
** Ersatz des MAX232 durch einen MAX3232&lt;br /&gt;
[[Bild:POWER.JPG|thumb|400px|5V Stromversorgung über USB Kabel, ohne 5 V Spannungsregler und Gleichrichterdioden, Vorsicht: kein Verpolungsschutz!  ]]&lt;br /&gt;
* ATmega32 vom ENC28J60 takten (OSC2)&lt;br /&gt;
* Betrieb mit Gleichspannung:&lt;br /&gt;
** Dioden D2 und D5 durch Drahtbrücken ersetzen, D1 und D4 nicht bestücken (komplette Entfernung des Brückengleichrichters, beinhaltet Verlust des Verpolungsschutzes)&lt;br /&gt;
** Diode D2 bestücken, D5 durch Drahtbrücke ersetzen, D1 und D4 nicht bestücken (Brückengleichrichter durch Verpolungsschutze ersetzen)&lt;br /&gt;
*** ??? Ist dies nicht kontraproduktiv? Bei mir wurde durch D2-Bestückung die Eingangsspannung von ca. 5,2 V am LM317T auf ca. 4,6 V gedrückt, so dass am ENC28J60 nur ca. 2,6 V ankamen (außerhalb der lt. Datenblatt &amp;quot;Operating voltage range of 3.14V to 3.45V&amp;quot;). Man müsste also ein geregeltes Netzteil mit ca. 5,5 V anschließen um 5 und 3,3 V zu erzielen. Dann lieber den Verpolungsschutz durch andere Maßnahmen sicherstellen.&lt;br /&gt;
** Beim Betrieb von USB beachten, dass USB-Spezifikation keinesfalls 5V garantiert, sondern Spannung bis runter 4.4V erlaubt und dann u.U. durch den LM317 nicht mehr genügend Spannung am ENC anliegt. Das äußert sich so, dass zwar der Atmega einwandfrei funktioniert, die Ethernet-Kommunikation aber nicht oder nur sehr sporadisch.&lt;br /&gt;
* Ersatz des ATmega32 durch einen ATmega644 oder ATmega1284p mit mehr FLASH-Speicher.&lt;br /&gt;
** Der ATmega644 und auch der ATMega1284p sind nicht mit der Pollin-Firmware kompatibel&lt;br /&gt;
* 100nF über alle drei IC Störunterdrückung zusätzlich bestücken&lt;br /&gt;
&lt;br /&gt;
== Inbetriebnahme der Originalsoftware ==&lt;br /&gt;
=== Einleitung ===&lt;br /&gt;
&lt;br /&gt;
Die bei Auslieferung (Stand September 2008) in den ATmega32 gebrannte Firmware stellt sich manchmal recht zickig an. Es scheint dann die Netzwerk-Schnittstelle, ggf. auch  die serielle Schnittstelle, nicht zu funktionieren. Falls es Probleme geben sollte, sollte man erst einmal ein Firmwareupdate versuchen. Dies geschieht über die serielle Schnittstelle mittels des Programmes NetServer (aktuelle Version 1.03, Februar 2010), die dem Bausatz beiliegt. &lt;br /&gt;
&lt;br /&gt;
Falls die serielle Schnittstelle ebenfalls nicht zugänglich ist, kann mit den im folgenden beschriebenen Schritten die Inbetriebnahme der Software möglich sein. Dazu benötigt man:&lt;br /&gt;
&lt;br /&gt;
* Einen Windows-PC mit Ethernet-Schnittstelle und RS232-Schnittstelle (ein Prolific RS232-USB Konverter funktioniert)&lt;br /&gt;
* Entweder&lt;br /&gt;
**zwei normale (&#039;&#039;straight through&#039;&#039;) Ethernet-Kabel und einen Ethernet Switch/Hub, oder&lt;br /&gt;
**ein gekreuztes(&#039;&#039;cross over&#039;&#039;) Ethernet-Kabel&lt;br /&gt;
* Einen AVR Programmer (Hardware und Software). Zum Beispiel einen [[AVR Dragon]] oder [[STK500]] mit [[AVR Studio]] oder das [[Pollin ATMEL Evaluations-Board]] und [[avrdude]].&lt;br /&gt;
* Die [http://www.pollin.de/shop/ds/MTQ5OTgxOTk-.html Pollin NetServer Software], Version 1.03 (oder neuer)&lt;br /&gt;
&lt;br /&gt;
=== Gelieferten ATmega32 richtig einstellen ===&lt;br /&gt;
[[image:fuse_bits_avr_studio.jpg|thumb|right|250px|Einstellungen der Fuse-Bits mittels AVR Studio 4]]&lt;br /&gt;
Die Fuses der gelieferten ATmega32s scheinen nicht immer mit den im Handbuch auf Seite 12 als erforderlich angegebenen Fuse-Einstellungen übereinzustimmen.&lt;br /&gt;
&lt;br /&gt;
Dies kann man mittels eines Programmers ändern. LFuse = 0xBF, HFuse = 0xD2. Das genaue Vorgehen hängt dabei vom verwendeten Programmer ab. Bei der Gelegenheit kann man ebenfalls eine Sicherheitskopie des ursprünglichen Flash-Inhalts und des EEPROMs anfertigen. Das EEPROM scheint die MAC-Adresse des Ethernet-Ports zu enthalten.&lt;br /&gt;
&lt;br /&gt;
Entgegen der Spezifikation im Handbuch von Pollin sollten die &#039;&#039;&#039;HFuses auf 0xC2&#039;&#039;&#039; gesetzt werden, d. h. CKOPT-Fuse programmiert (dies ist in der Software Version 1.03 bereits vollzogen). Das sorgt für einen stabilen Betrieb des AVR-Oszillators im &amp;quot;full rail-to-rail swing&amp;quot;-Mode bei 16 MHz. Atmel garantiert ansonsten nur stabilen Betrieb bis 8 MHz. Siehe ATmega32-Datenblatt, Kapitel 8.4, Crystal Oscillator.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Funktionsfähige Konfiguration - AVR-Prog ====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avrprog.png|thumb|right|250px]]&lt;br /&gt;
Benutzer von AVR-Prog können die nachfolgenden Einstellungen für die Lock- und Fuse-Bits verwenden. Hierbei handelt es sich um die ausgelesenen Einstellungen eines funktionsfähigen Controllers. Allerdings sollte, laut Handbuch des AVR-NET-IO-Boards, das Fuse-Bit EESAVE eigentlich gesetzt sein. &lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch per avrdude die Einstellung getroffen werden:&lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U lfuse:w:0xBF:m&lt;br /&gt;
und &lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U hfuse:w:0xC2:m&lt;br /&gt;
&lt;br /&gt;
Anschließend muß noch der Bootloader und die Firmware aktualisiert werden (siehe Handbuch AVR-NET-IO-Board Seite 12 Punkt 3).&lt;br /&gt;
&lt;br /&gt;
=== PC Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
==== PC normalerweise nicht im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
Betreibt man den PC nicht im 192.168.0.0/24 Subnetz, muss er wie folgt umkonfiguriert werden, oder die IP Adresse des Boards wird entsprechend angepasst. ( Siehe Handbuch Seite 14ff. Das ist meist sinnvoller und auch einfacher. ) &lt;br /&gt;
&lt;br /&gt;
Den PC vom normalen Netzwerk abstecken[1]. Zur Umkonfiguration dazu bei Windows XP in der Systemsteuerung &#039;&#039;Netzwerkverbindungen&#039;&#039; aufrufen und die lokale &#039;&#039;LAN-Verbindung&#039;&#039; markieren. Dann in der rechten Leiste &#039;&#039;Einstellungen dieser Verbindung ändern&#039;&#039; aufrufen. &lt;br /&gt;
&lt;br /&gt;
Es erscheint der Dialog &#039;&#039;Eigenschaften von &amp;lt;Verbindungsname&amp;gt;&#039;&#039;. In der Liste im Dialog zu &#039;&#039;Internetprotokoll (TCP/IP)&#039;&#039; gehen. Ein Doppelklick auf den Eintrag öffnet den &#039;&#039;Eigenschaften von Internetprotokoll (TCP/IP)&#039;&#039; Dialog.&lt;br /&gt;
&lt;br /&gt;
In diesem Dialog &#039;&#039;Folgende IP-Adresse verwenden:&#039;&#039; auswählen und zum Beispiel&lt;br /&gt;
&lt;br /&gt;
IP-Adresse: &#039;&#039;&#039;192.168.0.100&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Subnetzmaske: &#039;&#039;&#039;255.255.255.0&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Standardgateway: &#039;&#039;&#039;192.168.0.1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
eingeben. &lt;br /&gt;
&lt;br /&gt;
Anmerkung von bitman:&lt;br /&gt;
[1] Dies ist spätestens ab Windows XP nicht mehr notwendig, wenn das Netz 192.168.0.0/24 noch frei ist. Dann kann man einfach den Client &#039;&#039;zusätzlich&#039;&#039; in diesem Netzwerk zusätzlich einbinden über Einstellungen/Netzwerkverbindungen/Lanverbindung/Eigenschaften/TCP-IP/Eigenschaften/Erweitert/IP-Adresse hinzufügen. Es werden dann eben mehrere IP-Adressen an den NIC gebunden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle geöffneten Dialoge nacheinander mit OK schließen.&lt;br /&gt;
&lt;br /&gt;
Alternativ bietet sich das Umprogrammieren des Boards über die serielle Schnittstelle an. Die Werte für IP-Adresse, Netzmaske und Standard-Gateway werden mit den dokumentierten SETxx-Befehlen geändert, das Board neu gestartet und ans vorhandene Netzwerk gesteckt.&lt;br /&gt;
&lt;br /&gt;
Im EEPROM sind folgende Werte vorprogrammiert:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3EE - 3F3 MAC-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
3F4 - 3F7 GATEWAY&amp;lt;br&amp;gt;&lt;br /&gt;
3F8 - 3FB NETMASK&amp;lt;br&amp;gt;&lt;br /&gt;
3FC - 3FF IP-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PC bereits im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
In diesem Fall muss man prüfen, ob die IP-Adresse 192.168.0.90 bereits im Subnetz verwendet wird. Ist dies der Fall, muss das verwendete Gerät mit dieser IP vorübergehend aus dem Subnetz entfernt werden. Es sei denn, dabei handelt es sich um den PC. In diesem Fall muss er wie zuvor umkonfiguriert werden. Ansonsten kann der unverändert im Netz verbleiben.&lt;br /&gt;
&lt;br /&gt;
Dem AVR-NET-IO gibt man eine neue, zuvor unbenutzte Adresse (siehe unten). Dann kann das abgekoppelte Gerät wieder angeschlossen werden, beziehungsweise der PC zurückkonfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
=== AVR-NET-IO anschließen ===&lt;br /&gt;
&lt;br /&gt;
Musste man den PC umkonfigurieren, so werden jetzt nur der PC und der AVR-NET-IO über Ethernet miteinander verbunden. Je nach Ethernet-Kabel benötigt man dazu einen Switch/Hub oder nicht.&lt;br /&gt;
&lt;br /&gt;
Musste man den PC nicht umkonfigurieren, so kann man den AVR-NET-IO wie einen normalen Rechner an das vorhandenen Netz anschließen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich schließt man die serielle Schnittstelle des AVR-NET-IO an den PC an.&lt;br /&gt;
&lt;br /&gt;
=== Firmware 1.03 einspielen ===&lt;br /&gt;
&lt;br /&gt;
Laut Handbuch sollte der AVR-NET-IO jetzt über Ethernet funktionieren. Ebenso sollte er über die serielle Schnittstelle und ein Terminalprogramm konfigurierbar sein. Beides ist offensichtlich im Auslieferungszustand selten der Fall.&lt;br /&gt;
&lt;br /&gt;
Auch wenn sich Pollins NetServer Software nicht mit dem AVR-NET-IO verbinden lässt, so ist sie jedoch in der Lage eine neue Firmware 1.03 einzuspielen. Das Vorgehen ist im Handbuch auf Seite 12 beschrieben. NetServer präsentiert dabei ein paar einfache Anweisungen denen man folgen sollte.&lt;br /&gt;
&lt;br /&gt;
Wenn sich nach dem scheinbar erfolgreichem Einspielen der Firmware, später nichts tut, so sollte die Firmware mit gesetztem &amp;quot;FailSafe&amp;quot; in der mitgelieferten NetServer-Software nochmals Eingespielt werden.&lt;br /&gt;
&lt;br /&gt;
=== Abschluss ===&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte sich die NetServer Software mit dem AVR-NET-IO über Ethernet verbinden lassen. Dies macht es wiederum möglich, den AVR-NET-IO mit einer anderen IP-Adresse zu versehen. Will man den AVR-NET-IO in einem anderen Subnetz betreiben kann man dies jetzt einstellen.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse neu eingestellt hat, muss man den PC zurückkonfigurieren und kann dann sowohl den AVR-NET-IO und den PC zusammen betreiben.&lt;br /&gt;
&lt;br /&gt;
== Funktionsumfang der Originalsoftware ==&lt;br /&gt;
Die Originalsoftware ist sehr einfach gestrickt. Im AVR Net-IO läuft ein Programm, welches sowohl über die RS232 als auch über Ethernet angesprochen werden kann. Mit diesem Programm ist es möglich, alle Eingänge, alle Ausgänge anzusprechen, sowie die Konfiguration der Netzwerkeinstellungen zu verändern. Die Karte versteht dann folgende Befehle:&lt;br /&gt;
* SETPORT&lt;br /&gt;
* GETPORT&lt;br /&gt;
* SETIP&lt;br /&gt;
* GETIP&lt;br /&gt;
* SETMASK&lt;br /&gt;
* GETMASK&lt;br /&gt;
* SETGW&lt;br /&gt;
* GETGW&lt;br /&gt;
* GETSTATUS&lt;br /&gt;
* INITLCD&lt;br /&gt;
* WRITELCD&lt;br /&gt;
* CLEARLCD&lt;br /&gt;
* VERSION&lt;br /&gt;
&lt;br /&gt;
Dazu gibt es ein auf Windows lauffähiges Programm, welches alternativ zu einem Terminalprogramm benutzt werden kann. Es zeigt den Status der Portpins an und diese lassen sich auch mit der Maus verändern. Die Messwerte der 4 analogen Eingänge werden ständig angezeigt, wobei es möglich ist, durch einstellbare Faktoren die internen Werte gleich in sinnvolle Einheiten umrechnen zu lassen. Diese Messwerte können auch in eine Datei geloggt werden.&lt;br /&gt;
Neben der Möglichkeit, alle Kommandos auch händisch einzugeben, wird das Windows Programm auch benötigt, um einen Firmware-Update der Net-IO Platine durchzuführen.&lt;br /&gt;
Alles in allem ist dieses Windows-Frontend aber mehr als Testumgebung anzusehen, um damit die Hardware in Betrieb zu nehmen. Für einen sinnvollen Dauereinsatz ist das Programm deutlich zu einfach und unflexibel gestrickt.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Fehler ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[#Hardware-Umbauten_.26_-Verbesserungen|Hardware-Umbauten und Verbesserungen]]&amp;lt;br&amp;gt;&lt;br /&gt;
Käufer berichten von fehlenden Bauteilen im Bausatz (Wannenstecker, Widerstände, Kondensatoren, Induktivitäten). Für Reklamationen: [https://www.pollin.de/shop/kontakt_service/reklamation.html]&lt;br /&gt;
&lt;br /&gt;
* Die Stückliste auf Seite 4 in den Anleitung mit den Versionsangaben&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
:ist falsch. Pollin legt dem Bausatz irgendwann ab September 2008 einen gedruckten Korrekturzettel bei. Die Online-Version der Anleitung ist korrigiert.&lt;br /&gt;
* Im Schaltplan auf Seite 7 in den Anleitungen mit den Versionen&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
** &#039;&#039;Stand 03.09.2008, online, #all, wpe&#039;&#039; (Online)&lt;br /&gt;
:ist eine 25-polige SUB-D Buchse gezeichnet. Geliefert wird und in der Stückliste verzeichnet ist ein Stecker.&lt;br /&gt;
&lt;br /&gt;
* Die September 2008 ausgelieferte Firmware im ATmega32  funktioniert bei vielen nicht und muss erst upgedatet werden (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Im Flash der gelieferten AVR ist anders als beschrieben nur der Bootloader enthalten, die eigentliche Firmware muss erst mit Hilfe der Updatefunktion geladen werden. Wenn zusätzlich auch die Fuses falsch gebrannt sind, dann funktioniert das Update nicht, auch wenn das PC Programm was anderes behauptet.&lt;br /&gt;
&lt;br /&gt;
* Die Fuse-Einstellungen des ausgelieferten ATmega32 entspricht nicht der Anleitung (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Bausatz, gekauft am 27.10.08, Anleitungsversion 19.09.08, ohne Probleme oder erkennbare Fehler zusammengebaut und in Betrieb genommen.&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft 29.09.2008, Pinbelegung des 25 poligen D-Sub &amp;quot;Anschlusses&amp;quot; stimmt nicht mit der Anleitung überein. Der Aufdruck auf der Platine ist falsch. Pin1 &amp;lt;-&amp;gt; Pin13, Pin2 &amp;lt;-&amp;gt; Pin12 usw. Setzt man den D-Sub Stecker ein, so sind dessen Pinnummern korrekt. Bei einem Bausatz gekauft 05/2013 ist dies ebenfalls noch der Fall.&lt;br /&gt;
&lt;br /&gt;
* 3 Bausätze Anf. Oktober 2008 gekauft, bei einem waren 2 LM317 dabei, dafür fehlte der 7805 - aus der Bastelkiste ersetzt. Alle haben jedoch auf Anhieb funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft Ende Januar 2009. Die Lock-Bits (u.a. für PonyProg2000) werden falsch beschrieben. Die in Klammern aufgeführten Werte stimmen bei einem Bit nicht. Die Texte &amp;quot;Programmiert/Unprogrammiert&amp;quot; hingegen schon. Bei den Bauteilen gab es 4 Kondensatoren mit der Aufschrift &amp;quot;220&amp;quot;, ich habe diese durch welche mit 22p ersetzt, da ich nicht sicher war ob wirklich 22p geliefert wurden. Dafür wurden statt einem zwei 7805 und statt einem mindestens vier LM317 mitgeliefert.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 22.4.2009. Alles vollständig, zusammengebaut, läuft. Software-Version 1.03. Für den oben schon genannten Steckverbinder wurde eine Buchse geliefert. Allerdings stimmen die PIN-Nummern im Schaltplan nicht mit den PIN-Nummern auf der Buchse überein (sie sind gespiegelt), daher liefen die Test-LEDs zunächst nicht.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 11.7.2009. Spannungsregler LM317T fehlt, grüne statt roter LED. Ein Kondensator 22pF zu viel. LM317T wurde auf Anfrage kostenlos nachgeliefert (27.7.). Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 24.7.2009. Ein Quarz 16MHz zu viel, ebenfalls grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 20.08.2009. Ein Kondensator 22pF zuviel und grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli &#039;09 gekauft, grüne statt rote LED&lt;br /&gt;
&lt;br /&gt;
* Bausatz 25.09.09 geliefert, grüne Betriebs-LED, ein ELKO zuviel, Fehler 1µF am MAX232 statt 100nF behoben, richtiger C wird mitgeliefert, Aufbau komplett nach Pollin Anleitung durchgeführt, auf Anhieb fehlerfrei!&lt;br /&gt;
&lt;br /&gt;
* Bausatz 17.10.09 geliefert, grüne Betriebs-LED, zwei 100nF Kondensatoren zu wenig. Aufbau und Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz 21.10.09 gekauft, grüne Betriebs-LED. Aufbau problemlos, RS232 läuft nicht. LAN läuft&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, ENC28J60, MAX232 und ATmega32 fehlen, Nachlieferung nach einer Woche&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft,Bauteile komplett.Verbindungsaufbau Seriell klappt erst nach mehreren Versuchen.Problem gelöst:Spannung an MAX und Mega zu niedrig&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dez. 09 gekauft, grüne LED, 100µF Kondensator fehlt, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 09 gekauft, alle teile da nach Einstellen der fusebits lief alles perfekt&lt;br /&gt;
&lt;br /&gt;
* Bausatz Okt. 09 gekauft, ein 100nF Kondensator und 25MHz Quarz fehlten ... hab beim lokalen Elektronikhändler keinen 25Mhz Grundton Quarz sondern nur im 3. Oberton bekommen aber mit R2.2k parallel zum Quarz schwingt er in der Schaltung schön bei 25Mhz. Mit 1µF am MAX232 funktioniert jetzt auch die RS232.&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Feb. 10 gekauft, bei beiden fehlten 7805, L1+L2 je 100µH sowie 4x falscher Wert Kondensator an Max232 vorhanden. Fehlende Bauteile nachgelötet und Funktion getestet. Hat alles einwandfrei funktioniert!!!&lt;br /&gt;
&lt;br /&gt;
* Bausatz März. 10 gekauft, RS232 Printbuchse fehlt, dafür 1x 10pol Wannenstecker zuviel. Grüne LED statt Rot. Funktioniert ansonsten einwandfrei.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Jan. 10 gekauft, gelbe LED statt rot, C14...C17: 10µF, weder seriell noch via Ethernet Konnektivität. Nach Austausch von C14-C17 gegen 1µF, wenigstens serielle Kontaktaufnahme möglich, kein Ethernet auch nach Flash von 1.03 mit NetServer.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Feb. 10 gekauft, Spannungsregler LM317T fehlte&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft, gelbe statt rote LED geliefert, aber Aufbau und inbetriebnahme lt. Handbuch ohne Probleme&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft und gelbe statt rote LED geliefert, funzt wunderbar gemäß Anleitung&lt;br /&gt;
&lt;br /&gt;
* Fertig gelötete Platine gekauft. µC war falsch im Sockel.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - Funktion erst nach Ersetzung des ADM durch nen MAX&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - funktionierte sofort auch mit dem ADM232LJN.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft wurde mit grüner statt roter LED Ausgeliefert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juni 10 gekauft: wurde mit grüner statt roter Netz-LED ausgeliefert, 2x 22pF Kerko zuviel&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli 10 gekauft: 2 Quarze mit 16 MHz geliefert, statt 1x 16MHz und   1x25MHz.&lt;br /&gt;
&lt;br /&gt;
* Bausatz September 10 gekauft: hat sofort funktioniert. 1x 3,3k und 1x 10k Widerstand zuviel. Statt 100nF Kondensatoren wurden 1µF geliefert -&amp;gt; Platzprobleme auf der Platine durch grössere Bauform. LED grün.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober  6 gekauft: alles funktioniert. LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Fertigmodul Oktober 10 gekauft: Auf Anhieb alles funktioniert!&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft: komplett und sofort funktioniert (sogar mit der neusten Pollin Firmware 1.03 schon drauf) LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft. Nach Bezug neuer Feinst-Lötspitzen konnte ich ihn dann auch zusammenlöten. Es hat sofort alles funktioniert, obwohl ich nur 12V bzw. 9V Gleichspannung zur Verfügung hatte, und nicht sicher war, wieviel die Komponenten wirklich benötigen. Der Regler wird auch bei 9V Gleichspannungsversorgung noch sehr warm. Da muss auf jeden Fall ein Kühlkörper dran! Ich habe auch eine grüne LED bekommen, ist mir aber wurscht :-)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dezember 10 gekauft: komplett und funktionierte sofort. Firmware 1.03, grüne LED (Ein Quarz und ein IC-Sockel zu viel)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Januar 2011 gekauft: nur genau die richtige Anzahl Teile dabei, Firmware 1.03, grüne LED, ging auf Anhieb&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Januar 2011 gekauft: beide grüne LED, und 1x doppelter Satz Jumper/Stiftleiste, 22PF und Anschlussklemmen. Rest vollständig und beide haben sofort nach zusammenbau funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: die Diode D5 fehlt, 10 µF gegen 1 µF für MAX232 getauscht, Flash im ATmega32 war programmiert, passende IP-Adr über serielle Schnittstelle eingestellt; ADD-ON: für R1 war 22Ω statt 0,2Ω beigelegt, durch richtigen ersetzt, Beschreibung der LED Bestückung mangelhaft / oe1smc&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 1 Diode zuviel, 2 Spulen fehlen, LED grün. Die fehlenden Spulen wurden durch welche aus der Bastelkiste ersetzt - funktioniert. Der 7805 bekam einen kleinen Kühlkörper spendiert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 2x 10k Widerstände fehlen. Dafür eine Diode zu viel.&lt;br /&gt;
&lt;br /&gt;
* Ende Februar 2011: Zwei Bausaetze an jeweils zwei Adressen, alles in Ordnung.&lt;br /&gt;
&lt;br /&gt;
* Ende März 2011: 2x 25 Mhz Quarz statt 1x16 u. 1x25 Mhz. LED fehlt. Bausatz funktioniert nach Tausch des Quarz den mir mein Freund oe9rsv aus seinem Fundus spendiert hat. Danke auch für die Hilfe beim Fehler suchen.&lt;br /&gt;
&lt;br /&gt;
* Mitte 2010 gekauft: 1x 100nF fehlt&lt;br /&gt;
&lt;br /&gt;
* Mitte Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, Pollin wollte, dass ich das ganze Paket zurückschicke für einen Austausch. Ein 51Ω zu viel. 16Mhz im Handel und 25Mhz vom alten Mobo ausgelötet. Läuft wunderbar.&lt;br /&gt;
&lt;br /&gt;
* Anfang Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, nach kurzer Mail an Pollin (leider ohne Antwort) wurden diese nach ca. 1 Woche in einem Brief nachgeliefert.&lt;br /&gt;
&lt;br /&gt;
* August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: Platine fehlte -&amp;gt; in Nachlieferung&lt;br /&gt;
&lt;br /&gt;
* 30 August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* 06 Sep. 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: 6 Stück bestellt, bei einem haben die 100nF Kondensatoren gefehlt, bei einem zwei LM317 statt 7805 und LM317. Angerufen, 3 Tage später Nachlieferung erhalten.&lt;br /&gt;
&lt;br /&gt;
* Nov. 2011: &#039;&#039;&#039;Net_IO&#039;&#039;&#039; vollständig. Einspielen der Firmware 1.03 war erforderlich. Bei &#039;&#039;&#039;Add-On&#039;&#039;&#039; immer noch falscher Q1 BC548 (NPN) in Stückliste und Lieferung. BC327-40 oder BC328-40 (PNP) nachgefordert. R11 und R24 mitgeliefert, entsprechend Beschreibung V1.1 von Pollin&#039;s Download. Beiliegende Beschreibung war älter, ohne diese Widerstände.&lt;br /&gt;
&lt;br /&gt;
* Ende Nov.2011, alle Teile dabei, Firmware war drauf, sofort funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Anfang Dez.2011, komplett bestückte Platine gekauft. Auf 7805 Kühlkörper gebaut, da er nach 1 Minute schon ausgestiegen ist (LED hat das Pumpen angefangen). Firmware 1.03 musste noch aufgespielt werden danach funktioniert alles einwandfrei. In Betrieb mit 10V DC&lt;br /&gt;
&lt;br /&gt;
* Ende Dez.2011 2xNET-IO und 2xADD bestellt, 4 verschieden volle Kisten bekommen... WSL16 ist mit Verriegelung, geht nicht aufs Board, Jumper fehlen, Spannungsregler doppelt, Poti Löcher zu klein, lsb3 fehlt, SD-Slot hab ich jetzt 3, 100nF hab ich jetzt 4 übrig ... also immer noch lustig. HW-Stand immer noch 1.0 ( gab es überhaupt eine 1.1?)&lt;br /&gt;
&lt;br /&gt;
* Mitte Jan. 2012, 10pol. beide Wannenstecker nicht dabei, Firmware war drauf, sofort funktioniert. 1 LED zuviel. Unproblematische Nachlieferung bei Reklamation (wegen der beiden Wannenstecker kam ein PAKET!).&lt;br /&gt;
&lt;br /&gt;
* Ende Feb. 2012, Um die PHP-Scripte über öffentlichen Webserver zu betreiben muss mit SETGW die Gateway-Adresse des lokalen Routers eingetragen werden. Bei der NAT im Router sollte z.B. Port 8080 auf den internen Port 50290 umgeleitet werden, da manche Provider diesen Port für die Socket-Kommunikation nicht zulassen.&lt;br /&gt;
&lt;br /&gt;
* Juli 2012, Bausatz vollständig, Nach Aufbau sofort den 7805 mit einem kleinen Kühlkörper versehen, da er sonst thermisch überlastet wird! hat weder auf LAN noch RS232 reagiert, musste erst Update einspielen (Download Pollin), danach funktionierte alles einwandfrei!&lt;br /&gt;
&lt;br /&gt;
* Januar 2013, Bausatz vollständig, 7805 mit Kühlkörper versehen, nach Aufbau ohne Probleme funktioniert&lt;br /&gt;
&lt;br /&gt;
* Februar 2013, Bausatz komplett, Aufbau ohne Probleme, 7805 nur leicht warm, LAN funktioniert, RS232 noch ohne Funktion.&lt;br /&gt;
&lt;br /&gt;
* Oktober 2013, Bausatz komplett laut Stückliste, problemloser Aufbau, 7805 leicht warm am 10V DC. RS232 und LAN funktionierten auf Anhieb.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die nachfolgend genannten Änderungen sind in der Bauanleitung V1.1 mit Stand 07.12.2011 (Wichtig! Es gibt mehr als eine V1.1 ...) bereits berücksichtigt, R3 wird allerdings mit 3,6kΩ angegeben.&lt;br /&gt;
&lt;br /&gt;
Um bei einem Neuaufbau parallele Widerstände zu vermeiden, sollten folgende Änderungen auf dem Addon-Board gemacht werden:&lt;br /&gt;
*R2 1,5kΩ ersetzen mit 2kΩ&lt;br /&gt;
*R3 1,8K ersetzen mit 3,3kΩ&lt;br /&gt;
*R19 470kΩ ersetzen zu 470Ω&lt;br /&gt;
*Q1 BC548 ersetzen durch BC327 oder BC328 (Hauptsache PNP! und mehr als 100mA)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039; beim Anschluß eines LC-Displays an J5 (über I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C, PCF 8574): Vcc/5V liegt an Pin 1, GND an Pin 2 von J5. Es gibt etliche Displays mit abweichender Belegung Pin 1 GND und Pin 2 Vcc!&lt;br /&gt;
&lt;br /&gt;
Auch beim Add-On gibt es fehlende oder falsche Bauteile im Bausatz und Fehler im Schaltplan und auf der Platine:&lt;br /&gt;
&lt;br /&gt;
*Stand Feb. 2011: R2 wird mit 2,2kΩ und R3 wird mit 3,6kΩ ausgeliefert. Somit werden die 3,3 V richtig erzeugt. R19 hat 470Ω. Der ISP-Anschluß ist nicht vollständig durchgeschleift, es besteht keine Verbindung der RESET-Leitung zwischen ISP und ISP1 (Abhilfe: Drahtbrücke einlöten, [http://www.mikrocontroller.net/topic/161354#1600385 Quelle]). &lt;br /&gt;
&lt;br /&gt;
*Stand Nov. 2011: bei mir ist die RESET-Leitung korrekt zw. ISP und ISP1 verbunden. Es gibt jetzt auch einen R24 (470Ohm) und R11 (1KOhm), der in der bei mir mitgelieferten Bauanleitung fehlt, in der zum Download (V1.1) angebotenen  aber drin steht. Es wird immer noch der falsche Q1 BC548C (NPN) mitgeliefert. Das Schaltsymbol für einen PNP ist richtig im Schaltplan gezeichnet.&lt;br /&gt;
&lt;br /&gt;
*Stand Dez. 2011: R24 (470Ω) kann mit 0Ω ersetzt und R11 (1kΩ) völlig weggelassen werden. Diese Widerstände bilden einen (eigentlich überflüssigen) Spannungsteiler in der MISO Leitung. Der Spannungsteiler hat allerdings auch eine Schutzfunktion, falls weitere Slaves am SPI-Bus hängen, die 5V-Pegel nutzen, z. B. ISP-Programmierer. (siehe [http://son.ffdf-clan.de/include.php?path=forumsthread&amp;amp;threadid=1167&amp;amp;postid=9203 1284p: Board, ich oder beide verwirrt?])&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Bausatz mit Stecker anstelle einer Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s dafür aber 3 Poti zuviel.&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Ebenfalls Bausatz mit Sub-D-Stecker statt Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s und 3 Potis zuviel.&lt;br /&gt;
&lt;br /&gt;
*Stand März 2013: Die Belegung der 25 pol. Sub-D-Buchse im Schaltplan der Anleitung V1.1 (und früher) ist falsch dargestellt. SCL und SDA des I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C-Busses liegen wie beim AVR-NET-IO Board auf Pin 2 bzw. 3 und nicht auf Pin 11 bzw. 12 der Buchse. Ein 1:1 verdrahtetes Kabel funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Andere Software für den Client-PC ==&lt;br /&gt;
=== NetIOLib ===&lt;br /&gt;
&lt;br /&gt;
In C# geschriebene Bibliothek zur Ansteuerung der Platine im Orginalzustand. Inkl. Beispielsoftware und Quellcode (GNU GPL) &lt;br /&gt;
&lt;br /&gt;
Links gehen nicht:&lt;br /&gt;
DLL: [http://www.tware.org/downloads/NetIOLib_dll.zip Download-Link]&lt;br /&gt;
Source: [http://www.tware.org/downloads/NetIOLib_src.zip Download-Link]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Multi-Control ===&lt;br /&gt;
Mti dem E2000-NET-IO-Multi-Control ist es möglich, die vom dem E2000-NET-IO-Designer erstellten Projekte zu öffnen und die AVR-NET-IO&#039;s zu steuern. &lt;br /&gt;
&lt;br /&gt;
Die Anwendung liegt dem [http://www.mikrocontroller.net/articles/AVR_Net-IO_Bausatz_von_Pollin#E2000-NET-IO-Designer_.28Windows.5BXP.2F7.5D_.2F_Android.29 E2000-NET-IO-Designer] bei.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== ControlIO ===&lt;br /&gt;
Einfache Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://www.mikrocontroller.net/topic/149695&lt;br /&gt;
&lt;br /&gt;
=== JAVA Lib ===&lt;br /&gt;
Einfache Java-Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://son.ffdf-clan.de/?path=forumsthread&amp;amp;threadid=611&lt;br /&gt;
&lt;br /&gt;
=== PHP ===&lt;br /&gt;
PHP Klasse zur Ansteuerung mit der Originalfirmware. (Opensource Lizenz)&lt;br /&gt;
http://blog.coldtobi.de/1_coldtobis_blog/archive/298_pollin_net-io_php_library.html&lt;br /&gt;
&lt;br /&gt;
PHP Funktionen zum Ansteuern der Originalfirmware. (Free for All Lizenz)&lt;br /&gt;
http://defcon-cc.dyndns.org/wiki/index.php/Pollin_AVR-NetIO_PHP_Wrapper&lt;br /&gt;
&lt;br /&gt;
== Clients für Smartphones ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== AVR NET IO Control (Windows Phone 7.5,7.8,8.0) ===&lt;br /&gt;
&lt;br /&gt;
Freie App zur steuerung des AVR NET IO.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;App im Market: [http://www.windowsphone.com/de-de/store/app/avr-net-io-controll/030d107f-9a27-4a62-ac8f-cb74e79c0500 AVR NET IO Control]&#039;&#039;&#039;&lt;br /&gt;
=== App NetIO (Windows Mobile 6.5) ===&lt;br /&gt;
Frei verfügbare App für Windows Mobile zur Ansteuerung mit der Orginalsoftware. Das HTC HD2 wird damit zur Fernsteuerung für das AVR Net-IO Board.&lt;br /&gt;
http://www.heesch.net/netio.aspx&lt;br /&gt;
&lt;br /&gt;
=== NET-IO Control (Android) ===&lt;br /&gt;
Eine Application für das Android Betriebssystem zur Steuerung des AVR Net-IO Boards. Es ist möglich, alle Ausgänge zu steuern und alle Eingänge anzuzeigen. Die Analogen Eingänge können mit einem Berechnungsfaktor versehen werden. &#039;&#039;Geplant ist noch ein Offsetwert.&#039;&#039; Außerdem kann jedem analogen Wert eine Einheit zugeordnet werden. Die Ausgänge können in der neusten Version in einen Tastermodus gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:NET-IO-Control.png|200px]] [[Datei:NET-IO-Control2.png|200px]] [[Datei:NET-IO-Control3.png|200px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Trial-Version: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControlTRAIL Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(In dieser Probe Version können nur 1 Output und 2 Inputs gesteuert werden)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vollversion: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControl Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(Kostet im Google-Play 3,00 €)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Designer (Windows[XP/7] / Android) ===&lt;br /&gt;
[[Datei:E2000-NET-IO-Designer.png|400px|right]]&lt;br /&gt;
Mit dem E2000-NET-IO-Designer ist es möglich, eine grafische Oberfläche für die NET-IOs von Pollin zu erstellen. Dafür werden Knöpfe, Texte und Anzeigebilder zur Verfügung gestellt. Jedes dieser Element kann &amp;quot;Aufgaben&amp;quot; übernehmen um die NET-IOs zu steuern oder einen Status des NET-IO anzuzeigen. Weitere Icons können von dem Benutzer selbst in den entsprechenden Ordner gelegt werden und in die Oberfläche eingebunden werden. Es können mehrere Seiten designt werden die durch einen selbst positionierten Knopf erreichbar sind. Die Android Application arbeitet somit im Fullscreen-Modus. Des weiteren ist es möglich, mehrere NET-IO&#039;s in einem Projekt zu benutzen. Nach dem erstellen der grafischen Oberfläche mit dem E2000-NET-IO-Designer, kann mit der Android APP das Projekt einfach gedownloaded werden. Dafür baut die APP eine Verbindung zum E2000-NET-IO-Designer auf und läd alle benötigten Dateien herunter (Achtung: Firewall-Einstellungen beachten). Die designten Oberflächen können außerdem noch mit der E2000-NET-IO-Multi-Control.exe auf dem Computer ausgeführt werden. Dadurch ist es Möglich, seine NET-IOs vom PC aus zu steuern über eine selbst designte Oberfläche. &lt;br /&gt;
&lt;br /&gt;
Zum Ausführen des E2000-NET-IO-Designer muss .NET Framework 4.0 installiert sein und es muss eine Internetverbindung exisitieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Features:&#039;&#039;&#039;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Grafische Oberfläche am PC erstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verwendung von eigenen Button- und Hintergrundbildern&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designbare Anzeige von Digital- und Analogwerten&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Steuern von mehreren AVR-NET-IO Boards&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Unterstützung der E2000-NET-IO Firmware (mehere Boards gleichzeitig)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designte Steuerungen können auf dem PC oder dem Handy ausgeführt werden (Android)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Schnelle Verbindung&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Einfache übertragung auf das Handy durch Projekt Download&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/downloads.php?id=44 Download E2000-NET-IO-Designer]&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/hilfe/E2000Netdesigneruebersicht.html Online Hilfe]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== NetIO ( iPhone &amp;amp; Android ) ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;float: left; margin-right: 20px&amp;quot;&amp;gt;[[Datei:Netio_logo.png|70px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schöne universelle Fernbedienung für das Board (iOS / Android). Konfigurierbar über einen Browserbasierten Editor. &amp;lt;br&amp;gt;&lt;br /&gt;
Unterstützt TCP socket Verbindungen, kann http request absetzen und kann auch Webseiten einbinden (z.B. IP-Kameras). Kann mehrere &lt;br /&gt;
Boards gleichzeitig steuern! Einfach super schnell ins Projekt eingebaut... Es gibt Buttons (die auch als Taster konfiguriert werden können), Slider, Switches und einfache Labels. Dinge können geschaltet oder Daten ausgelesen und mit regex dargstellt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;float: right&#039;&amp;gt;&lt;br /&gt;
[[Datei:Netio_TV.png|190px]]&lt;br /&gt;
[[Datei:Netio_iphone5.png|190px]]&lt;br /&gt;
[[Datei:NetIO_appflow.jpg|190px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
AppStore Link: http://itunes.apple.com/app/netio/id464924297?mt=8 &amp;lt;br/&amp;gt;&lt;br /&gt;
Google Play Link: https://play.google.com/store/apps/details?id=com.luvago.netio &amp;lt;br/&amp;gt;&lt;br /&gt;
Die gleiche Konfiguration kann auch mit einem [https://github.com/davideickhoff/NetIO-OSX-Dashboard-Widget OSX Widget] benutzt werden. &amp;lt;br/&amp;gt;&lt;br /&gt;
Webseite: http://netio.davideickhoff.de &lt;br /&gt;
&lt;br /&gt;
Hinweis: &amp;lt;br&amp;gt; Man findet es unter &amp;quot;NetIO&amp;quot; im Store, im Icon selbst und in iTunes wird es als &amp;quot;Controller&amp;quot; angezeigt. &amp;lt;br&amp;gt;&lt;br /&gt;
Die eigene Konfiguration kann man mit dem [http://netio.davideickhoff.de/editor Online Editor] vorher am PC erstellen.&lt;br /&gt;
Eine fertige [http://netio.davideickhoff.de/editor/?config=pollin AVR-NET-IO Konfiguration] gibt es als funktionierendes Beispiel schon als Preset.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;div style=&#039;clear: both&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AVR Net IO (iPhone) ===&lt;br /&gt;
[[Datei:AVRNetIO-Screenshot1.png|160px|rechts]]&lt;br /&gt;
Update 15.12.2011: Die Neue Version 1.3 ist seit gestern im AppStore. Fehlerkorrekturen und ein robusteres Handling machen die App nun zur universellen AVR-Net-IO-Steuerung.&lt;br /&gt;
&lt;br /&gt;
Mit der [http://itunes.apple.com/de/app/avr-net-io/id460991760?mt=8 iPhone App AVR-Net-IO] kann das Board ferngesteuert werden. Die Fernsteuerung umfasst in der Version 1.1 folgende Funktionen:&amp;lt;br&amp;gt;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ADC-Werte zyklisch auslesen und darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Inputs zyklisch darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Outputs können über Buttons geschaltet werden. Die Werte der Digital-Outputs werden zuerst ausgelesen und zeigen den zuletzt gesetzten Wert an.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Terminal-Modus: Hier können beliebige AVR-Net-IO-Befehle eingegeben werden. Das Ergebnis wird 1:1 angezeigt, wie es vom Board kommt. Hilfreich für Tests bei Eigenentwicklungen und Konfigurationen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Mehr Infos gibt&#039;s dazu direkt von den Entwicklern auf [http://www.facebook.com/pages/AVR-Net-IO/187799687958255?ref=nf AVR-net-IO Facebook-Page] oder direkt auf deren Homepage [http://www.ondics.de/apps/1001/ Homepage].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Hinweis: MIt der aktuellen Version 1.1 gibt es noch bei manchen Boards Probleme beim auslesen und anzeigen der ADC- und Digital-Werte. Das Terminal funzt problemlos. Die Entwickler kümmern sich gerade drum und haben einen baldigen Update versprochen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Andere Software statt der Originalsoftware von Pollin ==&lt;br /&gt;
&lt;br /&gt;
Die Umrüstung auf einen Webserver durch Austausch der Software (und ev. des ATmega32) bietet sich an. Kleiner Hinweis dabei: wenn zum Flashen ein ISP-Adapter verwendet wird, diesen unbedingt vor dem Start der neuen Software abziehen! Der ISP arbeitet nämlich über dieselbe SPI-Schnittstelle über die auch der ENC28J60 angesteuert wird. Ein eventuell noch angeschlossener, wenn auch passiver ISP-Adapter stört diese Kommunikation, d.h. das Programm an sich scheint zu laufen, aber die Ethernet-Schnittstelle funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
=== E2000 - Logik ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:E2000-Logik-Bedienoberflaeche.jpg|500px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Anwenderfreundliche Logik-Software von Elektronik2000.de zur Steuerung des AVR-NET-IO. Der ATMEGA32 wird durch einen ATMEGA644 ersetzt und mit der E2000-Firmware beschrieben. Nun ist es möglich in der E2000-Logik Software eine Logikschaltung zu erstellen und diese auf das NET-IO-Board zu übertragen. Dort wird diese Schaltung simuliert. Dies funktioniert komplett ohne einen Computer.&lt;br /&gt;
&lt;br /&gt;
Durch ein Erweiterungsboard von Elektronik2000.de ist es auch möglich, das Board ohne Internet laufen zu lassen eine RTC (RealTimeClock) übernimmt dabei die Uhrzeitgesteuerten Funktionen. Auf dieser Erweiterung ist auch ein EEPROM integriert, um Firmware-Updates über Netzwerk einzuspielen (in Zukunft). Diese Erweiterung bietet außerdem die Anbindung an das E2000-Bus-System, durch das es möglich ist das AVR-NET-IO-Board durch weitere Ein- und Ausgänge zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Das Designen von Schaltaufgaben wird in diesem Programm grafisch dargestellt, durch das ein einfaches Anpassen seiner Logikschaltungen möglich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Steuerung des E2000-NET-IO ist möglich durch den Intregrieten Webserver, die PC-Software (E2000-NET-IO Control) oder der Androidsoftware. All diese Können gleichzeitig auf das E2000-NET-IO zugreifen und Funktionen ausführen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es auf der Entwicklerseite: [http://www.elektronik2000.de Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== Bascom Version von Hütti ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://bascom-forum.de/index.php/topic,1781.45.html )&lt;br /&gt;
dort am Ende der Seite.&lt;br /&gt;
&lt;br /&gt;
=== Ben&#039;s Bascom Quellcode ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://members.home.nl/bzijlstra/software/examples/enc28j60.htm )&lt;br /&gt;
&lt;br /&gt;
Muss aber für Bascom 1.11.9.3 angepasst werden, siehe Code von Hütti !&lt;br /&gt;
&lt;br /&gt;
=== U. Radigs Webserver ===&lt;br /&gt;
&lt;br /&gt;
Angepasster Sourcecode von U.Radig: http://www.mikrocontroller.net/attachment/40027/Webserver_MEGA32.hex&lt;br /&gt;
oder selbst anpassen: &lt;br /&gt;
Ändere in der Datei ENC28J60.H:&lt;br /&gt;
 #define ENC28J60_PIN_CS    4&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#988386)&lt;br /&gt;
&lt;br /&gt;
Temporären Dateien (*.d, *,lst,*.o) vorher im Verzeichnis löschen &#039;&#039;make clean&#039;&#039;, damit neu compiliert wird.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.0.99&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: uli1&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den orginal SourceCode gibt&#039;s übrigens hier:http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex&lt;br /&gt;
&lt;br /&gt;
Bei den Fuses BOOTRST ausschalten, da die Software keinen Bootloader enthält.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.1.90&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: tim&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://beitz-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://pieper-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterentwicklung des Radig-Codes von RoBue: &amp;lt;br&amp;gt;&lt;br /&gt;
- 1-Wire-Unterstützung (Anschlus an PORTA7) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA0-3 digitaler Eingang (ein/aus) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA4-6 analoger Eingang (0 - 1023) &amp;lt;br&amp;gt;&lt;br /&gt;
- LCD an PORTC &amp;lt;br&amp;gt;&lt;br /&gt;
- Schalten in Abhängigkeit von Temperatur und analogem Wert &amp;lt;br&amp;gt;&lt;br /&gt;
- (Teilweise) Administration über Weboberfläche &amp;lt;br&amp;gt;&lt;br /&gt;
- Erweiterung des cmd-Befehlsatzes für telnet/rs232 &amp;lt;br&amp;gt;&lt;br /&gt;
Gedacht ist der Einsatz des AVR-NET-IO-Bausatzes für Heizungs- oder Haussteuerung) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test: http://avrboard.eluhost.de/&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Quelle:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/43307/AVR-NET-IO_RoBue_V1.3.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/44569/AVR-NET-IO_RoBue_V1.4.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/46720/AVR-NET-IO_RoBue_1.5-final_hoffentlich_.zip)&lt;br /&gt;
&lt;br /&gt;
Bei der Ver 1.5 sind die Ports PD2+3 fürs 4bit LCD (Ext.) vertauscht ! Gruß B.P&lt;br /&gt;
&lt;br /&gt;
=== Simon Ks Webserver (uip-Stack) ===&lt;br /&gt;
Angepasster Sourcecode von Simon K: http://www.mikrocontroller.net/attachment/39939/uWebSrv.zip&lt;br /&gt;
IP: 192.168.0.93:8080&amp;lt;br&amp;gt;&lt;br /&gt;
Um diesen Code mit einem Atmega1284P verwenden zu können, muss in der main.c in Zeile 38, &amp;quot;TIMSK&amp;quot; durch &amp;quot;TIMSK1&amp;quot; ersetzt werden.&lt;br /&gt;
Die Fusebits für den Atmega1284p ohne Bootloader sind:&lt;br /&gt;
lfuse=0xFF, hfuse=0xD9, efuse=0xFF&lt;br /&gt;
&lt;br /&gt;
=== Ethersex Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.ethersex.de - Einfach für atmega32 compilieren und funktioniert.&lt;br /&gt;
&lt;br /&gt;
=== Etherrape Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.lochraster.org/etherrape/ &lt;br /&gt;
&lt;br /&gt;
ist in jedem Fall hier auch zu erwähnen zumal es sich beim etherrape um das Ursprungsprojekt von ethersex handelt.&lt;br /&gt;
Es scheint aber bei der Weiterentwicklung wenig zu passieren.&lt;br /&gt;
Ausführliche Dokumentation findet sich unter http://wiki.lochraster.org/wiki/Etherrape&lt;br /&gt;
&lt;br /&gt;
=== Mini SRCP Server (kommerziell, Closed-Source)===&lt;br /&gt;
&lt;br /&gt;
Damit wird die Platine zu einer Modellbahnsteuerung, die&lt;br /&gt;
über das Netzwerkprotokoll SRCP mit verschiedenen Programmen&lt;br /&gt;
gesteuert werden kann.&lt;br /&gt;
&lt;br /&gt;
[http://www.7soft.de/de/mini_srcp_server/index.html Infoseite] zur Hardware&lt;br /&gt;
und das zugrundeliegende [http://www.der-moba.de/index.php/Digitalprojekt Digitalprojekt].&lt;br /&gt;
&lt;br /&gt;
=== Avr ArtNET-Node ===&lt;br /&gt;
&lt;br /&gt;
Hiermit kann die Platine zu einem Art-Net Node werden, mit dem sich ein DMX-Universe über Ethernet übertragen lässt. Basiert auf den Quellen von Ulrich Radig.&lt;br /&gt;
&lt;br /&gt;
Dokumentation: [http://www.dmxcontrol.de/wiki/Art-Net-Node_f%C3%BCr_25_Euro Art-Net-Node für 25 Euro]&lt;br /&gt;
&lt;br /&gt;
=== Webserver von G. Menke ===&lt;br /&gt;
&lt;br /&gt;
Ein Webserver (basierend auf den Sourcen von U. Radig), der so angepasst ist, dass alle Ein- und Ausgänge wie bei der originalen Pollin-Software genutzt werden können (8xDIGOUT, 4xDIGIN, 4xADIN). Der Webserver kann daher direkt auf das Net-IO geladen werden. Im ZIP-File sind ein ReadMe und alle C-Sourcen enthalten.&amp;lt;br&amp;gt;&amp;lt;i&amp;gt;Download&amp;lt;/i&amp;gt;:&lt;br /&gt;
[http://gm.stream-center.de/webserver/ Webserver mit passender IO]&lt;br /&gt;
&lt;br /&gt;
=== OpenMCP ===&lt;br /&gt;
&lt;br /&gt;
Tolles Projekt, welches viele Features bietet und stabil läuft. Hervorzuheben ist die Übersichtlichkeit der Programmteile/Module und die vielleicht nicht ganz komplette Dokumentation. Man merkt, dass viel Arbeit und Liebe in diesem Projekt steckt. Herausgekommen ist dabei eine einfach zu handhabende Entwicklungsumgebung. Anfänger können, dank des gut durchdachten CGI-Systems, welches sich um alle wichtigen Sachen kümmert, leicht eigene CGI implementieren. Alle Ausgaben erfolgen nur mit printf über die Standardausgabe und werden automatisch richtig per Netzwerk übertragen, dadurch ist es auch für den Anfänger recht gut geeignet, da man sich nicht mit der Netzwerkprogrammierung auseinander setzen muss.&lt;br /&gt;
&lt;br /&gt;
Die Software belegt im Moment (Stand Juli 2010) ca. 55 Kb im Flash, so dass man das Board mit einem grösseren µC (z.B. ATMega644) aufrüsten muss.&lt;br /&gt;
&lt;br /&gt;
[http://wiki.neo-guerillaz.de Projekt und Doku]&lt;br /&gt;
&lt;br /&gt;
Der Autor stellt zwei über das Internet erreichbare Testboards bereit unter http://www.neo-guerillaz.de:81 und http://www.neo-guerillaz.de:82 die beide unter OpenMCP laufen, je auf einen AVR-NETIO mit einem ATmega644 und dem eigentlichen Board mit einem ATmega2561. Zusätzlich ist gerade eine Version für das myAVR in Arbeit die schon ordentlich Fortschritte macht.&lt;br /&gt;
&lt;br /&gt;
=== OpenMLP ===&lt;br /&gt;
Auf openMCP basierender Port nach [http://avr.myluna.de LunaAVR] (GPL). Umfangreiche Funktionalität und direkte Anpassung an die Sprache. Abgespeckte Version auch auf Atmega32 lauffähig. Die Original-Dokumentation kann zum Großteil hergenommen werden. Einige Zusatzfeatures. Leichte Konfiguration und guter Einstieg für Anfänger und zum Verständnis der Serverfunktionalität.&lt;br /&gt;
&lt;br /&gt;
[http://avr.myluna.de/doku.php?id=de:openmlp Artikelseite]&lt;br /&gt;
[http://forum.myluna.de/viewtopic.php?f=8&amp;amp;t=13 Forum]&lt;br /&gt;
&lt;br /&gt;
=== ENC28J60 I/O-Webserver von Thomas Heldt ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul-Webserver (Softwarekompatibel zum Pollin Webserver), der durch div. Module erweitert werden kann, Software in Bascom basierend auf dem Code von Ben Zijlsta wurde erweitert und angepasst:&lt;br /&gt;
&lt;br /&gt;
[http://mikrocontroller.heldt.eu/index.php?page=enc28j60-io-webserver Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
=== AVR-Netino ===&lt;br /&gt;
[http://code.google.com/p/avr-netino/ Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
Arduino fürs Net-IO&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/topic/109988&lt;br /&gt;
* [http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en022889 ENC28J60 Produktseite]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/DeviceDoc/39662c.pdf ENC28J60 Datenblatt(pdf)]&lt;br /&gt;
* [http://son.ffdf-clan.de Forum für AVR-Net-IO]&lt;br /&gt;
* [http://avr.myluna.de/doku.php?id=de:openmlp LunaAVR openMLP ]&lt;br /&gt;
* [http://bascom-forum.de/index.php/topic,1781.0.html Bascom Forum ]&lt;br /&gt;
* [http://hobbyelektronik.org/w/index.php/AVR-NET-IO-Shield Shield für den NET-IO]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Category:Ethernet|P]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78890</id>
		<title>AVR Net-IO Bausatz von Pollin</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=78890"/>
		<updated>2013-10-11T00:37:24Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* AVR-NET-IO */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier steht eine Beschreibung des Pollin Bausatzes [http://www.pollin.de/shop/shop.php?cf=detail.php&amp;amp;pg=NQ==&amp;amp;a=MTQ5OTgxOTk= AVR-NET-IO. Best.Nr. 810 058], oder als aufgebautes Fertigmodul, Best.Nr. 810 073. &lt;br /&gt;
&lt;br /&gt;
Einige Features: Ethernet-Platine mit ATmega32 und Netzwerkcontroller ENC28J60. Die Platine verfügt über 8 digitale Ausgänge, 4 digitale und 4 ADC-Eingänge, welche alle über einen Netzwerkanschluss (TCP/IP) abgerufen bzw. geschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AVR-NET-IO ADD-ON.JPG|thumb|right|400px|AVR-NET-IO (links) mit zusätzlicher SUB-D Anschlussplatine (rechts, nicht im Lieferumfang)und Add-On-Board (oben, mit aufgelötetem RFM12-433-Modul, beides nicht im Lieferumfang). Ebenso ist zusätzlich ein nicht im Lieferumfang enthaltener kleiner Kühlkörper auf einem der Spannungsregler montiert und die Schraubklemmen zur Stromversorgung wurden durch Buchsen ersetzt.]]&lt;br /&gt;
&lt;br /&gt;
== Technische Daten ==&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 9 V AC/DC&lt;br /&gt;
* Stromaufnahme ca. 190 mA&lt;br /&gt;
* 8 digitale Ausgänge (0/5 V) [PC0-PC7 an J3]&lt;br /&gt;
* 4 digitale Eingänge (0/5 V) [PA0-PA3 an J3]&lt;br /&gt;
* 4 ADC-Eingänge (10 Bit) [PA4-PA7 an Schraubklemmen]&lt;br /&gt;
* LCD-Anschluss (HD44780 komp. Controller nötig) [PD2-7,PB0,PB3 an EXT]&lt;br /&gt;
* [[ENC28J60]]&lt;br /&gt;
* [http://www.atmel.com/dyn/Products/Product_card.asp?part_id=2014 ATmega32] Mikrocontroller&lt;br /&gt;
&lt;br /&gt;
Maße (L×B×H): 108×76×22 mm.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Die Schaltung des AVR-NET-IO ist recht einfach:&lt;br /&gt;
* Ein ATmega32 Mikrocontroller enthält die gesamte Software&lt;br /&gt;
* Ein ENC28J60 Ethernet-Controller für das Senden und Empfangen von Ethernet Frames (MAC und PHY Ethernet Layer) ist über [[SPI]] mit dem ATmega32 verbunden&lt;br /&gt;
* Ein Ethernet RJ-45 MagJack TRJ 0011 BA NL von [http://www.trxcom.com/ Trxcom] mit eingebautem Übertrager und Anzeige-LEDs am ENC28J60.&lt;br /&gt;
* Ein MAX232 für die serielle Schnittstelle&lt;br /&gt;
* Zwei Spannungsregler, 5 V und 3,3 V&lt;br /&gt;
* &amp;quot;Hühnerfutter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Fast alle I/O Pins des ATmega32 sind irgendwo auf Anschlüssen herausgeführt. Entweder auf dem SUB-D Stecker, dem EXT oder ISP Wannensteckern oder den blauen Anschlussklemmen. Eine Schutzbeschaltung gibt es nicht.&lt;br /&gt;
&lt;br /&gt;
Die blauen Anschlussklemmen haben eine Nut und eine Feder mit denen man&lt;br /&gt;
sie zusammenstecken kann, dadurch ist das Anlöten wesentlich leichter&lt;br /&gt;
und sie stehen auch sauber in der Reihe.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
Seit Januar 2010 gibt es auch eine Erweiterungsplatine &lt;br /&gt;
&lt;br /&gt;
[http://www.pollin.de/shop/dt/Nzg4OTgxOTk-/Bausaetze/Diverse/Bausatz_Add_on_fuer_AVR_NET_IO.html Add-on für AVR-NET-IO-Board Best.Nr. 810 112]&lt;br /&gt;
&lt;br /&gt;
Diese Platine erweitert das NET-IO um:&lt;br /&gt;
&lt;br /&gt;
* SD-Karten-Slot über SPI&lt;br /&gt;
* Display über PCF 8574&lt;br /&gt;
* Infrarot-Empfang&lt;br /&gt;
* [[RFM12]] Funkmodul (nicht im Lieferumfang enthalten)&lt;br /&gt;
&lt;br /&gt;
Eine Aufstellung bekannter Fehler findet sich weiter unten [[#Bekannte Fehler|Bekannte Fehler - Erweiterungplatine]] &lt;br /&gt;
&lt;br /&gt;
Erste Erfahrungsberichte im Forum http://www.mikrocontroller.net/topic/161354&lt;br /&gt;
&lt;br /&gt;
=== Hardware-Umbauten &amp;amp; -Verbesserungen ===&lt;br /&gt;
&lt;br /&gt;
* Kühlkörper auf dem 7805 - (Vorsicht: Der LM317T ist rückseitig spannungsführend. Den Kühlkörper also keinesfalls an beide Spannungsregler anschließen!)&lt;br /&gt;
* MAX232 nach anfänglicher Konfiguration nicht bestücken um Strom zu sparen oder um zwei weitere I/O-Pins zu gewinnen&lt;br /&gt;
* 10µF-Elkos für MAX232N (C14-C17) durch 1µF ersetzen. Eine 10µF-Version für den MAX232 gibt es nicht. Die 10µF-Elkos können auch Ursache einer nicht funktionierenden RS232 sein.&lt;br /&gt;
** Laut Spezifikation sind auch mehr als 1µF erlaubt. Selbst Atmel verwendet beim STK500 10µF. Dies führt keinesfalls dazu, dass die RS232 nicht mehr funktioniert.&lt;br /&gt;
* Die IC-Fassungen aus &amp;quot;Pollins Resterampe&amp;quot; durch Fassungen mit gedrehten Kontakten ersetzen. &lt;br /&gt;
* &#039;&#039;Netz&#039;&#039; LED nicht bestücken oder größere Widerstände einlöten um Strom zu sparen (R3)&lt;br /&gt;
* Vorwiderstände der Ethernet-LEDs größer machen (z.&amp;amp;nbsp;B. verdoppeln) um Strom zu sparen (R6,R7)&lt;br /&gt;
* Linear-Spannungsregler ersetzen&lt;br /&gt;
* Kondensator an AREF-Pin des ATmega32 (ATmega32 Datenblatt) (100nF gegen Masse)&lt;br /&gt;
* Kondensator an den RESET-Pin des ATmega32 ([http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Atmel Application Note AVR042: AVR Hardware Design Considerations]) Wenn man diese Quelle genauer liest, ist das aber eher unnötig. - Kondensator bei selbstbau-ISP empfehlenswert.&lt;br /&gt;
* Umbau auf 3,3 V:&lt;br /&gt;
** Ersatz der Spannungsregler durch einen einzigen 3,3 V Regler&lt;br /&gt;
** Anpassen (verkleinern) des LED-Vorwiderstands R3 für 3,3 Volt Betrieb&lt;br /&gt;
** Reduktion der Taktfrequenz (Austausch von Q2) auf den bei 3,3V erlaubten Bereich des ATmega32 ( ATmega32(L)  3.3V /8.0 Mhz Takt )&lt;br /&gt;
** Ersatz des MAX232 durch einen MAX3232&lt;br /&gt;
[[Bild:POWER.JPG|thumb|400px|5V Stromversorgung über USB Kabel, ohne 5 V Spannungsregler und Gleichrichterdioden, Vorsicht: kein Verpolungsschutz!  ]]&lt;br /&gt;
* ATmega32 vom ENC28J60 takten (OSC2)&lt;br /&gt;
* Betrieb mit Gleichspannung:&lt;br /&gt;
** Dioden D2 und D5 durch Drahtbrücken ersetzen, D1 und D4 nicht bestücken (komplette Entfernung des Brückengleichrichters, beinhaltet Verlust des Verpolungsschutzes)&lt;br /&gt;
** Diode D2 bestücken, D5 durch Drahtbrücke ersetzen, D1 und D4 nicht bestücken (Brückengleichrichter durch Verpolungsschutze ersetzen)&lt;br /&gt;
*** ??? Ist dies nicht kontraproduktiv? Bei mir wurde durch D2-Bestückung die Eingangsspannung von ca. 5,2 V am LM317T auf ca. 4,6 V gedrückt, so dass am ENC28J60 nur ca. 2,6 V ankamen (außerhalb der lt. Datenblatt &amp;quot;Operating voltage range of 3.14V to 3.45V&amp;quot;). Man müsste also ein geregeltes Netzteil mit ca. 5,5 V anschließen um 5 und 3,3 V zu erzielen. Dann lieber den Verpolungsschutz durch andere Maßnahmen sicherstellen.&lt;br /&gt;
** Beim Betrieb von USB beachten, dass USB-Spezifikation keinesfalls 5V garantiert, sondern Spannung bis runter 4.4V erlaubt und dann u.U. durch den LM317 nicht mehr genügend Spannung am ENC anliegt. Das äußert sich so, dass zwar der Atmega einwandfrei funktioniert, die Ethernet-Kommunikation aber nicht oder nur sehr sporadisch.&lt;br /&gt;
* Ersatz des ATmega32 durch einen ATmega644 oder ATmega1284p mit mehr FLASH-Speicher.&lt;br /&gt;
** Der ATmega644 und auch der ATMega1284p sind nicht mit der Pollin-Firmware kompatibel&lt;br /&gt;
* 100nF über alle drei IC Störunterdrückung zusätzlich bestücken&lt;br /&gt;
&lt;br /&gt;
== Inbetriebnahme der Originalsoftware ==&lt;br /&gt;
=== Einleitung ===&lt;br /&gt;
&lt;br /&gt;
Die bei Auslieferung (Stand September 2008) in den ATmega32 gebrannte Firmware stellt sich manchmal recht zickig an. Es scheint dann die Netzwerk-Schnittstelle, ggf. auch  die serielle Schnittstelle, nicht zu funktionieren. Falls es Probleme geben sollte, sollte man erst einmal ein Firmwareupdate versuchen. Dies geschieht über die serielle Schnittstelle mittels des Programmes NetServer (aktuelle Version 1.03, Februar 2010), die dem Bausatz beiliegt. &lt;br /&gt;
&lt;br /&gt;
Falls die serielle Schnittstelle ebenfalls nicht zugänglich ist, kann mit den im folgenden beschriebenen Schritten die Inbetriebnahme der Software möglich sein. Dazu benötigt man:&lt;br /&gt;
&lt;br /&gt;
* Einen Windows-PC mit Ethernet-Schnittstelle und RS232-Schnittstelle (ein Prolific RS232-USB Konverter funktioniert)&lt;br /&gt;
* Entweder&lt;br /&gt;
**zwei normale (&#039;&#039;straight through&#039;&#039;) Ethernet-Kabel und einen Ethernet Switch/Hub, oder&lt;br /&gt;
**ein gekreuztes(&#039;&#039;cross over&#039;&#039;) Ethernet-Kabel&lt;br /&gt;
* Einen AVR Programmer (Hardware und Software). Zum Beispiel einen [[AVR Dragon]] oder [[STK500]] mit [[AVR Studio]] oder das [[Pollin ATMEL Evaluations-Board]] und [[avrdude]].&lt;br /&gt;
* Die [http://www.pollin.de/shop/ds/MTQ5OTgxOTk-.html Pollin NetServer Software], Version 1.03 (oder neuer)&lt;br /&gt;
&lt;br /&gt;
=== Gelieferten ATmega32 richtig einstellen ===&lt;br /&gt;
[[image:fuse_bits_avr_studio.jpg|thumb|right|250px|Einstellungen der Fuse-Bits mittels AVR Studio 4]]&lt;br /&gt;
Die Fuses der gelieferten ATmega32s scheinen nicht immer mit den im Handbuch auf Seite 12 als erforderlich angegebenen Fuse-Einstellungen übereinzustimmen.&lt;br /&gt;
&lt;br /&gt;
Dies kann man mittels eines Programmers ändern. LFuse = 0xBF, HFuse = 0xD2. Das genaue Vorgehen hängt dabei vom verwendeten Programmer ab. Bei der Gelegenheit kann man ebenfalls eine Sicherheitskopie des ursprünglichen Flash-Inhalts und des EEPROMs anfertigen. Das EEPROM scheint die MAC-Adresse des Ethernet-Ports zu enthalten.&lt;br /&gt;
&lt;br /&gt;
Entgegen der Spezifikation im Handbuch von Pollin sollten die &#039;&#039;&#039;HFuses auf 0xC2&#039;&#039;&#039; gesetzt werden, d. h. CKOPT-Fuse programmiert (dies ist in der Software Version 1.03 bereits vollzogen). Das sorgt für einen stabilen Betrieb des AVR-Oszillators im &amp;quot;full rail-to-rail swing&amp;quot;-Mode bei 16 MHz. Atmel garantiert ansonsten nur stabilen Betrieb bis 8 MHz. Siehe ATmega32-Datenblatt, Kapitel 8.4, Crystal Oscillator.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Funktionsfähige Konfiguration - AVR-Prog ====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avrprog.png|thumb|right|250px]]&lt;br /&gt;
Benutzer von AVR-Prog können die nachfolgenden Einstellungen für die Lock- und Fuse-Bits verwenden. Hierbei handelt es sich um die ausgelesenen Einstellungen eines funktionsfähigen Controllers. Allerdings sollte, laut Handbuch des AVR-NET-IO-Boards, das Fuse-Bit EESAVE eigentlich gesetzt sein. &lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch per avrdude die Einstellung getroffen werden:&lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U lfuse:w:0xBF:m&lt;br /&gt;
und &lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U hfuse:w:0xC2:m&lt;br /&gt;
&lt;br /&gt;
Anschließend muß noch der Bootloader und die Firmware aktualisiert werden (siehe Handbuch AVR-NET-IO-Board Seite 12 Punkt 3).&lt;br /&gt;
&lt;br /&gt;
=== PC Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
==== PC normalerweise nicht im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
Betreibt man den PC nicht im 192.168.0.0/24 Subnetz, muss er wie folgt umkonfiguriert werden, oder die IP Adresse des Boards wird entsprechend angepasst. ( Siehe Handbuch Seite 14ff. Das ist meist sinnvoller und auch einfacher. ) &lt;br /&gt;
&lt;br /&gt;
Den PC vom normalen Netzwerk abstecken[1]. Zur Umkonfiguration dazu bei Windows XP in der Systemsteuerung &#039;&#039;Netzwerkverbindungen&#039;&#039; aufrufen und die lokale &#039;&#039;LAN-Verbindung&#039;&#039; markieren. Dann in der rechten Leiste &#039;&#039;Einstellungen dieser Verbindung ändern&#039;&#039; aufrufen. &lt;br /&gt;
&lt;br /&gt;
Es erscheint der Dialog &#039;&#039;Eigenschaften von &amp;lt;Verbindungsname&amp;gt;&#039;&#039;. In der Liste im Dialog zu &#039;&#039;Internetprotokoll (TCP/IP)&#039;&#039; gehen. Ein Doppelklick auf den Eintrag öffnet den &#039;&#039;Eigenschaften von Internetprotokoll (TCP/IP)&#039;&#039; Dialog.&lt;br /&gt;
&lt;br /&gt;
In diesem Dialog &#039;&#039;Folgende IP-Adresse verwenden:&#039;&#039; auswählen und zum Beispiel&lt;br /&gt;
&lt;br /&gt;
IP-Adresse: &#039;&#039;&#039;192.168.0.100&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Subnetzmaske: &#039;&#039;&#039;255.255.255.0&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Standardgateway: &#039;&#039;&#039;192.168.0.1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
eingeben. &lt;br /&gt;
&lt;br /&gt;
Anmerkung von bitman:&lt;br /&gt;
[1] Dies ist spätestens ab Windows XP nicht mehr notwendig, wenn das Netz 192.168.0.0/24 noch frei ist. Dann kann man einfach den Client &#039;&#039;zusätzlich&#039;&#039; in diesem Netzwerk zusätzlich einbinden über Einstellungen/Netzwerkverbindungen/Lanverbindung/Eigenschaften/TCP-IP/Eigenschaften/Erweitert/IP-Adresse hinzufügen. Es werden dann eben mehrere IP-Adressen an den NIC gebunden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle geöffneten Dialoge nacheinander mit OK schließen.&lt;br /&gt;
&lt;br /&gt;
Alternativ bietet sich das Umprogrammieren des Boards über die serielle Schnittstelle an. Die Werte für IP-Adresse, Netzmaske und Standard-Gateway werden mit den dokumentierten SETxx-Befehlen geändert, das Board neu gestartet und ans vorhandene Netzwerk gesteckt.&lt;br /&gt;
&lt;br /&gt;
Im EEPROM sind folgende Werte vorprogrammiert:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3EE - 3F3 MAC-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
3F4 - 3F7 GATEWAY&amp;lt;br&amp;gt;&lt;br /&gt;
3F8 - 3FB NETMASK&amp;lt;br&amp;gt;&lt;br /&gt;
3FC - 3FF IP-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PC bereits im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
In diesem Fall muss man prüfen, ob die IP-Adresse 192.168.0.90 bereits im Subnetz verwendet wird. Ist dies der Fall, muss das verwendete Gerät mit dieser IP vorübergehend aus dem Subnetz entfernt werden. Es sei denn, dabei handelt es sich um den PC. In diesem Fall muss er wie zuvor umkonfiguriert werden. Ansonsten kann der unverändert im Netz verbleiben.&lt;br /&gt;
&lt;br /&gt;
Dem AVR-NET-IO gibt man eine neue, zuvor unbenutzte Adresse (siehe unten). Dann kann das abgekoppelte Gerät wieder angeschlossen werden, beziehungsweise der PC zurückkonfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
=== AVR-NET-IO anschließen ===&lt;br /&gt;
&lt;br /&gt;
Musste man den PC umkonfigurieren, so werden jetzt nur der PC und der AVR-NET-IO über Ethernet miteinander verbunden. Je nach Ethernet-Kabel benötigt man dazu einen Switch/Hub oder nicht.&lt;br /&gt;
&lt;br /&gt;
Musste man den PC nicht umkonfigurieren, so kann man den AVR-NET-IO wie einen normalen Rechner an das vorhandenen Netz anschließen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich schließt man die serielle Schnittstelle des AVR-NET-IO an den PC an.&lt;br /&gt;
&lt;br /&gt;
=== Firmware 1.03 einspielen ===&lt;br /&gt;
&lt;br /&gt;
Laut Handbuch sollte der AVR-NET-IO jetzt über Ethernet funktionieren. Ebenso sollte er über die serielle Schnittstelle und ein Terminalprogramm konfigurierbar sein. Beides ist offensichtlich im Auslieferungszustand selten der Fall.&lt;br /&gt;
&lt;br /&gt;
Auch wenn sich Pollins NetServer Software nicht mit dem AVR-NET-IO verbinden lässt, so ist sie jedoch in der Lage eine neue Firmware 1.03 einzuspielen. Das Vorgehen ist im Handbuch auf Seite 12 beschrieben. NetServer präsentiert dabei ein paar einfache Anweisungen denen man folgen sollte.&lt;br /&gt;
&lt;br /&gt;
Wenn sich nach dem scheinbar erfolgreichem Einspielen der Firmware, später nichts tut, so sollte die Firmware mit gesetztem &amp;quot;FailSafe&amp;quot; in der mitgelieferten NetServer-Software nochmals Eingespielt werden.&lt;br /&gt;
&lt;br /&gt;
=== Abschluss ===&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte sich die NetServer Software mit dem AVR-NET-IO über Ethernet verbinden lassen. Dies macht es wiederum möglich, den AVR-NET-IO mit einer anderen IP-Adresse zu versehen. Will man den AVR-NET-IO in einem anderen Subnetz betreiben kann man dies jetzt einstellen.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse neu eingestellt hat, muss man den PC zurückkonfigurieren und kann dann sowohl den AVR-NET-IO und den PC zusammen betreiben.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Fehler ==&lt;br /&gt;
=== AVR-NET-IO ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[#Hardware-Umbauten_.26_-Verbesserungen|Hardware-Umbauten und Verbesserungen]]&amp;lt;br&amp;gt;&lt;br /&gt;
Käufer berichten von fehlenden Bauteilen im Bausatz (Wannenstecker, Widerstände, Kondensatoren, Induktivitäten). Für Reklamationen: [https://www.pollin.de/shop/kontakt_service/reklamation.html]&lt;br /&gt;
&lt;br /&gt;
* Die Stückliste auf Seite 4 in den Anleitung mit den Versionsangaben&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
:ist falsch. Pollin legt dem Bausatz irgendwann ab September 2008 einen gedruckten Korrekturzettel bei. Die Online-Version der Anleitung ist korrigiert.&lt;br /&gt;
* Im Schaltplan auf Seite 7 in den Anleitungen mit den Versionen&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
** &#039;&#039;Stand 03.09.2008, online, #all, wpe&#039;&#039; (Online)&lt;br /&gt;
:ist eine 25-polige SUB-D Buchse gezeichnet. Geliefert wird und in der Stückliste verzeichnet ist ein Stecker.&lt;br /&gt;
&lt;br /&gt;
* Die September 2008 ausgelieferte Firmware im ATmega32  funktioniert bei vielen nicht und muss erst upgedatet werden (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Im Flash der gelieferten AVR ist anders als beschrieben nur der Bootloader enthalten, die eigentliche Firmware muss erst mit Hilfe der Updatefunktion geladen werden. Wenn zusätzlich auch die Fuses falsch gebrannt sind, dann funktioniert das Update nicht, auch wenn das PC Programm was anderes behauptet.&lt;br /&gt;
&lt;br /&gt;
* Die Fuse-Einstellungen des ausgelieferten ATmega32 entspricht nicht der Anleitung (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Bausatz, gekauft am 27.10.08, Anleitungsversion 19.09.08, ohne Probleme oder erkennbare Fehler zusammengebaut und in Betrieb genommen.&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft 29.09.2008, Pinbelegung des 25 poligen D-Sub &amp;quot;Anschlusses&amp;quot; stimmt nicht mit der Anleitung überein. Der Aufdruck auf der Platine ist falsch. Pin1 &amp;lt;-&amp;gt; Pin13, Pin2 &amp;lt;-&amp;gt; Pin12 usw. Setzt man den D-Sub Stecker ein, so sind dessen Pinnummern korrekt. Bei einem Bausatz gekauft 05/2013 ist dies ebenfalls noch der Fall.&lt;br /&gt;
&lt;br /&gt;
* 3 Bausätze Anf. Oktober 2008 gekauft, bei einem waren 2 LM317 dabei, dafür fehlte der 7805 - aus der Bastelkiste ersetzt. Alle haben jedoch auf Anhieb funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft Ende Januar 2009. Die Lock-Bits (u.a. für PonyProg2000) werden falsch beschrieben. Die in Klammern aufgeführten Werte stimmen bei einem Bit nicht. Die Texte &amp;quot;Programmiert/Unprogrammiert&amp;quot; hingegen schon. Bei den Bauteilen gab es 4 Kondensatoren mit der Aufschrift &amp;quot;220&amp;quot;, ich habe diese durch welche mit 22p ersetzt, da ich nicht sicher war ob wirklich 22p geliefert wurden. Dafür wurden statt einem zwei 7805 und statt einem mindestens vier LM317 mitgeliefert.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 22.4.2009. Alles vollständig, zusammengebaut, läuft. Software-Version 1.03. Für den oben schon genannten Steckverbinder wurde eine Buchse geliefert. Allerdings stimmen die PIN-Nummern im Schaltplan nicht mit den PIN-Nummern auf der Buchse überein (sie sind gespiegelt), daher liefen die Test-LEDs zunächst nicht.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 11.7.2009. Spannungsregler LM317T fehlt, grüne statt roter LED. Ein Kondensator 22pF zu viel. LM317T wurde auf Anfrage kostenlos nachgeliefert (27.7.). Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 24.7.2009. Ein Quarz 16MHz zu viel, ebenfalls grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 20.08.2009. Ein Kondensator 22pF zuviel und grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli &#039;09 gekauft, grüne statt rote LED&lt;br /&gt;
&lt;br /&gt;
* Bausatz 25.09.09 geliefert, grüne Betriebs-LED, ein ELKO zuviel, Fehler 1µF am MAX232 statt 100nF behoben, richtiger C wird mitgeliefert, Aufbau komplett nach Pollin Anleitung durchgeführt, auf Anhieb fehlerfrei!&lt;br /&gt;
&lt;br /&gt;
* Bausatz 17.10.09 geliefert, grüne Betriebs-LED, zwei 100nF Kondensatoren zu wenig. Aufbau und Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz 21.10.09 gekauft, grüne Betriebs-LED. Aufbau problemlos, RS232 läuft nicht. LAN läuft&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, ENC28J60, MAX232 und ATmega32 fehlen, Nachlieferung nach einer Woche&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft,Bauteile komplett.Verbindungsaufbau Seriell klappt erst nach mehreren Versuchen.Problem gelöst:Spannung an MAX und Mega zu niedrig&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dez. 09 gekauft, grüne LED, 100µF Kondensator fehlt, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 09 gekauft, alle teile da nach Einstellen der fusebits lief alles perfekt&lt;br /&gt;
&lt;br /&gt;
* Bausatz Okt. 09 gekauft, ein 100nF Kondensator und 25MHz Quarz fehlten ... hab beim lokalen Elektronikhändler keinen 25Mhz Grundton Quarz sondern nur im 3. Oberton bekommen aber mit R2.2k parallel zum Quarz schwingt er in der Schaltung schön bei 25Mhz. Mit 1µF am MAX232 funktioniert jetzt auch die RS232.&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Feb. 10 gekauft, bei beiden fehlten 7805, L1+L2 je 100µH sowie 4x falscher Wert Kondensator an Max232 vorhanden. Fehlende Bauteile nachgelötet und Funktion getestet. Hat alles einwandfrei funktioniert!!!&lt;br /&gt;
&lt;br /&gt;
* Bausatz März. 10 gekauft, RS232 Printbuchse fehlt, dafür 1x 10pol Wannenstecker zuviel. Grüne LED statt Rot. Funktioniert ansonsten einwandfrei.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Jan. 10 gekauft, gelbe LED statt rot, C14...C17: 10µF, weder seriell noch via Ethernet Konnektivität. Nach Austausch von C14-C17 gegen 1µF, wenigstens serielle Kontaktaufnahme möglich, kein Ethernet auch nach Flash von 1.03 mit NetServer.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Feb. 10 gekauft, Spannungsregler LM317T fehlte&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft, gelbe statt rote LED geliefert, aber Aufbau und inbetriebnahme lt. Handbuch ohne Probleme&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft und gelbe statt rote LED geliefert, funzt wunderbar gemäß Anleitung&lt;br /&gt;
&lt;br /&gt;
* Fertig gelötete Platine gekauft. µC war falsch im Sockel.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - Funktion erst nach Ersetzung des ADM durch nen MAX&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - funktionierte sofort auch mit dem ADM232LJN.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft wurde mit grüner statt roter LED Ausgeliefert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juni 10 gekauft: wurde mit grüner statt roter Netz-LED ausgeliefert, 2x 22pF Kerko zuviel&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli 10 gekauft: 2 Quarze mit 16 MHz geliefert, statt 1x 16MHz und   1x25MHz.&lt;br /&gt;
&lt;br /&gt;
* Bausatz September 10 gekauft: hat sofort funktioniert. 1x 3,3k und 1x 10k Widerstand zuviel. Statt 100nF Kondensatoren wurden 1µF geliefert -&amp;gt; Platzprobleme auf der Platine durch grössere Bauform. LED grün.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober  6 gekauft: alles funktioniert. LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Fertigmodul Oktober 10 gekauft: Auf Anhieb alles funktioniert!&lt;br /&gt;
&lt;br /&gt;
* Bausatz Oktober 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft: komplett und sofort funktioniert (sogar mit der neusten Pollin Firmware 1.03 schon drauf) LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft. Nach Bezug neuer Feinst-Lötspitzen konnte ich ihn dann auch zusammenlöten. Es hat sofort alles funktioniert, obwohl ich nur 12V bzw. 9V Gleichspannung zur Verfügung hatte, und nicht sicher war, wieviel die Komponenten wirklich benötigen. Der Regler wird auch bei 9V Gleichspannungsversorgung noch sehr warm. Da muss auf jeden Fall ein Kühlkörper dran! Ich habe auch eine grüne LED bekommen, ist mir aber wurscht :-)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dezember 10 gekauft: komplett und funktionierte sofort. Firmware 1.03, grüne LED (Ein Quarz und ein IC-Sockel zu viel)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Januar 2011 gekauft: nur genau die richtige Anzahl Teile dabei, Firmware 1.03, grüne LED, ging auf Anhieb&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Januar 2011 gekauft: beide grüne LED, und 1x doppelter Satz Jumper/Stiftleiste, 22PF und Anschlussklemmen. Rest vollständig und beide haben sofort nach zusammenbau funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: die Diode D5 fehlt, 10 µF gegen 1 µF für MAX232 getauscht, Flash im ATmega32 war programmiert, passende IP-Adr über serielle Schnittstelle eingestellt; ADD-ON: für R1 war 22Ω statt 0,2Ω beigelegt, durch richtigen ersetzt, Beschreibung der LED Bestückung mangelhaft / oe1smc&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 1 Diode zuviel, 2 Spulen fehlen, LED grün. Die fehlenden Spulen wurden durch welche aus der Bastelkiste ersetzt - funktioniert. Der 7805 bekam einen kleinen Kühlkörper spendiert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 2x 10k Widerstände fehlen. Dafür eine Diode zu viel.&lt;br /&gt;
&lt;br /&gt;
* Ende Februar 2011: Zwei Bausaetze an jeweils zwei Adressen, alles in Ordnung.&lt;br /&gt;
&lt;br /&gt;
* Ende März 2011: 2x 25 Mhz Quarz statt 1x16 u. 1x25 Mhz. LED fehlt. Bausatz funktioniert nach Tausch des Quarz den mir mein Freund oe9rsv aus seinem Fundus spendiert hat. Danke auch für die Hilfe beim Fehler suchen.&lt;br /&gt;
&lt;br /&gt;
* Mitte 2010 gekauft: 1x 100nF fehlt&lt;br /&gt;
&lt;br /&gt;
* Mitte Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, Pollin wollte, dass ich das ganze Paket zurückschicke für einen Austausch. Ein 51Ω zu viel. 16Mhz im Handel und 25Mhz vom alten Mobo ausgelötet. Läuft wunderbar.&lt;br /&gt;
&lt;br /&gt;
* Anfang Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, nach kurzer Mail an Pollin (leider ohne Antwort) wurden diese nach ca. 1 Woche in einem Brief nachgeliefert.&lt;br /&gt;
&lt;br /&gt;
* August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: Platine fehlte -&amp;gt; in Nachlieferung&lt;br /&gt;
&lt;br /&gt;
* 30 August 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* 06 Sep. 2011: alles 1a...&lt;br /&gt;
&lt;br /&gt;
* August 2011: 6 Stück bestellt, bei einem haben die 100nF Kondensatoren gefehlt, bei einem zwei LM317 statt 7805 und LM317. Angerufen, 3 Tage später Nachlieferung erhalten.&lt;br /&gt;
&lt;br /&gt;
* Nov. 2011: &#039;&#039;&#039;Net_IO&#039;&#039;&#039; vollständig. Einspielen der Firmware 1.03 war erforderlich. Bei &#039;&#039;&#039;Add-On&#039;&#039;&#039; immer noch falscher Q1 BC548 (NPN) in Stückliste und Lieferung. BC327-40 oder BC328-40 (PNP) nachgefordert. R11 und R24 mitgeliefert, entsprechend Beschreibung V1.1 von Pollin&#039;s Download. Beiliegende Beschreibung war älter, ohne diese Widerstände.&lt;br /&gt;
&lt;br /&gt;
* Ende Nov.2011, alle Teile dabei, Firmware war drauf, sofort funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Anfang Dez.2011, komplett bestückte Platine gekauft. Auf 7805 Kühlkörper gebaut, da er nach 1 Minute schon ausgestiegen ist (LED hat das Pumpen angefangen). Firmware 1.03 musste noch aufgespielt werden danach funktioniert alles einwandfrei. In Betrieb mit 10V DC&lt;br /&gt;
&lt;br /&gt;
* Ende Dez.2011 2xNET-IO und 2xADD bestellt, 4 verschieden volle Kisten bekommen... WSL16 ist mit Verriegelung, geht nicht aufs Board, Jumper fehlen, Spannungsregler doppelt, Poti Löcher zu klein, lsb3 fehlt, SD-Slot hab ich jetzt 3, 100nF hab ich jetzt 4 übrig ... also immer noch lustig. HW-Stand immer noch 1.0 ( gab es überhaupt eine 1.1?)&lt;br /&gt;
&lt;br /&gt;
* Mitte Jan. 2012, 10pol. beide Wannenstecker nicht dabei, Firmware war drauf, sofort funktioniert. 1 LED zuviel. Unproblematische Nachlieferung bei Reklamation (wegen der beiden Wannenstecker kam ein PAKET!).&lt;br /&gt;
&lt;br /&gt;
* Ende Feb. 2012, Um die PHP-Scripte über öffentlichen Webserver zu betreiben muss mit SETGW die Gateway-Adresse des lokalen Routers eingetragen werden. Bei der NAT im Router sollte z.B. Port 8080 auf den internen Port 50290 umgeleitet werden, da manche Provider diesen Port für die Socket-Kommunikation nicht zulassen.&lt;br /&gt;
&lt;br /&gt;
* Juli 2012, Bausatz vollständig, Nach Aufbau sofort den 7805 mit einem kleinen Kühlkörper versehen, da er sonst thermisch überlastet wird! hat weder auf LAN noch RS232 reagiert, musste erst Update einspielen (Download Pollin), danach funktionierte alles einwandfrei!&lt;br /&gt;
&lt;br /&gt;
* Januar 2013, Bausatz vollständig, 7805 mit Kühlkörper versehen, nach Aufbau ohne Probleme funktioniert&lt;br /&gt;
&lt;br /&gt;
* Februar 2013, Bausatz komplett, Aufbau ohne Probleme, 7805 nur leicht warm, LAN funktioniert, RS232 noch ohne Funktion.&lt;br /&gt;
&lt;br /&gt;
* Oktober 2013, Bausatz komplett laut Stückliste, problemloser Aufbau, 7805 leicht warm am 10V DC. RS232 und LAN funktionierten auf Anhieb.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die nachfolgend genannten Änderungen sind in der Bauanleitung V1.1 mit Stand 07.12.2011 (Wichtig! Es gibt mehr als eine V1.1 ...) bereits berücksichtigt, R3 wird allerdings mit 3,6kΩ angegeben.&lt;br /&gt;
&lt;br /&gt;
Um bei einem Neuaufbau parallele Widerstände zu vermeiden, sollten folgende Änderungen auf dem Addon-Board gemacht werden:&lt;br /&gt;
*R2 1,5kΩ ersetzen mit 2kΩ&lt;br /&gt;
*R3 1,8K ersetzen mit 3,3kΩ&lt;br /&gt;
*R19 470kΩ ersetzen zu 470Ω&lt;br /&gt;
*Q1 BC548 ersetzen durch BC327 oder BC328 (Hauptsache PNP! und mehr als 100mA)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039; beim Anschluß eines LC-Displays an J5 (über I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C, PCF 8574): Vcc/5V liegt an Pin 1, GND an Pin 2 von J5. Es gibt etliche Displays mit abweichender Belegung Pin 1 GND und Pin 2 Vcc!&lt;br /&gt;
&lt;br /&gt;
Auch beim Add-On gibt es fehlende oder falsche Bauteile im Bausatz und Fehler im Schaltplan und auf der Platine:&lt;br /&gt;
&lt;br /&gt;
*Stand Feb. 2011: R2 wird mit 2,2kΩ und R3 wird mit 3,6kΩ ausgeliefert. Somit werden die 3,3 V richtig erzeugt. R19 hat 470Ω. Der ISP-Anschluß ist nicht vollständig durchgeschleift, es besteht keine Verbindung der RESET-Leitung zwischen ISP und ISP1 (Abhilfe: Drahtbrücke einlöten, [http://www.mikrocontroller.net/topic/161354#1600385 Quelle]). &lt;br /&gt;
&lt;br /&gt;
*Stand Nov. 2011: bei mir ist die RESET-Leitung korrekt zw. ISP und ISP1 verbunden. Es gibt jetzt auch einen R24 (470Ohm) und R11 (1KOhm), der in der bei mir mitgelieferten Bauanleitung fehlt, in der zum Download (V1.1) angebotenen  aber drin steht. Es wird immer noch der falsche Q1 BC548C (NPN) mitgeliefert. Das Schaltsymbol für einen PNP ist richtig im Schaltplan gezeichnet.&lt;br /&gt;
&lt;br /&gt;
*Stand Dez. 2011: R24 (470Ω) kann mit 0Ω ersetzt und R11 (1kΩ) völlig weggelassen werden. Diese Widerstände bilden einen (eigentlich überflüssigen) Spannungsteiler in der MISO Leitung. Der Spannungsteiler hat allerdings auch eine Schutzfunktion, falls weitere Slaves am SPI-Bus hängen, die 5V-Pegel nutzen, z. B. ISP-Programmierer. (siehe [http://son.ffdf-clan.de/include.php?path=forumsthread&amp;amp;threadid=1167&amp;amp;postid=9203 1284p: Board, ich oder beide verwirrt?])&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Bausatz mit Stecker anstelle einer Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s dafür aber 3 Poti zuviel.&lt;br /&gt;
&lt;br /&gt;
*Sept&#039;12: Ebenfalls Bausatz mit Sub-D-Stecker statt Buchse ausgeliefert, 5mm anstelle von 3mm LED&#039;s und 3 Potis zuviel.&lt;br /&gt;
&lt;br /&gt;
*Stand März 2013: Die Belegung der 25 pol. Sub-D-Buchse im Schaltplan der Anleitung V1.1 (und früher) ist falsch dargestellt. SCL und SDA des I&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;C-Busses liegen wie beim AVR-NET-IO Board auf Pin 2 bzw. 3 und nicht auf Pin 11 bzw. 12 der Buchse. Ein 1:1 verdrahtetes Kabel funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Andere Software für den Client-PC ==&lt;br /&gt;
=== NetIOLib ===&lt;br /&gt;
&lt;br /&gt;
In C# geschriebene Bibliothek zur Ansteuerung der Platine im Orginalzustand. Inkl. Beispielsoftware und Quellcode (GNU GPL) &lt;br /&gt;
&lt;br /&gt;
Links gehen nicht:&lt;br /&gt;
DLL: [http://www.tware.org/downloads/NetIOLib_dll.zip Download-Link]&lt;br /&gt;
Source: [http://www.tware.org/downloads/NetIOLib_src.zip Download-Link]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Multi-Control ===&lt;br /&gt;
Mti dem E2000-NET-IO-Multi-Control ist es möglich, die vom dem E2000-NET-IO-Designer erstellten Projekte zu öffnen und die AVR-NET-IO&#039;s zu steuern. &lt;br /&gt;
&lt;br /&gt;
Die Anwendung liegt dem [http://www.mikrocontroller.net/articles/AVR_Net-IO_Bausatz_von_Pollin#E2000-NET-IO-Designer_.28Windows.5BXP.2F7.5D_.2F_Android.29 E2000-NET-IO-Designer] bei.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== ControlIO ===&lt;br /&gt;
Einfache Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://www.mikrocontroller.net/topic/149695&lt;br /&gt;
&lt;br /&gt;
=== JAVA Lib ===&lt;br /&gt;
Einfache Java-Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://son.ffdf-clan.de/?path=forumsthread&amp;amp;threadid=611&lt;br /&gt;
&lt;br /&gt;
=== PHP ===&lt;br /&gt;
PHP Klasse zur Ansteuerung mit der Originalfirmware. (Opensource Lizenz)&lt;br /&gt;
http://blog.coldtobi.de/1_coldtobis_blog/archive/298_pollin_net-io_php_library.html&lt;br /&gt;
&lt;br /&gt;
PHP Funktionen zum Ansteuern der Originalfirmware. (Free for All Lizenz)&lt;br /&gt;
http://defcon-cc.dyndns.org/wiki/index.php/Pollin_AVR-NetIO_PHP_Wrapper&lt;br /&gt;
&lt;br /&gt;
== Clients für Smartphones ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== AVR NET IO Control (Windows Phone 7.5,7.8,8.0) ===&lt;br /&gt;
&lt;br /&gt;
Freie App zur steuerung des AVR NET IO.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;App im Market: [http://www.windowsphone.com/de-de/store/app/avr-net-io-controll/030d107f-9a27-4a62-ac8f-cb74e79c0500 AVR NET IO Control]&#039;&#039;&#039;&lt;br /&gt;
=== App NetIO (Windows Mobile 6.5) ===&lt;br /&gt;
Frei verfügbare App für Windows Mobile zur Ansteuerung mit der Orginalsoftware. Das HTC HD2 wird damit zur Fernsteuerung für das AVR Net-IO Board.&lt;br /&gt;
http://www.heesch.net/netio.aspx&lt;br /&gt;
&lt;br /&gt;
=== NET-IO Control (Android) ===&lt;br /&gt;
Eine Application für das Android Betriebssystem zur Steuerung des AVR Net-IO Boards. Es ist möglich, alle Ausgänge zu steuern und alle Eingänge anzuzeigen. Die Analogen Eingänge können mit einem Berechnungsfaktor versehen werden. &#039;&#039;Geplant ist noch ein Offsetwert.&#039;&#039; Außerdem kann jedem analogen Wert eine Einheit zugeordnet werden. Die Ausgänge können in der neusten Version in einen Tastermodus gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:NET-IO-Control.png|200px]] [[Datei:NET-IO-Control2.png|200px]] [[Datei:NET-IO-Control3.png|200px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Trial-Version: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControlTRAIL Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(In dieser Probe Version können nur 1 Output und 2 Inputs gesteuert werden)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vollversion: [https://play.google.com/store/apps/details?id=de.android.AVR.NETIOControl Google-Play]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(Kostet im Google-Play 3,00 €)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== E2000-NET-IO-Designer (Windows[XP/7] / Android) ===&lt;br /&gt;
[[Datei:E2000-NET-IO-Designer.png|400px|right]]&lt;br /&gt;
Mit dem E2000-NET-IO-Designer ist es möglich, eine grafische Oberfläche für die NET-IOs von Pollin zu erstellen. Dafür werden Knöpfe, Texte und Anzeigebilder zur Verfügung gestellt. Jedes dieser Element kann &amp;quot;Aufgaben&amp;quot; übernehmen um die NET-IOs zu steuern oder einen Status des NET-IO anzuzeigen. Weitere Icons können von dem Benutzer selbst in den entsprechenden Ordner gelegt werden und in die Oberfläche eingebunden werden. Es können mehrere Seiten designt werden die durch einen selbst positionierten Knopf erreichbar sind. Die Android Application arbeitet somit im Fullscreen-Modus. Des weiteren ist es möglich, mehrere NET-IO&#039;s in einem Projekt zu benutzen. Nach dem erstellen der grafischen Oberfläche mit dem E2000-NET-IO-Designer, kann mit der Android APP das Projekt einfach gedownloaded werden. Dafür baut die APP eine Verbindung zum E2000-NET-IO-Designer auf und läd alle benötigten Dateien herunter (Achtung: Firewall-Einstellungen beachten). Die designten Oberflächen können außerdem noch mit der E2000-NET-IO-Multi-Control.exe auf dem Computer ausgeführt werden. Dadurch ist es Möglich, seine NET-IOs vom PC aus zu steuern über eine selbst designte Oberfläche. &lt;br /&gt;
&lt;br /&gt;
Zum Ausführen des E2000-NET-IO-Designer muss .NET Framework 4.0 installiert sein und es muss eine Internetverbindung exisitieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Features:&#039;&#039;&#039;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Grafische Oberfläche am PC erstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verwendung von eigenen Button- und Hintergrundbildern&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designbare Anzeige von Digital- und Analogwerten&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Steuern von mehreren AVR-NET-IO Boards&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Unterstützung der E2000-NET-IO Firmware (mehere Boards gleichzeitig)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Designte Steuerungen können auf dem PC oder dem Handy ausgeführt werden (Android)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Schnelle Verbindung&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Einfache übertragung auf das Handy durch Projekt Download&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/downloads.php?id=44 Download E2000-NET-IO-Designer]&lt;br /&gt;
&lt;br /&gt;
[http://www.elektronik2000.de/hilfe/E2000Netdesigneruebersicht.html Online Hilfe]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind auf der Entwickler-Seite zu finden:  [http://elektronik2000.de/ Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== NetIO ( iPhone &amp;amp; Android ) ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;float: left; margin-right: 20px&amp;quot;&amp;gt;[[Datei:Netio_logo.png|70px]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Schöne universelle Fernbedienung für das Board (iOS / Android). Konfigurierbar über einen Browserbasierten Editor. &amp;lt;br&amp;gt;&lt;br /&gt;
Unterstützt TCP socket Verbindungen, kann http request absetzen und kann auch Webseiten einbinden (z.B. IP-Kameras). Kann mehrere &lt;br /&gt;
Boards gleichzeitig steuern! Einfach super schnell ins Projekt eingebaut... Es gibt Buttons (die auch als Taster konfiguriert werden können), Slider, Switches und einfache Labels. Dinge können geschaltet oder Daten ausgelesen und mit regex dargstellt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&#039;float: right&#039;&amp;gt;&lt;br /&gt;
[[Datei:Netio_TV.png|190px]]&lt;br /&gt;
[[Datei:Netio_iphone5.png|190px]]&lt;br /&gt;
[[Datei:NetIO_appflow.jpg|190px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
AppStore Link: http://itunes.apple.com/app/netio/id464924297?mt=8 &amp;lt;br/&amp;gt;&lt;br /&gt;
Google Play Link: https://play.google.com/store/apps/details?id=com.luvago.netio &amp;lt;br/&amp;gt;&lt;br /&gt;
Die gleiche Konfiguration kann auch mit einem [https://github.com/davideickhoff/NetIO-OSX-Dashboard-Widget OSX Widget] benutzt werden. &amp;lt;br/&amp;gt;&lt;br /&gt;
Webseite: http://netio.davideickhoff.de &lt;br /&gt;
&lt;br /&gt;
Hinweis: &amp;lt;br&amp;gt; Man findet es unter &amp;quot;NetIO&amp;quot; im Store, im Icon selbst und in iTunes wird es als &amp;quot;Controller&amp;quot; angezeigt. &amp;lt;br&amp;gt;&lt;br /&gt;
Die eigene Konfiguration kann man mit dem [http://netio.davideickhoff.de/editor Online Editor] vorher am PC erstellen.&lt;br /&gt;
Eine fertige [http://netio.davideickhoff.de/editor/?config=pollin AVR-NET-IO Konfiguration] gibt es als funktionierendes Beispiel schon als Preset.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;div style=&#039;clear: both&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AVR Net IO (iPhone) ===&lt;br /&gt;
[[Datei:AVRNetIO-Screenshot1.png|160px|rechts]]&lt;br /&gt;
Update 15.12.2011: Die Neue Version 1.3 ist seit gestern im AppStore. Fehlerkorrekturen und ein robusteres Handling machen die App nun zur universellen AVR-Net-IO-Steuerung.&lt;br /&gt;
&lt;br /&gt;
Mit der [http://itunes.apple.com/de/app/avr-net-io/id460991760?mt=8 iPhone App AVR-Net-IO] kann das Board ferngesteuert werden. Die Fernsteuerung umfasst in der Version 1.1 folgende Funktionen:&amp;lt;br&amp;gt;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ADC-Werte zyklisch auslesen und darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Inputs zyklisch darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Outputs können über Buttons geschaltet werden. Die Werte der Digital-Outputs werden zuerst ausgelesen und zeigen den zuletzt gesetzten Wert an.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Terminal-Modus: Hier können beliebige AVR-Net-IO-Befehle eingegeben werden. Das Ergebnis wird 1:1 angezeigt, wie es vom Board kommt. Hilfreich für Tests bei Eigenentwicklungen und Konfigurationen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Mehr Infos gibt&#039;s dazu direkt von den Entwicklern auf [http://www.facebook.com/pages/AVR-Net-IO/187799687958255?ref=nf AVR-net-IO Facebook-Page] oder direkt auf deren Homepage [http://www.ondics.de/apps/1001/ Homepage].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Hinweis: MIt der aktuellen Version 1.1 gibt es noch bei manchen Boards Probleme beim auslesen und anzeigen der ADC- und Digital-Werte. Das Terminal funzt problemlos. Die Entwickler kümmern sich gerade drum und haben einen baldigen Update versprochen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Andere Software statt der Originalsoftware von Pollin ==&lt;br /&gt;
&lt;br /&gt;
Die Umrüstung auf einen Webserver durch Austausch der Software (und ev. des ATmega32) bietet sich an. Kleiner Hinweis dabei: wenn zum Flashen ein ISP-Adapter verwendet wird, diesen unbedingt vor dem Start der neuen Software abziehen! Der ISP arbeitet nämlich über dieselbe SPI-Schnittstelle über die auch der ENC28J60 angesteuert wird. Ein eventuell noch angeschlossener, wenn auch passiver ISP-Adapter stört diese Kommunikation, d.h. das Programm an sich scheint zu laufen, aber die Ethernet-Schnittstelle funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
=== E2000 - Logik ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:E2000-Logik-Bedienoberflaeche.jpg|500px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Anwenderfreundliche Logik-Software von Elektronik2000.de zur Steuerung des AVR-NET-IO. Der ATMEGA32 wird durch einen ATMEGA644 ersetzt und mit der E2000-Firmware beschrieben. Nun ist es möglich in der E2000-Logik Software eine Logikschaltung zu erstellen und diese auf das NET-IO-Board zu übertragen. Dort wird diese Schaltung simuliert. Dies funktioniert komplett ohne einen Computer.&lt;br /&gt;
&lt;br /&gt;
Durch ein Erweiterungsboard von Elektronik2000.de ist es auch möglich, das Board ohne Internet laufen zu lassen eine RTC (RealTimeClock) übernimmt dabei die Uhrzeitgesteuerten Funktionen. Auf dieser Erweiterung ist auch ein EEPROM integriert, um Firmware-Updates über Netzwerk einzuspielen (in Zukunft). Diese Erweiterung bietet außerdem die Anbindung an das E2000-Bus-System, durch das es möglich ist das AVR-NET-IO-Board durch weitere Ein- und Ausgänge zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Das Designen von Schaltaufgaben wird in diesem Programm grafisch dargestellt, durch das ein einfaches Anpassen seiner Logikschaltungen möglich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Steuerung des E2000-NET-IO ist möglich durch den Intregrieten Webserver, die PC-Software (E2000-NET-IO Control) oder der Androidsoftware. All diese Können gleichzeitig auf das E2000-NET-IO zugreifen und Funktionen ausführen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es auf der Entwicklerseite: [http://www.elektronik2000.de Elektronik2000.de]&lt;br /&gt;
&lt;br /&gt;
=== Bascom Version von Hütti ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://bascom-forum.de/index.php/topic,1781.45.html )&lt;br /&gt;
dort am Ende der Seite.&lt;br /&gt;
&lt;br /&gt;
=== Ben&#039;s Bascom Quellcode ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://members.home.nl/bzijlstra/software/examples/enc28j60.htm )&lt;br /&gt;
&lt;br /&gt;
Muss aber für Bascom 1.11.9.3 angepasst werden, siehe Code von Hütti !&lt;br /&gt;
&lt;br /&gt;
=== U. Radigs Webserver ===&lt;br /&gt;
&lt;br /&gt;
Angepasster Sourcecode von U.Radig: http://www.mikrocontroller.net/attachment/40027/Webserver_MEGA32.hex&lt;br /&gt;
oder selbst anpassen: &lt;br /&gt;
Ändere in der Datei ENC28J60.H:&lt;br /&gt;
 #define ENC28J60_PIN_CS    4&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#988386)&lt;br /&gt;
&lt;br /&gt;
Temporären Dateien (*.d, *,lst,*.o) vorher im Verzeichnis löschen &#039;&#039;make clean&#039;&#039;, damit neu compiliert wird.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.0.99&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: uli1&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den orginal SourceCode gibt&#039;s übrigens hier:http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex&lt;br /&gt;
&lt;br /&gt;
Bei den Fuses BOOTRST ausschalten, da die Software keinen Bootloader enthält.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.1.90&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: tim&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://beitz-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://pieper-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterentwicklung des Radig-Codes von RoBue: &amp;lt;br&amp;gt;&lt;br /&gt;
- 1-Wire-Unterstützung (Anschlus an PORTA7) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA0-3 digitaler Eingang (ein/aus) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA4-6 analoger Eingang (0 - 1023) &amp;lt;br&amp;gt;&lt;br /&gt;
- LCD an PORTC &amp;lt;br&amp;gt;&lt;br /&gt;
- Schalten in Abhängigkeit von Temperatur und analogem Wert &amp;lt;br&amp;gt;&lt;br /&gt;
- (Teilweise) Administration über Weboberfläche &amp;lt;br&amp;gt;&lt;br /&gt;
- Erweiterung des cmd-Befehlsatzes für telnet/rs232 &amp;lt;br&amp;gt;&lt;br /&gt;
Gedacht ist der Einsatz des AVR-NET-IO-Bausatzes für Heizungs- oder Haussteuerung) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test: http://avrboard.eluhost.de/&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Quelle:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/43307/AVR-NET-IO_RoBue_V1.3.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/44569/AVR-NET-IO_RoBue_V1.4.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/46720/AVR-NET-IO_RoBue_1.5-final_hoffentlich_.zip)&lt;br /&gt;
&lt;br /&gt;
Bei der Ver 1.5 sind die Ports PD2+3 fürs 4bit LCD (Ext.) vertauscht ! Gruß B.P&lt;br /&gt;
&lt;br /&gt;
=== Simon Ks Webserver (uip-Stack) ===&lt;br /&gt;
Angepasster Sourcecode von Simon K: http://www.mikrocontroller.net/attachment/39939/uWebSrv.zip&lt;br /&gt;
IP: 192.168.0.93:8080&amp;lt;br&amp;gt;&lt;br /&gt;
Um diesen Code mit einem Atmega1284P verwenden zu können, muss in der main.c in Zeile 38, &amp;quot;TIMSK&amp;quot; durch &amp;quot;TIMSK1&amp;quot; ersetzt werden.&lt;br /&gt;
Die Fusebits für den Atmega1284p ohne Bootloader sind:&lt;br /&gt;
lfuse=0xFF, hfuse=0xD9, efuse=0xFF&lt;br /&gt;
&lt;br /&gt;
=== Ethersex Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.ethersex.de - Einfach für atmega32 compilieren und funktioniert.&lt;br /&gt;
&lt;br /&gt;
=== Etherrape Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.lochraster.org/etherrape/ &lt;br /&gt;
&lt;br /&gt;
ist in jedem Fall hier auch zu erwähnen zumal es sich beim etherrape um das Ursprungsprojekt von ethersex handelt.&lt;br /&gt;
Es scheint aber bei der Weiterentwicklung wenig zu passieren.&lt;br /&gt;
Ausführliche Dokumentation findet sich unter http://wiki.lochraster.org/wiki/Etherrape&lt;br /&gt;
&lt;br /&gt;
=== Mini SRCP Server (kommerziell, Closed-Source)===&lt;br /&gt;
&lt;br /&gt;
Damit wird die Platine zu einer Modellbahnsteuerung, die&lt;br /&gt;
über das Netzwerkprotokoll SRCP mit verschiedenen Programmen&lt;br /&gt;
gesteuert werden kann.&lt;br /&gt;
&lt;br /&gt;
[http://www.7soft.de/de/mini_srcp_server/index.html Infoseite] zur Hardware&lt;br /&gt;
und das zugrundeliegende [http://www.der-moba.de/index.php/Digitalprojekt Digitalprojekt].&lt;br /&gt;
&lt;br /&gt;
=== Avr ArtNET-Node ===&lt;br /&gt;
&lt;br /&gt;
Hiermit kann die Platine zu einem Art-Net Node werden, mit dem sich ein DMX-Universe über Ethernet übertragen lässt. Basiert auf den Quellen von Ulrich Radig.&lt;br /&gt;
&lt;br /&gt;
Dokumentation: [http://www.dmxcontrol.de/wiki/Art-Net-Node_f%C3%BCr_25_Euro Art-Net-Node für 25 Euro]&lt;br /&gt;
&lt;br /&gt;
=== Webserver von G. Menke ===&lt;br /&gt;
&lt;br /&gt;
Ein Webserver (basierend auf den Sourcen von U. Radig), der so angepasst ist, dass alle Ein- und Ausgänge wie bei der originalen Pollin-Software genutzt werden können (8xDIGOUT, 4xDIGIN, 4xADIN). Der Webserver kann daher direkt auf das Net-IO geladen werden. Im ZIP-File sind ein ReadMe und alle C-Sourcen enthalten.&amp;lt;br&amp;gt;&amp;lt;i&amp;gt;Download&amp;lt;/i&amp;gt;:&lt;br /&gt;
[http://gm.stream-center.de/webserver/ Webserver mit passender IO]&lt;br /&gt;
&lt;br /&gt;
=== OpenMCP ===&lt;br /&gt;
&lt;br /&gt;
Tolles Projekt, welches viele Features bietet und stabil läuft. Hervorzuheben ist die Übersichtlichkeit der Programmteile/Module und die vielleicht nicht ganz komplette Dokumentation. Man merkt, dass viel Arbeit und Liebe in diesem Projekt steckt. Herausgekommen ist dabei eine einfach zu handhabende Entwicklungsumgebung. Anfänger können, dank des gut durchdachten CGI-Systems, welches sich um alle wichtigen Sachen kümmert, leicht eigene CGI implementieren. Alle Ausgaben erfolgen nur mit printf über die Standardausgabe und werden automatisch richtig per Netzwerk übertragen, dadurch ist es auch für den Anfänger recht gut geeignet, da man sich nicht mit der Netzwerkprogrammierung auseinander setzen muss.&lt;br /&gt;
&lt;br /&gt;
Die Software belegt im Moment (Stand Juli 2010) ca. 55 Kb im Flash, so dass man das Board mit einem grösseren µC (z.B. ATMega644) aufrüsten muss.&lt;br /&gt;
&lt;br /&gt;
[http://wiki.neo-guerillaz.de Projekt und Doku]&lt;br /&gt;
&lt;br /&gt;
Der Autor stellt zwei über das Internet erreichbare Testboards bereit unter http://www.neo-guerillaz.de:81 und http://www.neo-guerillaz.de:82 die beide unter OpenMCP laufen, je auf einen AVR-NETIO mit einem ATmega644 und dem eigentlichen Board mit einem ATmega2561. Zusätzlich ist gerade eine Version für das myAVR in Arbeit die schon ordentlich Fortschritte macht.&lt;br /&gt;
&lt;br /&gt;
=== OpenMLP ===&lt;br /&gt;
Auf openMCP basierender Port nach [http://avr.myluna.de LunaAVR] (GPL). Umfangreiche Funktionalität und direkte Anpassung an die Sprache. Abgespeckte Version auch auf Atmega32 lauffähig. Die Original-Dokumentation kann zum Großteil hergenommen werden. Einige Zusatzfeatures. Leichte Konfiguration und guter Einstieg für Anfänger und zum Verständnis der Serverfunktionalität.&lt;br /&gt;
&lt;br /&gt;
[http://avr.myluna.de/doku.php?id=de:openmlp Artikelseite]&lt;br /&gt;
[http://forum.myluna.de/viewtopic.php?f=8&amp;amp;t=13 Forum]&lt;br /&gt;
&lt;br /&gt;
=== ENC28J60 I/O-Webserver von Thomas Heldt ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul-Webserver (Softwarekompatibel zum Pollin Webserver), der durch div. Module erweitert werden kann, Software in Bascom basierend auf dem Code von Ben Zijlsta wurde erweitert und angepasst:&lt;br /&gt;
&lt;br /&gt;
[http://mikrocontroller.heldt.eu/index.php?page=enc28j60-io-webserver Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
=== AVR-Netino ===&lt;br /&gt;
[http://code.google.com/p/avr-netino/ Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
Arduino fürs Net-IO&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/topic/109988&lt;br /&gt;
* [http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en022889 ENC28J60 Produktseite]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/DeviceDoc/39662c.pdf ENC28J60 Datenblatt(pdf)]&lt;br /&gt;
* [http://son.ffdf-clan.de Forum für AVR-Net-IO]&lt;br /&gt;
* [http://avr.myluna.de/doku.php?id=de:openmlp LunaAVR openMLP ]&lt;br /&gt;
* [http://bascom-forum.de/index.php/topic,1781.0.html Bascom Forum ]&lt;br /&gt;
* [http://hobbyelektronik.org/w/index.php/AVR-NET-IO-Shield Shield für den NET-IO]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Category:Ethernet|P]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=78539</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=78539"/>
		<updated>2013-09-17T19:22:15Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstanten==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf beim WinAVR mit AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File, wie geht das? =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
Allerdings kann volatile nur bei den Variablen sinnvoll genutzt werden, die &amp;quot;von außen&amp;quot; auch änderbar sind. Bei lokalen Variablen, auch statischen, einer Funktion kann das nur passieren, wenn ihre Adresse einer ISR z.B. durch einen globalen Pointer bekannt gemacht wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t *v;&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
  *v = 42;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i; // kann sich nie unerwartet ändern -&amp;gt; volatile nutzlos, behindert nur Optimizer&lt;br /&gt;
&lt;br /&gt;
  volatile uint8_t j; // kann sich unerwartet ändern (über globalen *v)&lt;br /&gt;
  v = &amp;amp;j;&lt;br /&gt;
  ...&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
    if( j == 5 )&lt;br /&gt;
      i = 3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78458</id>
		<title>AVR-Tutorial: SRAM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78458"/>
		<updated>2013-09-09T07:56:50Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==SRAM - Der Speicher des Controllers==&lt;br /&gt;
&lt;br /&gt;
Nachdem in einem der vorangegangenen Kapitel eine [[AVR-Tutorial:_PWM|Software-PWM]] vorgestellt und in einem weiteren Kapitel darüber gesprochen wurde, wie man mit [[AVR-Tutorial:_Schieberegister|Schieberegistern]] die Anzahl an I/O-Pins erhöhen kann, wäre es naheliegend, beides zu kombinieren und den ATmega8 mal 20 oder 30 LEDs ansteuern zu lassen. Wenn es da nicht ein Problem gäbe: die Software-PWM hält ihre Daten in Registern, so wie das praktisch alle Programme bisher machten. Während allerdings 6 PWM-Kanäle noch problemlos in den Registern untergebracht werden konnten, ist dies mit 30 oder noch mehr PWM-Kanälen nicht mehr möglich. Es gibt schlicht und ergreifend nicht genug Register.&lt;br /&gt;
&lt;br /&gt;
Es gibt aber einen Ausweg. Der ATmega8 verfügt über 1kByte &#039;&#039;&#039;SRAM&#039;&#039;&#039; (statisches RAM). Dieses RAM wurde bereits indirekt durch den &#039;&#039;&#039;Stack&#039;&#039;&#039; benutzt. Bei jedem Aufruf eines Unterprogrammes, sei es über einen expliziten &#039;&#039;&#039;CALL&#039;&#039;&#039; (bzw. &#039;&#039;&#039;RCALL&#039;&#039;&#039;) oder einen Interrupt, wird die Rücksprungadresse irgendwo gespeichert. Dies geschieht genau in diesem SRAM. Auch &#039;&#039;&#039;PUSH&#039;&#039;&#039; und &#039;&#039;&#039;POP&#039;&#039;&#039; operieren in diesem Speicher.&lt;br /&gt;
&lt;br /&gt;
Ein Programm darf Speicherzellen im &#039;&#039;&#039;SRAM&#039;&#039;&#039; direkt benutzen und dort Werte speichern bzw. von dort Werte einlesen. Es muss nur darauf geachtet werden, dass es zu keiner Kollision mit dem Stack kommt, in dem z.&amp;amp;nbsp;B. die erwähnten Rücksprungadressen für Unterprogramme gespeichert werden. Da viele Programme aber lediglich ein paar Byte &#039;&#039;&#039;SRAM&#039;&#039;&#039; brauchen, der Rücksprungstack von der oberen Grenze des &#039;&#039;&#039;SRAM&#039;&#039;&#039; nach unten wächst und der ATmega8 immerhin über &#039;&#039;&#039;1kByte&#039;&#039;&#039; &#039;&#039;&#039;SRAM&#039;&#039;&#039; verfügt, ist dies in der Praxis kein all zu großes Problem.&lt;br /&gt;
&lt;br /&gt;
==Das .DSEG und .BYTE==&lt;br /&gt;
Um dem Assembler mitzuteilen, dass sich der folgende Abschnitt auf das SRAM bezieht, gibt es die Direktive &#039;&#039;&#039;.DSEG&#039;&#039;&#039; (Data Segment). Alle nach einer &#039;&#039;&#039;.DSEG&#039;&#039;&#039; Direktive folgenden Speicherreservierungen werden vom Assembler im SRAM durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Die Direktive &#039;&#039;&#039;.BYTE&#039;&#039;&#039; stellt dabei eine derartige Speicherreservierung dar. Es ermöglicht, der Speicherreservierung einen Namen zu geben und es erlaubt auch, nicht nur 1 Byte sondern eine ganze Reihe von Bytes unter einem Namen zu reservieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
           .DSEG                ; Umschalten auf das SRAM Datensegment&lt;br /&gt;
Counter:   .BYTE  1             ; 1 Byte unter dem Namen &#039;Counter&#039; reservieren&lt;br /&gt;
Test:      .BYTE  20            ; 20 Byte unter dem Namen &#039;Test&#039; reservieren&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==spezielle Befehle==&lt;br /&gt;
Für den Zugriff auf den &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicher gibt es spezielle Befehle. Diese holen entweder den momentanen Inhalt einer Speicherzelle und legen ihn in einem Register ab oder legen den Inhalt eines Registers in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
===LDS===&lt;br /&gt;
Liest die angegebene &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle und legt den gelesenen Wert in einem Register ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDS  r17, Counter      ; liest die Speicherzelle mit dem Namen &#039;Counter&#039;&lt;br /&gt;
                               ; und legt den gelesenen Wert im Register r17 ab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===STS===&lt;br /&gt;
Legt den in einem Register gespeicherten Wert in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STS  Counter, r17      ; Speichert den Inhalt von r17 in der&lt;br /&gt;
                               ; Speicherzelle &#039;Counter&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Implementierung der [[AVR-Tutorial:_PWM|Software-PWM]], die den PWM-Zähler sowie die einzelnen OCR-Grenzwerte im &#039;&#039;&#039;SRAM&#039;&#039;&#039; anstelle von Registern speichert, könnte z.&amp;amp;nbsp;B. so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        sts     OCR_1, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        sts     OCR_2, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        sts     OCR_3, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        sts     OCR_4, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        sts     OCR_5, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        sts     OCR_6, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                   ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp               ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount    ; den PWM-Zaehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1              ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128         ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM            ; Nein&lt;br /&gt;
        clr     temp1              ; Ja: PWM-Zaehler wieder auf 0&lt;br /&gt;
 &lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1    ; den PWM-Zaehler wieder speichern&lt;br /&gt;
        ldi     temp, 0b11000000   ; 0 .. LED an, 1 .. LED aus&lt;br /&gt;
 &lt;br /&gt;
        lds     temp2, OCR_1&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 1 erreicht&lt;br /&gt;
        brlt    OneOn&lt;br /&gt;
        ori     temp, $01&lt;br /&gt;
 &lt;br /&gt;
OneOn:  lds     temp2, OCR_2&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 2 erreicht&lt;br /&gt;
        brlt    TwoOn&lt;br /&gt;
        ori     temp, $02&lt;br /&gt;
 &lt;br /&gt;
TwoOn:  lds     temp2, OCR_3&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 3 erreicht&lt;br /&gt;
        brlt    ThreeOn&lt;br /&gt;
        ori     temp, $04&lt;br /&gt;
 &lt;br /&gt;
ThreeOn:lds     temp2, OCR_4&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 4 erreicht&lt;br /&gt;
        brlt    FourOn&lt;br /&gt;
        ori     temp, $08&lt;br /&gt;
 &lt;br /&gt;
FourOn: lds     temp2, OCR_5&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 5 erreicht&lt;br /&gt;
        brlt    FiveOn&lt;br /&gt;
        ori     temp, $10&lt;br /&gt;
 &lt;br /&gt;
FiveOn: lds     temp2, OCR_6&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 6 erreicht&lt;br /&gt;
        brlt    SetBits&lt;br /&gt;
        ori     temp, $20&lt;br /&gt;
 &lt;br /&gt;
SetBits:                           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        out     PORTB, temp&lt;br /&gt;
&lt;br /&gt;
        pop     temp               ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
 &lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; **********************************************&lt;br /&gt;
; &lt;br /&gt;
          .DSEG                       ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE   1                   ; Der PWM-Counter (0 bis 127)&lt;br /&gt;
OCR_1:    .BYTE   1                   ; 6 Bytes für die OCR-Register&lt;br /&gt;
OCR_2:    .BYTE   1&lt;br /&gt;
OCR_3:    .BYTE   1&lt;br /&gt;
OCR_4:    .BYTE   1&lt;br /&gt;
OCR_5:    .BYTE   1&lt;br /&gt;
OCR_6:    .BYTE   1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ISR sichert alle verwendeten Register und stellt sie am Ende der ISR wieder her. Dies ist zwar streng genommen in diesem Beispiel nicht notwendig, da das eigentliche Programm in der Hauptschleife ja nichts tut, aber ausser ein bischen Zeit kostet das nichts und es erspart das Kopfkratzen, wenn dann irgendwann in der Hauptschleife Ergänzungen und anderer Code dazu kommen. Man kann natürlich einige Register speziell für die Verwendung ausschliesslich in der ISR reservieren und sich so das Sichern/Wiederherstellen ersparen, aber das SREG muss im Normalfall innerhalb einer ISR auf jeden Fall gesichert und wiederhergestellt werden.&lt;br /&gt;
&lt;br /&gt;
==Spezielle Register==&lt;br /&gt;
===Der Z-Pointer (R30 und R31)===&lt;br /&gt;
Das Registerpärchen &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039; kann zu einem einzigen logischen Register zusammengefasst werden und heisst dann &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;. Diesem kann eine spezielle Aufgabe zukommen, indem er als Adressangabe fungieren kann, von welcher Speicherzelle im SRAM ein Ladevorgang (bzw. Speichervorgang) durchgeführt werden soll. Anstatt die Speicheradresse wie beim &#039;&#039;&#039;LDS&#039;&#039;&#039; bzw. &#039;&#039;&#039;STS&#039;&#039;&#039; direkt im Programmcode anzugeben, kann diese Speicheradresse zunächst in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; geladen werden und der Lesevorgang (Schreibvorgang) über diesen &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; abgewickelt werden. Dadurch wird aber die &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicheradresse berechenbar, denn natürlich kann mit den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;, wie mit den anderen Registern auch, Arithmetik betrieben werden. Besonders komfortabel ist dies, da im Ladebefehl noch zusätzliche Manipulationen angegeben werden können, die oft benötigte arithmetische Operationen implementieren.&lt;br /&gt;
&lt;br /&gt;
===LD===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z+&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, -Z&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lädt das Register &#039;&#039;&#039;rxx&#039;&#039;&#039; mit dem Inhalt der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===LDD===&lt;br /&gt;
* &#039;&#039;&#039;LDD rxx, Z+q&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Ladeanweisung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDD r18, Z + $28&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt der Speicherzellen $1000 + $28 = $1028 in das Register r18 geladen.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===ST===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ST Z, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST Z+, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST -Z, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Speichert den Inhalt des Register &#039;&#039;&#039;rxx&#039;&#039;&#039; in der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===STD===&lt;br /&gt;
* &#039;&#039;&#039;STD Z+q, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Speicheranweisung&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STD Z + $28, r18&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt des Registers r18 in der Speicherzellen $1000 + $28 = $1028 gespeichert.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Durch Verwendung des &#039;&#039;&#039;Z-Pointers&#039;&#039;&#039; ist es möglich die Interrupt Funktion wesentlich kürzer und vor allem ohne ständige Wiederholung von im Prinzip immer gleichem Code zu formulieren. Man stelle sich nur mal vor wie dieser Code aussehen würde, wenn anstelle von 6 PWM Stufen, deren 40 gebraucht würden. Mit dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; ist es möglich diesen auf das erste der OCR Bytes zu setzen und dann in einer Schleife eines nach dem anderen abzuarbeiten. Nach dem Laden des jeweiligen OCR Wertes, wird der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; automatisch durch den &#039;&#039;&#039;LD&#039;&#039;&#039;-Befehl auf das nächste zu verarbeitende OCR Byte weitergezählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
.def temp3 = r19&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0              ; den PWM Counter auf 0 setzen&lt;br /&gt;
        sts     PWMCount, temp2&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp                  ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        push    R30&lt;br /&gt;
        push    R31&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount       ; den PWM ZAehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1                 ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128            ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM               ; Nein&lt;br /&gt;
        clr     temp1                 ; Ja: PWM Zaehler auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1       ; den PWM Zaehler wieder speichern&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
        ldi     temp3, $01            ; das Bitmuster für PWM Nr. i&lt;br /&gt;
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus&lt;br /&gt;
&lt;br /&gt;
pwmloop:&lt;br /&gt;
        ld      temp2, Z+             ; den OCR Wert für PWM Nr. i holen und Z-Pointer erhöhen&lt;br /&gt;
        cp      temp1, temp2          ; ist der Grenzwert für PWM Nr. i erreicht?&lt;br /&gt;
        brlo    LedOn&lt;br /&gt;
        or      temp, temp3&lt;br /&gt;
LedOn:&lt;br /&gt;
        lsl     temp3                 ; das Bitmuster schieben&lt;br /&gt;
        cpi     temp3, $40            ; alle Bits behandelt ?&lt;br /&gt;
        brne    pwmloop               ; nächster Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
        out     PORTB, temp           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
&lt;br /&gt;
        pop     temp                  ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     R31&lt;br /&gt;
        pop     R30&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
&lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; *********************************************************************&lt;br /&gt;
;&lt;br /&gt;
        .DSEG                         ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE  1                    ; der PWM Zaehler (0 bis 127)&lt;br /&gt;
OCR:      .BYTE  6                    ; 6 Bytes für die OCR Register&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===X-Pointer, Y-Pointer===&lt;br /&gt;
Neben dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gibt es noch den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;. Sie werden gebildet von den Registerpärchen&lt;br /&gt;
* &#039;&#039;&#039;X-Pointer&#039;&#039;&#039;: r26, r27&lt;br /&gt;
* &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;: r28, r29&lt;br /&gt;
* &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;: r30, r31&lt;br /&gt;
Alles über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gesagte gilt sinngemäß auch für den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039; mit einer Ausnahme: Mit dem &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; ist kein Zugriff &#039;&#039;&#039;LDD&#039;&#039;&#039;/&#039;&#039;&#039;STD&#039;&#039;&#039; mit einem Displacement möglich.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Schieberegister|&lt;br /&gt;
zurücklink=AVR-Tutorial: Schieberegister|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=7-Segment-Anzeige|&lt;br /&gt;
vorlink=AVR-Tutorial: 7-Segment-Anzeige}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|SRAM]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78457</id>
		<title>AVR-Tutorial: SRAM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78457"/>
		<updated>2013-09-09T07:53:34Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==SRAM - Der Speicher des Controllers==&lt;br /&gt;
&lt;br /&gt;
Nachdem in einem der vorangegangenen Kapitel eine [[AVR-Tutorial:_PWM|Software-PWM]] vorgestellt und in einem weiteren Kapitel darüber gesprochen wurde, wie man mit [[AVR-Tutorial:_Schieberegister|Schieberegistern]] die Anzahl an I/O-Pins erhöhen kann, wäre es naheliegend, beides zu kombinieren und den ATmega8 mal 20 oder 30 LEDs ansteuern zu lassen. Wenn es da nicht ein Problem gäbe: die Software-PWM hält ihre Daten in Registern, so wie das praktisch alle Programme bisher machten. Während allerdings 6 PWM-Kanäle noch problemlos in den Registern untergebracht werden konnten, ist dies mit 30 oder noch mehr PWM-Kanälen nicht mehr möglich. Es gibt schlicht und ergreifend nicht genug Register.&lt;br /&gt;
&lt;br /&gt;
Es gibt aber einen Ausweg. Der ATmega8 verfügt über 1kByte &#039;&#039;&#039;SRAM&#039;&#039;&#039; (statisches RAM). Dieses RAM wurde bereits indirekt durch den &#039;&#039;&#039;Stack&#039;&#039;&#039; benutzt. Bei jedem Aufruf eines Unterprogrammes, sei es über einen expliziten &#039;&#039;&#039;CALL&#039;&#039;&#039; (bzw. &#039;&#039;&#039;RCALL&#039;&#039;&#039;) oder einen Interrupt, wird die Rücksprungadresse irgendwo gespeichert. Dies geschieht genau in diesem SRAM. Auch &#039;&#039;&#039;PUSH&#039;&#039;&#039; und &#039;&#039;&#039;POP&#039;&#039;&#039; operieren in diesem Speicher.&lt;br /&gt;
&lt;br /&gt;
Ein Programm darf Speicherzellen im &#039;&#039;&#039;SRAM&#039;&#039;&#039; direkt benutzen und dort Werte speichern bzw. von dort Werte einlesen. Es muss nur darauf geachtet werden, dass es zu keiner Kollision mit dem Stack kommt, in dem z.&amp;amp;nbsp;B. die erwähnten Rücksprungadressen für Unterprogramme gespeichert werden. Da viele Programme aber lediglich ein paar Byte &#039;&#039;&#039;SRAM&#039;&#039;&#039; brauchen, der Rücksprungstack von der oberen Grenze des &#039;&#039;&#039;SRAM&#039;&#039;&#039; nach unten wächst und der ATmega8 immerhin über &#039;&#039;&#039;1kByte&#039;&#039;&#039; &#039;&#039;&#039;SRAM&#039;&#039;&#039; verfügt, ist dies in der Praxis kein all zu großes Problem.&lt;br /&gt;
&lt;br /&gt;
==Das .DSEG und .BYTE==&lt;br /&gt;
Um dem Assembler mitzuteilen, dass sich der folgende Abschnitt auf das SRAM bezieht, gibt es die Direktive &#039;&#039;&#039;.DSEG&#039;&#039;&#039; (Data Segment). Alle nach einer &#039;&#039;&#039;.DSEG&#039;&#039;&#039; Direktive folgenden Speicherreservierungen werden vom Assembler im SRAM durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Die Direktive &#039;&#039;&#039;.BYTE&#039;&#039;&#039; stellt dabei eine derartige Speicherreservierung dar. Es ermöglicht, der Speicherreservierung einen Namen zu geben und es erlaubt auch, nicht nur 1 Byte sondern eine ganze Reihe von Bytes unter einem Namen zu reservieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
           .DSEG                ; Umschalten auf das SRAM Datensegment&lt;br /&gt;
Counter:   .BYTE  1             ; 1 Byte unter dem Namen &#039;Counter&#039; reservieren&lt;br /&gt;
Test:      .BYTE  20            ; 20 Byte unter dem Namen &#039;Test&#039; reservieren&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==spezielle Befehle==&lt;br /&gt;
Für den Zugriff auf den &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicher gibt es spezielle Befehle. Diese holen entweder den momentanen Inhalt einer Speicherzelle und legen ihn in einem Register ab oder legen den Inhalt eines Registers in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
===LDS===&lt;br /&gt;
Liest die angegebene &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle und legt den gelesenen Wert in einem Register ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDS  r17, Counter      ; liest die Speicherzelle mit dem Namen &#039;Counter&#039;&lt;br /&gt;
                               ; und legt den gelesenen Wert im Register r17 ab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===STS===&lt;br /&gt;
Legt den in einem Register gespeicherten Wert in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STS  Counter, r17      ; Speichert den Inhalt von r17 in der&lt;br /&gt;
                               ; Speicherzelle &#039;Counter&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Implementierung der [[AVR-Tutorial:_PWM|Software-PWM]], die den PWM-Zähler sowie die einzelnen OCR-Grenzwerte im &#039;&#039;&#039;SRAM&#039;&#039;&#039; anstelle von Registern speichert, könnte z.&amp;amp;nbsp;B. so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        sts     OCR_1, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        sts     OCR_2, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        sts     OCR_3, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        sts     OCR_4, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        sts     OCR_5, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        sts     OCR_6, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                   ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp               ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount    ; den PWM-Zaehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1              ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128         ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM            ; Nein&lt;br /&gt;
        clr     temp1              ; Ja: PWM-Zaehler wieder auf 0&lt;br /&gt;
 &lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1    ; den PWM-Zaehler wieder speichern&lt;br /&gt;
        ldi     temp, 0b11000000   ; 0 .. LED an, 1 .. LED aus&lt;br /&gt;
 &lt;br /&gt;
        lds     temp2, OCR_1&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 1 erreicht&lt;br /&gt;
        brlt    OneOn&lt;br /&gt;
        ori     temp, $01&lt;br /&gt;
 &lt;br /&gt;
OneOn:  lds     temp2, OCR_2&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 2 erreicht&lt;br /&gt;
        brlt    TwoOn&lt;br /&gt;
        ori     temp, $02&lt;br /&gt;
 &lt;br /&gt;
TwoOn:  lds     temp2, OCR_3&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 3 erreicht&lt;br /&gt;
        brlt    ThreeOn&lt;br /&gt;
        ori     temp, $04&lt;br /&gt;
 &lt;br /&gt;
ThreeOn:lds     temp2, OCR_4&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 4 erreicht&lt;br /&gt;
        brlt    FourOn&lt;br /&gt;
        ori     temp, $08&lt;br /&gt;
 &lt;br /&gt;
FourOn: lds     temp2, OCR_5&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 5 erreicht&lt;br /&gt;
        brlt    FiveOn&lt;br /&gt;
        ori     temp, $10&lt;br /&gt;
 &lt;br /&gt;
FiveOn: lds     temp2, OCR_6&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 6 erreicht&lt;br /&gt;
        brlt    SetBits&lt;br /&gt;
        ori     temp, $20&lt;br /&gt;
 &lt;br /&gt;
SetBits:                           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        out     PORTB, temp&lt;br /&gt;
&lt;br /&gt;
        pop     temp               ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
 &lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; **********************************************&lt;br /&gt;
; &lt;br /&gt;
          .DSEG                       ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE   1                   ; Der PWM-Counter (0 bis 127)&lt;br /&gt;
OCR_1:    .BYTE   1                   ; 6 Bytes für die OCR-Register&lt;br /&gt;
OCR_2:    .BYTE   1&lt;br /&gt;
OCR_3:    .BYTE   1&lt;br /&gt;
OCR_4:    .BYTE   1&lt;br /&gt;
OCR_5:    .BYTE   1&lt;br /&gt;
OCR_6:    .BYTE   1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Spezielle Register==&lt;br /&gt;
===Der Z-Pointer (R30 und R31)===&lt;br /&gt;
Das Registerpärchen &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039; kann zu einem einzigen logischen Register zusammengefasst werden und heisst dann &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;. Diesem kann eine spezielle Aufgabe zukommen, indem er als Adressangabe fungieren kann, von welcher Speicherzelle im SRAM ein Ladevorgang (bzw. Speichervorgang) durchgeführt werden soll. Anstatt die Speicheradresse wie beim &#039;&#039;&#039;LDS&#039;&#039;&#039; bzw. &#039;&#039;&#039;STS&#039;&#039;&#039; direkt im Programmcode anzugeben, kann diese Speicheradresse zunächst in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; geladen werden und der Lesevorgang (Schreibvorgang) über diesen &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; abgewickelt werden. Dadurch wird aber die &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicheradresse berechenbar, denn natürlich kann mit den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;, wie mit den anderen Registern auch, Arithmetik betrieben werden. Besonders komfortabel ist dies, da im Ladebefehl noch zusätzliche Manipulationen angegeben werden können, die oft benötigte arithmetische Operationen implementieren.&lt;br /&gt;
&lt;br /&gt;
===LD===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z+&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, -Z&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lädt das Register &#039;&#039;&#039;rxx&#039;&#039;&#039; mit dem Inhalt der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===LDD===&lt;br /&gt;
* &#039;&#039;&#039;LDD rxx, Z+q&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Ladeanweisung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDD r18, Z + $28&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt der Speicherzellen $1000 + $28 = $1028 in das Register r18 geladen.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===ST===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ST Z, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST Z+, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST -Z, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Speichert den Inhalt des Register &#039;&#039;&#039;rxx&#039;&#039;&#039; in der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===STD===&lt;br /&gt;
* &#039;&#039;&#039;STD Z+q, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Speicheranweisung&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STD Z + $28, r18&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt des Registers r18 in der Speicherzellen $1000 + $28 = $1028 gespeichert.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Durch Verwendung des &#039;&#039;&#039;Z-Pointers&#039;&#039;&#039; ist es möglich die Interrupt Funktion wesentlich kürzer und vor allem ohne ständige Wiederholung von im Prinzip immer gleichem Code zu formulieren. Man stelle sich nur mal vor wie dieser Code aussehen würde, wenn anstelle von 6 PWM Stufen, deren 40 gebraucht würden. Mit dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; ist es möglich diesen auf das erste der OCR Bytes zu setzen und dann in einer Schleife eines nach dem anderen abzuarbeiten. Nach dem Laden des jeweiligen OCR Wertes, wird der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; automatisch durch den &#039;&#039;&#039;LD&#039;&#039;&#039;-Befehl auf das nächste zu verarbeitende OCR Byte weitergezählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
.def temp3 = r19&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0              ; den PWM Counter auf 0 setzen&lt;br /&gt;
        sts     PWMCount, temp2&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp                  ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        push    R30&lt;br /&gt;
        push    R31&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount       ; den PWM ZAehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1                 ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128            ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM               ; Nein&lt;br /&gt;
        clr     temp1                 ; Ja: PWM Zaehler auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1       ; den PWM Zaehler wieder speichern&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
        ldi     temp3, $01            ; das Bitmuster für PWM Nr. i&lt;br /&gt;
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus&lt;br /&gt;
&lt;br /&gt;
pwmloop:&lt;br /&gt;
        ld      temp2, Z+             ; den OCR Wert für PWM Nr. i holen und Z-Pointer erhöhen&lt;br /&gt;
        cp      temp1, temp2          ; ist der Grenzwert für PWM Nr. i erreicht?&lt;br /&gt;
        brlo    LedOn&lt;br /&gt;
        or      temp, temp3&lt;br /&gt;
LedOn:&lt;br /&gt;
        lsl     temp3                 ; das Bitmuster schieben&lt;br /&gt;
        cpi     temp3, $40            ; alle Bits behandelt ?&lt;br /&gt;
        brne    pwmloop               ; nächster Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
        out     PORTB, temp           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
&lt;br /&gt;
        pop     temp                  ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     R31&lt;br /&gt;
        pop     R30&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
&lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; *********************************************************************&lt;br /&gt;
;&lt;br /&gt;
        .DSEG                         ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE  1                    ; der PWM Zaehler (0 bis 127)&lt;br /&gt;
OCR:      .BYTE  6                    ; 6 Bytes für die OCR Register&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===X-Pointer, Y-Pointer===&lt;br /&gt;
Neben dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gibt es noch den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;. Sie werden gebildet von den Registerpärchen&lt;br /&gt;
* &#039;&#039;&#039;X-Pointer&#039;&#039;&#039;: r26, r27&lt;br /&gt;
* &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;: r28, r29&lt;br /&gt;
* &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;: r30, r31&lt;br /&gt;
Alles über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gesagte gilt sinngemäß auch für den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039; mit einer Ausnahme: Mit dem &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; ist kein Zugriff &#039;&#039;&#039;LDD&#039;&#039;&#039;/&#039;&#039;&#039;STD&#039;&#039;&#039; mit einem Displacement möglich.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Schieberegister|&lt;br /&gt;
zurücklink=AVR-Tutorial: Schieberegister|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=7-Segment-Anzeige|&lt;br /&gt;
vorlink=AVR-Tutorial: 7-Segment-Anzeige}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|SRAM]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78456</id>
		<title>AVR-Tutorial: SRAM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78456"/>
		<updated>2013-09-09T07:51:27Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==SRAM - Der Speicher des Controllers==&lt;br /&gt;
&lt;br /&gt;
Nachdem in einem der vorangegangenen Kapitel eine [[AVR-Tutorial:_PWM|Software-PWM]] vorgestellt und in einem weiteren Kapitel darüber gesprochen wurde, wie man mit [[AVR-Tutorial:_Schieberegister|Schieberegistern]] die Anzahl an I/O-Pins erhöhen kann, wäre es naheliegend, beides zu kombinieren und den ATmega8 mal 20 oder 30 LEDs ansteuern zu lassen. Wenn es da nicht ein Problem gäbe: die Software-PWM hält ihre Daten in Registern, so wie das praktisch alle Programme bisher machten. Während allerdings 6 PWM-Kanäle noch problemlos in den Registern untergebracht werden konnten, ist dies mit 30 oder noch mehr PWM-Kanälen nicht mehr möglich. Es gibt schlicht und ergreifend nicht genug Register.&lt;br /&gt;
&lt;br /&gt;
Es gibt aber einen Ausweg. Der ATmega8 verfügt über 1kByte &#039;&#039;&#039;SRAM&#039;&#039;&#039; (statisches RAM). Dieses RAM wurde bereits indirekt durch den &#039;&#039;&#039;Stack&#039;&#039;&#039; benutzt. Bei jedem Aufruf eines Unterprogrammes, sei es über einen expliziten &#039;&#039;&#039;CALL&#039;&#039;&#039; (bzw. &#039;&#039;&#039;RCALL&#039;&#039;&#039;) oder einen Interrupt, wird die Rücksprungadresse irgendwo gespeichert. Dies geschieht genau in diesem SRAM. Auch &#039;&#039;&#039;PUSH&#039;&#039;&#039; und &#039;&#039;&#039;POP&#039;&#039;&#039; operieren in diesem Speicher.&lt;br /&gt;
&lt;br /&gt;
Ein Programm darf Speicherzellen im &#039;&#039;&#039;SRAM&#039;&#039;&#039; direkt benutzen und dort Werte speichern bzw. von dort Werte einlesen. Es muss nur darauf geachtet werden, dass es zu keiner Kollision mit dem Stack kommt, in dem z.&amp;amp;nbsp;B. die erwähnten Rücksprungadressen für Unterprogramme gespeichert werden. Da viele Programme aber lediglich ein paar Byte &#039;&#039;&#039;SRAM&#039;&#039;&#039; brauchen, der Rücksprungstack von der oberen Grenze des &#039;&#039;&#039;SRAM&#039;&#039;&#039; nach unten wächst und der ATmega8 immerhin über &#039;&#039;&#039;1kByte&#039;&#039;&#039; &#039;&#039;&#039;SRAM&#039;&#039;&#039; verfügt, ist dies in der Praxis kein all zu großes Problem.&lt;br /&gt;
&lt;br /&gt;
==Das .DSEG und .BYTE==&lt;br /&gt;
Um dem Assembler mitzuteilen, dass sich der folgende Abschnitt auf das SRAM bezieht, gibt es die Direktive &#039;&#039;&#039;.DSEG&#039;&#039;&#039; (Data Segment). Alle nach einer &#039;&#039;&#039;.DSEG&#039;&#039;&#039; Direktive folgenden Speicherreservierungen werden vom Assembler im SRAM durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Die Direktive &#039;&#039;&#039;.BYTE&#039;&#039;&#039; stellt dabei eine derartige Speicherreservierung dar. Es ermöglicht, der Speicherreservierung einen Namen zu geben und es erlaubt auch, nicht nur 1 Byte sondern eine ganze Reihe von Bytes unter einem Namen zu reservieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
           .DSEG                ; Umschalten auf das SRAM Datensegment&lt;br /&gt;
Counter:   .BYTE  1             ; 1 Byte unter dem Namen &#039;Counter&#039; reservieren&lt;br /&gt;
Test:      .BYTE  20            ; 20 Byte unter dem Namen &#039;Test&#039; reservieren&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==spezielle Befehle==&lt;br /&gt;
Für den Zugriff auf den &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicher gibt es spezielle Befehle. Diese holen entweder den momentanen Inhalt einer Speicherzelle und legen ihn in einem Register ab oder legen den Inhalt eines Registers in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
===LDS===&lt;br /&gt;
Liest die angegebene &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle und legt den gelesenen Wert in einem Register ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDS  r17, Counter      ; liest die Speicherzelle mit dem Namen &#039;Counter&#039;&lt;br /&gt;
                               ; und legt den gelesenen Wert im Register r17 ab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===STS===&lt;br /&gt;
Legt den in einem Register gespeicherten Wert in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STS  Counter, r17      ; Speichert den Inhalt von r17 in der&lt;br /&gt;
                               ; Speicherzelle &#039;Counter&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Implementierung der [[AVR-Tutorial:_PWM|Software-PWM]], die den PWM-Zähler sowie die einzelnen OCR-Grenzwerte im &#039;&#039;&#039;SRAM&#039;&#039;&#039; anstelle von Registern speichert, könnte z.&amp;amp;nbsp;B. so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        sts     OCR_1, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        sts     OCR_2, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        sts     OCR_3, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        sts     OCR_4, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        sts     OCR_5, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        sts     OCR_6, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                   ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp               ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount    ; den PWM-Zaehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1              ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128         ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM            ; Nein&lt;br /&gt;
        clr     temp1              ; Ja: PWM-Zaehler wieder auf 0&lt;br /&gt;
 &lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1    ; den PWM-Zaehler wieder speichern&lt;br /&gt;
        ldi     temp, 0b11000000   ; 0 .. LED an, 1 .. LED aus&lt;br /&gt;
 &lt;br /&gt;
        lds     temp2, OCR_1&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 1 erreicht&lt;br /&gt;
        brlt    OneOn&lt;br /&gt;
        ori     temp, $01&lt;br /&gt;
 &lt;br /&gt;
OneOn:  lds     temp2, OCR_2&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 2 erreicht&lt;br /&gt;
        brlt    TwoOn&lt;br /&gt;
        ori     temp, $02&lt;br /&gt;
 &lt;br /&gt;
TwoOn:  lds     temp2, OCR_3&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 3 erreicht&lt;br /&gt;
        brlt    ThreeOn&lt;br /&gt;
        ori     temp, $04&lt;br /&gt;
 &lt;br /&gt;
ThreeOn:lds     temp2, OCR_4&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 4 erreicht&lt;br /&gt;
        brlt    FourOn&lt;br /&gt;
        ori     temp, $08&lt;br /&gt;
 &lt;br /&gt;
FourOn: lds     temp2, OCR_5&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 5 erreicht&lt;br /&gt;
        brlt    FiveOn&lt;br /&gt;
        ori     temp, $10&lt;br /&gt;
 &lt;br /&gt;
FiveOn: lds     temp2, OCR_6&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 6 erreicht&lt;br /&gt;
        brlt    SetBits&lt;br /&gt;
        ori     temp, $20&lt;br /&gt;
 &lt;br /&gt;
SetBits:                           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        out     PORTB, temp&lt;br /&gt;
&lt;br /&gt;
        pop     temp               ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
 &lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; **********************************************&lt;br /&gt;
; &lt;br /&gt;
          .DSEG                       ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE   1                   ; Der PWM-Counter (0 bis 127)&lt;br /&gt;
OCR_1:    .BYTE   1                   ; 6 Bytes für die OCR-Register&lt;br /&gt;
OCR_2:    .BYTE   1&lt;br /&gt;
OCR_3:    .BYTE   1&lt;br /&gt;
OCR_4:    .BYTE   1&lt;br /&gt;
OCR_5:    .BYTE   1&lt;br /&gt;
OCR_6:    .BYTE   1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Spezielle Register==&lt;br /&gt;
===Der Z-Pointer (R30 und R31)===&lt;br /&gt;
Das Registerpärchen &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039; kann zu einem einzigen logischen Register zusammengefasst werden und heisst dann &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;. Diesem kann eine spezielle Aufgabe zukommen, indem er als Adressangabe fungieren kann, von welcher Speicherzelle im SRAM ein Ladevorgang (bzw. Speichervorgang) durchgeführt werden soll. Anstatt die Speicheradresse wie beim &#039;&#039;&#039;LDS&#039;&#039;&#039; bzw. &#039;&#039;&#039;STS&#039;&#039;&#039; direkt im Programmcode anzugeben, kann diese Speicheradresse zunächst in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; geladen werden und der Lesevorgang (Schreibvorgang) über diesen &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; abgewickelt werden. Dadurch wird aber die &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicheradresse berechenbar, denn natürlich kann mit den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;, wie mit den anderen Registern auch, Arithmetik betrieben werden. Besonders komfortabel ist dies, da im Ladebefehl noch zusätzliche Manipulationen angegeben werden können, die oft benötigte arithmetische Operationen implementieren.&lt;br /&gt;
&lt;br /&gt;
===LD===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z+&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, -Z&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lädt das Register &#039;&#039;&#039;rxx&#039;&#039;&#039; mit dem Inhalt der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===LDD===&lt;br /&gt;
* &#039;&#039;&#039;LDD rxx, Z+q&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Ladeanweisung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDD r18, Z + $28&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt der Speicherzellen $1000 + $28 = $1028 in das Register r18 geladen.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===ST===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ST Z, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST Z+, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST -Z, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Speichert den Inhalt des Register &#039;&#039;&#039;rxx&#039;&#039;&#039; in der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===STD===&lt;br /&gt;
* &#039;&#039;&#039;STD Z+q, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Speicheranweisung&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STD Z + $28, r18&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt des Registers r18 in der Speicherzellen $1000 + $28 = $1028 gespeichert.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Durch Verwendung des &#039;&#039;&#039;Z-Pointers&#039;&#039;&#039; ist es möglich die Interrupt Funktion wesentlich kürzer und vor allem ohne ständige Wiederholung von im Prinzip immer gleichem Code zu formulieren. Man stelle sich nur mal vor wie dieser Code aussehen würde, wenn anstelle von 6 PWM Stufen, deren 40 gebraucht würden. Mit dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; ist es möglich diesen auf das erste der OCR Bytes zu setzen und dann in einer Schleife eines nach dem anderen abzuarbeiten. Nach dem Laden des jeweiligen OCR Wertes, wird der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; automatisch durch den &#039;&#039;&#039;LD&#039;&#039;&#039;-Befehl auf das nächste zu verarbeitende OCR Byte weitergezählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
.def temp3 = r19&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0              ; den PWM Counter auf 0 setzen&lt;br /&gt;
        sts     PWMCount, temp2&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp                  ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount       ; den PWM ZAehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1                 ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128            ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM               ; Nein&lt;br /&gt;
        clr     temp1                 ; Ja: PWM Zaehler auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1       ; den PWM Zaehler wieder speichern&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
        ldi     temp3, $01            ; das Bitmuster für PWM Nr. i&lt;br /&gt;
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus&lt;br /&gt;
&lt;br /&gt;
pwmloop:&lt;br /&gt;
        ld      temp2, Z+             ; den OCR Wert für PWM Nr. i holen und Z-Pointer erhöhen&lt;br /&gt;
        cp      temp1, temp2          ; ist der Grenzwert für PWM Nr. i erreicht?&lt;br /&gt;
        brlo    LedOn&lt;br /&gt;
        or      temp, temp3&lt;br /&gt;
LedOn:&lt;br /&gt;
        lsl     temp3                 ; das Bitmuster schieben&lt;br /&gt;
        cpi     temp3, $40            ; alle Bits behandelt ?&lt;br /&gt;
        brne    pwmloop               ; nächster Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
        out     PORTB, temp           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
&lt;br /&gt;
        pop     temp                  ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
&lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; *********************************************************************&lt;br /&gt;
;&lt;br /&gt;
        .DSEG                         ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE  1                    ; der PWM Zaehler (0 bis 127)&lt;br /&gt;
OCR:      .BYTE  6                    ; 6 Bytes für die OCR Register&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===X-Pointer, Y-Pointer===&lt;br /&gt;
Neben dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gibt es noch den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;. Sie werden gebildet von den Registerpärchen&lt;br /&gt;
* &#039;&#039;&#039;X-Pointer&#039;&#039;&#039;: r26, r27&lt;br /&gt;
* &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;: r28, r29&lt;br /&gt;
* &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;: r30, r31&lt;br /&gt;
Alles über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gesagte gilt sinngemäß auch für den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039; mit einer Ausnahme: Mit dem &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; ist kein Zugriff &#039;&#039;&#039;LDD&#039;&#039;&#039;/&#039;&#039;&#039;STD&#039;&#039;&#039; mit einem Displacement möglich.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Schieberegister|&lt;br /&gt;
zurücklink=AVR-Tutorial: Schieberegister|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=7-Segment-Anzeige|&lt;br /&gt;
vorlink=AVR-Tutorial: 7-Segment-Anzeige}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|SRAM]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78455</id>
		<title>AVR-Tutorial: SRAM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78455"/>
		<updated>2013-09-09T07:50:04Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==SRAM - Der Speicher des Controllers==&lt;br /&gt;
&lt;br /&gt;
Nachdem in einem der vorangegangenen Kapitel eine [[AVR-Tutorial:_PWM|Software-PWM]] vorgestellt und in einem weiteren Kapitel darüber gesprochen wurde, wie man mit [[AVR-Tutorial:_Schieberegister|Schieberegistern]] die Anzahl an I/O-Pins erhöhen kann, wäre es naheliegend, beides zu kombinieren und den ATmega8 mal 20 oder 30 LEDs ansteuern zu lassen. Wenn es da nicht ein Problem gäbe: die Software-PWM hält ihre Daten in Registern, so wie das praktisch alle Programme bisher machten. Während allerdings 6 PWM-Kanäle noch problemlos in den Registern untergebracht werden konnten, ist dies mit 30 oder noch mehr PWM-Kanälen nicht mehr möglich. Es gibt schlicht und ergreifend nicht genug Register.&lt;br /&gt;
&lt;br /&gt;
Es gibt aber einen Ausweg. Der ATmega8 verfügt über 1kByte &#039;&#039;&#039;SRAM&#039;&#039;&#039; (statisches RAM). Dieses RAM wurde bereits indirekt durch den &#039;&#039;&#039;Stack&#039;&#039;&#039; benutzt. Bei jedem Aufruf eines Unterprogrammes, sei es über einen expliziten &#039;&#039;&#039;CALL&#039;&#039;&#039; (bzw. &#039;&#039;&#039;RCALL&#039;&#039;&#039;) oder einen Interrupt, wird die Rücksprungadresse irgendwo gespeichert. Dies geschieht genau in diesem SRAM. Auch &#039;&#039;&#039;PUSH&#039;&#039;&#039; und &#039;&#039;&#039;POP&#039;&#039;&#039; operieren in diesem Speicher.&lt;br /&gt;
&lt;br /&gt;
Ein Programm darf Speicherzellen im &#039;&#039;&#039;SRAM&#039;&#039;&#039; direkt benutzen und dort Werte speichern bzw. von dort Werte einlesen. Es muss nur darauf geachtet werden, dass es zu keiner Kollision mit dem Stack kommt, in dem z.&amp;amp;nbsp;B. die erwähnten Rücksprungadressen für Unterprogramme gespeichert werden. Da viele Programme aber lediglich ein paar Byte &#039;&#039;&#039;SRAM&#039;&#039;&#039; brauchen, der Rücksprungstack von der oberen Grenze des &#039;&#039;&#039;SRAM&#039;&#039;&#039; nach unten wächst und der ATmega8 immerhin über &#039;&#039;&#039;1kByte&#039;&#039;&#039; &#039;&#039;&#039;SRAM&#039;&#039;&#039; verfügt, ist dies in der Praxis kein all zu großes Problem.&lt;br /&gt;
&lt;br /&gt;
==Das .DSEG und .BYTE==&lt;br /&gt;
Um dem Assembler mitzuteilen, dass sich der folgende Abschnitt auf das SRAM bezieht, gibt es die Direktive &#039;&#039;&#039;.DSEG&#039;&#039;&#039; (Data Segment). Alle nach einer &#039;&#039;&#039;.DSEG&#039;&#039;&#039; Direktive folgenden Speicherreservierungen werden vom Assembler im SRAM durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Die Direktive &#039;&#039;&#039;.BYTE&#039;&#039;&#039; stellt dabei eine derartige Speicherreservierung dar. Es ermöglicht, der Speicherreservierung einen Namen zu geben und es erlaubt auch, nicht nur 1 Byte sondern eine ganze Reihe von Bytes unter einem Namen zu reservieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
           .DSEG                ; Umschalten auf das SRAM Datensegment&lt;br /&gt;
Counter:   .BYTE  1             ; 1 Byte unter dem Namen &#039;Counter&#039; reservieren&lt;br /&gt;
Test:      .BYTE  20            ; 20 Byte unter dem Namen &#039;Test&#039; reservieren&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==spezielle Befehle==&lt;br /&gt;
Für den Zugriff auf den &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicher gibt es spezielle Befehle. Diese holen entweder den momentanen Inhalt einer Speicherzelle und legen ihn in einem Register ab oder legen den Inhalt eines Registers in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
===LDS===&lt;br /&gt;
Liest die angegebene &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle und legt den gelesenen Wert in einem Register ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDS  r17, Counter      ; liest die Speicherzelle mit dem Namen &#039;Counter&#039;&lt;br /&gt;
                               ; und legt den gelesenen Wert im Register r17 ab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===STS===&lt;br /&gt;
Legt den in einem Register gespeicherten Wert in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STS  Counter, r17      ; Speichert den Inhalt von r17 in der&lt;br /&gt;
                               ; Speicherzelle &#039;Counter&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Implementierung der [[AVR-Tutorial:_PWM|Software-PWM]], die den PWM-Zähler sowie die einzelnen OCR-Grenzwerte im &#039;&#039;&#039;SRAM&#039;&#039;&#039; anstelle von Registern speichert, könnte z.&amp;amp;nbsp;B. so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        sts     OCR_1, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        sts     OCR_2, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        sts     OCR_3, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        sts     OCR_4, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        sts     OCR_5, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        sts     OCR_6, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; *************************************************************************&lt;br /&gt;
; Behandlung des Timer Overflows&lt;br /&gt;
;&lt;br /&gt;
; realisiert die PWM auf 6 Kanälen&lt;br /&gt;
;&lt;br /&gt;
; veränderte Register: keine&lt;br /&gt;
;&lt;br /&gt;
timer0_overflow:                   ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp               ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount    ; den PWM-Zaehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1              ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128         ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM            ; Nein&lt;br /&gt;
        clr     temp1              ; Ja: PWM-Zaehler wieder auf 0&lt;br /&gt;
 &lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1    ; den PWM-Zaehler wieder speichern&lt;br /&gt;
        ldi     temp, 0b11000000   ; 0 .. LED an, 1 .. LED aus&lt;br /&gt;
 &lt;br /&gt;
        lds     temp2, OCR_1&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 1 erreicht&lt;br /&gt;
        brlt    OneOn&lt;br /&gt;
        ori     temp, $01&lt;br /&gt;
 &lt;br /&gt;
OneOn:  lds     temp2, OCR_2&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 2 erreicht&lt;br /&gt;
        brlt    TwoOn&lt;br /&gt;
        ori     temp, $02&lt;br /&gt;
 &lt;br /&gt;
TwoOn:  lds     temp2, OCR_3&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 3 erreicht&lt;br /&gt;
        brlt    ThreeOn&lt;br /&gt;
        ori     temp, $04&lt;br /&gt;
 &lt;br /&gt;
ThreeOn:lds     temp2, OCR_4&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 4 erreicht&lt;br /&gt;
        brlt    FourOn&lt;br /&gt;
        ori     temp, $08&lt;br /&gt;
 &lt;br /&gt;
FourOn: lds     temp2, OCR_5&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 5 erreicht&lt;br /&gt;
        brlt    FiveOn&lt;br /&gt;
        ori     temp, $10&lt;br /&gt;
 &lt;br /&gt;
FiveOn: lds     temp2, OCR_6&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 6 erreicht&lt;br /&gt;
        brlt    SetBits&lt;br /&gt;
        ori     temp, $20&lt;br /&gt;
 &lt;br /&gt;
SetBits:                           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        out     PORTB, temp&lt;br /&gt;
&lt;br /&gt;
        pop     temp               ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
 &lt;br /&gt;
        reti&lt;br /&gt;
;&lt;br /&gt;
; **********************************************&lt;br /&gt;
; &lt;br /&gt;
          .DSEG                       ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE   1                   ; Der PWM-Counter (0 bis 127)&lt;br /&gt;
OCR_1:    .BYTE   1                   ; 6 Bytes für die OCR-Register&lt;br /&gt;
OCR_2:    .BYTE   1&lt;br /&gt;
OCR_3:    .BYTE   1&lt;br /&gt;
OCR_4:    .BYTE   1&lt;br /&gt;
OCR_5:    .BYTE   1&lt;br /&gt;
OCR_6:    .BYTE   1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Spezielle Register==&lt;br /&gt;
===Der Z-Pointer (R30 und R31)===&lt;br /&gt;
Das Registerpärchen &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039; kann zu einem einzigen logischen Register zusammengefasst werden und heisst dann &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;. Diesem kann eine spezielle Aufgabe zukommen, indem er als Adressangabe fungieren kann, von welcher Speicherzelle im SRAM ein Ladevorgang (bzw. Speichervorgang) durchgeführt werden soll. Anstatt die Speicheradresse wie beim &#039;&#039;&#039;LDS&#039;&#039;&#039; bzw. &#039;&#039;&#039;STS&#039;&#039;&#039; direkt im Programmcode anzugeben, kann diese Speicheradresse zunächst in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; geladen werden und der Lesevorgang (Schreibvorgang) über diesen &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; abgewickelt werden. Dadurch wird aber die &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicheradresse berechenbar, denn natürlich kann mit den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;, wie mit den anderen Registern auch, Arithmetik betrieben werden. Besonders komfortabel ist dies, da im Ladebefehl noch zusätzliche Manipulationen angegeben werden können, die oft benötigte arithmetische Operationen implementieren.&lt;br /&gt;
&lt;br /&gt;
===LD===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z+&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, -Z&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lädt das Register &#039;&#039;&#039;rxx&#039;&#039;&#039; mit dem Inhalt der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===LDD===&lt;br /&gt;
* &#039;&#039;&#039;LDD rxx, Z+q&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Ladeanweisung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDD r18, Z + $28&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt der Speicherzellen $1000 + $28 = $1028 in das Register r18 geladen.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===ST===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ST Z, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST Z+, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST -Z, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Speichert den Inhalt des Register &#039;&#039;&#039;rxx&#039;&#039;&#039; in der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===STD===&lt;br /&gt;
* &#039;&#039;&#039;STD Z+q, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Speicheranweisung&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STD Z + $28, r18&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt des Registers r18 in der Speicherzellen $1000 + $28 = $1028 gespeichert.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Durch Verwendung des &#039;&#039;&#039;Z-Pointers&#039;&#039;&#039; ist es möglich die Interrupt Funktion wesentlich kürzer und vor allem ohne ständige Wiederholung von im Prinzip immer gleichem Code zu formulieren. Man stelle sich nur mal vor wie dieser Code aussehen würde, wenn anstelle von 6 PWM Stufen, deren 40 gebraucht würden. Mit dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; ist es möglich diesen auf das erste der OCR Bytes zu setzen und dann in einer Schleife eines nach dem anderen abzuarbeiten. Nach dem Laden des jeweiligen OCR Wertes, wird der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; automatisch durch den &#039;&#039;&#039;LD&#039;&#039;&#039;-Befehl auf das nächste zu verarbeitende OCR Byte weitergezählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
.def temp3 = r19&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0              ; den PWM Counter auf 0 setzen&lt;br /&gt;
        sts     PWMCount, temp2&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        lds     temp1, PWMCount       ; den PWM ZAehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1                 ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128            ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM               ; Nein&lt;br /&gt;
        clr     temp1                 ; Ja: PWM Zaehler auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1       ; den PWM Zaehler wieder speichern&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
        ldi     temp3, $01            ; das Bitmuster für PWM Nr. i&lt;br /&gt;
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus&lt;br /&gt;
&lt;br /&gt;
pwmloop:&lt;br /&gt;
        ld      temp2, Z+             ; den OCR Wert für PWM Nr. i holen und Z-Pointer erhöhen&lt;br /&gt;
        cp      temp1, temp2          ; ist der Grenzwert für PWM Nr. i erreicht?&lt;br /&gt;
        brlo    LedOn&lt;br /&gt;
        or      temp, temp3&lt;br /&gt;
LedOn:&lt;br /&gt;
        lsl     temp3                 ; das Bitmuster schieben&lt;br /&gt;
        cpi     temp3, $40            ; alle Bits behandelt ?&lt;br /&gt;
        brne    pwmloop               ; nächster Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
        out     PORTB, temp           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        reti&lt;br /&gt;
&lt;br /&gt;
        .DSEG                         ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE  1                    ; der PWM Zaehler (0 bis 127)&lt;br /&gt;
OCR:      .BYTE  6                    ; 6 Bytes für die OCR Register&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===X-Pointer, Y-Pointer===&lt;br /&gt;
Neben dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gibt es noch den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;. Sie werden gebildet von den Registerpärchen&lt;br /&gt;
* &#039;&#039;&#039;X-Pointer&#039;&#039;&#039;: r26, r27&lt;br /&gt;
* &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;: r28, r29&lt;br /&gt;
* &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;: r30, r31&lt;br /&gt;
Alles über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gesagte gilt sinngemäß auch für den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039; mit einer Ausnahme: Mit dem &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; ist kein Zugriff &#039;&#039;&#039;LDD&#039;&#039;&#039;/&#039;&#039;&#039;STD&#039;&#039;&#039; mit einem Displacement möglich.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Schieberegister|&lt;br /&gt;
zurücklink=AVR-Tutorial: Schieberegister|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=7-Segment-Anzeige|&lt;br /&gt;
vorlink=AVR-Tutorial: 7-Segment-Anzeige}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|SRAM]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78454</id>
		<title>AVR-Tutorial: SRAM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_SRAM&amp;diff=78454"/>
		<updated>2013-09-09T07:48:48Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Beispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==SRAM - Der Speicher des Controllers==&lt;br /&gt;
&lt;br /&gt;
Nachdem in einem der vorangegangenen Kapitel eine [[AVR-Tutorial:_PWM|Software-PWM]] vorgestellt und in einem weiteren Kapitel darüber gesprochen wurde, wie man mit [[AVR-Tutorial:_Schieberegister|Schieberegistern]] die Anzahl an I/O-Pins erhöhen kann, wäre es naheliegend, beides zu kombinieren und den ATmega8 mal 20 oder 30 LEDs ansteuern zu lassen. Wenn es da nicht ein Problem gäbe: die Software-PWM hält ihre Daten in Registern, so wie das praktisch alle Programme bisher machten. Während allerdings 6 PWM-Kanäle noch problemlos in den Registern untergebracht werden konnten, ist dies mit 30 oder noch mehr PWM-Kanälen nicht mehr möglich. Es gibt schlicht und ergreifend nicht genug Register.&lt;br /&gt;
&lt;br /&gt;
Es gibt aber einen Ausweg. Der ATmega8 verfügt über 1kByte &#039;&#039;&#039;SRAM&#039;&#039;&#039; (statisches RAM). Dieses RAM wurde bereits indirekt durch den &#039;&#039;&#039;Stack&#039;&#039;&#039; benutzt. Bei jedem Aufruf eines Unterprogrammes, sei es über einen expliziten &#039;&#039;&#039;CALL&#039;&#039;&#039; (bzw. &#039;&#039;&#039;RCALL&#039;&#039;&#039;) oder einen Interrupt, wird die Rücksprungadresse irgendwo gespeichert. Dies geschieht genau in diesem SRAM. Auch &#039;&#039;&#039;PUSH&#039;&#039;&#039; und &#039;&#039;&#039;POP&#039;&#039;&#039; operieren in diesem Speicher.&lt;br /&gt;
&lt;br /&gt;
Ein Programm darf Speicherzellen im &#039;&#039;&#039;SRAM&#039;&#039;&#039; direkt benutzen und dort Werte speichern bzw. von dort Werte einlesen. Es muss nur darauf geachtet werden, dass es zu keiner Kollision mit dem Stack kommt, in dem z.&amp;amp;nbsp;B. die erwähnten Rücksprungadressen für Unterprogramme gespeichert werden. Da viele Programme aber lediglich ein paar Byte &#039;&#039;&#039;SRAM&#039;&#039;&#039; brauchen, der Rücksprungstack von der oberen Grenze des &#039;&#039;&#039;SRAM&#039;&#039;&#039; nach unten wächst und der ATmega8 immerhin über &#039;&#039;&#039;1kByte&#039;&#039;&#039; &#039;&#039;&#039;SRAM&#039;&#039;&#039; verfügt, ist dies in der Praxis kein all zu großes Problem.&lt;br /&gt;
&lt;br /&gt;
==Das .DSEG und .BYTE==&lt;br /&gt;
Um dem Assembler mitzuteilen, dass sich der folgende Abschnitt auf das SRAM bezieht, gibt es die Direktive &#039;&#039;&#039;.DSEG&#039;&#039;&#039; (Data Segment). Alle nach einer &#039;&#039;&#039;.DSEG&#039;&#039;&#039; Direktive folgenden Speicherreservierungen werden vom Assembler im SRAM durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Die Direktive &#039;&#039;&#039;.BYTE&#039;&#039;&#039; stellt dabei eine derartige Speicherreservierung dar. Es ermöglicht, der Speicherreservierung einen Namen zu geben und es erlaubt auch, nicht nur 1 Byte sondern eine ganze Reihe von Bytes unter einem Namen zu reservieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
           .DSEG                ; Umschalten auf das SRAM Datensegment&lt;br /&gt;
Counter:   .BYTE  1             ; 1 Byte unter dem Namen &#039;Counter&#039; reservieren&lt;br /&gt;
Test:      .BYTE  20            ; 20 Byte unter dem Namen &#039;Test&#039; reservieren&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==spezielle Befehle==&lt;br /&gt;
Für den Zugriff auf den &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicher gibt es spezielle Befehle. Diese holen entweder den momentanen Inhalt einer Speicherzelle und legen ihn in einem Register ab oder legen den Inhalt eines Registers in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
===LDS===&lt;br /&gt;
Liest die angegebene &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle und legt den gelesenen Wert in einem Register ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDS  r17, Counter      ; liest die Speicherzelle mit dem Namen &#039;Counter&#039;&lt;br /&gt;
                               ; und legt den gelesenen Wert im Register r17 ab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===STS===&lt;br /&gt;
Legt den in einem Register gespeicherten Wert in einer &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicherzelle ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STS  Counter, r17      ; Speichert den Inhalt von r17 in der&lt;br /&gt;
                               ; Speicherzelle &#039;Counter&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Implementierung der [[AVR-Tutorial:_PWM|Software-PWM]], die den PWM-Zähler sowie die einzelnen OCR-Grenzwerte im &#039;&#039;&#039;SRAM&#039;&#039;&#039; anstelle von Registern speichert, könnte z.&amp;amp;nbsp;B. so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        sts     OCR_1, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        sts     OCR_2, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        sts     OCR_3, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        sts     OCR_4, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        sts     OCR_5, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        sts     OCR_6, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
 &lt;br /&gt;
timer0_overflow:                   ; Timer 0 Overflow Handler&lt;br /&gt;
        push    temp               ; Alle verwendeten Register sichern&lt;br /&gt;
        push    temp1&lt;br /&gt;
        push    temp2&lt;br /&gt;
        in      temp, SREG&lt;br /&gt;
        push    temp&lt;br /&gt;
&lt;br /&gt;
        lds     temp1, PWMCount    ; den PWM-Zaehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1              ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128         ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM            ; Nein&lt;br /&gt;
        clr     temp1              ; Ja: PWM-Zaehler wieder auf 0&lt;br /&gt;
 &lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1    ; den PWM-Zaehler wieder speichern&lt;br /&gt;
        ldi     temp, 0b11000000   ; 0 .. LED an, 1 .. LED aus&lt;br /&gt;
 &lt;br /&gt;
        lds     temp2, OCR_1&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 1 erreicht&lt;br /&gt;
        brlt    OneOn&lt;br /&gt;
        ori     temp, $01&lt;br /&gt;
 &lt;br /&gt;
OneOn:  lds     temp2, OCR_2&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 2 erreicht&lt;br /&gt;
        brlt    TwoOn&lt;br /&gt;
        ori     temp, $02&lt;br /&gt;
 &lt;br /&gt;
TwoOn:  lds     temp2, OCR_3&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 3 erreicht&lt;br /&gt;
        brlt    ThreeOn&lt;br /&gt;
        ori     temp, $04&lt;br /&gt;
 &lt;br /&gt;
ThreeOn:lds     temp2, OCR_4&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 4 erreicht&lt;br /&gt;
        brlt    FourOn&lt;br /&gt;
        ori     temp, $08&lt;br /&gt;
 &lt;br /&gt;
FourOn: lds     temp2, OCR_5&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 5 erreicht&lt;br /&gt;
        brlt    FiveOn&lt;br /&gt;
        ori     temp, $10&lt;br /&gt;
 &lt;br /&gt;
FiveOn: lds     temp2, OCR_6&lt;br /&gt;
        cp      temp1, temp2       ; Ist der Grenzwert für LED 6 erreicht&lt;br /&gt;
        brlt    SetBits&lt;br /&gt;
        ori     temp, $20&lt;br /&gt;
 &lt;br /&gt;
SetBits:                           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        out     PORTB, temp&lt;br /&gt;
&lt;br /&gt;
        pop     temp               ; die gesicherten Register wiederherstellen&lt;br /&gt;
        out     SREG, temp&lt;br /&gt;
        pop     temp2&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        pop     temp&lt;br /&gt;
 &lt;br /&gt;
        reti&lt;br /&gt;
 &lt;br /&gt;
          .DSEG                       ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE   1                   ; Der PWM-Counter (0 bis 127)&lt;br /&gt;
OCR_1:    .BYTE   1                   ; 6 Bytes für die OCR-Register&lt;br /&gt;
OCR_2:    .BYTE   1&lt;br /&gt;
OCR_3:    .BYTE   1&lt;br /&gt;
OCR_4:    .BYTE   1&lt;br /&gt;
OCR_5:    .BYTE   1&lt;br /&gt;
OCR_6:    .BYTE   1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Spezielle Register==&lt;br /&gt;
===Der Z-Pointer (R30 und R31)===&lt;br /&gt;
Das Registerpärchen &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039; kann zu einem einzigen logischen Register zusammengefasst werden und heisst dann &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;. Diesem kann eine spezielle Aufgabe zukommen, indem er als Adressangabe fungieren kann, von welcher Speicherzelle im SRAM ein Ladevorgang (bzw. Speichervorgang) durchgeführt werden soll. Anstatt die Speicheradresse wie beim &#039;&#039;&#039;LDS&#039;&#039;&#039; bzw. &#039;&#039;&#039;STS&#039;&#039;&#039; direkt im Programmcode anzugeben, kann diese Speicheradresse zunächst in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; geladen werden und der Lesevorgang (Schreibvorgang) über diesen &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; abgewickelt werden. Dadurch wird aber die &#039;&#039;&#039;SRAM&#039;&#039;&#039;-Speicheradresse berechenbar, denn natürlich kann mit den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; und &#039;&#039;&#039;R31&#039;&#039;&#039;, wie mit den anderen Registern auch, Arithmetik betrieben werden. Besonders komfortabel ist dies, da im Ladebefehl noch zusätzliche Manipulationen angegeben werden können, die oft benötigte arithmetische Operationen implementieren.&lt;br /&gt;
&lt;br /&gt;
===LD===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, Z+&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;LD rxx, -Z&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lädt das Register &#039;&#039;&#039;rxx&#039;&#039;&#039; mit dem Inhalt der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===LDD===&lt;br /&gt;
* &#039;&#039;&#039;LDD rxx, Z+q&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Ladeanweisung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        LDD r18, Z + $28&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt der Speicherzellen $1000 + $28 = $1028 in das Register r18 geladen.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===ST===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ST Z, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST Z+, rxx&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;ST -Z, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Speichert den Inhalt des Register &#039;&#039;&#039;rxx&#039;&#039;&#039; in der Speicherzelle, deren Adresse im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; angegeben ist. Bei den Varianten mit &#039;&#039;&#039;Z+&#039;&#039;&#039; bzw. &#039;&#039;&#039;-Z&#039;&#039;&#039; wird zusätzlich der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;nach&#039;&#039;&#039;&#039;&#039; der Operation um 1 erhöht bzw. &#039;&#039;&#039;&#039;&#039;vor&#039;&#039;&#039;&#039;&#039; der Operation um 1 vermindert.&lt;br /&gt;
&lt;br /&gt;
===STD===&lt;br /&gt;
* &#039;&#039;&#039;STD Z+q, rxx&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt der Zugriff wieder über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; wobei vor dem Zugriff zur Adressangabe im &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; noch das Displacement &#039;&#039;&#039;q&#039;&#039;&#039; addiert wird.&lt;br /&gt;
&lt;br /&gt;
Enthält also der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; die Adresse $1000 und sei &#039;&#039;&#039;q&#039;&#039;&#039; der Wert $28, so wird mit einer Speicheranweisung&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
        STD Z + $28, r18&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Inhalt des Registers r18 in der Speicherzellen $1000 + $28 = $1028 gespeichert.&lt;br /&gt;
&lt;br /&gt;
Der Wertebereich für &#039;&#039;&#039;q&#039;&#039;&#039; erstreckt sich von 0 bis 63.&lt;br /&gt;
&lt;br /&gt;
===Beispiel===&lt;br /&gt;
&lt;br /&gt;
Durch Verwendung des &#039;&#039;&#039;Z-Pointers&#039;&#039;&#039; ist es möglich die Interrupt Funktion wesentlich kürzer und vor allem ohne ständige Wiederholung von im Prinzip immer gleichem Code zu formulieren. Man stelle sich nur mal vor wie dieser Code aussehen würde, wenn anstelle von 6 PWM Stufen, deren 40 gebraucht würden. Mit dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; ist es möglich diesen auf das erste der OCR Bytes zu setzen und dann in einer Schleife eines nach dem anderen abzuarbeiten. Nach dem Laden des jeweiligen OCR Wertes, wird der &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; automatisch durch den &#039;&#039;&#039;LD&#039;&#039;&#039;-Befehl auf das nächste zu verarbeitende OCR Byte weitergezählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
.def temp3 = r19&lt;br /&gt;
  &lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                  ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow       ; Timer Overflow Handler&lt;br /&gt;
 &lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
  &lt;br /&gt;
        ldi     temp, 0xFF            ; Port B auf Ausgang&lt;br /&gt;
        out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 1&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 10&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 20&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 80&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
        ldi     temp2, 127&lt;br /&gt;
        st      Z+, temp2&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp2, 0              ; den PWM Counter auf 0 setzen&lt;br /&gt;
        sts     PWMCount, temp2&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;CS00)       ; CS00 setzen: Teiler 1&lt;br /&gt;
        out     TCCR0, temp&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;TOIE0)      ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp&lt;br /&gt;
 &lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:   rjmp    loop&lt;br /&gt;
&lt;br /&gt;
timer0_overflow:                      ; Timer 0 Overflow Handler&lt;br /&gt;
        lds     temp1, PWMCount       ; den PWM ZAehler aus dem Speicher holen&lt;br /&gt;
        inc     temp1                 ; Zaehler erhoehen&lt;br /&gt;
        cpi     temp1, 128            ; wurde 128 erreicht ?&lt;br /&gt;
        brne    WorkPWM               ; Nein&lt;br /&gt;
        clr     temp1                 ; Ja: PWM Zaehler auf 0 setzen&lt;br /&gt;
&lt;br /&gt;
WorkPWM:&lt;br /&gt;
        sts     PWMCount, temp1       ; den PWM Zaehler wieder speichern&lt;br /&gt;
&lt;br /&gt;
        ldi     r30,LOW(OCR)          ; den Z-Pointer mit dem Start der OCR Bytes laden&lt;br /&gt;
        ldi     r31,HIGH(OCR)&lt;br /&gt;
        ldi     temp3, $01            ; das Bitmuster für PWM Nr. i&lt;br /&gt;
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus&lt;br /&gt;
&lt;br /&gt;
pwmloop:&lt;br /&gt;
        ld      temp2, Z+             ; den OCR Wert für PWM Nr. i holen und Z-Pointer erhöhen&lt;br /&gt;
        cp      temp1, temp2          ; ist der Grenzwert für PWM Nr. i erreicht?&lt;br /&gt;
        brlo    LedOn&lt;br /&gt;
        or      temp, temp3&lt;br /&gt;
LedOn:&lt;br /&gt;
        lsl     temp3                 ; das Bitmuster schieben&lt;br /&gt;
        cpi     temp3, $40            ; alle Bits behandelt ?&lt;br /&gt;
        brne    pwmloop               ; nächster Schleifendurchlauf&lt;br /&gt;
&lt;br /&gt;
        out     PORTB, temp           ; Die neue Bitbelegung am Port ausgeben&lt;br /&gt;
        reti&lt;br /&gt;
&lt;br /&gt;
        .DSEG                         ; das Folgende kommt ins SRAM&lt;br /&gt;
&lt;br /&gt;
PWMCount: .BYTE  1                    ; der PWM Zaehler (0 bis 127)&lt;br /&gt;
OCR:      .BYTE  6                    ; 6 Bytes für die OCR Register&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===X-Pointer, Y-Pointer===&lt;br /&gt;
Neben dem &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gibt es noch den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;. Sie werden gebildet von den Registerpärchen&lt;br /&gt;
* &#039;&#039;&#039;X-Pointer&#039;&#039;&#039;: r26, r27&lt;br /&gt;
* &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039;: r28, r29&lt;br /&gt;
* &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039;: r30, r31&lt;br /&gt;
Alles über den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; gesagte gilt sinngemäß auch für den &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; bzw. &#039;&#039;&#039;Y-Pointer&#039;&#039;&#039; mit einer Ausnahme: Mit dem &#039;&#039;&#039;X-Pointer&#039;&#039;&#039; ist kein Zugriff &#039;&#039;&#039;LDD&#039;&#039;&#039;/&#039;&#039;&#039;STD&#039;&#039;&#039; mit einem Displacement möglich.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Schieberegister|&lt;br /&gt;
zurücklink=AVR-Tutorial: Schieberegister|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=7-Segment-Anzeige|&lt;br /&gt;
vorlink=AVR-Tutorial: 7-Segment-Anzeige}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|SRAM]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-Tutorial:_7-Segment-Anzeige&amp;diff=77812</id>
		<title>Diskussion:AVR-Tutorial: 7-Segment-Anzeige</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-Tutorial:_7-Segment-Anzeige&amp;diff=77812"/>
		<updated>2013-07-30T19:33:25Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* 8fach Latches bzw D Flip-Flop erwähnen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das Programm zur Multiplexanzeige paßt nicht zur oben abgebildeten Schaltung:&lt;br /&gt;
Die Digits werden mit dem Pegel Low eingeschaltet. Im Programm wird High benutzt.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Die Konstanten im Programm sind so gewählt dass 0 (low) für ein aktives Segment steht, das müsste also stimmen.&lt;br /&gt;
-[[Benutzer:Andreas|andreas]] 20:04, 18. Okt 2007 (CEST)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Die Segmente sind schon O.K., ich meine aber die Digits an PC0 - PC3.&lt;br /&gt;
Die sind auch low-aktiv, werden im Prog aber als high-aktiv behandelt. Deshalb wird nur &amp;quot;Mist&amp;quot; angezeigt, weil 3 Digits zur selben Zeit angesteuert werden.&lt;br /&gt;
--[[Benutzer:Wibwato|thomas]] 12:45, 7. Dez 2007 (CET)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Da scheint tatsächlich ein Bug zu sein. Das AUSschalten aller vier PNP-Transistoren vor dem ANschalten der jeweiligen Ziffer im Multiplex ist wohl falsch: http://www.mikrocontroller.net/topic/134185&lt;br /&gt;
[[Benutzer:Stefan|Stefan]] 14:36, 6. Apr. 2009 (CEST)&lt;br /&gt;
&lt;br /&gt;
== 8fach Latches bzw D Flip-Flop erwähnen ==&lt;br /&gt;
&lt;br /&gt;
Was haltet ihr davon, wenn man in diesem Tutorial noch kurz Latches bzw D Flip-Flops erwähnt, die man zwischen µC und 7Segmentanzeigen schalten kann. Somit wären dann eine viel langsamere Ansteuerung möglich. Denke da zB an 74HCT573 bzw 574, da diese gleich 8 Signale puffern können.&lt;br /&gt;
&lt;br /&gt;
Klar geht es auch so, aber manchmal sucht man vielleicht gerade nach dieser Möglichkeit und kommt dann im Tutorial vorbei. Denke, dass es auch nicht schadet, wenn Anfänger neben den Schieberegistern hier den parallelen &amp;quot;Partner&amp;quot; sehen.&lt;br /&gt;
&lt;br /&gt;
Wie steht ihr hierzu?&lt;br /&gt;
&lt;br /&gt;
Gruß&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Wieso kann man langsamer multiplexen mit Latches? Das ist ein Irrtum. Ausserdem verwirrt es Anfänger mehr als es ihnen hilft. Porterweiterung per Schieberegister oder Latches ist ein anders Thema und darum auch in einem anderen Tutorial.&lt;br /&gt;
&lt;br /&gt;
MFG&lt;br /&gt;
Falk&lt;br /&gt;
&lt;br /&gt;
== Anewndungsbeispiel ==&lt;br /&gt;
Ich hab den im November 2012 eingefügten Link zu einem Anwendungsbeispiel wieder entfernt. Zum einen ist das ein C-Programm und hat als solches in einem Assembler-Tutorial IMHO nicht wirklich etwas verloren. Zum anderen zeigt dieses Anwendungsbeispiel Multiplexing genau so, wie man es nicht macht: mit Schleifenkonstrukten und Delays innerhalb der Schleife.&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=77811</id>
		<title>AVR-Tutorial: 7-Segment-Anzeige</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=77811"/>
		<updated>2013-07-30T19:27:53Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Anwendungsbeispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die Ausgabe von Zahlenwerten auf ein Text-LCD ist sicherlich das Nonplusultra, aber manchmal liegen die Dinge sehr viel einfacher. Um beispielsweise eine Temperatur anzuzeigen ist ein LCD etwas Overkill. In solchen Fällen kann die Ausgabe auf ein paar 7-Segmentanzeigen gemacht werden. Ausserdem haben 7-Segmentanzeigen einen ganz besonderen Charme :-)&lt;br /&gt;
&lt;br /&gt;
==Typen von 7-Segment Anzeigen==&lt;br /&gt;
&lt;br /&gt;
Eine einzelne 7-Segmentanzeige besteht aus sieben (mit Dezimalpunkt acht) einzelnen [[LED]]s in einem gemeinsamen Gehäuse. Aus praktischen Gründen wird einer der beiden Anschlüsse jeder LED mit den gleichen Anschlüssen der anderen LED verbunden und gemeinsam aus dem Gehäuse herausgeführt. Das spart Pins am Gehäuse und später bei der Ansteuerung. Dementsprechend spricht man von Anzeigen mit &#039;&#039;gemeinsamer Anode&#039;&#039; (engl. common anode) bzw. &#039;&#039;gemeinsamer Kathode&#039;&#039; (engl. common cathode).&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_00.gif | framed | center | Interne Verschaltung der 7-Segmentanzeigen]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Eine einzelne 7-Segment Anzeige==&lt;br /&gt;
&lt;br /&gt;
===Schaltung===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_02.gif | thumb| left | 80px| Pin-&amp;lt;br/&amp;gt;belegung einer 7-Segment-&amp;lt;br/&amp;gt;anzeige]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_01.gif | thumb | right |240px| Ansteuerung einer einzelnen 7-Segmentanzeige]]&lt;br /&gt;
&lt;br /&gt;
Eine einzelne 7-Segmentanzeige wird nach dem folgenden Schema am &#039;&#039;&#039;Port D&#039;&#039;&#039; des Mega8 angeschlossen. Port D wurde deshalb gewählt, da er am Mega8 als einziger Port aus den vollen 8 Bit besteht. Die 7-Segmentanzeige hat neben den Segmenten &#039;&#039;&#039;a&#039;&#039;&#039; bis &#039;&#039;&#039;g&#039;&#039;&#039;  eine gemeinsame Anode &#039;&#039;&#039;CA&#039;&#039;&#039; sowie einen Dezimalpunkt &#039;&#039;&#039;dp&#039;&#039;&#039; (siehe Bild).&lt;br /&gt;
&lt;br /&gt;
Welcher Pin an der Anzeige welchem Segment (a-g) bzw. dem Dezimalpunkt entspricht wird am besten dem Datenblatt zur Anzeige entnommen. Hat man kein Datenblatt dann kann man auch empirisch die Pinbelegung feststellen, indem  man mit einer 5V Quelle und einem 1k Widerstand einfach probweise alle Pins &#039;abklappert&#039;. 1k deswegen, damit man im Zweifellsfall Low-Current LED nicht überfordert. Eine 7-Segment Anzeige, die auf die üblichen 10 bis 20mA ausgelegt ist, wird damit immer noch leuchten, wenn auch schwach. Aber mehr braucht es ja auch nicht um die Pinbelegung feststellen zu können. Im Folgenden wird von dieser Segmentbelegung ausgegangen:&lt;br /&gt;
&lt;br /&gt;
Wird eine andere Belegung genutzt dann ist das prinzipiell möglich, jedoch müsste das in der Programmierung berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
Da eine 7-Segmentanzeige konzeptionell sieben einzelnen LEDs entspricht, ergibt sich im Prinzip keine Änderung in der Ansteuerung einer derartigen Anzeige im Vergleich zur LED Ansteuerung wie sie im [[AVR-Tutorial: IO-Grundlagen]] gezeigt wird. Genau wie bei den einzelnen LEDs wird eine davon eingeschaltet, indem der zugehörige Port Pin auf 0 gesetzt wird. Aber anders als bei einzelnen LED möchte man mit einer derartigen Anzeige eine Ziffernanzeige erhalten. Dazu ist es lediglich notwendig, für eine bestimmte Ziffer die richtigen LEDs einzuschalten.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
===Codetabelle===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_02a.gif | right | thumb | 80px | Darstellung der Ziffer &amp;quot;3&amp;quot;]]&lt;br /&gt;
Die Umkodierung von einzelnen Ziffern in ein bestimmtes Ausgabemuster kann über eine sog. Codetabelle geschehen: Die auszugebende Ziffer wird als Offset zum Anfang dieser Tabelle aufgefasst und aus der Tabelle erhält man ein Byte (Code), welches direkt auf den Port ausgegeben werden kann und das entsprechende Bitmuster enthält, sodass die für diese Ziffer notwendigen LED ein- bzw. ausgeschaltet sind.&lt;br /&gt;
&lt;br /&gt;
;Beispiel: Um die Ziffer &#039;&#039;&#039;3&#039;&#039;&#039; anzuzeigen, müssen auf der Anzeige die Segmente &#039;&#039;&#039;a&#039;&#039;&#039;, &#039;&#039;&#039;b&#039;&#039;&#039;, &#039;&#039;&#039;c&#039;&#039;&#039;, &#039;&#039;&#039;d&#039;&#039;&#039; und &#039;&#039;&#039;g&#039;&#039;&#039; aufleuchten. Alle anderen Segmente sollen dunkel sein.&lt;br /&gt;
&lt;br /&gt;
Aus dem Anschlußschema ergibt sich, dass die dazu notwendige Ausgabe am Port binär &#039;&#039;&#039;10110000&#039;&#039;&#039; lauten muss. Untersucht man dies für alle Ziffern, so ergibt sich folgende Tabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
    .db  0b11000000     ; 0: a, b, c, d, e, f&lt;br /&gt;
    .db  0b11111001     ; 1: b, c&lt;br /&gt;
    .db  0b10100100     ; 2: a, b, d, e, g&lt;br /&gt;
    .db  0b10110000     ; 3: a, b, c, d, g&lt;br /&gt;
    .db  0b10011001     ; 4: b, c, f, g&lt;br /&gt;
    .db  0b10010010     ; 5: a, c, d, f, g&lt;br /&gt;
    .db  0b10000010     ; 6: a, c, d, e, f, g&lt;br /&gt;
    .db  0b11111000     ; 7: a, b, c&lt;br /&gt;
    .db  0b10000000     ; 8: a, b, c, d, e, f, g&lt;br /&gt;
    .db  0b10010000     ; 9: a, b, c, d, f, g&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Programm===&lt;br /&gt;
&lt;br /&gt;
Das Testprogramm stellt nacheinander die Ziffern 0 bis 9 auf der 7-Segmentanzeige dar. Die jeweils auszugebende Zahl steht im Register &#039;&#039;&#039;count&#039;&#039;&#039; und wird innerhalb der Schleife um jeweils 1 erhöht. Hat das Register den Wert 10 erreicht, so wird es wieder auf 0 zurückgesetzt. Nach der Erhöhung folgt eine Warteschleife, welche dafür sorgt, dass bis zur nächsten Ausgabe eine gewisse Zeit vergeht. Normalerweise macht man keine derartigen langen Warteschleifen, aber hier geht es ja nicht ums Warten sondern um die Ansteuerung einer 7-Segmentanzeige. Einen Timer dafür zu benutzen wäre zunächst zuviel Aufwand.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Ausgabe und damit der in diesem Artikel interessante Teil findet jedoch direkt nach dem Label &amp;lt;i&amp;gt;loop&amp;lt;/i&amp;gt; statt. Die bereits bekannte Codetabelle wird mittels &#039;&#039;&#039;.db&#039;&#039;&#039; Direktive (&#039;&#039;&#039;d&#039;&#039;&#039;efine &#039;&#039;&#039;b&#039;&#039;&#039;yte) in den [[Speicher#Flash-ROM | Flash-Speicher]] gelegt. Der Zugriff darauf erfolgt über den Z-Pointer und dem Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039;. Zusätzlich wird vor dem Zugriff noch der Wert des Registers &#039;&#039;&#039;count&#039;&#039;&#039; und damit der aktuelle Zählerwert zum Z-Pointer addiert.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss nur, dass der Zählerwert verdoppelt werden muss. Dies hat folgenden Grund: Wird die Tabelle so wie hier gezeigt mittels einzelnen &#039;&#039;&#039;.db&#039;&#039;&#039; Anweisungen aufgebaut, so fügt der Assembler sog. &#039;&#039;&#039;Padding Bytes&#039;&#039;&#039; zwischen die einzelnen Bytes ein, damit jede &#039;&#039;&#039;.db&#039;&#039;&#039; Anweisung auf einer geraden Speicheradresse liegt. Dies ist eine direkte Folge der Tatsache, dass der Flash-Speicher &#039;&#039;&#039;wortweise&#039;&#039;&#039; (16 Bit) und nicht &#039;&#039;&#039;byteweise&#039;&#039;&#039; (8 Bit) organisiert ist. Da aber von einem .db in der Tabelle zum nächsten .db eine Differenz von 2 Bytes vorliegt, muss dies in der Berechnung berücksichtigt werden. Im zweiten Beispiel auf dieser Seite wird dies anders gemacht. Dort wird gezeigt wie man durch eine andere Schreibweise der Tabelle das Erzeugen der Padding Bytes durch den Assembler verhindern kann.&lt;br /&gt;
&lt;br /&gt;
Aus dem gleichen Grund wird auch der Z-Pointer mit dem 2-fachen der Startadresse der Tabelle geladen. Die Startadresse wird vom Assembler in wortweiser Adressierung eingesetzt, &#039;&#039;&#039;lpm&#039;&#039;&#039; möchte die Zugriffsadresse als Byteadresse angegeben haben.&lt;br /&gt;
&lt;br /&gt;
Interessant ist auch, dass in der Berechnung ein Register benötigt wird, welches den Wert 0 enthält. Dies deshalb, da es im AVR keinen Befehl gibt der eine Konstante mit gleichzeitiger Berücksichtigung des Carry-Bits addieren kann. Daher muss diese Konstante zunächst in ein Register geladen werden und erst dann kann die Addition mithilfe dieses Registers vorgenommen werden. Das Interessante daran ist nun, dass dieser Umstand in sehr vielen Programmen vorkommt und es sich bei der Konstanten in der überwiegenden Mehrzahl der Fälle um die Konstante 0 handelt. Viele Programmierer reservieren daher von vorne herein ein Register für diesen Zweck und nennen es das Zero-Register. Sinnvollerweise legt man dieses Register in den Bereich r0..r15, da diese Register etwas zweitklassig sind (ldi, cpi etc. funktionieren nicht damit).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def zero  = r1&lt;br /&gt;
.def count = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
           rjmp    main                ; Reset Handler&lt;br /&gt;
;&lt;br /&gt;
main:&lt;br /&gt;
           ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
           out     SPH, temp1&lt;br /&gt;
           ldi     temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
           out     SPL, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
           ldi     temp1, $FF        ; die Anzeige hängt am Port D&lt;br /&gt;
           out     DDRD, temp1       ; alle Pins auf Ausgang&lt;br /&gt;
;&lt;br /&gt;
           ldi     count, 0          ; und den Zähler initialisieren&lt;br /&gt;
           mov     zero, count&lt;br /&gt;
;&lt;br /&gt;
loop:&lt;br /&gt;
           ldi     ZL, LOW(Codes*2)  ; die Startadresse der Tabelle in den&lt;br /&gt;
           ldi     ZH, HIGH(Codes*2) ; Z-Pointer laden&lt;br /&gt;
&lt;br /&gt;
           mov     temp1, count      ; die wortweise Adressierung der Tabelle&lt;br /&gt;
           add     temp1, count      ; berücksichtigen&lt;br /&gt;
&lt;br /&gt;
           add     ZL, temp1         ; und ausgehend vom Tabellenanfang&lt;br /&gt;
           adc     ZH, zero          ; die Adresse des Code Bytes berechnen&lt;br /&gt;
&lt;br /&gt;
           lpm                       ; dieses Code Byte in das Register r0 laden&lt;br /&gt;
&lt;br /&gt;
           out     PORTD, r0         ; und an die Anzeige ausgeben&lt;br /&gt;
;&lt;br /&gt;
           inc     count             ; den Zähler erhöhen, wobei der Zähler&lt;br /&gt;
           cpi     count, 10         ; immer nur von 0 bis 9 zählen soll&lt;br /&gt;
           brne    wait&lt;br /&gt;
           ldi     count, 0&lt;br /&gt;
;&lt;br /&gt;
wait:      ldi     r17, 10           ; und etwas warten, damit die Ziffer auf&lt;br /&gt;
wait0:     ldi     r18, 0            ; der Anzeige auch lesbar ist, bevor die&lt;br /&gt;
wait1:     ldi     r19, 0            ; nächste Ziffer gezeigt wird&lt;br /&gt;
wait2:     dec     r19&lt;br /&gt;
           brne    wait2&lt;br /&gt;
           dec     r18&lt;br /&gt;
           brne    wait1&lt;br /&gt;
           dec     r17&lt;br /&gt;
           brne    wait0&lt;br /&gt;
;&lt;br /&gt;
           rjmp    loop              ; auf zur nächsten Ausgabe&lt;br /&gt;
;&lt;br /&gt;
Codes:                               ; Die Codetabelle für die Ziffern 0 bis 9&lt;br /&gt;
                                     ; sie regelt, welche Segmente für eine bestimmte&lt;br /&gt;
                                     ; Ziffer eingeschaltet werden müssen&lt;br /&gt;
                                     ;&lt;br /&gt;
           .db     0b11000000        ; 0: a, b, c, d, e, f&lt;br /&gt;
           .db     0b11111001        ; 1: b, c&lt;br /&gt;
           .db     0b10100100        ; 2: a, b, d, e, g&lt;br /&gt;
           .db     0b10110000        ; 3: a, b, c, d, g&lt;br /&gt;
           .db     0b10011001        ; 4: b, c, f, g&lt;br /&gt;
           .db     0b10010010        ; 5: a, c, d, f, g&lt;br /&gt;
           .db     0b10000010        ; 6: a, c, d, e, f, g&lt;br /&gt;
           .db     0b11111000        ; 7: a, b, c&lt;br /&gt;
           .db     0b10000000        ; 8: a, b, c, d, e, f, g&lt;br /&gt;
           .db     0b10010000        ; 9: a, b, c, d, f, g &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mehrere 7-Segment Anzeigen (Multiplexen)==&lt;br /&gt;
&lt;br /&gt;
Mit dem bisherigen Vorwissen könnte man sich jetzt daran machen, auch einmal drei oder vier Anzeigen mit dem Mega8 anzusteuern. Leider gibt es da ein Problem, denn für eine Anzeige sind acht Portpins notwendig - vier Anzeigen würden demnach 32 Portpins benötigen. Die hat der Mega8 aber nicht. Dafür gibt es aber mehrere Auswege. Schieberegister sind bereits in einem anderen [[AVR-Tutorial:_Schieberegister|Tutorial]] beschrieben. Damit könnte man sich ganz leicht die benötigten 32 Ausgangsleitungen mit nur 3 Portpins erzeugen. Das Prinzip der Ansteuerung unterscheidet sich in nichts von der Ansteurung einer einzelnen 7-Segment Anzeige, lediglich die Art und Weise, wie die &#039;Ausgangspins&#039; zu ihren Werten kommen ist anders und durch die Verwendung von Schieberegistern vorgegeben. An dieser Stelle soll aber eine andere Variante der Ansteuerung gezeigt werden. Im Folgenden werden wir uns daher das [[Multiplexen]] einmal näher ansehen.&lt;br /&gt;
&lt;br /&gt;
Multiplexen bedeutet, dass nicht alle vier Anzeigen gleichzeitig eingeschaltet sind, sondern immer nur Eine für eine kurze Zeit. Geschieht der Wechsel zwischen den Anzeigen schneller als wir Menschen das wahrnehmen können, so erscheinen uns alle vier Anzeigen gleichzeitig in Betrieb zu sein obwohl immer nur Eine für eine kurze Zeit aufleuchtet. Dabei handelt es sich praktisch um einen Sonderfall einer [[LED-Matrix]] mit nur einer Zeile. Die vier Anzeigen können sich dadurch die einzelnen Segmentleitungen teilen und alles was benötigt wird sind 4 zusätzliche Steuerleitungen für die 4 Anzeigen, mit denen jeweils eine Anzeige eingeschaltet wird. Dieses Ein/Ausschalten wird mit einem pnp-Transistor in der Versorgungsspannung jeder Anzeige realisiert, die vom Mega8 am &#039;&#039;&#039;PortC&#039;&#039;&#039; angesteuert werden.&lt;br /&gt;
&lt;br /&gt;
Ein Aspekt dieser Ansteuerungsart ist die Multiplexfrequenz, also ein kompletter Zyklus das Weiterschaltens von einer Anzeige zur nächsten . Sie muss hoch genug sein, um ein Flimmern der Anzeige zu vermeiden. Das menschliche Auge ist träge, im Kino reichen 24 Bilder pro Sekunde, beim Fernseher sind es 50. Um auf der sicheren Seite zu sein, dass auch Standbilder ruhig wirken, sollen jedes Segment mit mindestens 100 Hz angesteuert werden, es also mindestens alle 10ms angeschaltet ist. In Ausnahmefällen können aber selbst 100 Hz noch flimmern, z.&amp;amp;nbsp;B. wenn die Anzeige schnell bewegt wird oder wenn es zu Interferenzerscheinungen mit künstlichen Lichtquellen kommt, die mit Wechselstrom betrieben werden.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_03.gif | thumb|right|280px | Ansteuerung von vier 7-Segmentanzeigen per Zeit-Multiplex]]&lt;br /&gt;
Bei genauerer Betrachtung fällt auch auf, dass die vier Anzeigen nicht mehr ganz so hell leuchten wie die eine einzelne Anzeige ohne Multiplexen. Bei wenigen Anzeigen ist dies praktisch kaum sichtbar, erst bei mehreren Anzeigen wird es deutlich. Um dem entgegen zu wirken lässt man pro Segment einfach mehr Strom fließen, bei LEDs dürfen dann 20mA überschritten werden. Als Faustregel gilt, dass der n-fache Strom für die (1/n)-fache Zeit fließen darf. Details finden sich im Datenblatt unter dem Punkt &#039;&#039;&#039;Peak-Current&#039;&#039;&#039; (Spitzenstrom) und &#039;&#039;&#039;Duty-Cycle&#039;&#039;&#039; ([[PWM|Tastverhältnis]]).&lt;br /&gt;
 &lt;br /&gt;
Allerdings gibt es noch ein anderes Problem wenn insgesamt zu viele Anzeigen gemultiplext werden. Die Pulsströme durch die LEDs werden einfach zu hoch. Die meisten LEDs kann man bis 8:1 multiplexen, manchmal auch bis 16:1. Hier fliesst aber schon ein Pulsstrom von 320mA (16 x 20mA), was nicht mehr ganz ungefährlich ist. &#039;&#039;&#039;Strom lässt sich durch Multiplexen nicht sparen&#039;&#039;&#039;, denn die verbrauchte Leistung ändert sich beim n-fachen Strom für 1/n der Zeit nicht. Kritisch wird es aber, wenn das Multiplexen deaktiviert wird (Ausfall der Ansteuerung durch Hardware- oder Softwarefehler) und der n-fache Strom dauerhaft durch eine Segment-LED fließt. Bei 320mA werden die meisten LEDs innerhalb von Sekunden zerstört. Hier muss sichergestellt werden, dass sowohl Programm (Breakpoint im Debugger) als auch Schaltung (Reset, Power-On, [http://www.mikrocontroller.net/topic/107941]) diesen Fall verhindern. Prinzipiell sollte man immer den Pulsstrom und die Multiplexfrequenz einmal überschlagen, bevor der Lötkolben angeworfen wird.&lt;br /&gt;
&lt;br /&gt;
Sollten die Anzeigen zu schwach leuchten so können, wie bereits beschrieben, die Ströme durch die Anzeigen erhöht werden. Dazu werden die 330Ω Widerstände kleiner gemacht. Da hier 4 Anzeigen gemultiplext werden, würden sich Widerstände in der Größenordnung von 100Ω anbieten. Auch muss dann der Basiswiderstand der Transistoren verkleinert werden. Auch muss berücksichtigt werden, dass der Mega8 in Summe an seinen Portpins und an den Versorgungsleitungen nicht beliebig viel Strom liefern oder abführen kann. Auch hier ist daher wieder ein Blick ins Datenblatt angebracht und gegebenenfalls muss wieder ein Transistor als Verstärker eingesetzt werden (oder eben fertige Treiberstufen in IC-Form).&lt;br /&gt;
&lt;br /&gt;
===Programm===&lt;br /&gt;
&lt;br /&gt;
Das folgende Programm zeigt eine Möglichkeit zum Multiplexen. Dazu wird ein Timer benutzt, der in regelmässigen Zeitabständen einen Overflow [[Interrupt]] auslöst. Innerhalb der Overflow Interrupt Routine wird&lt;br /&gt;
* die momentan erleuchtete Anzeige abgeschaltet&lt;br /&gt;
* das Muster für die nächste Anzeige am Port D ausgegeben&lt;br /&gt;
* die nächste Anzeige durch eine entsprechende Ausgabe am Port C eingeschaltet&lt;br /&gt;
&lt;br /&gt;
Da Interruptfunktionen kurz sein sollten, holt die Interrupt Routine das auszugebende Muster für jede Stelle direkt aus dem SRAM, wo sie die Ausgabefunktion hinterlassen hat. Dies hat 2 Vorteile:&lt;br /&gt;
* Zum einen braucht die Interrupt Routine die Umrechnung einer Ziffer in das entsprechende Bitmuster nicht selbst machen&lt;br /&gt;
* Zum anderen ist die Anzeigefunktion dadurch unabhängig von dem was angezeigt wird. Die Interrupt Routine gibt das Bitmuster so aus, wie sie es aus dem SRAM liest. Werden die SRAM Zellen mit geeigneten Bitmustern gefüllt, können so auch einige Buchstaben oder Balkengrafik oder auch kleine Balken-Animationen abgespielt werden. Insbesondere letzteres sieht man manchmal bei Consumer-Geräten kurz nach dem Einschalten des Gerätes um eine Art Defektkontrolle zu ermöglichen oder einfach nur als optischer Aufputz.&lt;br /&gt;
&lt;br /&gt;
Die Funktion out_number ist in einer ähnlichen Form auch schon an anderer Stelle vorgekommen: Sie verwendet die Technik der fortgesetzten Subtraktionen um eine Zahl in einzelne Ziffern zu zerlegen. Sobald jede Stelle feststeht, wird über die Codetabelle das Bitmuster aufgesucht, welches für die Interrupt Funktion an der entsprechenden Stelle im SRAM abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung&#039;&#039;&#039;: Anders als bei der weiter oben gezeigten Variante wurde die Codetabelle ohne Padding Bytes angelegt. Dadurch ist es auch nicht notwendig derartige Padding Bytes in der Programmierung zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist wieder die übliche Portinitialisierung, Timerinitialisierung und eine einfache Anwendung, indem ein 16 Bit Zähler laufend erhöht und über die Funktion out_number ausgegeben wird. Wie schon im ersten Beispiel, wurde auch hier kein Aufwand getrieben: Zähler um 1 erhöhen und mit Warteschleifen eine gewisse Verzögerungszeit einhalten. In einer realen Applikation wird man das natürlich nicht so machen, sondern ebenfalls einen Timer für diesen Teilaspekt der Aufgabenstellung einsetzen.&lt;br /&gt;
&lt;br /&gt;
Weiterhin ist auch noch interessant. Die Overflow Interrupt Funktion ist wieder so ausgelegt, dass sie völlig transparent zum restlichen Programm ablaufen kann. Dies bedeutet, dass alle verwendeten Register beim Aufruf der Interrupt Funktion gesichert und beim Verlassen wiederhergestellt werden. Dadurch ist man auf der absolut sicheren Seite, hat aber den Nachteil etwas Rechenzeit für manchmal unnötige Sicherungs- und Aufräumarbeiten zu &#039;verschwenden&#039;. Stehen genug freie Register zur Verfügung, dann wird man natürlich diesen Aufwand nicht treiben, sondern ein paar Register ausschließlich für die Zwecke der Behandlung der 7-Segment Anzeige abstellen und sich damit den Aufwand der Registersicherung sparen (mit Ausnahme von &#039;&#039;&#039;SREG&#039;&#039;&#039; natürlich!).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
           rjmp    main                ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
           rjmp    multiplex&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
;********************************************************************&lt;br /&gt;
; Die Multiplexfunktion&lt;br /&gt;
;&lt;br /&gt;
; Aufgabe dieser Funktion ist es, bei jedem Durchlauf eine andere Stelle&lt;br /&gt;
; der 7-Segmentanzeige zu aktivieren und das dort vorgesehene Muster&lt;br /&gt;
; auszugeben&lt;br /&gt;
; Die Funktion wird regelmässig in einem Timer Interrupt aufgerufen&lt;br /&gt;
;&lt;br /&gt;
; verwendet werden 6 Bytes im SRAM (siehe .DSEG weiter unten im Programm)&lt;br /&gt;
;    NextDigit    Bitmuster für die Aktivierung des nächsten Segments&lt;br /&gt;
;    NextSegment  Nummer des nächsten aktiven Segments&lt;br /&gt;
;    Segment0     Ausgabemuster für Segment 0&lt;br /&gt;
;    Segment1     Ausgabemuster für Segment 1&lt;br /&gt;
;    Segment2     Ausgabemuster für Segment 2&lt;br /&gt;
;    Segment3     Ausgabemuster für Segment 3&lt;br /&gt;
;&lt;br /&gt;
; NextSegment ist einfach nur ein Zähler, der bei jedem Aufruf der Funktion&lt;br /&gt;
;   um 1 weitergezählt wird und bei 4 wieder auf 0 zurückgestellt wird.&lt;br /&gt;
;   er wird benutzt, um ausgehend von der Adresse von Segment0, auf das&lt;br /&gt;
;   jeweils als nächstes auszugebende Muster aus Segement0, Segment1,&lt;br /&gt;
;   Segment2 oder Segment3 zuzugreifen. Die Adresse von Segment0 wird&lt;br /&gt;
;   in den Z-Pointer geladen und NextSegment addiert.&lt;br /&gt;
;&lt;br /&gt;
; NextDigit enthält das Bitmuster, welches direkt an den Port C ausgegeben&lt;br /&gt;
;   wird und den jeweils nächsten Transistor durchschaltet.&lt;br /&gt;
;   Dazu enthält NextDigit am Anfang das Bitmuster 0b11111110, welches&lt;br /&gt;
;   bei jedem Aufruf um 1 Stelle nach links verschoben wird. Beim nächsten&lt;br /&gt;
;   Aufruf findet sich dann 0b11111101 in NextDigit, dann 0b11111011 und&lt;br /&gt;
;   zu guter letzt 0b11110111. Wird beim nächsten Schiebevorgang 0b11101111&lt;br /&gt;
;   erkannt, dann wird NextDigit auf 0b11111110 zurückgesetzt (und NextSegment&lt;br /&gt;
;   auf 0) und das ganze Spiel beginnt beim nächsten Funktionsaufruf wieder&lt;br /&gt;
;   von vorne.&lt;br /&gt;
;&lt;br /&gt;
; Segment0 .. 3  enthalten die auszugebenden Bitmuster für die Einzelleds&lt;br /&gt;
;   der jeweiligen 7-Segment Anzeigen. Diese Muster werden so wie sie sind&lt;br /&gt;
;   einfach ausgegeben. Soll eine der Anzeigen etwas bestimmtes anzeigen&lt;br /&gt;
;   (zb eine Ziffer), so obliegt es dem Code, der Werte in diese SRAM&lt;br /&gt;
;   Zellen schreibt, das dafür zuständige Bitmuster dort zu hinterlassen.&lt;br /&gt;
;   Die Multiplexroutine kümmert sich nicht darum, dass diese Bitmuster&lt;br /&gt;
;   in irgendeiner Art und Weise sinnvoll (oder was man dafür halten könnte)&lt;br /&gt;
;   sind.&lt;br /&gt;
;&lt;br /&gt;
; veränderte CPU-Register: keine&lt;br /&gt;
;&lt;br /&gt;
multiplex:&lt;br /&gt;
           push    temp                ; Alle verwendeten Register sichern&lt;br /&gt;
           push    temp1&lt;br /&gt;
           in      temp, SREG&lt;br /&gt;
           push    temp&lt;br /&gt;
           push    ZL&lt;br /&gt;
           push    ZH&lt;br /&gt;
&lt;br /&gt;
           ldi     temp1, 0            ; Die 7 Segment ausschalten&lt;br /&gt;
           out     PORTC, temp1&lt;br /&gt;
&lt;br /&gt;
                                       ; Das Muster für die nächste Stelle ausgeben&lt;br /&gt;
                                       ; Dazu zunächst mal berechnen, welches Segment als&lt;br /&gt;
                                       ; nächstest ausgegeben werden muss&lt;br /&gt;
           ldi     ZL, LOW( Segment0 ) &lt;br /&gt;
           ldi     ZH, HIGH( Segment0 )&lt;br /&gt;
           lds     temp, NextSegment&lt;br /&gt;
           add     ZL, temp&lt;br /&gt;
           adc     ZH, temp1&lt;br /&gt;
&lt;br /&gt;
           ld      temp, Z             ; das entsprechende Muster holen und ausgeben&lt;br /&gt;
           out     PORTD, temp&lt;br /&gt;
&lt;br /&gt;
           lds     temp1, NextDigit    ; Und die betreffende Stelle einschalten&lt;br /&gt;
           out     PORTC, temp1&lt;br /&gt;
&lt;br /&gt;
           lds     temp, NextSegment&lt;br /&gt;
           inc     temp&lt;br /&gt;
           sec&lt;br /&gt;
           rol     temp1               ; beim nächsten Interrupt kommt reihum die&lt;br /&gt;
           cpi     temp1, 0b11101111   ; nächste Stelle dran.&lt;br /&gt;
           brne    multi1&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           ldi     temp1, 0b11111110&lt;br /&gt;
&lt;br /&gt;
multi1:&lt;br /&gt;
           sts     NextSegment, temp&lt;br /&gt;
           sts     NextDigit, temp1&lt;br /&gt;
&lt;br /&gt;
           pop     ZH                  ; die gesicherten Register wiederherstellen&lt;br /&gt;
           pop     ZL&lt;br /&gt;
           pop     temp&lt;br /&gt;
           out     SREG, temp&lt;br /&gt;
           pop     temp1&lt;br /&gt;
           pop     temp&lt;br /&gt;
           reti&lt;br /&gt;
;&lt;br /&gt;
;************************************************************************&lt;br /&gt;
; 16 Bit-Zahl aus dem Registerpaar temp (=low), temp1 (=high) ausgeben&lt;br /&gt;
; die Zahl muss kleiner als 10000 sein, da die Zehntausenderstelle&lt;br /&gt;
; nicht berücksichtigt wird.&lt;br /&gt;
; Werden mehr als 4 7-Segmentanzeigen eingesetzt, dann muss dies&lt;br /&gt;
; natürlich auch hier berücksichtigt werden&lt;br /&gt;
;&lt;br /&gt;
out_number:&lt;br /&gt;
           push    temp&lt;br /&gt;
           push    temp1&lt;br /&gt;
&lt;br /&gt;
           ldi     temp2, -1            ; Die Tausenderstelle bestimmen&lt;br /&gt;
_out_tausend:&lt;br /&gt;
           inc     temp2&lt;br /&gt;
           subi    temp, low(1000)      ; -1000&lt;br /&gt;
           sbci    temp1, high(1000)&lt;br /&gt;
           brcc    _out_tausend&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; fuer diese Ziffer das Codemuster fuer&lt;br /&gt;
           ldi     ZH, high(2*Codes)    ; die Anzeige in der Codetabelle nachschlagen&lt;br /&gt;
           add     ZL, temp2&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment3, r0         ; und dieses Muster im SRAM ablegen&lt;br /&gt;
                                        ; die OvI Routine sorgt dann duer die Anzeige&lt;br /&gt;
           ldi     temp2, 10&lt;br /&gt;
&lt;br /&gt;
_out_hundert:                           ; die Hunderterstelle bestimmen&lt;br /&gt;
           dec     temp2                &lt;br /&gt;
           subi    temp, low(-100)      ; +100&lt;br /&gt;
           sbci    temp1, high(-100)&lt;br /&gt;
           brcs    _out_hundert&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; wieder in der Codetabelle das entsprechende&lt;br /&gt;
           ldi     ZH, high(2*Codes)    ; Muster nachschlagen&lt;br /&gt;
           add     ZL, temp2&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment2, r0         ; und im SRAM hinterlassen&lt;br /&gt;
&lt;br /&gt;
           ldi     temp2, -1&lt;br /&gt;
_out_zehn:                              ; die Zehnerstelle bestimmen&lt;br /&gt;
           inc     temp2&lt;br /&gt;
           subi    temp, low(10)        ; -10&lt;br /&gt;
           sbci    temp1, high(10)&lt;br /&gt;
           brcc    _out_zehn&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; wie gehabt: Die Ziffer in der Codetabelle&lt;br /&gt;
           ldi     ZH, high(2*Codes)    ; aufsuchen&lt;br /&gt;
           add     ZL, temp2&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment1, r0         ; und entsprechend im SRAM ablegen&lt;br /&gt;
&lt;br /&gt;
_out_einer:                             ; bleiben noch die Einer&lt;br /&gt;
           subi    temp, low(-10)       ; +10&lt;br /&gt;
           sbci    temp1, high(-10)&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; ... Codetabelle&lt;br /&gt;
           ldi     ZH, high(2*Codes)&lt;br /&gt;
           add     ZL, temp&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment0, r0         ; und ans SRAm ausgeben&lt;br /&gt;
&lt;br /&gt;
           pop     temp1&lt;br /&gt;
           pop     temp&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
;&lt;br /&gt;
;**************************************************************************&lt;br /&gt;
;&lt;br /&gt;
main:&lt;br /&gt;
           ldi     temp, HIGH(RAMEND)&lt;br /&gt;
           out     SPH, temp&lt;br /&gt;
           ldi     temp, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
           out     SPL, temp&lt;br /&gt;
;                                     die Segmenttreiber initialisieren&lt;br /&gt;
           ldi     temp, $FF&lt;br /&gt;
           out     DDRD, temp&lt;br /&gt;
;                                     die Treiber für die einzelnen Stellen&lt;br /&gt;
           ldi     temp, $0F&lt;br /&gt;
           out     DDRC, temp&lt;br /&gt;
;                                     initialisieren der Steuerung für die&lt;br /&gt;
;                                     Interrupt Routine&lt;br /&gt;
           ldi     temp, 0b11111110&lt;br /&gt;
           sts     NextDigit, temp&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           sts     NextSegment, temp&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, ( 1 &amp;lt;&amp;lt; CS01 ) | ( 1 &amp;lt;&amp;lt; CS00 )&lt;br /&gt;
           out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
           out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
           sei&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           ldi     temp1, 0&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           inc     temp&lt;br /&gt;
           brne    _loop&lt;br /&gt;
           inc     temp1&lt;br /&gt;
_loop:&lt;br /&gt;
           rcall    out_number&lt;br /&gt;
&lt;br /&gt;
           cpi     temp, low( 4000 )&lt;br /&gt;
           brne    wait&lt;br /&gt;
           cpi     temp1, high( 4000 )&lt;br /&gt;
           brne    wait&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           ldi     temp1, 0&lt;br /&gt;
&lt;br /&gt;
wait:      ldi     r21, 1&lt;br /&gt;
wait0:     ldi     r22, 0&lt;br /&gt;
wait1:     ldi     r23, 0&lt;br /&gt;
wait2:     dec     r23&lt;br /&gt;
           brne    wait2&lt;br /&gt;
           dec     r22&lt;br /&gt;
           brne    wait1&lt;br /&gt;
           dec     r21&lt;br /&gt;
           brne    wait0&lt;br /&gt;
&lt;br /&gt;
           rjmp    loop&lt;br /&gt;
&lt;br /&gt;
Codes:&lt;br /&gt;
    .db  0b11000000, 0b11111001     ; 0: a, b, c, d, e, f&lt;br /&gt;
                                    ; 1: b, c&lt;br /&gt;
    .db  0b10100100, 0b10110000     ; 2: a, b, d, e, g&lt;br /&gt;
                                    ; 3: a, b, c, d, g&lt;br /&gt;
    .db  0b10011001, 0b10010010     ; 4: b, c, f, g&lt;br /&gt;
                                    ; 5: a, c, d, f, g&lt;br /&gt;
    .db  0b10000010, 0b11111000     ; 6: a, c, d, e, f, g&lt;br /&gt;
                                    ; 7: a, b, c&lt;br /&gt;
    .db  0b10000000, 0b10010000     ; 8: a, b, c, d, e, f, g&lt;br /&gt;
                                    ; 9: a, b, c, d, f, g &lt;br /&gt;
&lt;br /&gt;
           .DSEG&lt;br /&gt;
NextDigit:   .byte 1         ; Bitmuster für die Aktivierung des nächsten Segments&lt;br /&gt;
NextSegment: .byte 1         ; Nummer des nächsten aktiven Segments&lt;br /&gt;
Segment0:    .byte 1         ; Ausgabemuster für Segment 0&lt;br /&gt;
Segment1:    .byte 1         ; Ausgabemuster für Segment 1&lt;br /&gt;
Segment2:    .byte 1         ; Ausgabemuster für Segment 2&lt;br /&gt;
Segment3:    .byte 1         ; Ausgabemuster für Segment 3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Forenbeiträge ==&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/154257 Geisterleuchten bei 7-Segment Anzeige]&lt;br /&gt;
&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/262337#2736074 Re: Zehn 7Segmente im Multiplexbetrieb : Hilfe !] - Ausführliche Erklärung wie und warum man entweder die &#039;&#039;&#039;Stellen&#039;&#039;&#039; oder die &#039;&#039;&#039;Segmente&#039;&#039;&#039; multiplext.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=SRAM|&lt;br /&gt;
zurücklink=AVR-Tutorial: SRAM|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Servos|&lt;br /&gt;
vorlink=AVR-Tutorial: Servo}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|7-Segment-Anzeige]]&lt;br /&gt;
[[Category:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=77809</id>
		<title>AVR-Tutorial: 7-Segment-Anzeige</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=77809"/>
		<updated>2013-07-30T19:14:38Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Schaltung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die Ausgabe von Zahlenwerten auf ein Text-LCD ist sicherlich das Nonplusultra, aber manchmal liegen die Dinge sehr viel einfacher. Um beispielsweise eine Temperatur anzuzeigen ist ein LCD etwas Overkill. In solchen Fällen kann die Ausgabe auf ein paar 7-Segmentanzeigen gemacht werden. Ausserdem haben 7-Segmentanzeigen einen ganz besonderen Charme :-)&lt;br /&gt;
&lt;br /&gt;
==Typen von 7-Segment Anzeigen==&lt;br /&gt;
&lt;br /&gt;
Eine einzelne 7-Segmentanzeige besteht aus sieben (mit Dezimalpunkt acht) einzelnen [[LED]]s in einem gemeinsamen Gehäuse. Aus praktischen Gründen wird einer der beiden Anschlüsse jeder LED mit den gleichen Anschlüssen der anderen LED verbunden und gemeinsam aus dem Gehäuse herausgeführt. Das spart Pins am Gehäuse und später bei der Ansteuerung. Dementsprechend spricht man von Anzeigen mit &#039;&#039;gemeinsamer Anode&#039;&#039; (engl. common anode) bzw. &#039;&#039;gemeinsamer Kathode&#039;&#039; (engl. common cathode).&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_00.gif | framed | center | Interne Verschaltung der 7-Segmentanzeigen]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Eine einzelne 7-Segment Anzeige==&lt;br /&gt;
&lt;br /&gt;
===Schaltung===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_02.gif | thumb| left | 80px| Pin-&amp;lt;br/&amp;gt;belegung einer 7-Segment-&amp;lt;br/&amp;gt;anzeige]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_01.gif | thumb | right |240px| Ansteuerung einer einzelnen 7-Segmentanzeige]]&lt;br /&gt;
&lt;br /&gt;
Eine einzelne 7-Segmentanzeige wird nach dem folgenden Schema am &#039;&#039;&#039;Port D&#039;&#039;&#039; des Mega8 angeschlossen. Port D wurde deshalb gewählt, da er am Mega8 als einziger Port aus den vollen 8 Bit besteht. Die 7-Segmentanzeige hat neben den Segmenten &#039;&#039;&#039;a&#039;&#039;&#039; bis &#039;&#039;&#039;g&#039;&#039;&#039;  eine gemeinsame Anode &#039;&#039;&#039;CA&#039;&#039;&#039; sowie einen Dezimalpunkt &#039;&#039;&#039;dp&#039;&#039;&#039; (siehe Bild).&lt;br /&gt;
&lt;br /&gt;
Welcher Pin an der Anzeige welchem Segment (a-g) bzw. dem Dezimalpunkt entspricht wird am besten dem Datenblatt zur Anzeige entnommen. Hat man kein Datenblatt dann kann man auch empirisch die Pinbelegung feststellen, indem  man mit einer 5V Quelle und einem 1k Widerstand einfach probweise alle Pins &#039;abklappert&#039;. 1k deswegen, damit man im Zweifellsfall Low-Current LED nicht überfordert. Eine 7-Segment Anzeige, die auf die üblichen 10 bis 20mA ausgelegt ist, wird damit immer noch leuchten, wenn auch schwach. Aber mehr braucht es ja auch nicht um die Pinbelegung feststellen zu können. Im Folgenden wird von dieser Segmentbelegung ausgegangen:&lt;br /&gt;
&lt;br /&gt;
Wird eine andere Belegung genutzt dann ist das prinzipiell möglich, jedoch müsste das in der Programmierung berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
Da eine 7-Segmentanzeige konzeptionell sieben einzelnen LEDs entspricht, ergibt sich im Prinzip keine Änderung in der Ansteuerung einer derartigen Anzeige im Vergleich zur LED Ansteuerung wie sie im [[AVR-Tutorial: IO-Grundlagen]] gezeigt wird. Genau wie bei den einzelnen LEDs wird eine davon eingeschaltet, indem der zugehörige Port Pin auf 0 gesetzt wird. Aber anders als bei einzelnen LED möchte man mit einer derartigen Anzeige eine Ziffernanzeige erhalten. Dazu ist es lediglich notwendig, für eine bestimmte Ziffer die richtigen LEDs einzuschalten.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
===Codetabelle===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_02a.gif | right | thumb | 80px | Darstellung der Ziffer &amp;quot;3&amp;quot;]]&lt;br /&gt;
Die Umkodierung von einzelnen Ziffern in ein bestimmtes Ausgabemuster kann über eine sog. Codetabelle geschehen: Die auszugebende Ziffer wird als Offset zum Anfang dieser Tabelle aufgefasst und aus der Tabelle erhält man ein Byte (Code), welches direkt auf den Port ausgegeben werden kann und das entsprechende Bitmuster enthält, sodass die für diese Ziffer notwendigen LED ein- bzw. ausgeschaltet sind.&lt;br /&gt;
&lt;br /&gt;
;Beispiel: Um die Ziffer &#039;&#039;&#039;3&#039;&#039;&#039; anzuzeigen, müssen auf der Anzeige die Segmente &#039;&#039;&#039;a&#039;&#039;&#039;, &#039;&#039;&#039;b&#039;&#039;&#039;, &#039;&#039;&#039;c&#039;&#039;&#039;, &#039;&#039;&#039;d&#039;&#039;&#039; und &#039;&#039;&#039;g&#039;&#039;&#039; aufleuchten. Alle anderen Segmente sollen dunkel sein.&lt;br /&gt;
&lt;br /&gt;
Aus dem Anschlußschema ergibt sich, dass die dazu notwendige Ausgabe am Port binär &#039;&#039;&#039;10110000&#039;&#039;&#039; lauten muss. Untersucht man dies für alle Ziffern, so ergibt sich folgende Tabelle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
    .db  0b11000000     ; 0: a, b, c, d, e, f&lt;br /&gt;
    .db  0b11111001     ; 1: b, c&lt;br /&gt;
    .db  0b10100100     ; 2: a, b, d, e, g&lt;br /&gt;
    .db  0b10110000     ; 3: a, b, c, d, g&lt;br /&gt;
    .db  0b10011001     ; 4: b, c, f, g&lt;br /&gt;
    .db  0b10010010     ; 5: a, c, d, f, g&lt;br /&gt;
    .db  0b10000010     ; 6: a, c, d, e, f, g&lt;br /&gt;
    .db  0b11111000     ; 7: a, b, c&lt;br /&gt;
    .db  0b10000000     ; 8: a, b, c, d, e, f, g&lt;br /&gt;
    .db  0b10010000     ; 9: a, b, c, d, f, g&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Programm===&lt;br /&gt;
&lt;br /&gt;
Das Testprogramm stellt nacheinander die Ziffern 0 bis 9 auf der 7-Segmentanzeige dar. Die jeweils auszugebende Zahl steht im Register &#039;&#039;&#039;count&#039;&#039;&#039; und wird innerhalb der Schleife um jeweils 1 erhöht. Hat das Register den Wert 10 erreicht, so wird es wieder auf 0 zurückgesetzt. Nach der Erhöhung folgt eine Warteschleife, welche dafür sorgt, dass bis zur nächsten Ausgabe eine gewisse Zeit vergeht. Normalerweise macht man keine derartigen langen Warteschleifen, aber hier geht es ja nicht ums Warten sondern um die Ansteuerung einer 7-Segmentanzeige. Einen Timer dafür zu benutzen wäre zunächst zuviel Aufwand.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Ausgabe und damit der in diesem Artikel interessante Teil findet jedoch direkt nach dem Label &amp;lt;i&amp;gt;loop&amp;lt;/i&amp;gt; statt. Die bereits bekannte Codetabelle wird mittels &#039;&#039;&#039;.db&#039;&#039;&#039; Direktive (&#039;&#039;&#039;d&#039;&#039;&#039;efine &#039;&#039;&#039;b&#039;&#039;&#039;yte) in den [[Speicher#Flash-ROM | Flash-Speicher]] gelegt. Der Zugriff darauf erfolgt über den Z-Pointer und dem Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039;. Zusätzlich wird vor dem Zugriff noch der Wert des Registers &#039;&#039;&#039;count&#039;&#039;&#039; und damit der aktuelle Zählerwert zum Z-Pointer addiert.&lt;br /&gt;
&lt;br /&gt;
Beachtet werden muss nur, dass der Zählerwert verdoppelt werden muss. Dies hat folgenden Grund: Wird die Tabelle so wie hier gezeigt mittels einzelnen &#039;&#039;&#039;.db&#039;&#039;&#039; Anweisungen aufgebaut, so fügt der Assembler sog. &#039;&#039;&#039;Padding Bytes&#039;&#039;&#039; zwischen die einzelnen Bytes ein, damit jede &#039;&#039;&#039;.db&#039;&#039;&#039; Anweisung auf einer geraden Speicheradresse liegt. Dies ist eine direkte Folge der Tatsache, dass der Flash-Speicher &#039;&#039;&#039;wortweise&#039;&#039;&#039; (16 Bit) und nicht &#039;&#039;&#039;byteweise&#039;&#039;&#039; (8 Bit) organisiert ist. Da aber von einem .db in der Tabelle zum nächsten .db eine Differenz von 2 Bytes vorliegt, muss dies in der Berechnung berücksichtigt werden. Im zweiten Beispiel auf dieser Seite wird dies anders gemacht. Dort wird gezeigt wie man durch eine andere Schreibweise der Tabelle das Erzeugen der Padding Bytes durch den Assembler verhindern kann.&lt;br /&gt;
&lt;br /&gt;
Aus dem gleichen Grund wird auch der Z-Pointer mit dem 2-fachen der Startadresse der Tabelle geladen. Die Startadresse wird vom Assembler in wortweiser Adressierung eingesetzt, &#039;&#039;&#039;lpm&#039;&#039;&#039; möchte die Zugriffsadresse als Byteadresse angegeben haben.&lt;br /&gt;
&lt;br /&gt;
Interessant ist auch, dass in der Berechnung ein Register benötigt wird, welches den Wert 0 enthält. Dies deshalb, da es im AVR keinen Befehl gibt der eine Konstante mit gleichzeitiger Berücksichtigung des Carry-Bits addieren kann. Daher muss diese Konstante zunächst in ein Register geladen werden und erst dann kann die Addition mithilfe dieses Registers vorgenommen werden. Das Interessante daran ist nun, dass dieser Umstand in sehr vielen Programmen vorkommt und es sich bei der Konstanten in der überwiegenden Mehrzahl der Fälle um die Konstante 0 handelt. Viele Programmierer reservieren daher von vorne herein ein Register für diesen Zweck und nennen es das Zero-Register. Sinnvollerweise legt man dieses Register in den Bereich r0..r15, da diese Register etwas zweitklassig sind (ldi, cpi etc. funktionieren nicht damit).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def zero  = r1&lt;br /&gt;
.def count = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
           rjmp    main                ; Reset Handler&lt;br /&gt;
;&lt;br /&gt;
main:&lt;br /&gt;
           ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
           out     SPH, temp1&lt;br /&gt;
           ldi     temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
           out     SPL, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
           ldi     temp1, $FF        ; die Anzeige hängt am Port D&lt;br /&gt;
           out     DDRD, temp1       ; alle Pins auf Ausgang&lt;br /&gt;
;&lt;br /&gt;
           ldi     count, 0          ; und den Zähler initialisieren&lt;br /&gt;
           mov     zero, count&lt;br /&gt;
;&lt;br /&gt;
loop:&lt;br /&gt;
           ldi     ZL, LOW(Codes*2)  ; die Startadresse der Tabelle in den&lt;br /&gt;
           ldi     ZH, HIGH(Codes*2) ; Z-Pointer laden&lt;br /&gt;
&lt;br /&gt;
           mov     temp1, count      ; die wortweise Adressierung der Tabelle&lt;br /&gt;
           add     temp1, count      ; berücksichtigen&lt;br /&gt;
&lt;br /&gt;
           add     ZL, temp1         ; und ausgehend vom Tabellenanfang&lt;br /&gt;
           adc     ZH, zero          ; die Adresse des Code Bytes berechnen&lt;br /&gt;
&lt;br /&gt;
           lpm                       ; dieses Code Byte in das Register r0 laden&lt;br /&gt;
&lt;br /&gt;
           out     PORTD, r0         ; und an die Anzeige ausgeben&lt;br /&gt;
;&lt;br /&gt;
           inc     count             ; den Zähler erhöhen, wobei der Zähler&lt;br /&gt;
           cpi     count, 10         ; immer nur von 0 bis 9 zählen soll&lt;br /&gt;
           brne    wait&lt;br /&gt;
           ldi     count, 0&lt;br /&gt;
;&lt;br /&gt;
wait:      ldi     r17, 10           ; und etwas warten, damit die Ziffer auf&lt;br /&gt;
wait0:     ldi     r18, 0            ; der Anzeige auch lesbar ist, bevor die&lt;br /&gt;
wait1:     ldi     r19, 0            ; nächste Ziffer gezeigt wird&lt;br /&gt;
wait2:     dec     r19&lt;br /&gt;
           brne    wait2&lt;br /&gt;
           dec     r18&lt;br /&gt;
           brne    wait1&lt;br /&gt;
           dec     r17&lt;br /&gt;
           brne    wait0&lt;br /&gt;
;&lt;br /&gt;
           rjmp    loop              ; auf zur nächsten Ausgabe&lt;br /&gt;
;&lt;br /&gt;
Codes:                               ; Die Codetabelle für die Ziffern 0 bis 9&lt;br /&gt;
                                     ; sie regelt, welche Segmente für eine bestimmte&lt;br /&gt;
                                     ; Ziffer eingeschaltet werden müssen&lt;br /&gt;
                                     ;&lt;br /&gt;
           .db     0b11000000        ; 0: a, b, c, d, e, f&lt;br /&gt;
           .db     0b11111001        ; 1: b, c&lt;br /&gt;
           .db     0b10100100        ; 2: a, b, d, e, g&lt;br /&gt;
           .db     0b10110000        ; 3: a, b, c, d, g&lt;br /&gt;
           .db     0b10011001        ; 4: b, c, f, g&lt;br /&gt;
           .db     0b10010010        ; 5: a, c, d, f, g&lt;br /&gt;
           .db     0b10000010        ; 6: a, c, d, e, f, g&lt;br /&gt;
           .db     0b11111000        ; 7: a, b, c&lt;br /&gt;
           .db     0b10000000        ; 8: a, b, c, d, e, f, g&lt;br /&gt;
           .db     0b10010000        ; 9: a, b, c, d, f, g &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Mehrere 7-Segment Anzeigen (Multiplexen)==&lt;br /&gt;
&lt;br /&gt;
Mit dem bisherigen Vorwissen könnte man sich jetzt daran machen, auch einmal drei oder vier Anzeigen mit dem Mega8 anzusteuern. Leider gibt es da ein Problem, denn für eine Anzeige sind acht Portpins notwendig - vier Anzeigen würden demnach 32 Portpins benötigen. Die hat der Mega8 aber nicht. Dafür gibt es aber mehrere Auswege. Schieberegister sind bereits in einem anderen [[AVR-Tutorial:_Schieberegister|Tutorial]] beschrieben. Damit könnte man sich ganz leicht die benötigten 32 Ausgangsleitungen mit nur 3 Portpins erzeugen. Das Prinzip der Ansteuerung unterscheidet sich in nichts von der Ansteurung einer einzelnen 7-Segment Anzeige, lediglich die Art und Weise, wie die &#039;Ausgangspins&#039; zu ihren Werten kommen ist anders und durch die Verwendung von Schieberegistern vorgegeben. An dieser Stelle soll aber eine andere Variante der Ansteuerung gezeigt werden. Im Folgenden werden wir uns daher das [[Multiplexen]] einmal näher ansehen.&lt;br /&gt;
&lt;br /&gt;
Multiplexen bedeutet, dass nicht alle vier Anzeigen gleichzeitig eingeschaltet sind, sondern immer nur Eine für eine kurze Zeit. Geschieht der Wechsel zwischen den Anzeigen schneller als wir Menschen das wahrnehmen können, so erscheinen uns alle vier Anzeigen gleichzeitig in Betrieb zu sein obwohl immer nur Eine für eine kurze Zeit aufleuchtet. Dabei handelt es sich praktisch um einen Sonderfall einer [[LED-Matrix]] mit nur einer Zeile. Die vier Anzeigen können sich dadurch die einzelnen Segmentleitungen teilen und alles was benötigt wird sind 4 zusätzliche Steuerleitungen für die 4 Anzeigen, mit denen jeweils eine Anzeige eingeschaltet wird. Dieses Ein/Ausschalten wird mit einem pnp-Transistor in der Versorgungsspannung jeder Anzeige realisiert, die vom Mega8 am &#039;&#039;&#039;PortC&#039;&#039;&#039; angesteuert werden.&lt;br /&gt;
&lt;br /&gt;
Ein Aspekt dieser Ansteuerungsart ist die Multiplexfrequenz, also ein kompletter Zyklus das Weiterschaltens von einer Anzeige zur nächsten . Sie muss hoch genug sein, um ein Flimmern der Anzeige zu vermeiden. Das menschliche Auge ist träge, im Kino reichen 24 Bilder pro Sekunde, beim Fernseher sind es 50. Um auf der sicheren Seite zu sein, dass auch Standbilder ruhig wirken, sollen jedes Segment mit mindestens 100 Hz angesteuert werden, es also mindestens alle 10ms angeschaltet ist. In Ausnahmefällen können aber selbst 100 Hz noch flimmern, z.&amp;amp;nbsp;B. wenn die Anzeige schnell bewegt wird oder wenn es zu Interferenzerscheinungen mit künstlichen Lichtquellen kommt, die mit Wechselstrom betrieben werden.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_7_Seg_03.gif | thumb|right|280px | Ansteuerung von vier 7-Segmentanzeigen per Zeit-Multiplex]]&lt;br /&gt;
Bei genauerer Betrachtung fällt auch auf, dass die vier Anzeigen nicht mehr ganz so hell leuchten wie die eine einzelne Anzeige ohne Multiplexen. Bei wenigen Anzeigen ist dies praktisch kaum sichtbar, erst bei mehreren Anzeigen wird es deutlich. Um dem entgegen zu wirken lässt man pro Segment einfach mehr Strom fließen, bei LEDs dürfen dann 20mA überschritten werden. Als Faustregel gilt, dass der n-fache Strom für die (1/n)-fache Zeit fließen darf. Details finden sich im Datenblatt unter dem Punkt &#039;&#039;&#039;Peak-Current&#039;&#039;&#039; (Spitzenstrom) und &#039;&#039;&#039;Duty-Cycle&#039;&#039;&#039; ([[PWM|Tastverhältnis]]).&lt;br /&gt;
 &lt;br /&gt;
Allerdings gibt es noch ein anderes Problem wenn insgesamt zu viele Anzeigen gemultiplext werden. Die Pulsströme durch die LEDs werden einfach zu hoch. Die meisten LEDs kann man bis 8:1 multiplexen, manchmal auch bis 16:1. Hier fliesst aber schon ein Pulsstrom von 320mA (16 x 20mA), was nicht mehr ganz ungefährlich ist. &#039;&#039;&#039;Strom lässt sich durch Multiplexen nicht sparen&#039;&#039;&#039;, denn die verbrauchte Leistung ändert sich beim n-fachen Strom für 1/n der Zeit nicht. Kritisch wird es aber, wenn das Multiplexen deaktiviert wird (Ausfall der Ansteuerung durch Hardware- oder Softwarefehler) und der n-fache Strom dauerhaft durch eine Segment-LED fließt. Bei 320mA werden die meisten LEDs innerhalb von Sekunden zerstört. Hier muss sichergestellt werden, dass sowohl Programm (Breakpoint im Debugger) als auch Schaltung (Reset, Power-On, [http://www.mikrocontroller.net/topic/107941]) diesen Fall verhindern. Prinzipiell sollte man immer den Pulsstrom und die Multiplexfrequenz einmal überschlagen, bevor der Lötkolben angeworfen wird.&lt;br /&gt;
&lt;br /&gt;
Sollten die Anzeigen zu schwach leuchten so können, wie bereits beschrieben, die Ströme durch die Anzeigen erhöht werden. Dazu werden die 330Ω Widerstände kleiner gemacht. Da hier 4 Anzeigen gemultiplext werden, würden sich Widerstände in der Größenordnung von 100Ω anbieten. Auch muss dann der Basiswiderstand der Transistoren verkleinert werden. Auch muss berücksichtigt werden, dass der Mega8 in Summe an seinen Portpins und an den Versorgungsleitungen nicht beliebig viel Strom liefern oder abführen kann. Auch hier ist daher wieder ein Blick ins Datenblatt angebracht und gegebenenfalls muss wieder ein Transistor als Verstärker eingesetzt werden (oder eben fertige Treiberstufen in IC-Form).&lt;br /&gt;
&lt;br /&gt;
===Programm===&lt;br /&gt;
&lt;br /&gt;
Das folgende Programm zeigt eine Möglichkeit zum Multiplexen. Dazu wird ein Timer benutzt, der in regelmässigen Zeitabständen einen Overflow [[Interrupt]] auslöst. Innerhalb der Overflow Interrupt Routine wird&lt;br /&gt;
* die momentan erleuchtete Anzeige abgeschaltet&lt;br /&gt;
* das Muster für die nächste Anzeige am Port D ausgegeben&lt;br /&gt;
* die nächste Anzeige durch eine entsprechende Ausgabe am Port C eingeschaltet&lt;br /&gt;
&lt;br /&gt;
Da Interruptfunktionen kurz sein sollten, holt die Interrupt Routine das auszugebende Muster für jede Stelle direkt aus dem SRAM, wo sie die Ausgabefunktion hinterlassen hat. Dies hat 2 Vorteile:&lt;br /&gt;
* Zum einen braucht die Interrupt Routine die Umrechnung einer Ziffer in das entsprechende Bitmuster nicht selbst machen&lt;br /&gt;
* Zum anderen ist die Anzeigefunktion dadurch unabhängig von dem was angezeigt wird. Die Interrupt Routine gibt das Bitmuster so aus, wie sie es aus dem SRAM liest. Werden die SRAM Zellen mit geeigneten Bitmustern gefüllt, können so auch einige Buchstaben oder Balkengrafik oder auch kleine Balken-Animationen abgespielt werden. Insbesondere letzteres sieht man manchmal bei Consumer-Geräten kurz nach dem Einschalten des Gerätes um eine Art Defektkontrolle zu ermöglichen oder einfach nur als optischer Aufputz.&lt;br /&gt;
&lt;br /&gt;
Die Funktion out_number ist in einer ähnlichen Form auch schon an anderer Stelle vorgekommen: Sie verwendet die Technik der fortgesetzten Subtraktionen um eine Zahl in einzelne Ziffern zu zerlegen. Sobald jede Stelle feststeht, wird über die Codetabelle das Bitmuster aufgesucht, welches für die Interrupt Funktion an der entsprechenden Stelle im SRAM abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung&#039;&#039;&#039;: Anders als bei der weiter oben gezeigten Variante wurde die Codetabelle ohne Padding Bytes angelegt. Dadurch ist es auch nicht notwendig derartige Padding Bytes in der Programmierung zu berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Der Rest ist wieder die übliche Portinitialisierung, Timerinitialisierung und eine einfache Anwendung, indem ein 16 Bit Zähler laufend erhöht und über die Funktion out_number ausgegeben wird. Wie schon im ersten Beispiel, wurde auch hier kein Aufwand getrieben: Zähler um 1 erhöhen und mit Warteschleifen eine gewisse Verzögerungszeit einhalten. In einer realen Applikation wird man das natürlich nicht so machen, sondern ebenfalls einen Timer für diesen Teilaspekt der Aufgabenstellung einsetzen.&lt;br /&gt;
&lt;br /&gt;
Weiterhin ist auch noch interessant. Die Overflow Interrupt Funktion ist wieder so ausgelegt, dass sie völlig transparent zum restlichen Programm ablaufen kann. Dies bedeutet, dass alle verwendeten Register beim Aufruf der Interrupt Funktion gesichert und beim Verlassen wiederhergestellt werden. Dadurch ist man auf der absolut sicheren Seite, hat aber den Nachteil etwas Rechenzeit für manchmal unnötige Sicherungs- und Aufräumarbeiten zu &#039;verschwenden&#039;. Stehen genug freie Register zur Verfügung, dann wird man natürlich diesen Aufwand nicht treiben, sondern ein paar Register ausschließlich für die Zwecke der Behandlung der 7-Segment Anzeige abstellen und sich damit den Aufwand der Registersicherung sparen (mit Ausnahme von &#039;&#039;&#039;SREG&#039;&#039;&#039; natürlich!).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp  = r16&lt;br /&gt;
.def temp1 = r17&lt;br /&gt;
.def temp2 = r18&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
           rjmp    main                ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
           rjmp    multiplex&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
;********************************************************************&lt;br /&gt;
; Die Multiplexfunktion&lt;br /&gt;
;&lt;br /&gt;
; Aufgabe dieser Funktion ist es, bei jedem Durchlauf eine andere Stelle&lt;br /&gt;
; der 7-Segmentanzeige zu aktivieren und das dort vorgesehene Muster&lt;br /&gt;
; auszugeben&lt;br /&gt;
; Die Funktion wird regelmässig in einem Timer Interrupt aufgerufen&lt;br /&gt;
;&lt;br /&gt;
; verwendet werden 6 Bytes im SRAM (siehe .DSEG weiter unten im Programm)&lt;br /&gt;
;    NextDigit    Bitmuster für die Aktivierung des nächsten Segments&lt;br /&gt;
;    NextSegment  Nummer des nächsten aktiven Segments&lt;br /&gt;
;    Segment0     Ausgabemuster für Segment 0&lt;br /&gt;
;    Segment1     Ausgabemuster für Segment 1&lt;br /&gt;
;    Segment2     Ausgabemuster für Segment 2&lt;br /&gt;
;    Segment3     Ausgabemuster für Segment 3&lt;br /&gt;
;&lt;br /&gt;
; NextSegment ist einfach nur ein Zähler, der bei jedem Aufruf der Funktion&lt;br /&gt;
;   um 1 weitergezählt wird und bei 4 wieder auf 0 zurückgestellt wird.&lt;br /&gt;
;   er wird benutzt, um ausgehend von der Adresse von Segment0, auf das&lt;br /&gt;
;   jeweils als nächstes auszugebende Muster aus Segement0, Segment1,&lt;br /&gt;
;   Segment2 oder Segment3 zuzugreifen. Die Adresse von Segment0 wird&lt;br /&gt;
;   in den Z-Pointer geladen und NextSegment addiert.&lt;br /&gt;
;&lt;br /&gt;
; NextDigit enthält das Bitmuster, welches direkt an den Port C ausgegeben&lt;br /&gt;
;   wird und den jeweils nächsten Transistor durchschaltet.&lt;br /&gt;
;   Dazu enthält NextDigit am Anfang das Bitmuster 0b11111110, welches&lt;br /&gt;
;   bei jedem Aufruf um 1 Stelle nach links verschoben wird. Beim nächsten&lt;br /&gt;
;   Aufruf findet sich dann 0b11111101 in NextDigit, dann 0b11111011 und&lt;br /&gt;
;   zu guter letzt 0b11110111. Wird beim nächsten Schiebevorgang 0b11101111&lt;br /&gt;
;   erkannt, dann wird NextDigit auf 0b11111110 zurückgesetzt (und NextSegment&lt;br /&gt;
;   auf 0) und das ganze Spiel beginnt beim nächsten Funktionsaufruf wieder&lt;br /&gt;
;   von vorne.&lt;br /&gt;
;&lt;br /&gt;
; Segment0 .. 3  enthalten die auszugebenden Bitmuster für die Einzelleds&lt;br /&gt;
;   der jeweiligen 7-Segment Anzeigen. Diese Muster werden so wie sie sind&lt;br /&gt;
;   einfach ausgegeben. Soll eine der Anzeigen etwas bestimmtes anzeigen&lt;br /&gt;
;   (zb eine Ziffer), so obliegt es dem Code, der Werte in diese SRAM&lt;br /&gt;
;   Zellen schreibt, das dafür zuständige Bitmuster dort zu hinterlassen.&lt;br /&gt;
;   Die Multiplexroutine kümmert sich nicht darum, dass diese Bitmuster&lt;br /&gt;
;   in irgendeiner Art und Weise sinnvoll (oder was man dafür halten könnte)&lt;br /&gt;
;   sind.&lt;br /&gt;
;&lt;br /&gt;
; veränderte CPU-Register: keine&lt;br /&gt;
;&lt;br /&gt;
multiplex:&lt;br /&gt;
           push    temp                ; Alle verwendeten Register sichern&lt;br /&gt;
           push    temp1&lt;br /&gt;
           in      temp, SREG&lt;br /&gt;
           push    temp&lt;br /&gt;
           push    ZL&lt;br /&gt;
           push    ZH&lt;br /&gt;
&lt;br /&gt;
           ldi     temp1, 0            ; Die 7 Segment ausschalten&lt;br /&gt;
           out     PORTC, temp1&lt;br /&gt;
&lt;br /&gt;
                                       ; Das Muster für die nächste Stelle ausgeben&lt;br /&gt;
                                       ; Dazu zunächst mal berechnen, welches Segment als&lt;br /&gt;
                                       ; nächstest ausgegeben werden muss&lt;br /&gt;
           ldi     ZL, LOW( Segment0 ) &lt;br /&gt;
           ldi     ZH, HIGH( Segment0 )&lt;br /&gt;
           lds     temp, NextSegment&lt;br /&gt;
           add     ZL, temp&lt;br /&gt;
           adc     ZH, temp1&lt;br /&gt;
&lt;br /&gt;
           ld      temp, Z             ; das entsprechende Muster holen und ausgeben&lt;br /&gt;
           out     PORTD, temp&lt;br /&gt;
&lt;br /&gt;
           lds     temp1, NextDigit    ; Und die betreffende Stelle einschalten&lt;br /&gt;
           out     PORTC, temp1&lt;br /&gt;
&lt;br /&gt;
           lds     temp, NextSegment&lt;br /&gt;
           inc     temp&lt;br /&gt;
           sec&lt;br /&gt;
           rol     temp1               ; beim nächsten Interrupt kommt reihum die&lt;br /&gt;
           cpi     temp1, 0b11101111   ; nächste Stelle dran.&lt;br /&gt;
           brne    multi1&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           ldi     temp1, 0b11111110&lt;br /&gt;
&lt;br /&gt;
multi1:&lt;br /&gt;
           sts     NextSegment, temp&lt;br /&gt;
           sts     NextDigit, temp1&lt;br /&gt;
&lt;br /&gt;
           pop     ZH                  ; die gesicherten Register wiederherstellen&lt;br /&gt;
           pop     ZL&lt;br /&gt;
           pop     temp&lt;br /&gt;
           out     SREG, temp&lt;br /&gt;
           pop     temp1&lt;br /&gt;
           pop     temp&lt;br /&gt;
           reti&lt;br /&gt;
;&lt;br /&gt;
;************************************************************************&lt;br /&gt;
; 16 Bit-Zahl aus dem Registerpaar temp (=low), temp1 (=high) ausgeben&lt;br /&gt;
; die Zahl muss kleiner als 10000 sein, da die Zehntausenderstelle&lt;br /&gt;
; nicht berücksichtigt wird.&lt;br /&gt;
; Werden mehr als 4 7-Segmentanzeigen eingesetzt, dann muss dies&lt;br /&gt;
; natürlich auch hier berücksichtigt werden&lt;br /&gt;
;&lt;br /&gt;
out_number:&lt;br /&gt;
           push    temp&lt;br /&gt;
           push    temp1&lt;br /&gt;
&lt;br /&gt;
           ldi     temp2, -1            ; Die Tausenderstelle bestimmen&lt;br /&gt;
_out_tausend:&lt;br /&gt;
           inc     temp2&lt;br /&gt;
           subi    temp, low(1000)      ; -1000&lt;br /&gt;
           sbci    temp1, high(1000)&lt;br /&gt;
           brcc    _out_tausend&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; fuer diese Ziffer das Codemuster fuer&lt;br /&gt;
           ldi     ZH, high(2*Codes)    ; die Anzeige in der Codetabelle nachschlagen&lt;br /&gt;
           add     ZL, temp2&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment3, r0         ; und dieses Muster im SRAM ablegen&lt;br /&gt;
                                        ; die OvI Routine sorgt dann duer die Anzeige&lt;br /&gt;
           ldi     temp2, 10&lt;br /&gt;
&lt;br /&gt;
_out_hundert:                           ; die Hunderterstelle bestimmen&lt;br /&gt;
           dec     temp2                &lt;br /&gt;
           subi    temp, low(-100)      ; +100&lt;br /&gt;
           sbci    temp1, high(-100)&lt;br /&gt;
           brcs    _out_hundert&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; wieder in der Codetabelle das entsprechende&lt;br /&gt;
           ldi     ZH, high(2*Codes)    ; Muster nachschlagen&lt;br /&gt;
           add     ZL, temp2&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment2, r0         ; und im SRAM hinterlassen&lt;br /&gt;
&lt;br /&gt;
           ldi     temp2, -1&lt;br /&gt;
_out_zehn:                              ; die Zehnerstelle bestimmen&lt;br /&gt;
           inc     temp2&lt;br /&gt;
           subi    temp, low(10)        ; -10&lt;br /&gt;
           sbci    temp1, high(10)&lt;br /&gt;
           brcc    _out_zehn&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; wie gehabt: Die Ziffer in der Codetabelle&lt;br /&gt;
           ldi     ZH, high(2*Codes)    ; aufsuchen&lt;br /&gt;
           add     ZL, temp2&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment1, r0         ; und entsprechend im SRAM ablegen&lt;br /&gt;
&lt;br /&gt;
_out_einer:                             ; bleiben noch die Einer&lt;br /&gt;
           subi    temp, low(-10)       ; +10&lt;br /&gt;
           sbci    temp1, high(-10)&lt;br /&gt;
&lt;br /&gt;
           ldi     ZL, low(2*Codes)     ; ... Codetabelle&lt;br /&gt;
           ldi     ZH, high(2*Codes)&lt;br /&gt;
           add     ZL, temp&lt;br /&gt;
&lt;br /&gt;
           lpm&lt;br /&gt;
           sts     Segment0, r0         ; und ans SRAm ausgeben&lt;br /&gt;
&lt;br /&gt;
           pop     temp1&lt;br /&gt;
           pop     temp&lt;br /&gt;
&lt;br /&gt;
           ret&lt;br /&gt;
;&lt;br /&gt;
;**************************************************************************&lt;br /&gt;
;&lt;br /&gt;
main:&lt;br /&gt;
           ldi     temp, HIGH(RAMEND)&lt;br /&gt;
           out     SPH, temp&lt;br /&gt;
           ldi     temp, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
           out     SPL, temp&lt;br /&gt;
;                                     die Segmenttreiber initialisieren&lt;br /&gt;
           ldi     temp, $FF&lt;br /&gt;
           out     DDRD, temp&lt;br /&gt;
;                                     die Treiber für die einzelnen Stellen&lt;br /&gt;
           ldi     temp, $0F&lt;br /&gt;
           out     DDRC, temp&lt;br /&gt;
;                                     initialisieren der Steuerung für die&lt;br /&gt;
;                                     Interrupt Routine&lt;br /&gt;
           ldi     temp, 0b11111110&lt;br /&gt;
           sts     NextDigit, temp&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           sts     NextSegment, temp&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, ( 1 &amp;lt;&amp;lt; CS01 ) | ( 1 &amp;lt;&amp;lt; CS00 )&lt;br /&gt;
           out     TCCR0, temp&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 1 &amp;lt;&amp;lt; TOIE0&lt;br /&gt;
           out     TIMSK, temp&lt;br /&gt;
&lt;br /&gt;
           sei&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           ldi     temp1, 0&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
           inc     temp&lt;br /&gt;
           brne    _loop&lt;br /&gt;
           inc     temp1&lt;br /&gt;
_loop:&lt;br /&gt;
           rcall    out_number&lt;br /&gt;
&lt;br /&gt;
           cpi     temp, low( 4000 )&lt;br /&gt;
           brne    wait&lt;br /&gt;
           cpi     temp1, high( 4000 )&lt;br /&gt;
           brne    wait&lt;br /&gt;
&lt;br /&gt;
           ldi     temp, 0&lt;br /&gt;
           ldi     temp1, 0&lt;br /&gt;
&lt;br /&gt;
wait:      ldi     r21, 1&lt;br /&gt;
wait0:     ldi     r22, 0&lt;br /&gt;
wait1:     ldi     r23, 0&lt;br /&gt;
wait2:     dec     r23&lt;br /&gt;
           brne    wait2&lt;br /&gt;
           dec     r22&lt;br /&gt;
           brne    wait1&lt;br /&gt;
           dec     r21&lt;br /&gt;
           brne    wait0&lt;br /&gt;
&lt;br /&gt;
           rjmp    loop&lt;br /&gt;
&lt;br /&gt;
Codes:&lt;br /&gt;
    .db  0b11000000, 0b11111001     ; 0: a, b, c, d, e, f&lt;br /&gt;
                                    ; 1: b, c&lt;br /&gt;
    .db  0b10100100, 0b10110000     ; 2: a, b, d, e, g&lt;br /&gt;
                                    ; 3: a, b, c, d, g&lt;br /&gt;
    .db  0b10011001, 0b10010010     ; 4: b, c, f, g&lt;br /&gt;
                                    ; 5: a, c, d, f, g&lt;br /&gt;
    .db  0b10000010, 0b11111000     ; 6: a, c, d, e, f, g&lt;br /&gt;
                                    ; 7: a, b, c&lt;br /&gt;
    .db  0b10000000, 0b10010000     ; 8: a, b, c, d, e, f, g&lt;br /&gt;
                                    ; 9: a, b, c, d, f, g &lt;br /&gt;
&lt;br /&gt;
           .DSEG&lt;br /&gt;
NextDigit:   .byte 1         ; Bitmuster für die Aktivierung des nächsten Segments&lt;br /&gt;
NextSegment: .byte 1         ; Nummer des nächsten aktiven Segments&lt;br /&gt;
Segment0:    .byte 1         ; Ausgabemuster für Segment 0&lt;br /&gt;
Segment1:    .byte 1         ; Ausgabemuster für Segment 1&lt;br /&gt;
Segment2:    .byte 1         ; Ausgabemuster für Segment 2&lt;br /&gt;
Segment3:    .byte 1         ; Ausgabemuster für Segment 3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
*einfache [[Uhr]] mit Attiny 2313 und 7-Segment-Anzeige&lt;br /&gt;
&lt;br /&gt;
== Forenbeiträge ==&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/154257 Geisterleuchten bei 7-Segment Anzeige]&lt;br /&gt;
&lt;br /&gt;
*[http://www.mikrocontroller.net/topic/262337#2736074 Re: Zehn 7Segmente im Multiplexbetrieb : Hilfe !] - Ausführliche Erklärung wie und warum man entweder die &#039;&#039;&#039;Stellen&#039;&#039;&#039; oder die &#039;&#039;&#039;Segmente&#039;&#039;&#039; multiplext.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=SRAM|&lt;br /&gt;
zurücklink=AVR-Tutorial: SRAM|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Servos|&lt;br /&gt;
vorlink=AVR-Tutorial: Servo}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|7-Segment-Anzeige]]&lt;br /&gt;
[[Category:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77274</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77274"/>
		<updated>2013-06-26T20:49:49Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Fast PWM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstante?==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf beim WinAVR mit AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File, wie geht das? =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77273</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77273"/>
		<updated>2013-06-26T20:48:46Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Fast PWM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstante?==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf beim WinAVR mit AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File, wie geht das? =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77272</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77272"/>
		<updated>2013-06-26T20:39:37Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Fast PWM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstante?==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf beim WinAVR mit AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File, wie geht das? =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77271</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=77271"/>
		<updated>2013-06-26T20:35:16Z</updated>

		<summary type="html">&lt;p&gt;Kbuchegg: /* Das kann aber nicht alles gewesen sein? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa() (utoa(), ltoa(), ultoa(), ftoa() )===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden. Der große Vorteil von sprintf liegt darin, dass man über sehr mächtige Formatiermöglichkeiten verfügt, mit der man die Ausgabe einfach steuern und an seine Bedürfnisse anpassen kann. Dinge die man mit einer Funktion wie itoa alle selbst &#039;händisch&#039; erledigen muss.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  %[Modifizierer][Feldbreite][.Präzision]Typ&lt;br /&gt;
&lt;br /&gt;
(Die in eckigen Klammern [ ] angegebenen Elemente können auch weggelassen werden, wenn man sie nicht benötigt. Im einfachsten Fall benötigt man also nur das % und den Buchstaben zur Typ-Kennung.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt&lt;br /&gt;
       aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden&lt;br /&gt;
&lt;br /&gt;
Der&#039;&#039;Modifizierer&#039;&#039; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Feldbreite&#039;&#039; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen, damit die Einerstellen der Tabelleneinträge auch sauber untereinander stehen, auch dann wenn die Zahlen sich in unterschiedlichen Werftebereichen bewegen).&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;Präzision&#039;&#039; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
;&amp;lt;tt&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/tt&amp;gt;: Ausgabe eines float (oder double). Die Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle (6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen = 1)&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/121526?goto=new#new Formatierte Zahlenausgabe in C] von  Peter Dannegger&lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen. Wird das Ergebnis der Division in einen double umgewandelt, um es an i zuweisen zu können, ist das Kind schon in den Brunnen gefallen: Die Nachkommastellen sind schon längst weg bzw. waren nie vorhanden.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5.0 / 8.0;&lt;br /&gt;
&lt;br /&gt;
i = (double)j / k;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in dieser Operation vorkommenden Operanden. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden. Wohl kann es aber sein, dass double und float dieselbe Anzahl an Bits benutzen. Damit reduziert sich eine double Operation effektiv auf eine float Operation. (Anmerkung: mit dem C99-Standard hat sich dieses geändert. Dort gibt es dann auch echte float Operationen)&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstante?==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstanten in hexadezimaler bzw. oktaler Schreibweise je nach Wert einen signed oder einen unsigned Datentyp haben können.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L, UL und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* UL wie unsigned long. Eigentlich eine Zusammensetzung aus U und L&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf beim WinAVR mit AVR-Studio=&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif|thumb|right|300px|Project → Configuration Options  → Libraries → Available Link Objects]]&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif|thumb|right|300px|Custom Options → Custom Compilation Options → Linker Options]]&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floatingpoint-Verarbeitung unterstützt. Die meisten Programme benötigen keine Floatingpoint-Unterstützung, so dass dadurch wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man allerdings eine printf-Variante für die Ausgabe von Floatingpoint-Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz dafür, dass die Floatingpoint-Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floatingpoint-Verarbeitung zu aktivieren, geht man im &#039;&#039;&#039;AVR Studio 4&#039;&#039;&#039; wie folgt vor: &lt;br /&gt;
* Menüpunkt: &#039;&#039;Project → Configuration Options&#039;&#039;&lt;br /&gt;
* Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &#039;&#039;Libraries&#039;&#039; ausgewählt.&lt;br /&gt;
* Unter &#039;&#039;Available Link Objects&#039;&#039; werden Bibliotheken angeboten. Für die Aktivierung der Floatingpoint-Unterstützung sind 2 interessant&amp;lt;ref&amp;gt;&amp;lt;tt&amp;gt;libc.a&amp;lt;/tt&amp;gt; sollte nicht zu den Bibliotheken hinzugefügt werden, da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&amp;lt;/ref&amp;gt;:&lt;br /&gt;
** &amp;lt;tt&amp;gt;libprintf_flt.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
** &amp;lt;tt&amp;gt;libm.a&amp;lt;/tt&amp;gt;&lt;br /&gt;
* Beide Bibliotheken werden durch Aktivieren und einen Druck auf &#039;&#039;Add Library → &#039;&#039; in die rechte Spalte übernommen.&lt;br /&gt;
* Danach wählt man in der Navigationsleiste den Eintrag &#039;&#039;Custom Options&#039;&#039;.&lt;br /&gt;
* Unter &#039;&#039;Custom Compilation Options&#039;&#039; wird &#039;&#039;Linker Options&#039;&#039; ausgewählt und in das Textfeld rechts/unten folgender Text eingetragen:&lt;br /&gt;
         -Wl,-u,vfprintf&lt;br /&gt;
* Ein Druck auf &#039;&#039;Add&#039;&#039; befördert die Zeile in das Listenfeld darüber, welches Optionen für den Linker enthält.&lt;br /&gt;
* Mit &#039;&#039;OK&#039;&#039; wird die Konfiguration abgeschlossen.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;AVR Studio 5&#039;&#039;&#039; trägt man die Optionen an anderen Stellen ein ([http://www.mikrocontroller.net/topic/221583#2219612 Beitrag von Hal Smith]): &lt;br /&gt;
*&#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Libraries&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Libraries&#039;&#039; &amp;lt;tt&amp;gt;(-WI, -I), libprintf_flt.a libm.a&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
* &#039;&#039;Project Properties → Toolchain → AVR/GNU C-Linker → Miscellaneous&#039;&#039;&amp;lt;br/&amp;gt;In &#039;&#039;Other Linker Flags&#039;&#039; &amp;lt;tt&amp;gt;-Wl,-u,vfprintf&amp;lt;/tt&amp;gt; eintragen.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
= Wie funktioniert String-Verarbeitung in C? =&lt;br /&gt;
: → Siehe: &#039;&#039;[[String-Verarbeitung in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= struct Strukturen =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Strukturen in C]]&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
= Funktionszeiger =&lt;br /&gt;
&lt;br /&gt;
: → Siehe: &#039;&#039;[[Funktionszeiger in C]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Header File, wie geht das? =&lt;br /&gt;
&lt;br /&gt;
Ein Header File ist im Grunde nichts anderes als eine Sammlung aller Informationen, die ein &#039;Aussenstehender&#039; benötigt, um die in einem C-File gesammlten Funktionen benutzen zu können.&lt;br /&gt;
&lt;br /&gt;
Trotzdem gibt es immer wieder Schwierigkeiten, wie sich die Sache mit Header Files und/oder #include verhält und wie man eine derartige Lösung aufbauen kann.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei ein System bestehend aus 3 Funktionen&lt;br /&gt;
* main()&lt;br /&gt;
* functionA()&lt;br /&gt;
* functionB()&lt;br /&gt;
und einigen globalen Variablen. Für dieses System soll eine Aufteilung erfolgen, so dass funtionA samt seinen zugehörigen globalen Variablen in einer eigenen C-Datei residiert (FileA.c), functionB in einem eigenen File residiert (FileB.c) und main() als Hautpfunktion seine eigens C-File (Main.c) darstellt und jeweils die entsprechenden Header Files existieren.&lt;br /&gt;
&lt;br /&gt;
Kochrezeptartig kann man in vielen Fällen einfach so vorgehen:&lt;br /&gt;
Man schreibt erst mal den C-Code der Funktion, die man implementieren möchte. Zb fängt man an mit FileA.c&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionIntA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt geht man das File, so wie es auch der Compiler macht, von oben nach unten durch schaut den Code durch. Damit functionB aufgerufen werden kann, ist ein Prototyp dafür notwendig. Der kommt aus einem Header File, welches noch nicht existiert, aber im Laufe des Prozesses entstehen und FileB.h heissen wird. FileB.h deshalb, weil die Funktion in FileB.c enthalten ist und man zur Vermeidung von Konfusion die Header Files immer gleich benennt wie die C-Files, nur eben mit einer anderen Dateiendung. FileB.h wird noch geschrieben werden, das hindert uns jetzt aber nicht daran, so zu tun als ob es dieses schon geben würde und ein entsprechender #include ergänzt. (Solange nicht compiliert wird ist das ja auch kein Problem. Irgendwo muss man ja schliesslich mal anfangen die Ergänzungen zu machen.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gut. Die Variable VarExtB wird ebenfalls über den include hereingezogen&lt;br /&gt;
werden. Damit ist FileA.c erst mal vollständig. Da fehlt jetzt erst mal nichts mehr. Alles was in FileA.c vorkommt sind entweder C-Schlüsselwörter, durch FileA.c selbst definiert oder kommt über den #include herein.&lt;br /&gt;
&lt;br /&gt;
Der nächste Schritt ist die Überlegung: Was von dem Zeugs in FileA.C soll von anderer Stelle (von anderen C-Files aus) benutzt und verwendet werden können.&lt;br /&gt;
Welche Dinge muss FileA von sich preis geben.&lt;br /&gt;
&lt;br /&gt;
Da sind zu erst mal die beiden Variablen. Was soll mit denen geschehen?&lt;br /&gt;
VarExtA soll von aussen zugreifbar sein, VarIntA nicht. Und dann&lt;br /&gt;
natürlich die Funktion, die aufrufbar sein soll.&lt;br /&gt;
&lt;br /&gt;
Also beginnt man ein Header File für FileA.c zu schreiben, in das all das&lt;br /&gt;
reinkommt, was FileA.c nach aussen sichtbar machen will.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtA;&lt;br /&gt;
&lt;br /&gt;
int functionA( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable kriegt ein extern davor (und wird damit zu einer Deklaration), bei der Funktion wird einfach die Implementierung weggenommen und die somit zu einer Deklaration veränderte Zeile mit einem ; abgeschlossen. Wenn man möchte kann man den so geschaffenen Prototypen auch mit einem extern einleiten. Notwendig ist es aber nicht.&lt;br /&gt;
&lt;br /&gt;
Erneuter Blick aufs Header File. Kommt da irgendwas vor, was nicht&lt;br /&gt;
Standard C Schlüsselwort ist? Nein. Nichts.&lt;br /&gt;
Also ist dieses Header File somit ebenfalls in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit wird in FileA.c noch einen Include auf das&lt;br /&gt;
eigene Header File hinzugefügt, denn dann kann der Compiler überprüfen ob die&lt;br /&gt;
Angaben im Header File mit der tatsächlichen Implementierung&lt;br /&gt;
übereinstimmen. Und da VarIntA von aussen nicht sichtbar sein soll (auch&lt;br /&gt;
nicht durch Tricks), wird sie static gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileA.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtA;&lt;br /&gt;
static int VarIntA;&lt;br /&gt;
&lt;br /&gt;
#define PortpinX PortA.1&lt;br /&gt;
&lt;br /&gt;
int functionA( void )&lt;br /&gt;
{&lt;br /&gt;
  VarIntA = VarExtB;&lt;br /&gt;
  functionB();&lt;br /&gt;
  PortpinX = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dasselbe für FileB.c. Erst mal einfach runterschreiben&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit functionA aufgerufen werden kann, braucht es wieder einen Prototypen. Den&lt;br /&gt;
kriegt man über von FileA.h, welches daher includiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was noch? VarExtA. Diese Variable wird aber ebenfalls durch den #include als extern Deklaration ins FileB.c hereingezogen und ist somit bei der Übersetzung von FileB.c bekannt. Kommt sonst noch etwas vor? MeinePrivateFunktion. Diese Funktion soll nur in FileB.c benutzt werden und ist für jemanden ausserhalb FileB.c völlig uninteressant. Nichts destotrotz gilt die Regel: compiliert wird von oben nach unten und verwendet werden kann nur etwas, was auch bekannt ist. D.h. bevor der Aufruf der Funktion in functionB gemacht werden kann, muss es einen Protoypen der Funktion geben. Da diese Funktion aber nicht nach &#039;aussen&#039; exportiert wird, macht man den Protoypen gleich in das C-File mit hinein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Würde man die Funktion vorziehen, so dass der Funktionskörper vor der ersten Verwendung steht, dann würde man auch keinen Protoypen benötigen. Eine Funktionsdefinition fungiert als ihr eigener Protoyp.&lt;br /&gt;
&lt;br /&gt;
Kommt sonst noch etwas vor? Nix mehr. In FileB.c wird sonst nix mehr verwendet was nicht entweder C Schlüsselwort oder im File selber oder durch einen #include reinkommt.&lt;br /&gt;
&lt;br /&gt;
Dann wieder: das Header File für B schreiben. Dabei einfach nur überlegen: was soll von FileB.c nach aussen getragen werden?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.h&lt;br /&gt;
&lt;br /&gt;
extern int VarExtB;&lt;br /&gt;
&lt;br /&gt;
int functionB( void );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und zur Sicherheit wieder ins eigene C-File includen und alle Variablen&lt;br /&gt;
(oder auch Funktionen), die von aussen nicht sichtbar sein sollen, als&lt;br /&gt;
static markieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// FileB.c&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int VarExtB;&lt;br /&gt;
static int VarIntB;&lt;br /&gt;
&lt;br /&gt;
#define SettingB 0x01&lt;br /&gt;
&lt;br /&gt;
static void MeinePrivateFunktion();&lt;br /&gt;
&lt;br /&gt;
int functionB( void )&lt;br /&gt;
{&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  functionA();&lt;br /&gt;
  VarIntB = SettingB;&lt;br /&gt;
  MeinePrivateFunktion();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MeinePrivateFunktion()&lt;br /&gt;
{&lt;br /&gt;
  VarIntB = 8;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
damit bleibt nur noch main.c. Auch dieses wird erst mal einfach runtergeschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionA aufgerufen werden kann, braucht es einen Prototypen. Woher kommt er? Aus FileA.h. Also gleich mal includen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit functionB aufgerufen werden kann, braucht es einen Prototypen. Wo&lt;br /&gt;
kommt der her? Aus FileB.h. Also noch ein #include&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;FileA.h&amp;quot;&lt;br /&gt;
#include &amp;quot;FileB.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int c;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  functionA();&lt;br /&gt;
  functionB();&lt;br /&gt;
  VarExtA = 1;&lt;br /&gt;
  VarExtB = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt noch was? VarExtA ist durch den #include von FileA.h bereits&lt;br /&gt;
abgedeckt und VarExtB ist durch den #include von FileB.h bereits&lt;br /&gt;
abgedeckt. Also fehlt nix mehr. Auch in main.c sind damit alle Sachen&lt;br /&gt;
abgedeckt und es ist in sich vollständig.&lt;br /&gt;
&lt;br /&gt;
Das ist der Standardmechanismus:&lt;br /&gt;
* Implementierung der Funktionen im C File schreiben&lt;br /&gt;
* Überlegen, was von dieser Implementierung von aussen sichtbar sein soll.&lt;br /&gt;
* Dasjenige kommt als Deklaration ins Header File, alles andere am besten static machen.&lt;br /&gt;
* Für alles im C-File, das seinerseits woanders herkommt, gibt es einen #include, der das jeweils notwendige Header File einbindet.&lt;br /&gt;
* Werden im Header File Dinge von woanders benutzt, dann enthält das Header File einen entsprechenden #include&lt;br /&gt;
* Jedes File, sowohl Header-File als auch C-File ist in sich vollständig. Werden dort Dinge benutzt, dann müssen diese vor der Verwendung deklariert worden sein. Wie und wo diese Deklaration herkommt, ist dabei zweitrangig. Es kann sein, dass die Deklaration vor der Verwendung steht, es kann aber auch sein, dass die Deklaration über einen weiteren Include mit aufgenommen wird.&lt;br /&gt;
&lt;br /&gt;
= Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit? =&lt;br /&gt;
&lt;br /&gt;
[[Bild:c-flow.svg|right|thumb|260px|C-Programmierung: Workflow]]&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen, wie C-Compiler und Linker zusammenarbeiten. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Quelldateien bestehen, die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
;Compilieren: zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht aus jeder c-Datei eine sogenannte Object-Datei, in der bereits der Maschinencode für die im c-File programmierten Funktionen enthalten ist&lt;br /&gt;
;Linken: die einzelnen Object-Dateien werden mit zusätzlichen Bibliotheken und dem Startup-Code zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
Angenommen, das komplette Projekt besteht aus 2 Dateien:&lt;br /&gt;
&lt;br /&gt;
;main.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice (5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;func.c:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int twice (int number)&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden &amp;lt;tt&amp;gt;main.c&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.c&amp;lt;/tt&amp;gt; &#039;&#039;unabhängig&#039;&#039; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;tt&amp;gt;main.o&amp;lt;/tt&amp;gt; und &amp;lt;tt&amp;gt;func.o&amp;lt;/tt&amp;gt;, die den besagten Object-Code enthalten.&lt;br /&gt;
Diese beiden Zwischenergebnisse werden dann zusammen mit Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien in das Projekt mit aufzunehmen. Dadurch wird das entsprechende C-File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
;Achtung: Da jede der C-Dateien unabhängig von allen anderen compiliert wird, bedeutet das auch, dass jede der C-Dateien in sich vollständig sein muss!&lt;br /&gt;
&lt;br /&gt;
Wie eine C-Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
== Makefile ==&lt;br /&gt;
&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
&lt;br /&gt;
== AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Hier ist es besonders einfach, eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt, und anschliessend zeigt man AVR-Studio das zusätzliche C-File. AVR-Studio berücksicht dann diese Datei bei der Projekterzeugung, compiliert es und sorgt dafür, dass es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufiger Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &#039;&#039;Definition&#039;&#039; und &#039;&#039;Deklaration&#039;&#039; unterscheiden:&lt;br /&gt;
;Definition: Mit einer Definition wird der Compiler angewiesen, eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, dass im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
;Deklaration: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren, sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch, daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen, folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &#039;&#039;One Definition Rule&#039;&#039; (ODR). Sie besagt, dass in einem vollständigen Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern, eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;tt&amp;gt;extern&amp;lt;/tt&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist eine Definition!&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der *.c-Datei gemacht, die als Hauptdatei des Moduls fungiert, zu der diese Variable konzeptionell gehört. Im Zweifel ist das die *.c Datei, in der main() enthalten ist. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Was hat es mit volatile auf sich =&lt;br /&gt;
Immer wieder hört man im Forum die pauschale Aussage &amp;quot;Variablen die in einer ISR verwendet werden, müssen volatile sein&amp;quot;. Nun, das ist so nicht ganz richtig.&lt;br /&gt;
Welches Problem löst denn eigentlich volatile? Was ist denn das eigentliche Problem, das einer Lösung bedarf?&lt;br /&gt;
&lt;br /&gt;
Das Problem findet sich diesmal im Optimierer eines C Compilers. C Compiler übersetzen, wenn sie optimieren dürfen, den C Code nicht direkt so, wie ihn der Programmierer geschrieben hat, sondern sie versuchen Ressourcen einzusparen. Das kann sowohl Programmspeicher als auch Laufzeit, häufig auch beides gemeinsam sein. Zu diesem Zweck untersuchen sie das Programm und versuchen in der funktional gleichwertigen, in Maschinensprache übersetzen Version, Anweisungen einzusparen. Das dürfen sie auch. Der C-Standard erlaubt Optimierungen, solange die &#039;As-If&#039;-Regel eingehalten wird. Das bedeutet: Der Compiler darf das Programm umstellen und verändern, solange die Programmergebnisse dieselben bleiben. Eben &amp;quot;As-if&amp;quot; die Optimierung nie stattgefunden hätte.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir ein Beispiel&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
&lt;br /&gt;
  if( i == 5 )&lt;br /&gt;
    j = 8;&lt;br /&gt;
  else&lt;br /&gt;
    j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In diesem Programmausschnitt darf der Compiler seine Kenntnisse ausnutzen. Er weiß an dieser Stelle, dass i den Wert 2 hat. Damit ist aber auch klar, dass die Bedingung niemals wahr sein kann, denn 2 kann niemals gleich 5 sein. Wenn die Bedingung aber niemals wahr sein kann, dann kann auch die Zuweisung von 8 an j niemals ausgeführt werden. Der Compiler kann also diesen Programmtext zu diesem hier kürzen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = 2;&lt;br /&gt;
  j = 6;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ohne das sich an den Programmergebnissen etwas ändert. Die zweite Version ist aber kürzer und wird, wegen des Wegfalles des Vergleiches, auch schneller ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Eine andere Form der Optimierung betrifft die Verwaltung von µC-Ressourcen und da wieder ganz speziell die Register. Variablen werden ja erst mal im SRAM-Speicher des µC angelegt. Um mit den Werten von Variablen arbeiten zu können, müssen diese Wert aber vom SRAM-Speicher in µC-Register überführt werden (Register kann man sich wie Speicherstellen in der eigentlichen CPU vorstellen). Nur dort können diese Werte mittels Maschinenbefehlen manipuliert werden.&lt;br /&gt;
Jetzt haben aber µC nicht beliebig viele Register. Das bedeutet aber auch, der Compiler muss darüber Buch führen, welche Werte (welche Variablen) gerade in welchen Registern liegen und wenn alle Register belegt sind, muss ein anderes Register freigeräumt werden, in dem der Wert aus dem Register wieder ins SRAM zurück übertragen wird.&lt;br /&gt;
Allerdings kostet das auch Zeit. Der Compiler wird daher versuchen, Variablen, die in einem Programmstück oft benötigt werden, für längere Zeit in den Registern zu halten, um das Registerladen bzw. -zurückschreiben einzusparen. Die Grundannahme lautet dabei immer: In Anweisungen, in denen eine Variable nicht vorkommt, kann diese Variable auch nicht verändert werden. Im Programmstück&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
gibt es für i keine Möglichkeit, verändert zu werden. Der Compiler kann daher entscheiden, dass er diese Variable, *an dieser Stelle*, gar nicht aus dem SRAM laden muss, sondern sich den entsprechenden Wert in einem Register vorhält und dieses Register ausschließlich dafür reserviert. (Er könnte auch entscheiden, dass der Code nie ausgeführt werden kann, aber das ist eine andere Geschichte)&lt;br /&gt;
&lt;br /&gt;
Der springende Punkt ist nun, dass der Compiler hier eine zu kleine Sicht der Dinge hat. Betrachtet man nur dieses Code Stück, dann gibt es tatsächlich für i keine Möglichkeit, seinen Wert zu verändern. Aber die Dinge ändern sich, wenn Interrupts ins Spiel kommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Jetzt gibt es plötzlich eine Möglichkeit, wie i seinen Wert ändern kann: Wenn der entsprechende Interrupt ausgelöst wird, dann wird i auf den Wert 5 gesetzt. i, das ist aber nichts anderes als ein bestimmter Speicherbereich im SRAM. D.h. im SRAM wird die Variable tatsächlich korrekt auf den Wert 5 gesetzt. Nur: Als der Compiler die while-Schleife übersetzt hat, wusste er nichts davon, dass diese Möglichkeit existiert. Er hat entschieden, dass er an dieser Stelle den Wert der Variablen in einem CPU-Register halten wird, um Zugriffe einzusparen. Nur wird diese Kopie des Wertes im Register natürlich nicht verändert, wenn in der ISR das Original von i im SRAM verändert wird.&lt;br /&gt;
Fazit: Obwohl die ISR die Variable tatsächlich verändert, kriegt das der Code im while nicht mit, weil der Compiler es mit der Optimierung an dieser Stelle übertrieben hat. In der while-Schleife wird mit einer Kopie des Wertes von i in einem Register gearbeitet und nicht mit dem originalen Wert von i im SRAM.&lt;br /&gt;
&lt;br /&gt;
Und an dieser Stelle kommt jetzt volatile ins Spiel.&lt;br /&gt;
&lt;br /&gt;
volatile teilt dem Compiler mit, dass ausnahmslos alle Zugriffe auf eine Variable auch tatsächlich auszuführen sind und keine Optimierungen gemacht werden dürfen, weil eine Variable auf Wegen benutzt werden kann, die für den Compiler prinzipiell nicht einsichtig sind. Im obigen Beispiel könnte man argumentieren, dass der Compiler ja wohl die ISR bemerken könne und daher feststellen könnte, dass i tatsächlich verändert wird. Aber das stimmt so in der allgemeinen Form nicht. Niemand sagt, dass der Compiler die ISR überhaupt zu Gesicht bekommen muss, die könnte ja auch in einem ganz anderen C File stecken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint8_t i;&lt;br /&gt;
&lt;br /&gt;
ISR( irgendein_Interrupt )&lt;br /&gt;
{&lt;br /&gt;
  i = 5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  while( 1 ) {&lt;br /&gt;
    if( i == 5 )&lt;br /&gt;
      j = 8;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
wird i volatile gemacht, so verbietet man damit dem Compiler explizit, Annahmen über den Datenfluss von i zu treffen. Innerhalb der Schleife muss also tatsächlich jedes Mal wieder erneut i aus dem SRAM geholt werden und mit 5 verglichen werden. Abkürzungen durch Mehrfachverwendung von Registern oder sonstigen Optimierungstricks sind nicht erlaubt. Und damit ist das Problem gelöst. Wird i in der ISR verändert, so bekommt das auch die Abfrage mit, weil ja jetzt auf jeden Fall auf das Original im SRAM zurückgegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Das Gleiche gilt auch ebenso &amp;quot;in die andere Richtung&amp;quot;, wenn also i in der Schleife geändert und in der ISR nur gelesen wird. Auch hier könnte die Optimierung negativ zuschlagen und den Schreibzugriff nur auf eine lokale Kopie der Variable in einem Register durchführen (oder gar ganz wegfallen) lassen, weil der Lesezugriff außerhalb des direkten Programmflusses (in der ISR) für den Compiler nicht ersichtlich ist.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem (das ebenfalls mittels volatile gelöst wird) sind Variablen, die tatsächlich im Code überhaupt nie aktiv verändert werden, sondern es sich um Zusatzhardware handelt, die so verschaltet ist, dass sie im Programm in Form einer Variablen auftaucht, z.B. ein Uhren-IC (oder auch ganz banal: Portpins). In diesem Fall wird z.B. die Variable für Sekunden vom Programm gar nicht vom Programm selber verändert, ändert aber trotzdem ihren Inhalt. Die Zusatzhardware selbst macht das. Aus Programmsicht handelt es sich um Speicherzellen, die magisch selbsttätig ihren Wert ändern. Und damit dürfen selbstverständlich auch hier keinerlei Annahmen über den Inhalt der Variablen getroffen werden. Eine derart angebundene externe Hardware nennt man übrigens &amp;quot;memory-mapped&amp;quot;, weil sie ihre Werte ins Memory (=Hauptspeicher) mapped (=einblendet).&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
= Timer =&lt;br /&gt;
== Was macht ein Timer? ==&lt;br /&gt;
Oft hört man im Forum die Aussage: Timer sind so kompliziert!&lt;br /&gt;
&lt;br /&gt;
Aber eigentlich stimmt das nicht. Ganz im Gegenteil, Timer sind eigentlich eine sehr einfache Sache. Was genau macht eigentlich ein Timer? Die Antwort lautet: er zählt unabhängig vom restlichen Programmfluss vor sich hin. Und? Was macht er noch? Nichts. Das wars schon. Im Kern ist genau das auch schon alles was ein Timer macht.&lt;br /&gt;
[[Bild:Timer_Basis.gif|framed|center|ein 8-Bit Timer bei der Arbeit]]&lt;br /&gt;
&lt;br /&gt;
== Wie schnell macht er es? ==&lt;br /&gt;
Aber so einfach ist die Sache dann doch wieder nicht. Da erhebt sich zunächst mal die Frage: wie schnell zählt denn eigentlich so ein Timer? Normalerweise ist der Timer mit der Taktfrequenz des Prozessors gekoppelt, so dass zb bei einer Taktfrequnz von 1Mhz der Timer auch genau so schnell zählt. In 1 Sekunde zählt ein Timer also von 0 bis 1000000, also 1 Mio Zählschritte. Nun kann aber ein beispielsweise 8-Bit Timer nicht bis 1000000 zählen, dazu ist er nicht groß genug. Mit 8 Bit kann man bis 255 zählen. Zählt man da dann noch 1 dazu, dann läuft der Timer über und beginnt wieder bei 0. Man kann daher ruhigen Gewissens sagen: In 1 Sekunde zählt dieser Timer 3906 mal den Bereich von 0 bis 255 (und weiter auf 0) durch (das sind 256 Zählschritte) und zuätzlich schafft er es danach noch bis 64 zu zählen. Denn 3906 * 256 + 64 = 1000000 und wir haben wieder die 1 Mio Zählschritte, die der Timer in 1 Sekunde erledigt.&lt;br /&gt;
&lt;br /&gt;
Das ist ganz schön schnell. Und weil das oft zu schnell ist, hat jeder Timer noch die Möglichkeit sogenannte Vorteiler (Prescaler) vor den Zähltakt zu schalten. Zb einen Vorteiler von 8. Anstelle von 1Mhz bekommt der Timer dann eine Frequenz von 1Mhz / 8 (= 125kHz) präsentiert. Und dementsprechend würde er in 1 Sekunde dann nur noch von 0 bis 125000 (= 1.000.000 / 8) zählen. Als 8 Bit Timer bedeutet das, dass er in 1 Sekunde jetzt nur noch 488 komplette Zyklen 0 bis 255 schafft und dann noch bis 72 zählen kann. Denn 488 * 256 + 72 = 125000&lt;br /&gt;
&lt;br /&gt;
== Das kann aber nicht alles gewesen sein? ==&lt;br /&gt;
Bis jetzt ist das alles noch unspektakulär und man fragt sich: Was hab ich jetzt davon, wenn der Timer vor sich hinzählt? Nun, die Situation ändert sich, wenn man weiß, dass man bei bestimmten Ereignissen und/oder Zählerständen etwas auslösen lassen kann. So ist zb. dieser Überlauf von 255 auf 0 so ein Ereignis. Mittels eines Interrupts kann man auf dieses Ereignis reagieren lassen und als Folge davon wird eine Funktion vollautomatisch aufgerufen. Und zwar unabhängig davon, was der µC gerade sonst so tut. Und das ist schon recht cool, denn es bedeutet, dass man regelmäßig zu erfolgende Dinge in so eine ISR (Interrupt Service Routine, die Funktion die aufgerufen wird) stecken kann und der Timer sorgt ganz von alleine dafür, dass diese Funktionalität auch tatsächlich regelmäßig ausgeführt wird. Regelmäßig bedeutet in diesem Fall dann auch wirklich regelmäßig. Denn der Timer zählt ja losgelöst von den restlichen Arbeiten, die der µC sonst so erledigt, vor sich hin. Es spielt keine Rolle, ob der µC gerade mitten in einer komplizierten Berechnung steckt oder nicht. Der Timer zählt vor sich hin, und wenn das entsprechende Ereignis eintritt, wird der Interrupt ausgelöst. Und wenn der entsprechende Interrupt mit einer ISR-Funktion gekoppelt ist, dann wird der normale Programmfluss unterbrochen und der µC arbeitet genau diese eine Funktion ab. Und zwar in regelmässigen Zeitabständen, weil ja auch der Interrupt regelmässig auftritt.&lt;br /&gt;
[[Bild:Timer_ISR.gif|framed|center|ein 8-Bit Timer löst durch seinen Overflow regelmäßige ISR Aufrufe aus]]&lt;br /&gt;
Gut, beispielsweise 488 mal in der Sekunde mag für so manchen Zweck zu oft sein, aber es gibt ja auch noch andere Vorteiler (welche steht im Datenblatt) und dann kann man ja auch innerhalb der ISR in einer lokalen Variablen mitzählen und zb nur bei jedem 2.ten Aufruf eine Aktion machen, die dann nur noch 244 mal in der Sekunde ausgeführt wird. Hier gibt es also mehrere Möglichkeiten, wie man die Aufrufhäufigkeit weiter herunterteilen kann, so dass man sich der Zahl annähert, die man benötigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// der Timer wird mit 1Mhz getaktet. Vorteiler ist 8&lt;br /&gt;
// d.h. der Timer läuft mit 125kHz und würde daher in 1 Sekunde&lt;br /&gt;
// von 0 bis 124999 zaehlen.&lt;br /&gt;
// Aber nach jeweils 256 Zaehlungen erfolgt ein Overflow.&lt;br /&gt;
// Daher werden in 1 Sekunde 125000 / 256 = 488.28125 Overflows erzeugt&lt;br /&gt;
// Oder anders ausgedrückt:  1 / 488.2815 = 0.002048&lt;br /&gt;
// alle 0.002048 Sekunden erfolgt ein Overflow&lt;br /&gt;
//&lt;br /&gt;
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
&lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {    // nur bei jedem 200.ten Aufruf. Effektiv teilt dieses die&lt;br /&gt;
                             // die Aufruffrequenz des nachfolgenden Codes nochmal um&lt;br /&gt;
    swTeiler = 0;            // einen Faktor 200. Der nachfolgende Code wird daher nicht&lt;br /&gt;
                             // alle 0.002 Sekunden sondern alle 0.4096 Sekunden ausgeführt.&lt;br /&gt;
                             // Das reicht, dass man eine LED am Port schon blinken sieht.&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;    // alle Bits am Port umdrehen, einfach damit sich was tut&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
&lt;br /&gt;
  TIMSK |= (1&amp;lt;&amp;lt;TOIE0);  // den Overflow Interrupt des Timers freigeben&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, jetzt zählt der Timer bereits&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmäßig aufgerufen wird&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CTC Modus ==&lt;br /&gt;
Manchmal reicht das aber nicht. Benötigt man zb nicht 488 sondern möglichst genau 500 ISR Aufrufe in der Sekunde, so wird man weder mit Vorteiler noch durch Softwaremässiges Weiterteilen in der ISR zum Ziel kommen. Man kann natürlich die Taktfrequenz des kompletten Systems soweit umstellen, dass sich das alles ausgeht, aber oft ist das einfach nicht möglich. Was tun?&lt;br /&gt;
&lt;br /&gt;
Die Sache wäre einfacher, wenn man dem Timer vorschreiben könnte, nicht einfach nur von 0 bis 255 zu zählen, sondern wenn man ihm eine Obergrenze vorgeben könnte. Denn dann könnte man sich eine Obergrenze so bestimmen, dass dieser neue Zählbereich in 1 Sekunde ganz genau so oft durchlaufen werden kann, wie man es benötigt. Und hier kommt der sog. &amp;lt;b&amp;gt;CTC&amp;lt;/b&amp;gt; Modus ins Spiel. Denn genau darin besteht sein Wesen: Man gibt dem Timer eine Obergrenze vor. Erreicht seine Zählung diesen Wert, so wird der Timer auf 0 zurückgesetzt und beginnt wieder von vorne. Genau das was wir benötigen. Wollen wir exakt 500 ISR Ausfrufe in der Sekunde haben (bei 1 Mhz Systemtakt), dann wählen wir einen Vorteiler von 8 und setzen die Obergrenze auf 250-1 (nicht vergessen: wir brauchen 250 Zählschritte, das bedeutet der Timer muss von 0 bis 249 zählen, denn auch der Überlauf von 249 zurück auf 0 ist ein Zählschritt). Der Timer taktet dann mit 1Mhz / 8 = 125kHz und da nach jeweils 250 Zählschritten die Obergrenze erreicht ist, wird diese Obergrenze in 1 Sekunde 125000 / 250 = 500 mal erreicht. Genau so wie wir das wollten.&lt;br /&gt;
Wie wird dem Timer nun mitgeteilt, dass er eine spezielle Obergrenze benutzen soll? Nun, jeder Timer hat verschiedene Modi. Welche das bei einem konkreten µC und bei einem konkreten Timer genau sind, findet sich im Datenblatt im Abschnitt über die Timer. Normalerweise ist immer eines der letzteren Abschnitte eines jeden Kapitels im Datenblatt das interessantere: &amp;quot;Register Summary&amp;quot; oder &amp;quot;Register Description&amp;quot; genannt (die Datenblätter sind da nicht ganz einheitlich). So auch hier.&lt;br /&gt;
[[Bild:FAQ_Datenblatt_Timer_Mega16.png|framed|center|Auszug aus dem Atmel Datenblatt für den Mega16 - Suche im Datenblatt]]&lt;br /&gt;
In jedem Atmel Datenblatt findet sich bei jedem Timer immer auch besagter Abschnitt (im Inhaltsverzeichnis beim Kapitel über den jeweiligen Timer suchen. Im Datenblatt-PDF daher immer das Inhaltsverzeichnis anzeigen lassen!), und in diesem Abschnitt gibt es eine Tabelle, aus der hervorgeht, welche Modi es gibt, welche Bits dazu in den Konfigurationsregistern gesetzt werden müssen, wie sich dann die Obergrenze des Timer-Zählbereichs zusammensetzt und noch ein paar Angaben mehr. Beim Mega16 findet sich diese Tabelle für den Timer 0 zb auf Seite 83 und dort ist es die Tabelle 14.2. Diese Tabelle sieht im Datenblatt so aus&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}} &lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Mode || WGM01 || WGM00 || Timer/Counter || TOP || Update of || TOV0 Flag&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
!      || (CTC0) || (PWM0) || Mode of Operation || || OCR0 || Set-on&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 0 || Normal || 0xFF || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 1 || PWM, Phase Correct || 0xFF || TOP|| BOTTOM&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || 0 || CTC || OCR0 || Immediate || MAX&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || 1 || Fast PWM || 0xFF || BOTTOM || MAX&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dort beginnt man mit der Recherche und sucht sich die Bits für den gewünschten Modus raus. Mit diesen Bits sieht man dann in den Konfigurationsregistern nach, welches Bit zu welchem Register gehört und setzt es ganz einfach. In unserem Fall möchten wir den CTC Modus, also den Modus 2. Dazu muss das Bit WGM01 gesetzt werden und WGM00 muss auf 0 bleiben. Im Datenblatt ein wenig zurückscrollen bringt ans Licht, dass das Bit WGM01 im Konfigurationsregister TCCR0 angesiedelt ist. Weiters entnehmen wir der Tabelle, dass der Timer bis zum Wert in OCR0 zählen wird (die Spalte &amp;lt;b&amp;gt;TOP&amp;lt;/b&amp;gt;). Dort hinein müssen also die 250-1 als Obergrenze geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auch noch: Dieser Spezialmodus CTC löst keinen Overflow Interrupt aus, sondern einen sog. Compare Match Interrupt. Dies deshalb, weil die gewünschte Obergrenze ja laut Datenblatt in eines der sog. Compare Match Register geschrieben werden muss (OCR0). Das sind Spezialregister, die nach jedem Zählvorgang mit dem Zählregister verglichen werden. Stimmt ihr Inhalt mit dem des Zählregisters überein, so hat man einen Compare-Match und kann daran wieder eine Aktion (ISR) knüpfen. In diesem speziellen Fall des CTC Modus beinhaltet dieser Compare Match dann auch noch das automatische Rücksetzen des Timers auf 0.&lt;br /&gt;
&lt;br /&gt;
Und natürlich können auch mehrere Techniken kombiniert werden. Z. B. CTC Modus und zusätzliches weiteres softwaremässiges Herunterteilen in der ISR sieht dann so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_COMP_vect )    // Compare Match Interrupt Vector&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t swTeiler = 0;&lt;br /&gt;
 &lt;br /&gt;
  swTeiler++;&lt;br /&gt;
  if( swTeiler == 200 ) {&lt;br /&gt;
    swTeiler = 0;&lt;br /&gt;
    PORTD = PORTD ^ 0xFF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  DDRD = 0xFF;          // irgendein Port, damit wir auch was sehen&lt;br /&gt;
 &lt;br /&gt;
  TIMSK = (1&amp;lt;&amp;lt;OCIE0);               // den Output Compare Interrupt des Timers freigeben&lt;br /&gt;
  OCR0  = 250 - 1;                    // nach 250 Zaehlschritten -&amp;gt; Interrupt und Timer auf 0&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, CTC Modus&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  sei();                // und Interrupts generell freigeben&lt;br /&gt;
 &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
  }                     // der Timer selbst sorgt dafür, dass die ISR Funktion&lt;br /&gt;
}                       // regelmässig aufgerufen wird &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast PWM ==&lt;br /&gt;
&lt;br /&gt;
Aber der Timer kann noch mehr. Wenn der Timer so vor sich hinzählt, dann kann man bestimmte Output-Pins des µC an diesen Timer koppeln. Der Timer kann dann diesen Pin bei erreichen von bestimmten Zählerständen ganz von alleine wahlweise auf 0 schalten, auf 1 schalten oder umdrehen.&lt;br /&gt;
&lt;br /&gt;
Was passiert da genau? Im folgenden sei von der einfachsten Form der PWM auf einem Mega16 ausgegangen: Wenn der Timer in seiner Zählerei bei 0 ist, dann schaltet er den Pin auf 1, bei einem bestimmten Zählerstand soll er den Pin wieder auf 0 zurücksetzen und ansonsten soll der Timer wie gewohnt laufend von 0 bis 255 durchzählen.&lt;br /&gt;
Es ist die Rede vom Timer-Modus 3, siehe die vorhergehende Tabelle aus dem Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Der Tabelle entnehmen wir wieder: Die Bits WGM01 und WGM00 müssen auf 1 gestellt werden. Der TOP Wert (also der Wert, bis zu dem der Timer zählt) ist 0xFF (also 255) und das OCR0 Register steuert, bei welchem Zählerstand die Timerhardware den Pin wieder auf 0 zurück schaltet. Das Einschalten auf 1 ist vorgegeben (auch das kann man ändern, dazu später mehr).&lt;br /&gt;
&lt;br /&gt;
Stellt man also genau diese Konfiguration her und schreibt in das Register OCR0 beispielsweise den Wert 253, dann beginnt der Timer bei 0 zu zählen, wobei der den Ausgangspin auf 1 schaltet. Wird der Zählerstand 253 erreicht, dann schaltet der Timer den Pin wieder auf 0 zurück und zählt weiter bis 255 um dann wieder erneut bei 0 zu beginnen (und den Ausgansg-Pin wieder auf 1 zu schalten). Der Ausgangspin ist in diesem Fall also die meiste Zeit auf 1 und nur ganz kurz (während der Timer von 253 bis 255 zählt) auf 0.&lt;br /&gt;
&lt;br /&gt;
Schreibt am auf der anderen Seite in das Register OCR0 den Wert 3, dann passiert konzeptionell genau dasselbe nur mit anderen Zahlenwerten. Der Timer beginnt bei 0 zu zählen und schaltet den Ausgansgpin auf 1. Aber diesmal ist es bereits beim Zählerstand 3 so weit: Der Zählerstand stimmt mit dem Wert in OCR0 überein und als Folge davon wird der Ausgangspin wieder auf 0 gestellt. Der Timer zählt natürlich wie immer weiter bis 255 ehe dann das ganze Spiel wieder von vorne beginnt. In diesem Fall war also der Ausgangspin nur ganz kurze Zeit auf 1 (nämich in der Zeit, die der Timer benötigt um von 0 bis 3 zu zählen) und dann die meiste Zeit auf 0. Und genau darum geht es bei PWM: Mit dem Register OCR0 lässt sich daher der zeitliche Anteil steuern, in dem der Pin auf 1 liegt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
// Für einen Mega16.&lt;br /&gt;
// Andere Prozessoren: siehe Datenblatt wie die Timerkonfiguration einzustellen ist&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000UL&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB3);       // PB3 auf Ausgang stellen. Dieser Pin&lt;br /&gt;
                          // trägt auch die Bezeichnung OC0 und ist der Pin&lt;br /&gt;
                          // an dem der Timer 0 seine PWM ausgibt&lt;br /&gt;
&lt;br /&gt;
  OCR0  = 250;&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00) | (1&amp;lt;&amp;lt;CS01);    // Vorteiler 8, Fast PWM 8 Bit&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;COM01); &lt;br /&gt;
  &lt;br /&gt;
  while( 1 )&lt;br /&gt;
  {                     // hier braucht nichts mehr gemacht werden.&lt;br /&gt;
                        // der Timer selbst sorgt dafür, dass die PWM läuft&lt;br /&gt;
                        // wir wollen aber ein wenig Action haben.&lt;br /&gt;
                        // Also setzen wir das OCR0 Register auf verschiedene&lt;br /&gt;
                        // Werte, damit eine angeschlossene LED unterschiedliche&lt;br /&gt;
                        // Helligkeiten zeigt&lt;br /&gt;
    OCR0 = 30;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
    OCR0 = 200;&lt;br /&gt;
    _delay_ms( 1000 );&lt;br /&gt;
&lt;br /&gt;
    for( i = 0; i &amp;lt; 255; ++i ) {&lt;br /&gt;
      OCR0 = i;&lt;br /&gt;
      _delay_ms( 10 );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bleibt noch das gesetzte Bit COM01. Was hat es damit auf sich? Bisher war immer die Rede davon, das der Ausganspin bei einem Zählerstand von 0 auf 1 geschaltet wird usw. Das regelt genau dieses Bit. Im Datenblatt findet sich die Tabelle 14.4 auf der Seite 83, die genau regelt welche Bedeutung die Bits COM01 bzw COM00 haben, wenn der Timer Modus auf Fast-PWM eingestellt ist. Achtung: Je nach Timer-Modus haben diese Bits andere Bedeutungen! Man muss sich also immer die zum jeweiligen Timer-Modus gehörende Tabelle im Datenblatt suchen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:C]]&lt;br /&gt;
[[Kategorie:avr-gcc]]&lt;/div&gt;</summary>
		<author><name>Kbuchegg</name></author>
	</entry>
</feed>